rubocop-ast 0.2.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/lib/rubocop/ast.rb +5 -1
  4. data/lib/rubocop/ast/builder.rb +5 -1
  5. data/lib/rubocop/ast/ext/range.rb +28 -0
  6. data/lib/rubocop/ast/ext/set.rb +12 -0
  7. data/lib/rubocop/ast/node.rb +53 -3
  8. data/lib/rubocop/ast/node/block_node.rb +1 -1
  9. data/lib/rubocop/ast/node/break_node.rb +1 -6
  10. data/lib/rubocop/ast/node/case_match_node.rb +2 -4
  11. data/lib/rubocop/ast/node/case_node.rb +12 -4
  12. data/lib/rubocop/ast/node/const_node.rb +65 -0
  13. data/lib/rubocop/ast/node/defined_node.rb +2 -0
  14. data/lib/rubocop/ast/node/float_node.rb +1 -0
  15. data/lib/rubocop/ast/node/hash_node.rb +4 -8
  16. data/lib/rubocop/ast/node/if_node.rb +3 -5
  17. data/lib/rubocop/ast/node/index_node.rb +5 -3
  18. data/lib/rubocop/ast/node/indexasgn_node.rb +5 -3
  19. data/lib/rubocop/ast/node/int_node.rb +1 -0
  20. data/lib/rubocop/ast/node/lambda_node.rb +10 -3
  21. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +0 -7
  22. data/lib/rubocop/ast/node/mixin/parameterized_node.rb +55 -0
  23. data/lib/rubocop/ast/node/next_node.rb +12 -0
  24. data/lib/rubocop/ast/node/pair_node.rb +2 -2
  25. data/lib/rubocop/ast/node/resbody_node.rb +21 -0
  26. data/lib/rubocop/ast/node/rescue_node.rb +49 -0
  27. data/lib/rubocop/ast/node/return_node.rb +1 -13
  28. data/lib/rubocop/ast/node/send_node.rb +7 -1
  29. data/lib/rubocop/ast/node/super_node.rb +2 -0
  30. data/lib/rubocop/ast/node/when_node.rb +2 -4
  31. data/lib/rubocop/ast/node/yield_node.rb +2 -0
  32. data/lib/rubocop/ast/node_pattern.rb +5 -4
  33. data/lib/rubocop/ast/processed_source.rb +86 -22
  34. data/lib/rubocop/ast/traversal.rb +1 -1
  35. data/lib/rubocop/ast/version.rb +1 -1
  36. metadata +9 -5
  37. data/lib/rubocop/ast/node/retry_node.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d213ca265b42f0b66394057ee01a4a4573a28da98e7fc8caf52b5396fe098381
4
- data.tar.gz: 3a351df6a7f11cfdad47b7844eb0c0d9860e11e7fdd65f5e1405b1f0978081be
3
+ metadata.gz: 329f01e23bc173bdcfec3cec5ef0eaf411f9328dbb7bef472e9ea119a2989427
4
+ data.tar.gz: 9208c40cf5063f525dcdf44eb43d65e05814fa76931f52c3015c391e3e99b77b
5
5
  SHA512:
6
- metadata.gz: 1771b173896909ca2e9b68acf312c7b73e2e7308f834fc6d79871bd84b5012622e8727ab3e0cf98ca985cab32a70e65c83b174ff1bf42a59bba942720625b198
7
- data.tar.gz: 57539ef58f3fc0722220759e4875524b43f9cd89a919d47429327f2ab3eaa2cfb0b35d9e275b05ac683f4f860bf5ac1986619fc16aa71c5a6b80ff2797683fa0
6
+ metadata.gz: 487a0aad608835a2a7b25f10d74e5430dded65f3c444dd5bfa3747b553c4811b09768c6966db4815adb1d529e4677930ed19e1a6b57ae7d0844f6d590265da55
7
+ data.tar.gz: c054ab24f0760a913e605fb8754e2c76faf993cc965695bafcba8c9e3b5ed333b20a3a6df7d442be7aa234ff98e881617f824bf9e8b38b8e9d86d72dc8e78ca0
data/README.md CHANGED
@@ -35,8 +35,9 @@ See the [docs site](https://docs.rubocop.org/rubocop-ast) for more details.
35
35
 
36
36
  ### Parser compatibility switches
37
37
 
38
- The main `RuboCop` gem uses [legacy AST output from parser](https://github.com/whitequark/parser/#usage).
39
- This gem is meant to be compatible with all settings. For example, to have `-> { ... }` emitted
38
+ This gem, by default, uses most [legacy AST output from parser](https://github.com/whitequark/parser/#usage), except for `emit_forward_arg` which is set to `true`.
39
+
40
+ The main `RuboCop` gem uses these defaults (and is currently only compatible with these), but this gem can be used separately from `RuboCop` and is meant to be compatible with all settings. For example, to have `-> { ... }` emitted
40
41
  as `LambdaNode` instead of `SendNode`:
41
42
 
42
43
  ```ruby
@@ -4,6 +4,8 @@ require 'parser'
4
4
  require 'forwardable'
5
5
  require 'set'
6
6
 
7
+ require_relative 'ast/ext/range'
8
+ require_relative 'ast/ext/set'
7
9
  require_relative 'ast/node_pattern'
8
10
  require_relative 'ast/sexp'
9
11
  require_relative 'ast/node'
@@ -27,6 +29,7 @@ require_relative 'ast/node/break_node'
27
29
  require_relative 'ast/node/case_match_node'
28
30
  require_relative 'ast/node/case_node'
29
31
  require_relative 'ast/node/class_node'
32
+ require_relative 'ast/node/const_node'
30
33
  require_relative 'ast/node/def_node'
31
34
  require_relative 'ast/node/defined_node'
32
35
  require_relative 'ast/node/ensure_node'
@@ -41,12 +44,13 @@ require_relative 'ast/node/int_node'
41
44
  require_relative 'ast/node/keyword_splat_node'
42
45
  require_relative 'ast/node/lambda_node'
43
46
  require_relative 'ast/node/module_node'
47
+ require_relative 'ast/node/next_node'
44
48
  require_relative 'ast/node/or_node'
45
49
  require_relative 'ast/node/pair_node'
46
50
  require_relative 'ast/node/range_node'
47
51
  require_relative 'ast/node/regexp_node'
52
+ require_relative 'ast/node/rescue_node'
48
53
  require_relative 'ast/node/resbody_node'
49
- require_relative 'ast/node/retry_node'
50
54
  require_relative 'ast/node/return_node'
51
55
  require_relative 'ast/node/self_class_node'
52
56
  require_relative 'ast/node/send_node'
@@ -14,6 +14,8 @@ module RuboCop
14
14
  # parser = Parser::Ruby25.new(builder)
15
15
  # root_node = parser.parse(buffer)
16
16
  class Builder < Parser::Builders::Default
17
+ self.emit_forward_arg = true
18
+
17
19
  NODE_MAP = {
18
20
  and: AndNode,
19
21
  alias: AliasNode,
@@ -25,6 +27,7 @@ module RuboCop
25
27
  case_match: CaseMatchNode,
26
28
  case: CaseNode,
27
29
  class: ClassNode,
30
+ const: ConstNode,
28
31
  def: DefNode,
29
32
  defined?: DefinedNode,
30
33
  defs: DefNode,
@@ -42,11 +45,12 @@ module RuboCop
42
45
  kwsplat: KeywordSplatNode,
43
46
  lambda: LambdaNode,
44
47
  module: ModuleNode,
48
+ next: NextNode,
45
49
  or: OrNode,
46
50
  pair: PairNode,
47
51
  regexp: RegexpNode,
52
+ rescue: RescueNode,
48
53
  resbody: ResbodyNode,
49
- retry: RetryNode,
50
54
  return: ReturnNode,
51
55
  csend: SendNode,
52
56
  send: SendNode,
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ module Ext
6
+ # Extensions to Parser::AST::Range
7
+ module Range
8
+ # @return [Range] the range of line numbers for the node
9
+ # If `exclude_end` is `true`, then the range will be exclusive.
10
+ #
11
+ # Assume that `node` corresponds to the following array literal:
12
+ #
13
+ # [
14
+ # :foo,
15
+ # :bar
16
+ # ]
17
+ #
18
+ # node.loc.begin.line_span # => 1..1
19
+ # node.loc.expression.line_span(exclude_end: true) # => 1...4
20
+ def line_span(exclude_end: false)
21
+ ::Range.new(first_line, last_line, exclude_end)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ ::Parser::Source::Range.include ::RuboCop::AST::Ext::Range
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ test = :foo
4
+ case test
5
+ when Set[:foo]
6
+ # ok, RUBY_VERSION > 2.4
7
+ else
8
+ # Harmonize `Set#===`
9
+ class Set
10
+ alias === include?
11
+ end
12
+ end
@@ -38,7 +38,7 @@ module RuboCop
38
38
  IMMUTABLE_LITERALS = (LITERALS - MUTABLE_LITERALS).freeze
39
39
 
40
40
  EQUALS_ASSIGNMENTS = %i[lvasgn ivasgn cvasgn gvasgn
41
- casgn masgn].freeze
41
+ casgn masgn rasgn mrasgn].freeze
42
42
  SHORTHAND_ASSIGNMENTS = %i[op_asgn or_asgn and_asgn].freeze
43
43
  ASSIGNMENTS = (EQUALS_ASSIGNMENTS + SHORTHAND_ASSIGNMENTS).freeze
44
44
 
@@ -92,6 +92,16 @@ module RuboCop
92
92
  @mutable_attributes[:parent] = node
93
93
  end
94
94
 
95
+ # @return [Boolean]
96
+ def parent?
97
+ !!parent
98
+ end
99
+
100
+ # @return [Boolean]
101
+ def root?
102
+ !parent
103
+ end
104
+
95
105
  def complete!
96
106
  @mutable_attributes.freeze
97
107
  each_child_node(&:complete!)
@@ -116,12 +126,50 @@ module RuboCop
116
126
 
117
127
  # Returns the index of the receiver node in its siblings. (Sibling index
118
128
  # uses zero based numbering.)
129
+ # Use is discouraged, this is a potentially slow method.
119
130
  #
120
- # @return [Integer] the index of the receiver node in its siblings
131
+ # @return [Integer, nil] the index of the receiver node in its siblings
121
132
  def sibling_index
122
133
  parent&.children&.index { |sibling| sibling.equal?(self) }
123
134
  end
124
135
 
136
+ # Use is discouraged, this is a potentially slow method and can lead
137
+ # to even slower algorithms
138
+ # @return [Node, nil] the right (aka next) sibling
139
+ def right_sibling
140
+ return unless parent
141
+
142
+ parent.children[sibling_index + 1].freeze
143
+ end
144
+
145
+ # Use is discouraged, this is a potentially slow method and can lead
146
+ # to even slower algorithms
147
+ # @return [Node, nil] the left (aka previous) sibling
148
+ def left_sibling
149
+ i = sibling_index
150
+ return if i.nil? || i.zero?
151
+
152
+ parent.children[i - 1].freeze
153
+ end
154
+
155
+ # Use is discouraged, this is a potentially slow method and can lead
156
+ # to even slower algorithms
157
+ # @return [Array<Node>] the left (aka previous) siblings
158
+ def left_siblings
159
+ return [].freeze unless parent
160
+
161
+ parent.children[0...sibling_index].freeze
162
+ end
163
+
164
+ # Use is discouraged, this is a potentially slow method and can lead
165
+ # to even slower algorithms
166
+ # @return [Array<Node>] the right (aka next) siblings
167
+ def right_siblings
168
+ return [].freeze unless parent
169
+
170
+ parent.children[sibling_index + 1..-1].freeze
171
+ end
172
+
125
173
  # Common destructuring method. This can be used to normalize
126
174
  # destructuring for different variations of the node.
127
175
  # Some node types override this with their own custom
@@ -261,8 +309,10 @@ module RuboCop
261
309
  self
262
310
  end
263
311
 
312
+ # Note: Some rare nodes may have no source, like `s(:args)` in `foo {}`
313
+ # @return [String, nil]
264
314
  def source
265
- loc.expression.source
315
+ loc.expression&.source
266
316
  end
267
317
 
268
318
  def source_range
@@ -25,7 +25,7 @@ module RuboCop
25
25
  # @return [Array<Node>]
26
26
  def arguments
27
27
  if numblock_type?
28
- [] # Numbered parameters have no block arguments.
28
+ [].freeze # Numbered parameters have no block arguments.
29
29
  else
30
30
  node_parts[1]
31
31
  end
@@ -6,12 +6,7 @@ module RuboCop
6
6
  # plain node when the builder constructs the AST, making its methods
7
7
  # available to all `break` nodes within RuboCop.
8
8
  class BreakNode < Node
9
- include MethodDispatchNode
10
- include ParameterizedNode
11
-
12
- def arguments
13
- []
14
- end
9
+ include ParameterizedNode::WrappedArguments
15
10
  end
16
11
  end
17
12
  end
@@ -16,12 +16,10 @@ module RuboCop
16
16
  end
17
17
 
18
18
  # @deprecated Use `in_pattern_branches.each`
19
- def each_in_pattern
19
+ def each_in_pattern(&block)
20
20
  return in_pattern_branches.to_enum(__method__) unless block_given?
21
21
 
22
- in_pattern_branches.each do |condition|
23
- yield condition
24
- end
22
+ in_pattern_branches.each(&block)
25
23
 
26
24
  self
27
25
  end
@@ -16,12 +16,10 @@ module RuboCop
16
16
  end
17
17
 
18
18
  # @deprecated Use `when_branches.each`
19
- def each_when
19
+ def each_when(&block)
20
20
  return when_branches.to_enum(__method__) unless block_given?
21
21
 
22
- when_branches.each do |condition|
23
- yield condition
24
- end
22
+ when_branches.each(&block)
25
23
 
26
24
  self
27
25
  end
@@ -33,6 +31,16 @@ module RuboCop
33
31
  node_parts[1...-1]
34
32
  end
35
33
 
34
+ # Returns an array of all the when branches in the `case` statement.
35
+ #
36
+ # @return [Array<Node, nil>] an array of the bodies of the when branches
37
+ # and the else (if any). Note that these bodies could be nil.
38
+ def branches
39
+ bodies = when_branches.map(&:body)
40
+ bodies.push(else_branch) if else?
41
+ bodies
42
+ end
43
+
36
44
  # Returns the else branch of the `case` statement, if any.
37
45
  #
38
46
  # @return [Node] the else branch node of the `case` statement
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # A node extension for `const` nodes.
6
+ class ConstNode < Node
7
+ # The `send` node associated with this block.
8
+ #
9
+ # @return [Node, nil] the node associated with the scope (e.g. cbase, const, ...)
10
+ def namespace
11
+ children[0]
12
+ end
13
+
14
+ # @return [Symbol] the demodulized name of the constant: "::Foo::Bar" => :Bar
15
+ def short_name
16
+ children[1]
17
+ end
18
+
19
+ # The body of this block.
20
+ #
21
+ # @return [Boolean] if the constant is a Module / Class, according to the standard convention.
22
+ # Note: some classes might have uppercase in which case this method
23
+ # returns false
24
+ def module_name?
25
+ short_name.match?(/[[:lower:]]/)
26
+ end
27
+ alias class_name? module_name?
28
+
29
+ # @return [Boolean] if the constant starts with `::` (aka s(:cbase))
30
+ def absolute?
31
+ return false unless namespace
32
+
33
+ each_path.first.cbase_type?
34
+ end
35
+
36
+ # @return [Boolean] if the constant does not start with `::` (aka s(:cbase))
37
+ def relative?
38
+ !absolute?
39
+ end
40
+
41
+ # Yield nodes for the namespace
42
+ #
43
+ # For `::Foo::Bar::BAZ` => yields:
44
+ # s(:cbase), then
45
+ # s(:const, :Foo), then
46
+ # s(:const, s(:const, :Foo), :Bar)
47
+ def each_path(&block)
48
+ return to_enum(__method__) unless block_given?
49
+
50
+ descendants = []
51
+ last = self
52
+ loop do
53
+ last = last.children.first
54
+ break if last.nil?
55
+
56
+ descendants << last
57
+ break unless last.const_type?
58
+ end
59
+ descendants.reverse_each(&block)
60
+
61
+ self
62
+ end
63
+ end
64
+ end
65
+ end
@@ -12,6 +12,8 @@ module RuboCop
12
12
  def node_parts
13
13
  [nil, :defined?, *to_a]
14
14
  end
15
+
16
+ alias arguments children
15
17
  end
16
18
  end
17
19
  end
@@ -6,6 +6,7 @@ module RuboCop
6
6
  # node when the builder constructs the AST, making its methods available to
7
7
  # all `float` nodes within RuboCop.
8
8
  class FloatNode < Node
9
+ include BasicLiteralNode
9
10
  include NumericNode
10
11
  end
11
12
  end
@@ -56,12 +56,10 @@ module RuboCop
56
56
  #
57
57
  # @return [self] if a block is given
58
58
  # @return [Enumerator] if no block is given
59
- def each_key
59
+ def each_key(&block)
60
60
  return pairs.map(&:key).to_enum unless block_given?
61
61
 
62
- pairs.map(&:key).each do |key|
63
- yield key
64
- end
62
+ pairs.map(&:key).each(&block)
65
63
 
66
64
  self
67
65
  end
@@ -82,12 +80,10 @@ module RuboCop
82
80
  #
83
81
  # @return [self] if a block is given
84
82
  # @return [Enumerator] if no block is given
85
- def each_value
83
+ def each_value(&block)
86
84
  return pairs.map(&:value).to_enum unless block_given?
87
85
 
88
- pairs.map(&:value).each do |value|
89
- yield value
90
- end
86
+ pairs.map(&:value).each(&block)
91
87
 
92
88
  self
93
89
  end
@@ -147,7 +147,7 @@ module RuboCop
147
147
  def branches
148
148
  branches = [if_branch]
149
149
 
150
- return branches unless else_branch
150
+ return branches unless else?
151
151
 
152
152
  other_branches = if elsif_conditional?
153
153
  else_branch.branches
@@ -158,12 +158,10 @@ module RuboCop
158
158
  end
159
159
 
160
160
  # @deprecated Use `branches.each`
161
- def each_branch
161
+ def each_branch(&block)
162
162
  return branches.to_enum(__method__) unless block_given?
163
163
 
164
- branches.each do |branch|
165
- yield branch
166
- end
164
+ branches.each(&block)
167
165
  end
168
166
  end
169
167
  end
@@ -17,7 +17,7 @@ module RuboCop
17
17
  # The main RuboCop runs in legacy mode; this node is only used
18
18
  # if user `AST::Builder.modernize` or `AST::Builder.emit_index=true`
19
19
  class IndexNode < Node
20
- include ParameterizedNode
20
+ include ParameterizedNode::RestArguments
21
21
  include MethodDispatchNode
22
22
 
23
23
  # For similarity with legacy mode
@@ -35,11 +35,13 @@ module RuboCop
35
35
  :[]
36
36
  end
37
37
 
38
+ private
39
+
38
40
  # An array containing the arguments of the dispatched method.
39
41
  #
40
42
  # @return [Array<Node>] the arguments of the dispatched method
41
- def arguments
42
- node_parts[1..-1]
43
+ def first_argument_index
44
+ 1
43
45
  end
44
46
  end
45
47
  end
@@ -19,7 +19,7 @@ module RuboCop
19
19
  # The main RuboCop runs in legacy mode; this node is only used
20
20
  # if user `AST::Builder.modernize` or `AST::Builder.emit_index=true`
21
21
  class IndexasgnNode < Node
22
- include ParameterizedNode
22
+ include ParameterizedNode::RestArguments
23
23
  include MethodDispatchNode
24
24
 
25
25
  # For similarity with legacy mode
@@ -37,11 +37,13 @@ module RuboCop
37
37
  :[]=
38
38
  end
39
39
 
40
+ private
41
+
40
42
  # An array containing the arguments of the dispatched method.
41
43
  #
42
44
  # @return [Array<Node>] the arguments of the dispatched method
43
- def arguments
44
- node_parts[1..-1]
45
+ def first_argument_index
46
+ 1
45
47
  end
46
48
  end
47
49
  end
@@ -6,6 +6,7 @@ module RuboCop
6
6
  # node when the builder constructs the AST, making its methods available to
7
7
  # all `int` nodes within RuboCop.
8
8
  class IntNode < Node
9
+ include BasicLiteralNode
9
10
  include NumericNode
10
11
  end
11
12
  end
@@ -21,7 +21,7 @@ module RuboCop
21
21
  # The main RuboCop runs in legacy mode; this node is only used
22
22
  # if user `AST::Builder.modernize` or `AST::Builder.emit_lambda=true`
23
23
  class LambdaNode < Node
24
- include ParameterizedNode
24
+ include ParameterizedNode::RestArguments
25
25
  include MethodDispatchNode
26
26
 
27
27
  # For similarity with legacy mode
@@ -44,14 +44,21 @@ module RuboCop
44
44
  false
45
45
  end
46
46
 
47
+ # For similarity with legacy mode
48
+ def receiver
49
+ nil
50
+ end
51
+
47
52
  # For similarity with legacy mode
48
53
  def method_name
49
54
  :lambda
50
55
  end
51
56
 
57
+ private
58
+
52
59
  # For similarity with legacy mode
53
- def arguments
54
- []
60
+ def first_argument_index
61
+ 2
55
62
  end
56
63
  end
57
64
  end
@@ -26,13 +26,6 @@ module RuboCop
26
26
  node_parts[1]
27
27
  end
28
28
 
29
- # An array containing the arguments of the dispatched method.
30
- #
31
- # @return [Array<Node>] the arguments of the dispatched method
32
- def arguments
33
- node_parts[2..-1]
34
- end
35
-
36
29
  # The `block` node associated with this method dispatch, if any.
37
30
  #
38
31
  # @return [BlockNode, nil] the `block` node associated with this method
@@ -2,6 +2,8 @@
2
2
 
3
3
  module RuboCop
4
4
  module AST
5
+ # Requires implementing `arguments`.
6
+ #
5
7
  # Common functionality for nodes that are parameterized:
6
8
  # `send`, `super`, `zsuper`, `def`, `defs`
7
9
  # and (modern only): `index`, `indexasgn`, `lambda`
@@ -57,6 +59,59 @@ module RuboCop
57
59
  arguments? &&
58
60
  (last_argument.block_pass_type? || last_argument.blockarg_type?)
59
61
  end
62
+
63
+ # A specialized `ParameterizedNode` for node that have a single child
64
+ # containing either `nil`, an argument, or a `begin` node with all the
65
+ # arguments
66
+ module WrappedArguments
67
+ include ParameterizedNode
68
+ # @return [Array] The arguments of the node.
69
+ def arguments
70
+ first = children.first
71
+ if first&.begin_type?
72
+ first.children
73
+ else
74
+ children
75
+ end
76
+ end
77
+ end
78
+
79
+ # A specialized `ParameterizedNode`.
80
+ # Requires implementing `first_argument_index`
81
+ # Implements `arguments` as `children[first_argument_index..-1]`
82
+ # and optimizes other calls
83
+ module RestArguments
84
+ include ParameterizedNode
85
+ # @return [Array<Node>] arguments, if any
86
+ def arguments
87
+ children[first_argument_index..-1].freeze
88
+ end
89
+
90
+ # A shorthand for getting the first argument of the node.
91
+ # Equivalent to `arguments.first`.
92
+ #
93
+ # @return [Node, nil] the first argument of the node,
94
+ # or `nil` if there are no arguments
95
+ def first_argument
96
+ children[first_argument_index]
97
+ end
98
+
99
+ # A shorthand for getting the last argument of the node.
100
+ # Equivalent to `arguments.last`.
101
+ #
102
+ # @return [Node, nil] the last argument of the node,
103
+ # or `nil` if there are no arguments
104
+ def last_argument
105
+ children[-1] if arguments?
106
+ end
107
+
108
+ # Checks whether this node has any arguments.
109
+ #
110
+ # @return [Boolean] whether this node has any arguments
111
+ def arguments?
112
+ children.size > first_argument_index
113
+ end
114
+ end
60
115
  end
61
116
  end
62
117
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # A node extension for `next` nodes. This will be used in place of a
6
+ # plain node when the builder constructs the AST, making its methods
7
+ # available to all `next` nodes within RuboCop.
8
+ class NextNode < Node
9
+ include ParameterizedNode::WrappedArguments
10
+ end
11
+ end
12
+ end
@@ -32,7 +32,7 @@ module RuboCop
32
32
  #
33
33
  # @param [Boolean] with_spacing whether to include spacing
34
34
  # @return [String] the delimiter of the `pair`
35
- def delimiter(with_spacing = false)
35
+ def delimiter(*deprecated, with_spacing: deprecated.first)
36
36
  if with_spacing
37
37
  hash_rocket? ? SPACED_HASH_ROCKET : SPACED_COLON
38
38
  else
@@ -44,7 +44,7 @@ module RuboCop
44
44
  #
45
45
  # @param [Boolean] with_spacing whether to include spacing
46
46
  # @return [String] the inverse delimiter of the `pair`
47
- def inverse_delimiter(with_spacing = false)
47
+ def inverse_delimiter(*deprecated, with_spacing: deprecated.first)
48
48
  if with_spacing
49
49
  hash_rocket? ? SPACED_COLON : SPACED_HASH_ROCKET
50
50
  else
@@ -13,12 +13,33 @@ module RuboCop
13
13
  node_parts[2]
14
14
  end
15
15
 
16
+ # Returns an array of all the exceptions in the `rescue` clause.
17
+ #
18
+ # @return [Array<Node>] an array of exception nodes
19
+ def exceptions
20
+ exceptions_node = node_parts[0]
21
+ if exceptions_node.nil?
22
+ []
23
+ elsif exceptions_node.array_type?
24
+ exceptions_node.values
25
+ else
26
+ [exceptions_node]
27
+ end
28
+ end
29
+
16
30
  # Returns the exception variable of the `rescue` clause.
17
31
  #
18
32
  # @return [Node, nil] The exception variable of the `resbody`.
19
33
  def exception_variable
20
34
  node_parts[1]
21
35
  end
36
+
37
+ # Returns the index of the `resbody` branch within the exception handling statement.
38
+ #
39
+ # @return [Integer] the index of the `resbody` branch
40
+ def branch_index
41
+ parent.resbody_branches.index(self)
42
+ end
22
43
  end
23
44
  end
24
45
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # A node extension for `rescue` nodes. This will be used in place of a
6
+ # plain node when the builder constructs the AST, making its methods
7
+ # available to all `rescue` nodes within RuboCop.
8
+ class RescueNode < Node
9
+ # Returns the body of the rescue node.
10
+ #
11
+ # @return [Node, nil] The body of the rescue node.
12
+ def body
13
+ node_parts[0]
14
+ end
15
+
16
+ # Returns an array of all the rescue branches in the exception handling statement.
17
+ #
18
+ # @return [Array<ResbodyNode>] an array of `resbody` nodes
19
+ def resbody_branches
20
+ node_parts[1...-1]
21
+ end
22
+
23
+ # Returns an array of all the rescue branches in the exception handling statement.
24
+ #
25
+ # @return [Array<Node, nil>] an array of the bodies of the rescue branches
26
+ # and the else (if any). Note that these bodies could be nil.
27
+ def branches
28
+ bodies = resbody_branches.map(&:body)
29
+ bodies.push(else_branch) if else?
30
+ bodies
31
+ end
32
+
33
+ # Returns the else branch of the exception handling statement, if any.
34
+ #
35
+ # @return [Node] the else branch node of the exception handling statement
36
+ # @return [nil] if the exception handling statement does not have an else branch.
37
+ def else_branch
38
+ node_parts[-1]
39
+ end
40
+
41
+ # Checks whether this exception handling statement has an `else` branch.
42
+ #
43
+ # @return [Boolean] whether the exception handling statement has an `else` branch
44
+ def else?
45
+ loc.else
46
+ end
47
+ end
48
+ end
49
+ end
@@ -6,19 +6,7 @@ module RuboCop
6
6
  # plain node when the builder constructs the AST, making its methods
7
7
  # available to all `return` nodes within RuboCop.
8
8
  class ReturnNode < Node
9
- include MethodDispatchNode
10
- include ParameterizedNode
11
-
12
- # Returns the arguments of the `return`.
13
- #
14
- # @return [Array] The arguments of the `return`.
15
- def arguments
16
- if node_parts.one? && node_parts.first.begin_type?
17
- node_parts.first.children
18
- else
19
- node_parts
20
- end
21
- end
9
+ include ParameterizedNode::WrappedArguments
22
10
  end
23
11
  end
24
12
  end
@@ -6,13 +6,19 @@ module RuboCop
6
6
  # node when the builder constructs the AST, making its methods available
7
7
  # to all `send` nodes within RuboCop.
8
8
  class SendNode < Node
9
- include ParameterizedNode
9
+ include ParameterizedNode::RestArguments
10
10
  include MethodDispatchNode
11
11
 
12
12
  def_node_matcher :attribute_accessor?, <<~PATTERN
13
13
  [(send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $...)
14
14
  (_ _ _ _ ...)]
15
15
  PATTERN
16
+
17
+ private
18
+
19
+ def first_argument_index
20
+ 2
21
+ end
16
22
  end
17
23
  end
18
24
  end
@@ -16,6 +16,8 @@ module RuboCop
16
16
  def node_parts
17
17
  [nil, :super, *to_a]
18
18
  end
19
+
20
+ alias arguments children
19
21
  end
20
22
  end
21
23
  end
@@ -14,12 +14,10 @@ module RuboCop
14
14
  end
15
15
 
16
16
  # @deprecated Use `conditions.each`
17
- def each_condition
17
+ def each_condition(&block)
18
18
  return conditions.to_enum(__method__) unless block_given?
19
19
 
20
- conditions.each do |condition|
21
- yield condition
22
- end
20
+ conditions.each(&block)
23
21
 
24
22
  self
25
23
  end
@@ -16,6 +16,8 @@ module RuboCop
16
16
  def node_parts
17
17
  [nil, :yield, *to_a]
18
18
  end
19
+
20
+ alias arguments children
19
21
  end
20
22
  end
21
23
  end
@@ -99,6 +99,7 @@ module RuboCop
99
99
  # # These arguments can be patterns themselves, in
100
100
  # # which case a matcher responding to === will be
101
101
  # # passed.
102
+ # '# comment' # comments are accepted at the end of lines
102
103
  #
103
104
  # You can nest arbitrarily deep:
104
105
  #
@@ -122,6 +123,8 @@ module RuboCop
122
123
  class Compiler
123
124
  SYMBOL = %r{:(?:[\w+@*/?!<>=~|%^-]+|\[\]=?)}.freeze
124
125
  IDENTIFIER = /[a-zA-Z_][a-zA-Z0-9_-]*/.freeze
126
+ COMMENT = /#\s.*$/.freeze
127
+
125
128
  META = Regexp.union(
126
129
  %w"( ) { } [ ] $< < > $... $ ! ^ ` ... + * ?"
127
130
  ).freeze
@@ -202,7 +205,7 @@ module RuboCop
202
205
 
203
206
  def initialize(str, root = 'node0', node_var = root)
204
207
  @string = str
205
- # For def_node_pattern, root == node_var
208
+ # For def_node_matcher, root == node_var
206
209
  # For def_node_search, root is the root node to search on,
207
210
  # and node_var is the current descendant being searched.
208
211
  @root = root
@@ -455,7 +458,6 @@ module RuboCop
455
458
  [0..Float::INFINITY, 'true']
456
459
  end
457
460
 
458
- # rubocop:disable Metrics/AbcSize
459
461
  # rubocop:disable Metrics/MethodLength
460
462
  def compile_any_order(capture_all = nil)
461
463
  rest = capture_rest = nil
@@ -475,7 +477,6 @@ module RuboCop
475
477
  end
476
478
  end
477
479
  # rubocop:enable Metrics/MethodLength
478
- # rubocop:enable Metrics/AbcSize
479
480
 
480
481
  def insure_same_captures(enum, what)
481
482
  return to_enum __method__, enum, what unless block_given?
@@ -790,7 +791,7 @@ module RuboCop
790
791
  end
791
792
 
792
793
  def self.tokens(pattern)
793
- pattern.scan(TOKEN).grep_v(ONLY_SEPARATOR)
794
+ pattern.gsub(COMMENT, '').scan(TOKEN).grep_v(ONLY_SEPARATOR)
794
795
  end
795
796
 
796
797
  # This method minimizes the closure for our method
@@ -72,23 +72,23 @@ module RuboCop
72
72
  end
73
73
 
74
74
  # @deprecated Use `comments.each`
75
- def each_comment
76
- comments.each { |comment| yield comment }
75
+ def each_comment(&block)
76
+ comments.each(&block)
77
77
  end
78
78
 
79
- # @deprecated Use `comments.find`
80
- def find_comment
81
- comments.find { |comment| yield comment }
79
+ # @deprecated Use `comment_at_line`, `each_comment_in_lines`, or `comments.find`
80
+ def find_comment(&block)
81
+ comments.find(&block)
82
82
  end
83
83
 
84
84
  # @deprecated Use `tokens.each`
85
- def each_token
86
- tokens.each { |token| yield token }
85
+ def each_token(&block)
86
+ tokens.each(&block)
87
87
  end
88
88
 
89
89
  # @deprecated Use `tokens.find`
90
- def find_token
91
- tokens.find { |token| yield token }
90
+ def find_token(&block)
91
+ tokens.find(&block)
92
92
  end
93
93
 
94
94
  def file_path
@@ -99,22 +99,39 @@ module RuboCop
99
99
  ast.nil?
100
100
  end
101
101
 
102
+ # @return [Comment, nil] the comment at that line, if any.
103
+ def comment_at_line(line)
104
+ comment_index[line]
105
+ end
106
+
102
107
  # @return [Boolean] if the given line number has a comment.
103
108
  def line_with_comment?(line)
104
- comment_lines.include?(line)
109
+ comment_index.include?(line)
110
+ end
111
+
112
+ # Enumerates on the comments contained with the given `line_range`
113
+ def each_comment_in_lines(line_range)
114
+ return to_enum(:each_comment_in_lines, line_range) unless block_given?
115
+
116
+ line_range.each do |line|
117
+ if (comment = comment_index[line])
118
+ yield comment
119
+ end
120
+ end
105
121
  end
106
122
 
107
123
  # @return [Boolean] if any of the lines in the given `source_range` has a comment.
124
+ # Consider using `each_comment_in_lines` instead
108
125
  def contains_comment?(source_range)
109
- (source_range.line..source_range.last_line).any? do |line|
110
- line_with_comment?(line)
111
- end
126
+ each_comment_in_lines(source_range.line..source_range.last_line).any?
112
127
  end
113
128
  # @deprecated use contains_comment?
114
129
  alias commented? contains_comment?
115
130
 
131
+ # @deprecated Use `each_comment_in_lines`
132
+ # Should have been called `comments_before_or_at_line`. Doubtful it has of any valid use.
116
133
  def comments_before_line(line)
117
- comments.select { |c| c.location.line <= line }
134
+ each_comment_in_lines(0..line).to_a
118
135
  end
119
136
 
120
137
  def start_with?(string)
@@ -142,10 +159,26 @@ module RuboCop
142
159
  .length
143
160
  end
144
161
 
162
+ def tokens_within(range_or_node)
163
+ begin_index = first_token_index(range_or_node)
164
+ end_index = last_token_index(range_or_node)
165
+ sorted_tokens[begin_index..end_index]
166
+ end
167
+
168
+ def first_token_of(range_or_node)
169
+ sorted_tokens[first_token_index(range_or_node)]
170
+ end
171
+
172
+ def last_token_of(range_or_node)
173
+ sorted_tokens[last_token_index(range_or_node)]
174
+ end
175
+
145
176
  private
146
177
 
147
- def comment_lines
148
- @comment_lines ||= comments.map { |c| c.location.line }
178
+ def comment_index
179
+ @comment_index ||= {}.tap do |hash|
180
+ comments.each { |c| hash[c.location.line] = c }
181
+ end
149
182
  end
150
183
 
151
184
  def parse(source, ruby_version)
@@ -156,6 +189,9 @@ module RuboCop
156
189
  @buffer.source = source
157
190
  rescue EncodingError => e
158
191
  @parser_error = e
192
+ @ast = nil
193
+ @comments = []
194
+ @tokens = []
159
195
  return
160
196
  end
161
197
 
@@ -165,13 +201,15 @@ module RuboCop
165
201
  def tokenize(parser)
166
202
  begin
167
203
  ast, comments, tokens = parser.tokenize(@buffer)
168
-
169
- ast.respond_to?(:complete!) && ast.complete!
204
+ ast ||= nil # force `false` to `nil`, see https://github.com/whitequark/parser/pull/722
170
205
  rescue Parser::SyntaxError
171
206
  # All errors are in diagnostics. No need to handle exception.
207
+ comments = []
208
+ tokens = []
172
209
  end
173
210
 
174
- tokens = tokens.map { |t| Token.from_parser_token(t) } if tokens
211
+ ast&.complete!
212
+ tokens.map! { |t| Token.from_parser_token(t) }
175
213
 
176
214
  [ast, comments, tokens]
177
215
  end
@@ -191,9 +229,9 @@ module RuboCop
191
229
  when 2.7
192
230
  require 'parser/ruby27'
193
231
  Parser::Ruby27
194
- when 2.8
195
- require 'parser/ruby28'
196
- Parser::Ruby28
232
+ when 2.8, 3.0
233
+ require 'parser/ruby30'
234
+ Parser::Ruby30
197
235
  else
198
236
  raise ArgumentError,
199
237
  "RuboCop found unknown Ruby version: #{ruby_version.inspect}"
@@ -216,6 +254,32 @@ module RuboCop
216
254
  end
217
255
  end
218
256
  end
257
+
258
+ def first_token_index(range_or_node)
259
+ begin_pos = source_range(range_or_node).begin_pos
260
+ sorted_tokens.bsearch_index { |token| token.begin_pos >= begin_pos }
261
+ end
262
+
263
+ def last_token_index(range_or_node)
264
+ end_pos = source_range(range_or_node).end_pos
265
+ sorted_tokens.bsearch_index { |token| token.end_pos >= end_pos }
266
+ end
267
+
268
+ # The tokens list is always sorted by token position, except for cases when heredoc
269
+ # is passed as a method argument. In this case tokens are interleaved by
270
+ # heredoc contents' tokens.
271
+ def sorted_tokens
272
+ # Use stable sort.
273
+ @sorted_tokens ||= tokens.sort_by.with_index { |token, i| [token.begin_pos, i] }
274
+ end
275
+
276
+ def source_range(range_or_node)
277
+ if range_or_node.respond_to?(:source_range)
278
+ range_or_node.source_range
279
+ else
280
+ range_or_node
281
+ end
282
+ end
219
283
  end
220
284
  end
221
285
  end
@@ -28,7 +28,7 @@ module RuboCop
28
28
  arg_expr pin match_rest if_guard unless_guard
29
29
  match_with_trailing_comma].freeze
30
30
  MANY_CHILD_NODES = %i[dstr dsym xstr regexp array hash pair
31
- mlhs masgn or_asgn and_asgn
31
+ mlhs masgn or_asgn and_asgn rasgn mrasgn
32
32
  undef alias args super yield or and
33
33
  while_post until_post iflipflop eflipflop
34
34
  match_with_lvasgn begin kwbegin return
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module AST
5
5
  module Version
6
- STRING = '0.2.0'
6
+ STRING = '0.5.0'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-ast
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-07-19 00:00:00.000000000 Z
13
+ date: 2020-09-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: parser
@@ -18,14 +18,14 @@ dependencies:
18
18
  requirements:
19
19
  - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: 2.7.0.1
21
+ version: 2.7.1.5
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 2.7.0.1
28
+ version: 2.7.1.5
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: bundler
31
31
  requirement: !ruby/object:Gem::Requirement
@@ -59,6 +59,8 @@ files:
59
59
  - lib/rubocop-ast.rb
60
60
  - lib/rubocop/ast.rb
61
61
  - lib/rubocop/ast/builder.rb
62
+ - lib/rubocop/ast/ext/range.rb
63
+ - lib/rubocop/ast/ext/set.rb
62
64
  - lib/rubocop/ast/node.rb
63
65
  - lib/rubocop/ast/node/alias_node.rb
64
66
  - lib/rubocop/ast/node/and_node.rb
@@ -69,6 +71,7 @@ files:
69
71
  - lib/rubocop/ast/node/case_match_node.rb
70
72
  - lib/rubocop/ast/node/case_node.rb
71
73
  - lib/rubocop/ast/node/class_node.rb
74
+ - lib/rubocop/ast/node/const_node.rb
72
75
  - lib/rubocop/ast/node/def_node.rb
73
76
  - lib/rubocop/ast/node/defined_node.rb
74
77
  - lib/rubocop/ast/node/ensure_node.rb
@@ -94,12 +97,13 @@ files:
94
97
  - lib/rubocop/ast/node/mixin/parameterized_node.rb
95
98
  - lib/rubocop/ast/node/mixin/predicate_operator_node.rb
96
99
  - lib/rubocop/ast/node/module_node.rb
100
+ - lib/rubocop/ast/node/next_node.rb
97
101
  - lib/rubocop/ast/node/or_node.rb
98
102
  - lib/rubocop/ast/node/pair_node.rb
99
103
  - lib/rubocop/ast/node/range_node.rb
100
104
  - lib/rubocop/ast/node/regexp_node.rb
101
105
  - lib/rubocop/ast/node/resbody_node.rb
102
- - lib/rubocop/ast/node/retry_node.rb
106
+ - lib/rubocop/ast/node/rescue_node.rb
103
107
  - lib/rubocop/ast/node/return_node.rb
104
108
  - lib/rubocop/ast/node/self_class_node.rb
105
109
  - lib/rubocop/ast/node/send_node.rb
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module AST
5
- # A node extension for `retry` nodes. This will be used in place of a
6
- # plain node when the builder constructs the AST, making its methods
7
- # available to all `retry` nodes within RuboCop.
8
- class RetryNode < Node
9
- include MethodDispatchNode
10
- include ParameterizedNode
11
-
12
- def arguments
13
- []
14
- end
15
- end
16
- end
17
- end