rubocop-ast 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Maintainability](https://api.codeclimate.com/v1/badges/a29666e6373bc41bc0a9/maintainability)](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: []
|