class RuboCop::Cop::Style::EvalWithLocation
Ensures that eval methods (‘eval`, `instance_eval`, `class_eval` and `module_eval`) are given filename and line number values (`__FILE__
` and `__LINE__
`). This data is used to ensure that any errors raised within the evaluated code will be given the correct identification in a backtrace.
The cop also checks that the line number given relative to ‘__LINE__
` is correct.
This cop will autocorrect incorrect or missing filename and line number values. However, if ‘eval` is called without a binding argument, the cop will not attempt to automatically add a binding, or add filename and line values.
NOTE: This cop works only when a string literal is given as a code string. No offense is reported if a string variable is given as below:
- source,ruby
code = <<-RUBY
def do_something end
RUBY eval code # not checked.
@example
# bad eval <<-RUBY def do_something end RUBY # bad C.class_eval <<-RUBY def do_something end RUBY # good eval <<-RUBY, binding, __FILE__, __LINE__ + 1 def do_something end RUBY # good C.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def do_something end RUBY
Constants
- MSG
- MSG_EVAL
- MSG_INCORRECT_FILE
- MSG_INCORRECT_LINE
- RESTRICT_ON_SEND
Public Instance Methods
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 82 def on_send(node) # Classes should not redefine eval, but in case one does, it shouldn't # register an offense. Only `eval` without a receiver and `Kernel.eval` # are considered. return if node.method?(:eval) && !valid_eval_receiver?(node.receiver) code = node.first_argument return unless code&.type?(:str, :dstr) check_location(node, code) end
Private Instance Methods
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 187 def add_offense_for_different_line(node, line_node, line_diff) sign = line_diff.positive? ? :+ : :- return if line_with_offset?(line_node, sign, line_diff.abs) add_offense_for_incorrect_line(node.method_name, line_node, sign, line_diff.abs) end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 132 def add_offense_for_incorrect_line(method_name, line_node, sign, line_diff) expected = expected_line(sign, line_diff) message = format(MSG_INCORRECT_LINE, method_name: method_name, actual: line_node.source, expected: expected) add_offense(line_node, message: message) do |corrector| corrector.replace(line_node, expected) end end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 202 def add_offense_for_missing_line(node, code) register_offense(node) do |corrector| line_str = missing_line(node, code) corrector.insert_after(node.last_argument.source_range.end, ", #{line_str}") end end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 209 def add_offense_for_missing_location(node, code) if node.method?(:eval) && !with_binding?(node) register_offense(node) return end register_offense(node) do |corrector| line_str = missing_line(node, code) corrector.insert_after(node.last_argument.source_range.end, ", __FILE__, #{line_str}") end end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 181 def add_offense_for_same_line(node, line_node) return if special_line_keyword?(line_node) add_offense_for_incorrect_line(node.method_name, line_node, nil, 0) end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 144 def check_file(node, file_node) return if special_file_keyword?(file_node) message = format(MSG_INCORRECT_FILE, method_name: node.method_name, expected: '__FILE__', actual: file_node.source) add_offense(file_node, message: message) do |corrector| corrector.replace(file_node, '__FILE__') end end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 157 def check_line(node, code) line_node = node.last_argument return if line_node.variable? || (line_node.send_type? && !line_node.method?(:+)) line_diff = line_difference(line_node, code) if line_diff.zero? add_offense_for_same_line(node, line_node) else add_offense_for_different_line(node, line_node, line_diff) end end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 96 def check_location(node, code) file, line = file_and_line(node) if line check_file(node, file) check_line(node, code) elsif file check_file(node, file) add_offense_for_missing_line(node, code) else add_offense_for_missing_location(node, code) end end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 194 def expected_line(sign, line_diff) if line_diff.zero? '__LINE__' else "__LINE__ #{sign} #{line_diff.abs}" end end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 123 def file_and_line(node) base = node.method?(:eval) ? 2 : 1 [node.arguments[base], node.arguments[base + 1]] end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 169 def line_difference(line_node, code) string_first_line(code) - line_node.source_range.first_line end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 221 def missing_line(node, code) line_diff = line_difference(node.last_argument, code) sign = line_diff.positive? ? :+ : :- expected_line(sign, line_diff) end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 110 def register_offense(node, &block) msg = node.method?(:eval) ? MSG_EVAL : format(MSG, method_name: node.method_name) add_offense(node, message: msg, &block) end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 115 def special_file_keyword?(node) node.str_type? && node.source == '__FILE__' end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 119 def special_line_keyword?(node) node.int_type? && node.source == '__LINE__' end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 173 def string_first_line(str_node) if str_node.heredoc? str_node.loc.heredoc_body.first_line else str_node.source_range.first_line end end
Source
# File lib/rubocop/cop/style/eval_with_location.rb, line 128 def with_binding?(node) node.method?(:eval) ? node.arguments.size >= 2 : true end