class RuboCop::Cop::Style::EvalWithLocation

This cop checks `eval` method usage. `eval` can receive source location metadata, that are filename and line number. The metadata is used by backtraces. This cop recommends to pass the metadata to `eval` method.

@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_INCORRECT_LINE

Public Instance Methods

on_send(node) click to toggle source
# File lib/rubocop/cop/style/eval_with_location.rb, line 63
def on_send(node)
  eval_without_location?(node) do |code|
    if with_lineno?(node)
      on_with_lineno(node, code)
    else
      add_offense(node)
    end
  end
end

Private Instance Methods

add_offense_for_different_line(node, line_node, line_diff) click to toggle source
# File lib/rubocop/cop/style/eval_with_location.rb, line 135
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(
    node,
    location: line_node.loc.expression,
    message: message_incorrect_line(line_node, sign, line_diff.abs)
  )
end
add_offense_for_same_line(node, line_node) click to toggle source
# File lib/rubocop/cop/style/eval_with_location.rb, line 125
def add_offense_for_same_line(node, line_node)
  return if special_line_keyword?(line_node)

  add_offense(
    node,
    location: line_node.loc.expression,
    message: message_incorrect_line(line_node, nil, 0)
  )
end
message_incorrect_line(actual, sign, line_diff) click to toggle source

rubocop:enable Style/ConditionalAssignment

# File lib/rubocop/cop/style/eval_with_location.rb, line 96
def message_incorrect_line(actual, sign, line_diff)
  expected =
    if line_diff.zero?
      '__LINE__'
    else
      "__LINE__ #{sign} #{line_diff}"
    end
  format(MSG_INCORRECT_LINE, actual: actual.source, expected: expected)
end
on_with_lineno(node, code) click to toggle source
# File lib/rubocop/cop/style/eval_with_location.rb, line 106
def on_with_lineno(node, code)
  line_node = node.arguments.last
  lineno_range = line_node.loc.expression
  line_diff = string_first_line(code) - lineno_range.first_line
  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
special_file_keyword?(node) click to toggle source
# File lib/rubocop/cop/style/eval_with_location.rb, line 75
def special_file_keyword?(node)
  node.str_type? &&
    node.source == '__FILE__'
end
special_line_keyword?(node) click to toggle source
# File lib/rubocop/cop/style/eval_with_location.rb, line 80
def special_line_keyword?(node)
  node.int_type? &&
    node.source == '__LINE__'
end
string_first_line(str_node) click to toggle source
# File lib/rubocop/cop/style/eval_with_location.rb, line 117
def string_first_line(str_node)
  if str_node.heredoc?
    str_node.loc.heredoc_body.first_line
  else
    str_node.loc.expression.first_line
  end
end
with_lineno?(node) click to toggle source

FIXME: It's a Style/ConditionalAssignment's false positive. rubocop:disable Style/ConditionalAssignment

# File lib/rubocop/cop/style/eval_with_location.rb, line 87
def with_lineno?(node)
  if node.method_name == :eval
    node.arguments.size == 4
  else
    node.arguments.size == 3
  end
end