class RuboCop::Cop::Naming::RescuedExceptionsVariableName

Makes sure that rescued exceptions variables are named as expected.

The ‘PreferredName` config option takes a `String`. It represents the required name of the variable. Its default is `e`.

NOTE: This cop does not consider nested rescues because it cannot guarantee that the variable from the outer rescue is not used within the inner rescue (in which case, changing the inner variable would shadow the outer variable).

@example PreferredName: e (default)

# bad
begin
  # do something
rescue MyException => exception
  # do something
end

# good
begin
  # do something
rescue MyException => e
  # do something
end

# good
begin
  # do something
rescue MyException => _e
  # do something
end

@example PreferredName: exception

# bad
begin
  # do something
rescue MyException => e
  # do something
end

# good
begin
  # do something
rescue MyException => exception
  # do something
end

# good
begin
  # do something
rescue MyException => _exception
  # do something
end

Constants

MSG

Public Instance Methods

on_resbody(node) click to toggle source
# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 66
def on_resbody(node)
  offending_name = variable_name(node)
  return unless offending_name

  # Handle nested rescues by only requiring the outer one to use the
  # configured variable name, so that nested rescues don't use the same
  # variable.
  return if node.each_ancestor(:resbody).any?

  preferred_name = preferred_name(offending_name)
  return if preferred_name.to_sym == offending_name

  # check variable shadowing for exception variable
  return if shadowed_variable_name?(node)

  range = offense_range(node)
  message = message(node)

  add_offense(range, message: message) do |corrector|
    autocorrect(corrector, node, range, offending_name, preferred_name)
  end
end

Private Instance Methods

autocorrect(corrector, node, range, offending_name, preferred_name) click to toggle source
# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 96
def autocorrect(corrector, node, range, offending_name, preferred_name)
  corrector.replace(range, preferred_name)
  correct_node(corrector, node.body, offending_name, preferred_name)
  return unless (kwbegin_node = node.parent.each_ancestor(:kwbegin).first)

  kwbegin_node.right_siblings.each do |child_node|
    correct_node(corrector, child_node, offending_name, preferred_name)
  end
end
correct_node(corrector, node, offending_name, preferred_name) click to toggle source

rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity

# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 117
def correct_node(corrector, node, offending_name, preferred_name)
  return unless node

  node.each_node(:lvar, :lvasgn, :masgn) do |child_node|
    next unless variable_name_matches?(child_node, offending_name)

    if child_node.lvar_type?
      parent_node = child_node.parent
      if parent_node.respond_to?(:value_omission?) && parent_node.value_omission?
        corrector.insert_after(parent_node.loc.operator, " #{preferred_name}")
      else
        corrector.replace(child_node, preferred_name)
      end
    end

    if child_node.masgn_type? || child_node.lvasgn_type?
      correct_reassignment(corrector, child_node, offending_name, preferred_name)
      break
    end
  end
end
correct_reassignment(corrector, node, offending_name, preferred_name) click to toggle source

If the exception variable is reassigned, that assignment needs to be corrected. Further ‘lvar` nodes will not be corrected though since they now refer to a different variable.

# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 143
def correct_reassignment(corrector, node, offending_name, preferred_name)
  if node.lvasgn_type?
    correct_node(corrector, node.child_nodes.first, offending_name, preferred_name)
  elsif node.masgn_type?
    # With multiple assign, the assignments are in an array as the last child
    correct_node(corrector, node.children.last, offending_name, preferred_name)
  end
end
message(node) click to toggle source
# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 168
def message(node)
  offending_name = variable_name(node)
  preferred_name = preferred_name(offending_name)
  format(MSG, preferred: preferred_name, bad: offending_name)
end
offense_range(resbody) click to toggle source
# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 91
def offense_range(resbody)
  variable = resbody.exception_variable
  variable.source_range
end
preferred_name(variable_name) click to toggle source
# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 152
def preferred_name(variable_name)
  preferred_name = cop_config.fetch('PreferredName', 'e')
  if variable_name.to_s.start_with?('_')
    "_#{preferred_name}"
  else
    preferred_name
  end
end
shadowed_variable_name?(node) click to toggle source
# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 174
def shadowed_variable_name?(node)
  node.each_descendant(:lvar).any? { |n| n.children.first.to_s == preferred_name(n) }
end
variable_name(node) click to toggle source
# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 161
def variable_name(node)
  asgn_node = node.exception_variable
  return unless asgn_node

  asgn_node.children.last
end
variable_name_matches?(node, name) click to toggle source
# File lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb, line 106
def variable_name_matches?(node, name)
  if node.masgn_type?
    node.each_descendant(:lvasgn).any? do |lvasgn_node|
      variable_name_matches?(lvasgn_node, name)
    end
  else
    node.children.first == name
  end
end