forked from rubocop/rubocop-ast
/
traversal.rb
204 lines (172 loc) · 6.01 KB
/
traversal.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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# frozen_string_literal: true
# rubocop:disable Metrics/ModuleLength
module RuboCop
module AST
# Provides methods for traversing an AST.
# Does not transform an AST; for that, use Parser::AST::Processor.
# Override methods to perform custom processing. Remember to call `super`
# if you want to recursively process descendant nodes.
module Traversal
def walk(node)
return if node.nil?
send(:"on_#{node.type}", node)
nil
end
NO_CHILD_NODES = %i[true false nil int float complex
rational str sym regopt self lvar
ivar cvar gvar nth_ref back_ref cbase
arg restarg blockarg shadowarg
kwrestarg zsuper redo retry
forward_args forwarded_args
match_var match_nil_pattern empty_else
forward_arg lambda procarg0 __ENCODING__].freeze
ONE_CHILD_NODE = %i[splat kwsplat block_pass not break next
preexe postexe match_current_line defined?
arg_expr pin match_rest if_guard unless_guard
match_with_trailing_comma].freeze
MANY_CHILD_NODES = %i[dstr dsym xstr regexp array hash pair
mlhs masgn or_asgn and_asgn rasgn mrasgn
undef alias args super yield or and
while_post until_post iflipflop eflipflop
match_with_lvasgn begin kwbegin return
in_match match_alt
match_as array_pattern array_pattern_with_tail
hash_pattern const_pattern find_pattern
index indexasgn].freeze
SECOND_CHILD_ONLY = %i[lvasgn ivasgn cvasgn gvasgn optarg kwarg
kwoptarg].freeze
NO_CHILD_NODES.each do |type|
module_eval("def on_#{type}(node); end", __FILE__, __LINE__)
end
ONE_CHILD_NODE.each do |type|
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
def on_#{type}(node)
if (child = node.children[0])
send(:"on_\#{child.type}", child)
end
end
RUBY
end
MANY_CHILD_NODES.each do |type|
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
def on_#{type}(node)
node.children.each { |child| send(:"on_\#{child.type}", child) }
nil
end
RUBY
end
SECOND_CHILD_ONLY.each do |type|
# Guard clause is for nodes nested within mlhs
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
def on_#{type}(node)
if (child = node.children[1])
send(:"on_\#{child.type}", child)
end
end
RUBY
end
def on_const(node)
return unless (child = node.children[0])
send(:"on_#{child.type}", child)
end
def on_casgn(node)
children = node.children
if (child = children[0]) # always const???
send(:"on_#{child.type}", child)
end
return unless (child = children[2])
send(:"on_#{child.type}", child)
end
def on_class(node)
children = node.children
child = children[0] # always const???
send(:"on_#{child.type}", child)
if (child = children[1])
send(:"on_#{child.type}", child)
end
return unless (child = children[2])
send(:"on_#{child.type}", child)
end
def on_def(node)
children = node.children
on_args(children[1])
return unless (child = children[2])
send(:"on_#{child.type}", child)
end
def on_send(node)
node.children.each_with_index do |child, i|
next if i == 1
send(:"on_#{child.type}", child) if child
end
nil
end
alias on_csend on_send
def on_op_asgn(node)
children = node.children
child = children[0]
send(:"on_#{child.type}", child)
child = children[2]
send(:"on_#{child.type}", child)
end
def on_defs(node)
children = node.children
child = children[0]
send(:"on_#{child.type}", child)
on_args(children[2])
return unless (child = children[3])
send(:"on_#{child.type}", child)
end
def on_if(node)
children = node.children
child = children[0]
send(:"on_#{child.type}", child)
if (child = children[1])
send(:"on_#{child.type}", child)
end
return unless (child = children[2])
send(:"on_#{child.type}", child)
end
def on_while(node)
children = node.children
child = children[0]
send(:"on_#{child.type}", child)
return unless (child = children[1])
send(:"on_#{child.type}", child)
end
alias on_until on_while
alias on_module on_while
alias on_sclass on_while
def on_block(node)
children = node.children
child = children[0]
send(:"on_#{child.type}", child) # can be send, zsuper...
on_args(children[1])
return unless (child = children[2])
send(:"on_#{child.type}", child)
end
def on_case(node)
node.children.each do |child|
send(:"on_#{child.type}", child) if child
end
nil
end
alias on_rescue on_case
alias on_resbody on_case
alias on_ensure on_case
alias on_for on_case
alias on_when on_case
alias on_case_match on_case
alias on_in_pattern on_case
alias on_irange on_case
alias on_erange on_case
def on_numblock(node)
children = node.children
child = children[0]
send(:"on_#{child.type}", child)
return unless (child = children[2])
send(:"on_#{child.type}", child)
end
end
end
end
# rubocop:enable Metrics/ModuleLength