class RuboCop::Cop::Lint::ShadowedArgument

Checks for shadowed arguments.

This cop has ‘IgnoreImplicitReferences` configuration option. It means argument shadowing is used in order to pass parameters to zero arity `super` when `IgnoreImplicitReferences` is `true`.

@example

# bad
do_something do |foo|
  foo = 42
  puts foo
end

def do_something(foo)
  foo = 42
  puts foo
end

# good
do_something do |foo|
  foo = foo + 42
  puts foo
end

def do_something(foo)
  foo = foo + 42
  puts foo
end

def do_something(foo)
  puts foo
end

@example IgnoreImplicitReferences: false (default)

# bad
def do_something(foo)
  foo = 42
  super
end

def do_something(foo)
  foo = super
  bar
end

@example IgnoreImplicitReferences: true

# good
def do_something(foo)
  foo = 42
  super
end

def do_something(foo)
  foo = super
  bar
end

Constants

MSG

Public Class Methods

joining_forces() click to toggle source
# File lib/rubocop/cop/lint/shadowed_argument.rb, line 72
def self.joining_forces
  VariableForce
end

Public Instance Methods

after_leaving_scope(scope, _variable_table) click to toggle source
# File lib/rubocop/cop/lint/shadowed_argument.rb, line 76
def after_leaving_scope(scope, _variable_table)
  scope.variables.each_value { |variable| check_argument(variable) }
end

Private Instance Methods

argument_references(argument) click to toggle source

Get argument references without assignments’ references

# File lib/rubocop/cop/lint/shadowed_argument.rb, line 160
def argument_references(argument)
  assignment_references = argument.assignments.flat_map(&:references).map(&:source_range)

  argument.references.reject do |ref|
    next false unless ref.explicit?

    assignment_references.include?(ref.node.source_range)
  end
end
assignment_without_argument_usage(argument) { |node, location_known| ... } click to toggle source

Find the first argument assignment, which doesn’t reference the argument at the rhs. If the assignment occurs inside a branch or block, it is impossible to tell whether it’s executed, so precise shadowing location is not known.

# File lib/rubocop/cop/lint/shadowed_argument.rb, line 120
def assignment_without_argument_usage(argument)
  argument.assignments.reduce(true) do |location_known, assignment|
    assignment_node = assignment.meta_assignment_node || assignment.node

    # Shorthand assignments always use their arguments
    next false if assignment_node.shorthand_asgn?

    node_within_block_or_conditional =
      node_within_block_or_conditional?(assignment_node.parent, argument.scope.node)

    unless uses_var?(assignment_node, argument.name)
      # It's impossible to decide whether a branch or block is executed,
      # so the precise reassignment location is undecidable.
      next false if node_within_block_or_conditional

      yield(assignment.node, location_known)
      break
    end

    location_known
  end
end
check_argument(argument) click to toggle source
# File lib/rubocop/cop/lint/shadowed_argument.rb, line 82
def check_argument(argument)
  return unless argument.method_argument? || argument.block_argument?
  # Block local variables, i.e., variables declared after ; inside
  # |...| aren't really arguments.
  return if argument.explicit_block_local_variable?

  shadowing_assignment(argument) do |node|
    message = format(MSG, argument: argument.name)

    add_offense(node, message: message)
  end
end
ignore_implicit_references?() click to toggle source
# File lib/rubocop/cop/lint/shadowed_argument.rb, line 170
def ignore_implicit_references?
  cop_config['IgnoreImplicitReferences']
end
node_within_block_or_conditional?(node, stop_search_node) click to toggle source

Check whether the given node is nested into block or conditional.

# File lib/rubocop/cop/lint/shadowed_argument.rb, line 151
def node_within_block_or_conditional?(node, stop_search_node)
  return false if node == stop_search_node

  node.conditional? || node.block_type? ||
    node_within_block_or_conditional?(node.parent, stop_search_node)
end
reference_pos(node) click to toggle source
# File lib/rubocop/cop/lint/shadowed_argument.rb, line 143
def reference_pos(node)
  node = node.parent if node.parent.masgn_type?

  node.source_range.begin_pos
end
shadowing_assignment(argument) { |location_known ? node : declaration_node| ... } click to toggle source
# File lib/rubocop/cop/lint/shadowed_argument.rb, line 95
def shadowing_assignment(argument)
  return unless argument.referenced?

  assignment_without_argument_usage(argument) do |node, location_known|
    assignment_without_usage_pos = node.source_range.begin_pos

    references = argument_references(argument)

    # If argument was referenced before it was reassigned
    # then it's not shadowed
    next if references.any? do |reference|
      next true if !reference.explicit? && ignore_implicit_references?

      reference_pos(reference.node) <= assignment_without_usage_pos
    end

    yield location_known ? node : argument.declaration_node
  end
end