rubocop-ast 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -3
- data/lib/rubocop/ast/node.rb +23 -6
- data/lib/rubocop/ast/node/array_node.rb +2 -8
- data/lib/rubocop/ast/node/case_match_node.rb +1 -5
- data/lib/rubocop/ast/node/case_node.rb +1 -5
- data/lib/rubocop/ast/node/def_node.rb +4 -23
- data/lib/rubocop/ast/node/hash_node.rb +17 -0
- data/lib/rubocop/ast/node/if_node.rb +4 -9
- data/lib/rubocop/ast/node/when_node.rb +1 -5
- data/lib/rubocop/ast/node_pattern.rb +96 -84
- data/lib/rubocop/ast/processed_source.rb +16 -2
- data/lib/rubocop/ast/traversal.rb +1 -1
- data/lib/rubocop/ast/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d213ca265b42f0b66394057ee01a4a4573a28da98e7fc8caf52b5396fe098381
|
4
|
+
data.tar.gz: 3a351df6a7f11cfdad47b7844eb0c0d9860e11e7fdd65f5e1405b1f0978081be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1771b173896909ca2e9b68acf312c7b73e2e7308f834fc6d79871bd84b5012622e8727ab3e0cf98ca985cab32a70e65c83b174ff1bf42a59bba942720625b198
|
7
|
+
data.tar.gz: 57539ef58f3fc0722220759e4875524b43f9cd89a919d47429327f2ab3eaa2cfb0b35d9e275b05ac683f4f860bf5ac1986619fc16aa71c5a6b80ff2797683fa0
|
data/README.md
CHANGED
@@ -6,11 +6,12 @@
|
|
6
6
|
[](https://codeclimate.com/github/rubocop-hq/rubocop-ast/maintainability)
|
7
7
|
|
8
8
|
Contains the classes needed by [RuboCop](https://github.com/rubocop-hq/rubocop) to deal with Ruby's AST, in particular:
|
9
|
-
|
9
|
+
|
10
|
+
* `RuboCop::AST::Node` ([doc](docs/modules/ROOT/pages/node_types.adoc))
|
10
11
|
* `RuboCop::AST::NodePattern` ([doc](docs/modules/ROOT/pages/node_pattern.adoc))
|
11
12
|
|
12
13
|
This gem may be used independently from the main RuboCop gem. It was extracted from RuboCop in version 0.84 and its only
|
13
|
-
dependency is the
|
14
|
+
dependency is the [parser](https://github.com/whitequark/parser) gem, which `rubocop-ast` extends.
|
14
15
|
|
15
16
|
## Installation
|
16
17
|
|
@@ -28,7 +29,9 @@ gem 'rubocop-ast'
|
|
28
29
|
|
29
30
|
## Usage
|
30
31
|
|
31
|
-
Refer to the documentation of `RuboCop::AST::Node` and [`RuboCop::AST::NodePattern`](docs/modules/ROOT/pages/node_pattern.adoc)
|
32
|
+
Refer to the documentation of [`RuboCop::AST::Node`](docs/modules/ROOT/pages/node_types.adoc) and [`RuboCop::AST::NodePattern`](docs/modules/ROOT/pages/node_pattern.adoc)
|
33
|
+
|
34
|
+
See the [docs site](https://docs.rubocop.org/rubocop-ast) for more details.
|
32
35
|
|
33
36
|
### Parser compatibility switches
|
34
37
|
|
data/lib/rubocop/ast/node.rb
CHANGED
@@ -313,8 +313,8 @@ module RuboCop
|
|
313
313
|
def_node_matcher :defined_module0, <<~PATTERN
|
314
314
|
{(class (const $_ $_) ...)
|
315
315
|
(module (const $_ $_) ...)
|
316
|
-
(casgn $_ $_ (send (
|
317
|
-
(casgn $_ $_ (block (send (
|
316
|
+
(casgn $_ $_ (send #global_const?({:Class :Module}) :new ...))
|
317
|
+
(casgn $_ $_ (block (send #global_const?({:Class :Module}) :new ...) ...))}
|
318
318
|
PATTERN
|
319
319
|
|
320
320
|
private :defined_module0
|
@@ -496,16 +496,33 @@ module RuboCop
|
|
496
496
|
|
497
497
|
def_node_matcher :proc?, <<~PATTERN
|
498
498
|
{(block (send nil? :proc) ...)
|
499
|
-
(block (send (
|
500
|
-
(send (
|
499
|
+
(block (send #global_const?(:Proc) :new) ...)
|
500
|
+
(send #global_const?(:Proc) :new)}
|
501
501
|
PATTERN
|
502
502
|
|
503
503
|
def_node_matcher :lambda?, '({block numblock} (send nil? :lambda) ...)'
|
504
504
|
def_node_matcher :lambda_or_proc?, '{lambda? proc?}'
|
505
505
|
|
506
|
+
def_node_matcher :global_const?, '(const {nil? cbase} %1)'
|
507
|
+
|
506
508
|
def_node_matcher :class_constructor?, <<~PATTERN
|
507
|
-
{ (send (
|
508
|
-
(block (send (
|
509
|
+
{ (send #global_const?({:Class :Module}) :new ...)
|
510
|
+
(block (send #global_const?({:Class :Module}) :new ...) ...)}
|
511
|
+
PATTERN
|
512
|
+
|
513
|
+
def_node_matcher :struct_constructor?, <<~PATTERN
|
514
|
+
(block (send #global_const?(:Struct) :new ...) _ $_)
|
515
|
+
PATTERN
|
516
|
+
|
517
|
+
def_node_matcher :class_definition?, <<~PATTERN
|
518
|
+
{(class _ _ $_)
|
519
|
+
(sclass _ $_)
|
520
|
+
(block (send #global_const?({:Struct :Class}) :new ...) _ $_)}
|
521
|
+
PATTERN
|
522
|
+
|
523
|
+
def_node_matcher :module_definition?, <<~PATTERN
|
524
|
+
{(module _ $_)
|
525
|
+
(block (send #global_const?(:Module) :new ...) _ $_)}
|
509
526
|
PATTERN
|
510
527
|
|
511
528
|
# Some expressions are evaluated for their value, some for their side
|
@@ -14,15 +14,9 @@ module RuboCop
|
|
14
14
|
# Returns an array of all value nodes in the `array` literal.
|
15
15
|
#
|
16
16
|
# @return [Array<Node>] an array of value nodes
|
17
|
-
|
18
|
-
each_child_node.to_a
|
19
|
-
end
|
17
|
+
alias values children
|
20
18
|
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# @yieldparam [Node] node each node
|
24
|
-
# @return [self] if a block is given
|
25
|
-
# @return [Enumerator] if no block is given
|
19
|
+
# @deprecated Use `values.each` (a.k.a. `children.each`)
|
26
20
|
def each_value(&block)
|
27
21
|
return to_enum(__method__) unless block_given?
|
28
22
|
|
@@ -15,11 +15,7 @@ module RuboCop
|
|
15
15
|
'case'
|
16
16
|
end
|
17
17
|
|
18
|
-
#
|
19
|
-
# If no block is given, an `Enumerator` is returned.
|
20
|
-
#
|
21
|
-
# @return [self] if a block is given
|
22
|
-
# @return [Enumerator] if no block is given
|
18
|
+
# @deprecated Use `in_pattern_branches.each`
|
23
19
|
def each_in_pattern
|
24
20
|
return in_pattern_branches.to_enum(__method__) unless block_given?
|
25
21
|
|
@@ -15,11 +15,7 @@ module RuboCop
|
|
15
15
|
'case'
|
16
16
|
end
|
17
17
|
|
18
|
-
#
|
19
|
-
# If no block is given, an `Enumerator` is returned.
|
20
|
-
#
|
21
|
-
# @return [self] if a block is given
|
22
|
-
# @return [Enumerator] if no block is given
|
18
|
+
# @deprecated Use `when_branches.each`
|
23
19
|
def each_when
|
24
20
|
return when_branches.to_enum(__method__) unless block_given?
|
25
21
|
|
@@ -31,14 +31,14 @@ module RuboCop
|
|
31
31
|
#
|
32
32
|
# @return [Symbol] the name of the defined method
|
33
33
|
def method_name
|
34
|
-
|
34
|
+
children[-3]
|
35
35
|
end
|
36
36
|
|
37
37
|
# An array containing the arguments of the method definition.
|
38
38
|
#
|
39
39
|
# @return [Array<Node>] the arguments of the method definition
|
40
40
|
def arguments
|
41
|
-
|
41
|
+
children[-2]
|
42
42
|
end
|
43
43
|
|
44
44
|
# The body of the method definition.
|
@@ -49,33 +49,14 @@ module RuboCop
|
|
49
49
|
#
|
50
50
|
# @return [Node] the body of the method definition
|
51
51
|
def body
|
52
|
-
|
52
|
+
children[-1]
|
53
53
|
end
|
54
54
|
|
55
55
|
# The receiver of the method definition, if any.
|
56
56
|
#
|
57
57
|
# @return [Node, nil] the receiver of the method definition, or `nil`.
|
58
58
|
def receiver
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
# Custom destructuring method. This can be used to normalize
|
63
|
-
# destructuring for different variations of the node.
|
64
|
-
#
|
65
|
-
# In this case, the `def` node destructures into:
|
66
|
-
#
|
67
|
-
# `method_name, arguments, body`
|
68
|
-
#
|
69
|
-
# while the `defs` node destructures into:
|
70
|
-
#
|
71
|
-
# `receiver, method_name, arguments, body`
|
72
|
-
#
|
73
|
-
# so we reverse the destructured array to get the optional receiver
|
74
|
-
# at the end, where it can be discarded.
|
75
|
-
#
|
76
|
-
# @return [Array] the different parts of the `def` or `defs` node
|
77
|
-
def node_parts
|
78
|
-
to_a.reverse
|
59
|
+
children[-4]
|
79
60
|
end
|
80
61
|
end
|
81
62
|
end
|
@@ -8,6 +8,9 @@ module RuboCop
|
|
8
8
|
class HashNode < Node
|
9
9
|
# Returns an array of all the key value pairs in the `hash` literal.
|
10
10
|
#
|
11
|
+
# @note this may be different from children as `kwsplat` nodes are
|
12
|
+
# ignored.
|
13
|
+
#
|
11
14
|
# @return [Array<PairNode>] an array of `pair` nodes
|
12
15
|
def pairs
|
13
16
|
each_pair.to_a
|
@@ -23,6 +26,8 @@ module RuboCop
|
|
23
26
|
# Calls the given block for each `pair` node in the `hash` literal.
|
24
27
|
# If no block is given, an `Enumerator` is returned.
|
25
28
|
#
|
29
|
+
# @note `kwsplat` nodes are ignored.
|
30
|
+
#
|
26
31
|
# @return [self] if a block is given
|
27
32
|
# @return [Enumerator] if no block is given
|
28
33
|
def each_pair
|
@@ -37,6 +42,8 @@ module RuboCop
|
|
37
42
|
|
38
43
|
# Returns an array of all the keys in the `hash` literal.
|
39
44
|
#
|
45
|
+
# @note `kwsplat` nodes are ignored.
|
46
|
+
#
|
40
47
|
# @return [Array<Node>] an array of keys in the `hash` literal
|
41
48
|
def keys
|
42
49
|
each_key.to_a
|
@@ -45,6 +52,8 @@ module RuboCop
|
|
45
52
|
# Calls the given block for each `key` node in the `hash` literal.
|
46
53
|
# If no block is given, an `Enumerator` is returned.
|
47
54
|
#
|
55
|
+
# @note `kwsplat` nodes are ignored.
|
56
|
+
#
|
48
57
|
# @return [self] if a block is given
|
49
58
|
# @return [Enumerator] if no block is given
|
50
59
|
def each_key
|
@@ -59,6 +68,8 @@ module RuboCop
|
|
59
68
|
|
60
69
|
# Returns an array of all the values in the `hash` literal.
|
61
70
|
#
|
71
|
+
# @note `kwsplat` nodes are ignored.
|
72
|
+
#
|
62
73
|
# @return [Array<Node>] an array of values in the `hash` literal
|
63
74
|
def values
|
64
75
|
each_pair.map(&:value)
|
@@ -67,6 +78,8 @@ module RuboCop
|
|
67
78
|
# Calls the given block for each `value` node in the `hash` literal.
|
68
79
|
# If no block is given, an `Enumerator` is returned.
|
69
80
|
#
|
81
|
+
# @note `kwsplat` nodes are ignored.
|
82
|
+
#
|
70
83
|
# @return [self] if a block is given
|
71
84
|
# @return [Enumerator] if no block is given
|
72
85
|
def each_value
|
@@ -85,6 +98,8 @@ module RuboCop
|
|
85
98
|
# @note A multiline `pair` is considered to be on the same line if it
|
86
99
|
# shares any of its lines with another `pair`
|
87
100
|
#
|
101
|
+
# @note `kwsplat` nodes are ignored.
|
102
|
+
#
|
88
103
|
# @return [Boolean] whether any `pair` nodes are on the same line
|
89
104
|
def pairs_on_same_line?
|
90
105
|
pairs.each_cons(2).any? { |first, second| first.same_line?(second) }
|
@@ -93,6 +108,8 @@ module RuboCop
|
|
93
108
|
# Checks whether this `hash` uses a mix of hash rocket and colon
|
94
109
|
# delimiters for its pairs.
|
95
110
|
#
|
111
|
+
# @note `kwsplat` nodes are ignored.
|
112
|
+
#
|
96
113
|
# @return [Boolean] whether the `hash` uses mixed delimiters
|
97
114
|
def mixed_delimiters?
|
98
115
|
pairs.map(&:delimiter).uniq.size > 1
|
@@ -64,10 +64,9 @@ module RuboCop
|
|
64
64
|
#
|
65
65
|
# @return [String] the inverse keyword of the `if` statement
|
66
66
|
def inverse_keyword
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
'if'
|
67
|
+
case keyword
|
68
|
+
when 'if' then 'unless'
|
69
|
+
when 'unless' then 'if'
|
71
70
|
else
|
72
71
|
''
|
73
72
|
end
|
@@ -158,11 +157,7 @@ module RuboCop
|
|
158
157
|
branches.concat(other_branches)
|
159
158
|
end
|
160
159
|
|
161
|
-
#
|
162
|
-
# If no block is given, an `Enumerator` is returned.
|
163
|
-
#
|
164
|
-
# @return [self] if a block is given
|
165
|
-
# @return [Enumerator] if no block is given
|
160
|
+
# @deprecated Use `branches.each`
|
166
161
|
def each_branch
|
167
162
|
return branches.to_enum(__method__) unless block_given?
|
168
163
|
|
@@ -13,11 +13,7 @@ module RuboCop
|
|
13
13
|
node_parts[0...-1]
|
14
14
|
end
|
15
15
|
|
16
|
-
#
|
17
|
-
# If no block is given, an `Enumerator` is returned.
|
18
|
-
#
|
19
|
-
# @return [self] if a block is given
|
20
|
-
# @return [Enumerator] if no block is given
|
16
|
+
# @deprecated Use `conditions.each`
|
21
17
|
def each_condition
|
22
18
|
return conditions.to_enum(__method__) unless block_given?
|
23
19
|
|
@@ -84,7 +84,7 @@ module RuboCop
|
|
84
84
|
# # matching process starts
|
85
85
|
# '(send _ %named)' # arguments can also be passed as named
|
86
86
|
# # parameters (see `%1`)
|
87
|
-
# # Note that the macros `
|
87
|
+
# # Note that the macros `def_node_matcher` and
|
88
88
|
# # `def_node_search` accept default values for these.
|
89
89
|
# '(send _ %CONST)' # the named constant will act like `%1` and `%named`.
|
90
90
|
# '^^send' # each ^ ascends one level in the AST
|
@@ -96,6 +96,9 @@ module RuboCop
|
|
96
96
|
# # if that returns a truthy value, the match succeeds
|
97
97
|
# 'equal?(%1)' # predicates can be given 1 or more extra args
|
98
98
|
# '#method(%0, 1)' # funcalls can also be given 1 or more extra args
|
99
|
+
# # These arguments can be patterns themselves, in
|
100
|
+
# # which case a matcher responding to === will be
|
101
|
+
# # passed.
|
99
102
|
#
|
100
103
|
# You can nest arbitrarily deep:
|
101
104
|
#
|
@@ -110,11 +113,6 @@ module RuboCop
|
|
110
113
|
# and so on. Therefore, if you add methods which are named like
|
111
114
|
# `#prefix_type?` to the AST node class, then 'prefix' will become usable as
|
112
115
|
# a pattern.
|
113
|
-
#
|
114
|
-
# Also note that if you need a "guard clause" to protect against possible nils
|
115
|
-
# in a certain place in the AST, you can do it like this: `[!nil <pattern>]`
|
116
|
-
#
|
117
|
-
# The compiler code is very simple; don't be afraid to read through it!
|
118
116
|
class NodePattern
|
119
117
|
# @private
|
120
118
|
Invalid = Class.new(StandardError)
|
@@ -135,8 +133,10 @@ module RuboCop
|
|
135
133
|
PARAM_NUMBER = /%\d*/.freeze
|
136
134
|
|
137
135
|
SEPARATORS = /\s+/.freeze
|
138
|
-
|
139
|
-
|
136
|
+
ONLY_SEPARATOR = /\A#{SEPARATORS}\Z/.freeze
|
137
|
+
|
138
|
+
TOKENS = Regexp.union(META, PARAM_CONST, KEYWORD_NAME, PARAM_NUMBER, NUMBER,
|
139
|
+
METHOD_NAME, SYMBOL, STRING)
|
140
140
|
|
141
141
|
TOKEN = /\G(?:#{SEPARATORS}|#{TOKENS}|.)/.freeze
|
142
142
|
|
@@ -163,6 +163,7 @@ module RuboCop
|
|
163
163
|
CUR_NODE = "#{CUR_PLACEHOLDER} node@@@"
|
164
164
|
CUR_ELEMENT = "#{CUR_PLACEHOLDER} element@@@"
|
165
165
|
SEQ_HEAD_GUARD = '@@@seq guard head@@@'
|
166
|
+
MULTIPLE_CUR_PLACEHOLDER = /#{CUR_PLACEHOLDER}.*#{CUR_PLACEHOLDER}/.freeze
|
166
167
|
|
167
168
|
line = __LINE__
|
168
169
|
ANY_ORDER_TEMPLATE = ERB.new <<~RUBY.gsub("-%>\n", '%>')
|
@@ -199,22 +200,26 @@ module RuboCop
|
|
199
200
|
RUBY
|
200
201
|
REPEATED_TEMPLATE.location = [__FILE__, line + 1]
|
201
202
|
|
202
|
-
def initialize(str,
|
203
|
+
def initialize(str, root = 'node0', node_var = root)
|
203
204
|
@string = str
|
204
|
-
|
205
|
+
# For def_node_pattern, root == node_var
|
206
|
+
# For def_node_search, root is the root node to search on,
|
207
|
+
# and node_var is the current descendant being searched.
|
208
|
+
@root = root
|
209
|
+
@node_var = node_var
|
205
210
|
|
206
211
|
@temps = 0 # avoid name clashes between temp variables
|
207
212
|
@captures = 0 # number of captures seen
|
208
213
|
@unify = {} # named wildcard -> temp variable
|
209
214
|
@params = 0 # highest % (param) number seen
|
210
215
|
@keywords = Set[] # keyword parameters seen
|
211
|
-
run
|
216
|
+
run
|
212
217
|
end
|
213
218
|
|
214
|
-
def run
|
219
|
+
def run
|
215
220
|
@tokens = Compiler.tokens(@string)
|
216
221
|
|
217
|
-
@match_code = with_context(compile_expr, node_var, use_temp_node: false)
|
222
|
+
@match_code = with_context(compile_expr, @node_var, use_temp_node: false)
|
218
223
|
@match_code.prepend("(captures = Array.new(#{@captures})) && ") \
|
219
224
|
if @captures.positive?
|
220
225
|
|
@@ -234,6 +239,10 @@ module RuboCop
|
|
234
239
|
# CUR_NODE: Ruby code that evaluates to an AST node
|
235
240
|
# CUR_ELEMENT: Either the node or the type if in first element of
|
236
241
|
# a sequence (aka seq_head, e.g. "(seq_head first_node_arg ...")
|
242
|
+
if (atom = compile_atom(token))
|
243
|
+
return atom_to_expr(atom)
|
244
|
+
end
|
245
|
+
|
237
246
|
case token
|
238
247
|
when '(' then compile_seq
|
239
248
|
when '{' then compile_union
|
@@ -242,16 +251,10 @@ module RuboCop
|
|
242
251
|
when '$' then compile_capture
|
243
252
|
when '^' then compile_ascend
|
244
253
|
when '`' then compile_descend
|
245
|
-
when WILDCARD then
|
254
|
+
when WILDCARD then compile_new_wildcard(token[1..-1])
|
246
255
|
when FUNCALL then compile_funcall(token)
|
247
|
-
when LITERAL then compile_literal(token)
|
248
256
|
when PREDICATE then compile_predicate(token)
|
249
257
|
when NODE then compile_nodetype(token)
|
250
|
-
when KEYWORD then compile_keyword(token[1..-1])
|
251
|
-
when CONST then compile_const(token[1..-1])
|
252
|
-
when PARAM then compile_param(token[1..-1])
|
253
|
-
when CLOSING then fail_due_to("#{token} in invalid position")
|
254
|
-
when nil then fail_due_to('pattern ended prematurely')
|
255
258
|
else fail_due_to("invalid token #{token.inspect}")
|
256
259
|
end
|
257
260
|
end
|
@@ -260,7 +263,7 @@ module RuboCop
|
|
260
263
|
def tokens_until(stop, what)
|
261
264
|
return to_enum __method__, stop, what unless block_given?
|
262
265
|
|
263
|
-
fail_due_to("empty #{what}") if tokens.first == stop
|
266
|
+
fail_due_to("empty #{what}") if tokens.first == stop
|
264
267
|
yield until tokens.first == stop
|
265
268
|
tokens.shift
|
266
269
|
end
|
@@ -324,11 +327,15 @@ module RuboCop
|
|
324
327
|
# @private
|
325
328
|
# Builds Ruby code for a sequence
|
326
329
|
# (head *first_terms variadic_term *last_terms)
|
327
|
-
class Sequence
|
330
|
+
class Sequence
|
331
|
+
extend Forwardable
|
332
|
+
def_delegators :@compiler, :compile_guard_clause, :with_seq_head_context,
|
333
|
+
:with_child_context, :fail_due_to
|
334
|
+
|
328
335
|
def initialize(compiler, *arity_term_list)
|
329
336
|
@arities, @terms = arity_term_list.transpose
|
330
337
|
|
331
|
-
|
338
|
+
@compiler = compiler
|
332
339
|
@variadic_index = @arities.find_index { |a| a.is_a?(Range) }
|
333
340
|
fail_due_to 'multiple variable patterns in same sequence' \
|
334
341
|
if @variadic_index && !@arities.one? { |a| a.is_a?(Range) }
|
@@ -581,29 +588,18 @@ module RuboCop
|
|
581
588
|
end
|
582
589
|
end
|
583
590
|
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
elsif @unify.key?(name)
|
588
|
-
# we have already seen a wildcard with this name before
|
589
|
-
# so the value it matched the first time will already be stored
|
590
|
-
# in a temp. check if this value matches the one stored in the temp
|
591
|
-
"#{CUR_ELEMENT} == #{access_unify(name)}"
|
592
|
-
else
|
593
|
-
n = @unify[name] = "unify_#{name.gsub('-', '__')}"
|
594
|
-
# double assign to avoid "assigned but unused variable"
|
595
|
-
"(#{n} = #{CUR_ELEMENT}; " \
|
596
|
-
"#{n} = #{n}; true)"
|
597
|
-
end
|
598
|
-
end
|
591
|
+
# Known wildcards are considered atoms, see `compile_atom`
|
592
|
+
def compile_new_wildcard(name)
|
593
|
+
return 'true' if name.empty?
|
599
594
|
|
600
|
-
|
601
|
-
|
595
|
+
n = @unify[name] = "unify_#{name.gsub('-', '__')}"
|
596
|
+
# double assign to avoid "assigned but unused variable"
|
597
|
+
"(#{n} = #{CUR_ELEMENT}; #{n} = #{n}; true)"
|
602
598
|
end
|
603
599
|
|
604
600
|
def compile_predicate(predicate)
|
605
601
|
if predicate.end_with?('(') # is there an arglist?
|
606
|
-
args = compile_args
|
602
|
+
args = compile_args
|
607
603
|
predicate = predicate[0..-2] # drop the trailing (
|
608
604
|
"#{CUR_ELEMENT}.#{predicate}(#{args.join(',')})"
|
609
605
|
else
|
@@ -616,7 +612,7 @@ module RuboCop
|
|
616
612
|
# code is used in. pass target value as an argument
|
617
613
|
method = method[1..-1] # drop the leading #
|
618
614
|
if method.end_with?('(') # is there an arglist?
|
619
|
-
args = compile_args
|
615
|
+
args = compile_args
|
620
616
|
method = method[0..-2] # drop the trailing (
|
621
617
|
"#{method}(#{CUR_ELEMENT},#{args.join(',')})"
|
622
618
|
else
|
@@ -628,43 +624,44 @@ module RuboCop
|
|
628
624
|
"#{compile_guard_clause} && #{CUR_NODE}.#{type.tr('-', '_')}_type?"
|
629
625
|
end
|
630
626
|
|
631
|
-
def
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
627
|
+
def compile_args
|
628
|
+
tokens_until(')', 'call arguments').map do
|
629
|
+
arg = compile_arg
|
630
|
+
tokens.shift if tokens.first == ','
|
631
|
+
arg
|
632
|
+
end
|
637
633
|
end
|
638
634
|
|
639
|
-
def
|
640
|
-
"#{
|
635
|
+
def atom_to_expr(atom)
|
636
|
+
"#{atom} === #{CUR_ELEMENT}"
|
641
637
|
end
|
642
638
|
|
643
|
-
def
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
next if [')', ','].include?(token)
|
648
|
-
|
649
|
-
args << compile_arg(token)
|
639
|
+
def expr_to_atom(expr)
|
640
|
+
with_temp_variables do |compare|
|
641
|
+
in_context = with_context(expr, compare, use_temp_node: false)
|
642
|
+
"::RuboCop::AST::NodePattern::Matcher.new{|#{compare}| #{in_context}}"
|
650
643
|
end
|
651
644
|
end
|
652
645
|
|
653
|
-
|
654
|
-
|
646
|
+
# @return compiled atom (e.g. ":literal" or "SOME_CONST")
|
647
|
+
# or nil if not a simple atom (unknown wildcard, other tokens)
|
648
|
+
def compile_atom(token)
|
655
649
|
case token
|
656
|
-
when WILDCARD
|
657
|
-
access_unify(name) || fail_due_to('invalid in arglist: ' + token)
|
650
|
+
when WILDCARD then access_unify(token[1..-1]) # could be nil
|
658
651
|
when LITERAL then token
|
659
|
-
when KEYWORD then get_keyword(
|
660
|
-
when CONST then get_const(
|
661
|
-
when PARAM then get_param(
|
652
|
+
when KEYWORD then get_keyword(token[1..-1])
|
653
|
+
when CONST then get_const(token[1..-1])
|
654
|
+
when PARAM then get_param(token[1..-1])
|
662
655
|
when CLOSING then fail_due_to("#{token} in invalid position")
|
663
656
|
when nil then fail_due_to('pattern ended prematurely')
|
664
|
-
else fail_due_to("invalid token in arglist: #{token.inspect}")
|
665
657
|
end
|
666
658
|
end
|
667
659
|
|
660
|
+
def compile_arg
|
661
|
+
token = tokens.shift
|
662
|
+
compile_atom(token) || expr_to_atom(compile_expr(token))
|
663
|
+
end
|
664
|
+
|
668
665
|
def next_capture
|
669
666
|
index = @captures
|
670
667
|
@captures += 1
|
@@ -716,10 +713,10 @@ module RuboCop
|
|
716
713
|
@keywords.map { |k| format(pattern, keyword: k) }.join(',')
|
717
714
|
end
|
718
715
|
|
719
|
-
def
|
716
|
+
def emit_params(*first, forwarding: false)
|
720
717
|
params = emit_param_list
|
721
718
|
keywords = emit_keyword_list(forwarding: forwarding)
|
722
|
-
[params, keywords].reject(&:empty?).
|
719
|
+
[*first, params, keywords].reject(&:empty?).join(',')
|
723
720
|
end
|
724
721
|
|
725
722
|
def emit_method_code
|
@@ -754,7 +751,7 @@ module RuboCop
|
|
754
751
|
end
|
755
752
|
|
756
753
|
def auto_use_temp_node?(code)
|
757
|
-
code.
|
754
|
+
code.match?(MULTIPLE_CUR_PLACEHOLDER)
|
758
755
|
end
|
759
756
|
|
760
757
|
# with_<...>_context methods are used whenever the context,
|
@@ -793,16 +790,22 @@ module RuboCop
|
|
793
790
|
end
|
794
791
|
|
795
792
|
def self.tokens(pattern)
|
796
|
-
pattern.scan(TOKEN).
|
793
|
+
pattern.scan(TOKEN).grep_v(ONLY_SEPARATOR)
|
794
|
+
end
|
795
|
+
|
796
|
+
# This method minimizes the closure for our method
|
797
|
+
def wrapping_block(method_name, **defaults)
|
798
|
+
proc do |*args, **values|
|
799
|
+
send method_name, *args, **defaults, **values
|
800
|
+
end
|
797
801
|
end
|
798
802
|
|
799
803
|
def def_helper(base, method_name, **defaults)
|
800
804
|
location = caller_locations(3, 1).first
|
801
805
|
unless defaults.empty?
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
method_name = :"without_defaults_#{method_name}"
|
806
|
+
call = :"without_defaults_#{method_name}"
|
807
|
+
base.send :define_method, method_name, &wrapping_block(call, **defaults)
|
808
|
+
method_name = call
|
806
809
|
end
|
807
810
|
src = yield method_name
|
808
811
|
base.class_eval(src, location.path, location.lineno)
|
@@ -811,7 +814,7 @@ module RuboCop
|
|
811
814
|
def def_node_matcher(base, method_name, **defaults)
|
812
815
|
def_helper(base, method_name, **defaults) do |name|
|
813
816
|
<<~RUBY
|
814
|
-
def #{name}(node = self
|
817
|
+
def #{name}(#{emit_params('node = self')})
|
815
818
|
#{emit_method_code}
|
816
819
|
end
|
817
820
|
RUBY
|
@@ -828,20 +831,18 @@ module RuboCop
|
|
828
831
|
if method_name.to_s.end_with?('?')
|
829
832
|
on_match = 'return true'
|
830
833
|
else
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
RUBY
|
835
|
-
on_match = emit_yield_capture('node')
|
834
|
+
args = emit_params(":#{method_name}", @root, forwarding: true)
|
835
|
+
prelude = "return enum_for(#{args}) unless block_given?\n"
|
836
|
+
on_match = emit_yield_capture(@node_var)
|
836
837
|
end
|
837
838
|
emit_node_search_body(method_name, prelude: prelude, on_match: on_match)
|
838
839
|
end
|
839
840
|
|
840
841
|
def emit_node_search_body(method_name, prelude:, on_match:)
|
841
842
|
<<~RUBY
|
842
|
-
def #{method_name}(
|
843
|
+
def #{method_name}(#{emit_params(@root)})
|
843
844
|
#{prelude}
|
844
|
-
|
845
|
+
#{@root}.each_node do |#{@node_var}|
|
845
846
|
if #{match_code}
|
846
847
|
#{on_match}
|
847
848
|
end
|
@@ -874,7 +875,7 @@ module RuboCop
|
|
874
875
|
# as soon as it finds a descendant which matches. Otherwise, it will
|
875
876
|
# yield all descendants which match.
|
876
877
|
def def_node_search(method_name, pattern_str, **keyword_defaults)
|
877
|
-
Compiler.new(pattern_str, 'node')
|
878
|
+
Compiler.new(pattern_str, 'node0', 'node')
|
878
879
|
.def_node_search(self, method_name, **keyword_defaults)
|
879
880
|
end
|
880
881
|
end
|
@@ -883,8 +884,8 @@ module RuboCop
|
|
883
884
|
|
884
885
|
def initialize(str)
|
885
886
|
@pattern = str
|
886
|
-
compiler = Compiler.new(str)
|
887
|
-
src = "def match(
|
887
|
+
compiler = Compiler.new(str, 'node0')
|
888
|
+
src = "def match(#{compiler.emit_params('node0')});" \
|
888
889
|
"#{compiler.emit_method_code}end"
|
889
890
|
instance_eval(src, __FILE__, __LINE__ + 1)
|
890
891
|
end
|
@@ -933,6 +934,17 @@ module RuboCop
|
|
933
934
|
|
934
935
|
nil
|
935
936
|
end
|
937
|
+
|
938
|
+
# @api private
|
939
|
+
class Matcher
|
940
|
+
def initialize(&block)
|
941
|
+
@block = block
|
942
|
+
end
|
943
|
+
|
944
|
+
def ===(compare)
|
945
|
+
@block.call(compare)
|
946
|
+
end
|
947
|
+
end
|
936
948
|
end
|
937
949
|
end
|
938
950
|
end
|
@@ -71,18 +71,22 @@ module RuboCop
|
|
71
71
|
Digest::SHA1.hexdigest(@raw_source)
|
72
72
|
end
|
73
73
|
|
74
|
+
# @deprecated Use `comments.each`
|
74
75
|
def each_comment
|
75
76
|
comments.each { |comment| yield comment }
|
76
77
|
end
|
77
78
|
|
79
|
+
# @deprecated Use `comments.find`
|
78
80
|
def find_comment
|
79
81
|
comments.find { |comment| yield comment }
|
80
82
|
end
|
81
83
|
|
84
|
+
# @deprecated Use `tokens.each`
|
82
85
|
def each_token
|
83
86
|
tokens.each { |token| yield token }
|
84
87
|
end
|
85
88
|
|
89
|
+
# @deprecated Use `tokens.find`
|
86
90
|
def find_token
|
87
91
|
tokens.find { |token| yield token }
|
88
92
|
end
|
@@ -95,10 +99,20 @@ module RuboCop
|
|
95
99
|
ast.nil?
|
96
100
|
end
|
97
101
|
|
98
|
-
|
99
|
-
|
102
|
+
# @return [Boolean] if the given line number has a comment.
|
103
|
+
def line_with_comment?(line)
|
104
|
+
comment_lines.include?(line)
|
100
105
|
end
|
101
106
|
|
107
|
+
# @return [Boolean] if any of the lines in the given `source_range` has a comment.
|
108
|
+
def contains_comment?(source_range)
|
109
|
+
(source_range.line..source_range.last_line).any? do |line|
|
110
|
+
line_with_comment?(line)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
# @deprecated use contains_comment?
|
114
|
+
alias commented? contains_comment?
|
115
|
+
|
102
116
|
def comments_before_line(line)
|
103
117
|
comments.select { |c| c.location.line <= line }
|
104
118
|
end
|
@@ -34,7 +34,7 @@ module RuboCop
|
|
34
34
|
match_with_lvasgn begin kwbegin return
|
35
35
|
in_match match_alt
|
36
36
|
match_as array_pattern array_pattern_with_tail
|
37
|
-
hash_pattern const_pattern
|
37
|
+
hash_pattern const_pattern find_pattern
|
38
38
|
index indexasgn].freeze
|
39
39
|
SECOND_CHILD_ONLY = %i[lvasgn ivasgn cvasgn gvasgn optarg kwarg
|
40
40
|
kwoptarg].freeze
|
data/lib/rubocop/ast/version.rb
CHANGED
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-ast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
8
8
|
- Jonas Arvidsson
|
9
9
|
- Yuji Nakayama
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2020-
|
13
|
+
date: 2020-07-19 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: parser
|
@@ -125,7 +125,7 @@ metadata:
|
|
125
125
|
source_code_uri: https://github.com/rubocop-hq/rubocop-ast/
|
126
126
|
documentation_uri: https://docs.rubocop.org/rubocop-ast/
|
127
127
|
bug_tracker_uri: https://github.com/rubocop-hq/rubocop-ast/issues
|
128
|
-
post_install_message:
|
128
|
+
post_install_message:
|
129
129
|
rdoc_options: []
|
130
130
|
require_paths:
|
131
131
|
- lib
|
@@ -141,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
141
|
version: '0'
|
142
142
|
requirements: []
|
143
143
|
rubygems_version: 3.1.2
|
144
|
-
signing_key:
|
144
|
+
signing_key:
|
145
145
|
specification_version: 4
|
146
146
|
summary: RuboCop tools to deal with Ruby code AST.
|
147
147
|
test_files: []
|