rubocop-ast 0.0.2 → 0.4.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -4
  3. data/lib/rubocop/ast.rb +12 -7
  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 +63 -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 +952 -0
  38. data/lib/rubocop/ast/processed_source.rb +285 -0
  39. data/lib/rubocop/ast/token.rb +116 -0
  40. data/lib/rubocop/ast/traversal.rb +6 -4
  41. data/lib/rubocop/ast/version.rb +1 -1
  42. metadata +19 -13
  43. data/lib/rubocop/ast/node/retry_node.rb +0 -17
  44. data/lib/rubocop/error.rb +0 -34
  45. data/lib/rubocop/node_pattern.rb +0 -881
  46. data/lib/rubocop/processed_source.rb +0 -211
  47. data/lib/rubocop/token.rb +0 -114
@@ -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
@@ -2,8 +2,11 @@
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`
9
+ # and (modern only): `index`, `indexasgn`, `lambda`
7
10
  module ParameterizedNode
8
11
  # Checks whether this node's arguments are wrapped in parentheses.
9
12
  #
@@ -56,6 +59,59 @@ module RuboCop
56
59
  arguments? &&
57
60
  (last_argument.block_pass_type? || last_argument.blockarg_type?)
58
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
59
115
  end
60
116
  end
61
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
@@ -31,6 +31,62 @@ module RuboCop
31
31
  def content
32
32
  children.select(&:str_type?).map(&:str_content).join
33
33
  end
34
+
35
+ # @return [Bool] if the regexp is a /.../ literal
36
+ def slash_literal?
37
+ loc.begin.source == '/'
38
+ end
39
+
40
+ # @return [Bool] if the regexp is a %r{...} literal (using any delimiters)
41
+ def percent_r_literal?
42
+ !slash_literal?
43
+ end
44
+
45
+ # @return [String] the regexp delimiters (without %r)
46
+ def delimiters
47
+ [loc.begin.source[-1], loc.end.source[0]]
48
+ end
49
+
50
+ # @return [Bool] if char is one of the delimiters
51
+ def delimiter?(char)
52
+ delimiters.include?(char)
53
+ end
54
+
55
+ # @return [Bool] if regexp contains interpolation
56
+ def interpolation?
57
+ children.any?(&:begin_type?)
58
+ end
59
+
60
+ # @return [Bool] if regexp uses the multiline regopt
61
+ def multiline_mode?
62
+ regopt_include?(:m)
63
+ end
64
+
65
+ # @return [Bool] if regexp uses the extended regopt
66
+ def extended?
67
+ regopt_include?(:x)
68
+ end
69
+
70
+ # @return [Bool] if regexp uses the ignore-case regopt
71
+ def ignore_case?
72
+ regopt_include?(:i)
73
+ end
74
+
75
+ # @return [Bool] if regexp uses the single-interpolation regopt
76
+ def single_interpolation?
77
+ regopt_include?(:o)
78
+ end
79
+
80
+ # @return [Bool] if regexp uses the no-encoding regopt
81
+ def no_encoding?
82
+ regopt_include?(:n)
83
+ end
84
+
85
+ private
86
+
87
+ def regopt_include?(option)
88
+ regopt.children.include?(option)
89
+ end
34
90
  end
35
91
  end
36
92
  end
@@ -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,12 +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
- (send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $...)
13
+ [(send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $...)
14
+ (_ _ _ _ ...)]
14
15
  PATTERN
16
+
17
+ private
18
+
19
+ def first_argument_index
20
+ 2
21
+ end
15
22
  end
16
23
  end
17
24
  end