rubocop-ast 0.0.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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