class RuboCop::Cop::Lint::ShadowedArgument
end
bar
foo = super
def do_something(foo)
end
super
foo = 42
def do_something(foo)
# good
@example IgnoreImplicitReferences: true
end
bar
foo = super
def do_something(foo)
end
super
foo = 42
def do_something(foo)
# bad
@example IgnoreImplicitReferences: false (default)
end
puts foo
def do_something(foo)
end
puts foo
foo = foo + 42
def do_something(foo)
end
puts foo
foo = foo + 42
do_something do |foo|
# good
end
puts foo
foo = 42
def do_something(foo)
end
puts foo
foo = 42
do_something do |foo|
# bad
@example
to zero arity ‘super` when `IgnoreImplicitReferences` is `true`.
It means argument shadowing is used in order to pass parameters
This cop has `IgnoreImplicitReferences` configuration option.
Checks for shadowed arguments.
def self.joining_forces
def self.joining_forces VariableForce end
def after_leaving_scope(scope, _variable_table)
def after_leaving_scope(scope, _variable_table) scope.variables.each_value { |variable| check_argument(variable) } end
def argument_references(argument)
Get argument references without assignments' references
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
def assignment_without_argument_usage(argument)
shadowing location is not known.
block, it is impossible to tell whether it's executed, so precise
argument at the rhs. If the assignment occurs inside a branch or
Find the first argument assignment, which doesn't reference the
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? next false unless assignment_node.parent 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
def check_argument(argument)
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
def ignore_implicit_references?
def ignore_implicit_references? cop_config['IgnoreImplicitReferences'] end
def node_within_block_or_conditional?(node, stop_search_node)
Check whether the given node is nested into block or conditional.
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
def reference_pos(node)
def reference_pos(node) node = node.parent if node.parent.masgn_type? node.source_range.begin_pos end
def shadowing_assignment(argument)
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