rubocop-ast 0.0.3 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -4
  3. data/lib/rubocop/ast.rb +9 -1
  4. data/lib/rubocop/ast/builder.rb +8 -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 +81 -10
  8. data/lib/rubocop/ast/node/array_node.rb +2 -8
  9. data/lib/rubocop/ast/node/block_node.rb +1 -1
  10. data/lib/rubocop/ast/node/break_node.rb +1 -6
  11. data/lib/rubocop/ast/node/case_match_node.rb +3 -9
  12. data/lib/rubocop/ast/node/case_node.rb +13 -9
  13. data/lib/rubocop/ast/node/const_node.rb +65 -0
  14. data/lib/rubocop/ast/node/def_node.rb +5 -24
  15. data/lib/rubocop/ast/node/defined_node.rb +2 -0
  16. data/lib/rubocop/ast/node/float_node.rb +1 -0
  17. data/lib/rubocop/ast/node/forward_args_node.rb +15 -0
  18. data/lib/rubocop/ast/node/hash_node.rb +21 -8
  19. data/lib/rubocop/ast/node/if_node.rb +7 -14
  20. data/lib/rubocop/ast/node/index_node.rb +48 -0
  21. data/lib/rubocop/ast/node/indexasgn_node.rb +50 -0
  22. data/lib/rubocop/ast/node/int_node.rb +1 -0
  23. data/lib/rubocop/ast/node/lambda_node.rb +65 -0
  24. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +2 -8
  25. data/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb +99 -3
  26. data/lib/rubocop/ast/node/mixin/parameterized_node.rb +56 -0
  27. data/lib/rubocop/ast/node/next_node.rb +12 -0
  28. data/lib/rubocop/ast/node/pair_node.rb +2 -2
  29. data/lib/rubocop/ast/node/regexp_node.rb +56 -0
  30. data/lib/rubocop/ast/node/resbody_node.rb +21 -0
  31. data/lib/rubocop/ast/node/rescue_node.rb +49 -0
  32. data/lib/rubocop/ast/node/return_node.rb +1 -13
  33. data/lib/rubocop/ast/node/send_node.rb +9 -2
  34. data/lib/rubocop/ast/node/super_node.rb +2 -0
  35. data/lib/rubocop/ast/node/when_node.rb +3 -9
  36. data/lib/rubocop/ast/node/yield_node.rb +2 -0
  37. data/lib/rubocop/ast/node_pattern.rb +184 -115
  38. data/lib/rubocop/ast/processed_source.rb +98 -16
  39. data/lib/rubocop/ast/traversal.rb +6 -4
  40. data/lib/rubocop/ast/version.rb +1 -1
  41. metadata +16 -9
  42. data/lib/rubocop/ast/node/retry_node.rb +0 -17
@@ -24,21 +24,21 @@ module RuboCop
24
24
  #
25
25
  # @return [Boolean] whether the `def` node uses argument forwarding
26
26
  def argument_forwarding?
27
- arguments.any?(&:forward_args_type?)
27
+ arguments.any?(&:forward_args_type?) || arguments.any?(&:forward_arg_type?)
28
28
  end
29
29
 
30
30
  # The name of the defined method as a symbol.
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
@@ -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
@@ -5,6 +5,21 @@ module RuboCop
5
5
  # A node extension for `forward-args` nodes. This will be used in place
6
6
  # of a plain node when the builder constructs the AST, making its methods
7
7
  # available to all `forward-args` nodes within RuboCop.
8
+ #
9
+ # Not used with modern emitters:
10
+ #
11
+ # $ ruby-parse -e "def foo(...); end"
12
+ # (def :foo
13
+ # (args
14
+ # (forward-arg)) nil)
15
+ # $ ruby-parse --legacy -e "->(foo) { bar }"
16
+ # (def :foo
17
+ # (forward-args) nil)
18
+ #
19
+ # Note the extra 's' with legacy form.
20
+ #
21
+ # The main RuboCop runs in legacy mode; this node is only used
22
+ # if user `AST::Builder.modernize` or `AST::Builder.emit_lambda=true`
8
23
  class ForwardArgsNode < Node
9
24
  include CollectionNode
10
25
 
@@ -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,20 +52,22 @@ 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
- def each_key
59
+ def each_key(&block)
51
60
  return pairs.map(&:key).to_enum unless block_given?
52
61
 
53
- pairs.map(&:key).each do |key|
54
- yield key
55
- end
62
+ pairs.map(&:key).each(&block)
56
63
 
57
64
  self
58
65
  end
59
66
 
60
67
  # Returns an array of all the values in the `hash` literal.
61
68
  #
69
+ # @note `kwsplat` nodes are ignored.
70
+ #
62
71
  # @return [Array<Node>] an array of values in the `hash` literal
63
72
  def values
64
73
  each_pair.map(&:value)
@@ -67,14 +76,14 @@ module RuboCop
67
76
  # Calls the given block for each `value` node in the `hash` literal.
68
77
  # If no block is given, an `Enumerator` is returned.
69
78
  #
79
+ # @note `kwsplat` nodes are ignored.
80
+ #
70
81
  # @return [self] if a block is given
71
82
  # @return [Enumerator] if no block is given
72
- def each_value
83
+ def each_value(&block)
73
84
  return pairs.map(&:value).to_enum unless block_given?
74
85
 
75
- pairs.map(&:value).each do |value|
76
- yield value
77
- end
86
+ pairs.map(&:value).each(&block)
78
87
 
79
88
  self
80
89
  end
@@ -85,6 +94,8 @@ module RuboCop
85
94
  # @note A multiline `pair` is considered to be on the same line if it
86
95
  # shares any of its lines with another `pair`
87
96
  #
97
+ # @note `kwsplat` nodes are ignored.
98
+ #
88
99
  # @return [Boolean] whether any `pair` nodes are on the same line
89
100
  def pairs_on_same_line?
90
101
  pairs.each_cons(2).any? { |first, second| first.same_line?(second) }
@@ -93,6 +104,8 @@ module RuboCop
93
104
  # Checks whether this `hash` uses a mix of hash rocket and colon
94
105
  # delimiters for its pairs.
95
106
  #
107
+ # @note `kwsplat` nodes are ignored.
108
+ #
96
109
  # @return [Boolean] whether the `hash` uses mixed delimiters
97
110
  def mixed_delimiters?
98
111
  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
@@ -148,7 +147,7 @@ module RuboCop
148
147
  def branches
149
148
  branches = [if_branch]
150
149
 
151
- return branches unless else_branch
150
+ return branches unless else?
152
151
 
153
152
  other_branches = if elsif_conditional?
154
153
  else_branch.branches
@@ -158,17 +157,11 @@ 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
166
- def each_branch
160
+ # @deprecated Use `branches.each`
161
+ def each_branch(&block)
167
162
  return branches.to_enum(__method__) unless block_given?
168
163
 
169
- branches.each do |branch|
170
- yield branch
171
- end
164
+ branches.each(&block)
172
165
  end
173
166
  end
174
167
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # Used for modern support only!
6
+ # Not as thoroughly tested as legacy equivalent
7
+ #
8
+ # $ ruby-parse -e "foo[:bar]"
9
+ # (index
10
+ # (send nil :foo)
11
+ # (sym :bar))
12
+ # $ ruby-parse --legacy -e "foo[:bar]"
13
+ # (send
14
+ # (send nil :foo) :[]
15
+ # (sym :bar))
16
+ #
17
+ # The main RuboCop runs in legacy mode; this node is only used
18
+ # if user `AST::Builder.modernize` or `AST::Builder.emit_index=true`
19
+ class IndexNode < Node
20
+ include ParameterizedNode::RestArguments
21
+ include MethodDispatchNode
22
+
23
+ # For similarity with legacy mode
24
+ def attribute_accessor?
25
+ false
26
+ end
27
+
28
+ # For similarity with legacy mode
29
+ def assignment_method?
30
+ false
31
+ end
32
+
33
+ # For similarity with legacy mode
34
+ def method_name
35
+ :[]
36
+ end
37
+
38
+ private
39
+
40
+ # An array containing the arguments of the dispatched method.
41
+ #
42
+ # @return [Array<Node>] the arguments of the dispatched method
43
+ def first_argument_index
44
+ 1
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # Used for modern support only!
6
+ # Not as thoroughly tested as legacy equivalent
7
+ #
8
+ # $ ruby-parse -e "foo[:bar] = :baz"
9
+ # (indexasgn
10
+ # (send nil :foo)
11
+ # (sym :bar)
12
+ # (sym :baz))
13
+ # $ ruby-parse --legacy -e "foo[:bar] = :baz"
14
+ # (send
15
+ # (send nil :foo) :[]=
16
+ # (sym :bar)
17
+ # (sym :baz))
18
+ #
19
+ # The main RuboCop runs in legacy mode; this node is only used
20
+ # if user `AST::Builder.modernize` or `AST::Builder.emit_index=true`
21
+ class IndexasgnNode < Node
22
+ include ParameterizedNode::RestArguments
23
+ include MethodDispatchNode
24
+
25
+ # For similarity with legacy mode
26
+ def attribute_accessor?
27
+ false
28
+ end
29
+
30
+ # For similarity with legacy mode
31
+ def assignment_method?
32
+ true
33
+ end
34
+
35
+ # For similarity with legacy mode
36
+ def method_name
37
+ :[]=
38
+ end
39
+
40
+ private
41
+
42
+ # An array containing the arguments of the dispatched method.
43
+ #
44
+ # @return [Array<Node>] the arguments of the dispatched method
45
+ def first_argument_index
46
+ 1
47
+ end
48
+ end
49
+ end
50
+ 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
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # Used for modern support only:
6
+ # Not as thoroughly tested as legacy equivalent
7
+ #
8
+ # $ ruby-parse -e "->(foo) { bar }"
9
+ # (block
10
+ # (lambda)
11
+ # (args
12
+ # (arg :foo))
13
+ # (send nil :bar))
14
+ # $ ruby-parse --legacy -e "->(foo) { bar }"
15
+ # (block
16
+ # (send nil :lambda)
17
+ # (args
18
+ # (arg :foo))
19
+ # (send nil :bar))
20
+ #
21
+ # The main RuboCop runs in legacy mode; this node is only used
22
+ # if user `AST::Builder.modernize` or `AST::Builder.emit_lambda=true`
23
+ class LambdaNode < Node
24
+ include ParameterizedNode::RestArguments
25
+ include MethodDispatchNode
26
+
27
+ # For similarity with legacy mode
28
+ def lambda?
29
+ true
30
+ end
31
+
32
+ # For similarity with legacy mode
33
+ def lambda_literal?
34
+ true
35
+ end
36
+
37
+ # For similarity with legacy mode
38
+ def attribute_accessor?
39
+ false
40
+ end
41
+
42
+ # For similarity with legacy mode
43
+ def assignment_method?
44
+ false
45
+ end
46
+
47
+ # For similarity with legacy mode
48
+ def receiver
49
+ nil
50
+ end
51
+
52
+ # For similarity with legacy mode
53
+ def method_name
54
+ :lambda
55
+ end
56
+
57
+ private
58
+
59
+ # For similarity with legacy mode
60
+ def first_argument_index
61
+ 2
62
+ end
63
+ end
64
+ end
65
+ end
@@ -3,7 +3,8 @@
3
3
  module RuboCop
4
4
  module AST
5
5
  # Common functionality for nodes that are a kind of method dispatch:
6
- # `send`, `csend`, `super`, `zsuper`, `yield`, `defined?`
6
+ # `send`, `csend`, `super`, `zsuper`, `yield`, `defined?`,
7
+ # and (modern only): `index`, `indexasgn`, `lambda`
7
8
  module MethodDispatchNode
8
9
  extend NodePattern::Macros
9
10
  include MethodIdentifierPredicates
@@ -25,13 +26,6 @@ module RuboCop
25
26
  node_parts[1]
26
27
  end
27
28
 
28
- # An array containing the arguments of the dispatched method.
29
- #
30
- # @return [Array<Node>] the arguments of the dispatched method
31
- def arguments
32
- node_parts[2..-1]
33
- end
34
-
35
29
  # The `block` node associated with this method dispatch, if any.
36
30
  #
37
31
  # @return [BlockNode, nil] the `block` node associated with this method
@@ -6,15 +6,62 @@ module RuboCop
6
6
  # `send`, `csend`, `def`, `defs`, `super`, `zsuper`
7
7
  #
8
8
  # @note this mixin expects `#method_name` and `#receiver` to be implemented
9
- module MethodIdentifierPredicates
9
+ module MethodIdentifierPredicates # rubocop:disable Metrics/ModuleLength
10
10
  ENUMERATOR_METHODS = %i[collect collect_concat detect downto each
11
11
  find find_all find_index inject loop map!
12
12
  map reduce reject reject! reverse_each select
13
- select! times upto].freeze
13
+ select! times upto].to_set.freeze
14
+
15
+ ENUMERABLE_METHODS = (Enumerable.instance_methods + [:each]).to_set.freeze
14
16
 
15
17
  # http://phrogz.net/programmingruby/language.html#table_18.4
16
18
  OPERATOR_METHODS = %i[| ^ & <=> == === =~ > >= < <= << >> + - * /
17
- % ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].freeze
19
+ % ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].to_set.freeze
20
+
21
+ NONMUTATING_BINARY_OPERATOR_METHODS = %i[* / % + - == === != < > <= >= <=>].to_set.freeze
22
+ NONMUTATING_UNARY_OPERATOR_METHODS = %i[+@ -@ ~ !].to_set.freeze
23
+ NONMUTATING_OPERATOR_METHODS = (NONMUTATING_BINARY_OPERATOR_METHODS +
24
+ NONMUTATING_UNARY_OPERATOR_METHODS).freeze
25
+
26
+ NONMUTATING_ARRAY_METHODS = %i[
27
+ all? any? assoc at bsearch bsearch_index collect
28
+ combination compact count cycle deconstruct difference
29
+ dig drop drop_while each each_index empty? eql?
30
+ fetch filter find_index first flatten hash
31
+ include? index inspect intersection join
32
+ last length map max min minmax none? one? pack
33
+ permutation product rassoc reject
34
+ repeated_combination repeated_permutation reverse
35
+ reverse_each rindex rotate sample select shuffle
36
+ size slice sort sum take take_while
37
+ to_a to_ary to_h to_s transpose union uniq
38
+ values_at zip |
39
+ ].to_set.freeze
40
+
41
+ NONMUTATING_HASH_METHODS = %i[
42
+ any? assoc compact dig each each_key each_pair
43
+ each_value empty? eql? fetch fetch_values filter
44
+ flatten has_key? has_value? hash include? inspect
45
+ invert key key? keys? length member? merge rassoc
46
+ rehash reject select size slice to_a to_h to_hash
47
+ to_proc to_s transform_keys transform_values value?
48
+ values values_at
49
+ ].to_set.freeze
50
+
51
+ NONMUTATING_STRING_METHODS = %i[
52
+ ascii_only? b bytes bytesize byteslice capitalize
53
+ casecmp casecmp? center chars chomp chop chr codepoints
54
+ count crypt delete delete_prefix delete_suffix
55
+ downcase dump each_byte each_char each_codepoint
56
+ each_grapheme_cluster each_line empty? encode encoding
57
+ end_with? eql? getbyte grapheme_clusters gsub hash
58
+ hex include index inspect intern length lines ljust lstrip
59
+ match match? next oct ord partition reverse rindex rjust
60
+ rpartition rstrip scan scrub size slice squeeze start_with?
61
+ strip sub succ sum swapcase to_a to_c to_f to_i to_r to_s
62
+ to_str to_sym tr tr_s unicode_normalize unicode_normalized?
63
+ unpack unpack1 upcase upto valid_encoding?
64
+ ].to_set.freeze
18
65
 
19
66
  # Checks whether the method name matches the argument.
20
67
  #
@@ -31,6 +78,48 @@ module RuboCop
31
78
  OPERATOR_METHODS.include?(method_name)
32
79
  end
33
80
 
81
+ # Checks whether the method is a nonmutating binary operator method.
82
+ #
83
+ # @return [Boolean] whether the method is a nonmutating binary operator method
84
+ def nonmutating_binary_operator_method?
85
+ NONMUTATING_BINARY_OPERATOR_METHODS.include?(method_name)
86
+ end
87
+
88
+ # Checks whether the method is a nonmutating unary operator method.
89
+ #
90
+ # @return [Boolean] whether the method is a nonmutating unary operator method
91
+ def nonmutating_unary_operator_method?
92
+ NONMUTATING_UNARY_OPERATOR_METHODS.include?(method_name)
93
+ end
94
+
95
+ # Checks whether the method is a nonmutating operator method.
96
+ #
97
+ # @return [Boolean] whether the method is a nonmutating operator method
98
+ def nonmutating_operator_method?
99
+ NONMUTATING_OPERATOR_METHODS.include?(method_name)
100
+ end
101
+
102
+ # Checks whether the method is a nonmutating Array method.
103
+ #
104
+ # @return [Boolean] whether the method is a nonmutating Array method
105
+ def nonmutating_array_method?
106
+ NONMUTATING_ARRAY_METHODS.include?(method_name)
107
+ end
108
+
109
+ # Checks whether the method is a nonmutating Hash method.
110
+ #
111
+ # @return [Boolean] whether the method is a nonmutating Hash method
112
+ def nonmutating_hash_method?
113
+ NONMUTATING_HASH_METHODS.include?(method_name)
114
+ end
115
+
116
+ # Checks whether the method is a nonmutating String method.
117
+ #
118
+ # @return [Boolean] whether the method is a nonmutating String method
119
+ def nonmutating_string_method?
120
+ NONMUTATING_STRING_METHODS.include?(method_name)
121
+ end
122
+
34
123
  # Checks whether the method is a comparison method.
35
124
  #
36
125
  # @return [Boolean] whether the method is a comparison
@@ -53,6 +142,13 @@ module RuboCop
53
142
  method_name.to_s.start_with?('each_')
54
143
  end
55
144
 
145
+ # Checks whether the method is an Enumerable method.
146
+ #
147
+ # @return [Boolean] whether the method is an Enumerable method
148
+ def enumerable_method?
149
+ ENUMERABLE_METHODS.include?(method_name)
150
+ end
151
+
56
152
  # Checks whether the method is a predicate method.
57
153
  #
58
154
  # @return [Boolean] whether the method is a predicate method