forked from rubocop/rubocop
/
shadowing_outer_local_variable.rb
85 lines (74 loc) · 2.53 KB
/
shadowing_outer_local_variable.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# frozen_string_literal: true
module RuboCop
module Cop
module Lint
# Checks for the use of local variable names from an outer scope
# in block arguments or block-local variables. This mirrors the warning
# given by `ruby -cw` prior to Ruby 2.6:
# "shadowing outer local variable - foo".
#
# NOTE: Shadowing of variables in block passed to `Ractor.new` is allowed
# because `Ractor` should not access outer variables.
# eg. following style is encouraged:
#
# worker_id, pipe = env
# Ractor.new(worker_id, pipe) do |worker_id, pipe|
# end
#
# @example
#
# # bad
#
# def some_method
# foo = 1
#
# 2.times do |foo| # shadowing outer `foo`
# do_something(foo)
# end
# end
#
# @example
#
# # good
#
# def some_method
# foo = 1
#
# 2.times do |bar|
# do_something(bar)
# end
# end
class ShadowingOuterLocalVariable < Base
MSG = 'Shadowing outer local variable - `%<variable>s`.'
# @!method ractor_block?(node)
def_node_matcher :ractor_block?, <<~PATTERN
(block (send (const nil? :Ractor) :new ...) ...)
PATTERN
def self.joining_forces
VariableForce
end
def before_declaring_variable(variable, variable_table)
return if variable.should_be_unused?
return if ractor_block?(variable.scope.node)
outer_local_variable = variable_table.find_variable(variable.name)
return unless outer_local_variable
return if same_conditions_node_different_branch?(variable, outer_local_variable)
message = format(MSG, variable: variable.name)
add_offense(variable.declaration_node, message: message)
end
def same_conditions_node_different_branch?(variable, outer_local_variable)
variable_node = variable.scope.node.parent
return false unless variable_node.conditional?
outer_local_variable_node =
find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
outer_local_variable_node.conditional? && variable_node == outer_local_variable_node
end
def find_conditional_node_from_ascendant(node)
return unless (parent = node.parent)
return parent if parent.conditional?
find_conditional_node_from_ascendant(parent)
end
end
end
end
end