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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4108494066548a429e972d3f2afcaa9adfd39072a929308cdb20e4a602fef66d
4
- data.tar.gz: 413b4382539bffed1760d0599bc093ad40ffef64992f821484994150903812da
3
+ metadata.gz: d213ca265b42f0b66394057ee01a4a4573a28da98e7fc8caf52b5396fe098381
4
+ data.tar.gz: 3a351df6a7f11cfdad47b7844eb0c0d9860e11e7fdd65f5e1405b1f0978081be
5
5
  SHA512:
6
- metadata.gz: 97af9dd864e8f6867614a79b2aac596e18e147b9a55bcc67b8755e47ef762730ec69612dcc6858ee12017b33beae0b89e008e1267034f1730682015c4efa4255
7
- data.tar.gz: 807305ab6dc90885fff96d1387d787debf8ce8f27a0d150ad4bf86a76d4afb2d288f0771b2399cf3c3cfb1472c72cd750e4df9e97ad07467da4615a97c062f79
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
- * `RuboCop::AST::Node`
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 `parser` gem, which `rubocop-ast` extends.
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
 
@@ -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 (const nil? {:Class :Module}) :new ...))
317
- (casgn $_ $_ (block (send (const nil? {:Class :Module}) :new ...) ...))}
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 (const nil? :Proc) :new) ...)
500
- (send (const nil? :Proc) :new)}
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 (const nil? {:Class :Module}) :new ...)
508
- (block (send (const nil? {:Class :Module}) :new ...) ...)}
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
- def values
18
- each_child_node.to_a
19
- end
17
+ alias values children
20
18
 
21
- # Calls the given block for all values in the `array` literal.
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
- # Calls the given block for each `in_pattern` node in the `in` statement.
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
- # Calls the given block for each `when` node in the `case` statement.
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
- node_parts[2]
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
- node_parts[1]
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
- node_parts[0]
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
- node_parts[3]
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
- if keyword == 'if'
68
- 'unless'
69
- elsif keyword == 'unless'
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
- # Calls the given block for each branch node in the conditional statement.
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
- # Calls the given block for each condition node in the `when` branch.
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 `def_node_pattern` and
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
- TOKENS = Regexp.union(META, PARAM_CONST, KEYWORD_NAME, PARAM_NUMBER, NUMBER,
139
- METHOD_NAME, SYMBOL, STRING)
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, node_var = 'node0')
203
+ def initialize(str, root = 'node0', node_var = root)
203
204
  @string = str
204
- @root = node_var
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(node_var)
216
+ run
212
217
  end
213
218
 
214
- def run(node_var)
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 compile_wildcard(token[1..-1])
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 && what
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 < SimpleDelegator
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
- super(compiler)
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
- def compile_wildcard(name)
585
- if name.empty?
586
- 'true'
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
- def compile_literal(literal)
601
- "#{CUR_ELEMENT} == #{literal}"
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(tokens)
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(tokens)
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 compile_param(number)
632
- "#{get_param(number)} === #{CUR_ELEMENT}"
633
- end
634
-
635
- def compile_const(const)
636
- "#{get_const(const)} === #{CUR_ELEMENT}"
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 compile_keyword(keyword)
640
- "#{get_keyword(keyword)} === #{CUR_ELEMENT}"
635
+ def atom_to_expr(atom)
636
+ "#{atom} === #{CUR_ELEMENT}"
641
637
  end
642
638
 
643
- def compile_args(tokens)
644
- index = tokens.find_index { |token| token == ')' }
645
-
646
- tokens.slice!(0..index).each_with_object([]) do |token, args|
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
- def compile_arg(token)
654
- name = token[1..-1]
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(name)
660
- when CONST then get_const(name)
661
- when PARAM then get_param(name)
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 emit_trailing_params(forwarding: false)
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?).map { |p| ", #{p}" }.join
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.scan(CUR_PLACEHOLDER).count > 1
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).reject { |token| token =~ /\A#{SEPARATORS}\Z/ }
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
- base.send :define_method, method_name do |*args, **values|
803
- send method_name, *args, **defaults, **values
804
- end
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#{emit_trailing_params})
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
- prelude = <<~RUBY
832
- return enum_for(:#{method_name},
833
- node0#{emit_trailing_params(forwarding: true)}) unless block_given?
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}(node0#{emit_trailing_params})
843
+ def #{method_name}(#{emit_params(@root)})
843
844
  #{prelude}
844
- node0.each_node do |node|
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(node0#{compiler.emit_trailing_params});" \
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
- def commented?(source_range)
99
- comment_lines.include?(source_range.line)
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
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module AST
5
5
  module Version
6
- STRING = '0.1.0'
6
+ STRING = '0.2.0'
7
7
  end
8
8
  end
9
9
  end
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.1.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-06-26 00:00:00.000000000 Z
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: []