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 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: []