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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0750c7434a863460f8b900080b40ed7515c6e1eb415d0b91ce612f6bcac7a67
4
- data.tar.gz: 0b08bbe1e7ed4612351d4296264f18bbe0e3af5d069410d46e017f4826ac1bc9
3
+ metadata.gz: a691b22abba3de79be3aca5f66247b20b5bdd8fe0bcbe1403d5455c4da25fae9
4
+ data.tar.gz: d0984b47a3d0f683447361683acb58afa4fcfffb1597bed575cce1d34fafd90e
5
5
  SHA512:
6
- metadata.gz: 7259c958f33d8a754275a5c25d8b6094f2f41be2a058935dca5d689903accd0bba77bff7ac440501c35274fb00f9a9c29c4c02f8e988e2a7bb0f3fc334773cab
7
- data.tar.gz: e28f39434e75ad138bc49646e36d9bbb39fe25011d0bd41034c4da02d5bbb801b52e5caf06a05453e9ada004b158e1c573deb4749468f126fb4cbb88bebd3a74
6
+ metadata.gz: 165cfc53926eecd29099f29554dae5e751bb1bee7c4fc42f49abe93db18ea3dbf6589b7b57e6e16aaabf7e842ea253dd41609ab2af343c44db4181af4c815c79
7
+ data.tar.gz: f150ffa3ae23ae80f4e3572a529fd19d42509e025e013c02c52eb408c60f3feaf55ef1d53304c19586ad2bf3b3b0ca7aac18503bc45b56ed3ad534dc875d894f
data/README.md CHANGED
@@ -2,12 +2,16 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rubocop-ast.svg)](https://badge.fury.io/rb/rubocop-ast)
4
4
  [![CI](https://github.com/rubocop-hq/rubocop-ast/workflows/CI/badge.svg)](https://github.com/rubocop-hq/rubocop-ast/actions?query=workflow%3ACI)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/a29666e6373bc41bc0a9/test_coverage)](https://codeclimate.com/github/rubocop-hq/rubocop-ast/test_coverage)
6
+ [![Maintainability](https://api.codeclimate.com/v1/badges/a29666e6373bc41bc0a9/maintainability)](https://codeclimate.com/github/rubocop-hq/rubocop-ast/maintainability)
5
7
 
6
8
  Contains the classes needed by [RuboCop](https://github.com/rubocop-hq/rubocop) to deal with Ruby's AST, in particular:
7
- * `RuboCop::AST::Node`
8
- * `RuboCop::NodePattern` ([doc](manual/node_pattern.md))
9
9
 
10
- This gem may be used independently from the main RuboCop gem.
10
+ * `RuboCop::AST::Node` ([doc](docs/modules/ROOT/pages/node_types.adoc))
11
+ * `RuboCop::AST::NodePattern` ([doc](docs/modules/ROOT/pages/node_pattern.adoc))
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
14
+ dependency is the [parser](https://github.com/whitequark/parser) gem, which `rubocop-ast` extends.
11
15
 
12
16
  ## Installation
13
17
 
@@ -25,7 +29,20 @@ gem 'rubocop-ast'
25
29
 
26
30
  ## Usage
27
31
 
28
- Refer to the documentation of `RuboCop::AST::Node` and [`RuboCop::NodePattern`](manual/node_pattern.md)
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.
35
+
36
+ ### Parser compatibility switches
37
+
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
41
+ as `LambdaNode` instead of `SendNode`:
42
+
43
+ ```ruby
44
+ RuboCop::AST::Builder.emit_lambda = true
45
+ ```
29
46
 
30
47
  ## Contributing
31
48
 
@@ -2,10 +2,11 @@
2
2
 
3
3
  require 'parser'
4
4
  require 'forwardable'
5
+ require 'set'
5
6
 
6
- require_relative 'error'
7
- require_relative 'node_pattern'
8
-
7
+ require_relative 'ast/ext/range'
8
+ require_relative 'ast/ext/set'
9
+ require_relative 'ast/node_pattern'
9
10
  require_relative 'ast/sexp'
10
11
  require_relative 'ast/node'
11
12
  require_relative 'ast/node/mixin/method_identifier_predicates'
@@ -28,6 +29,7 @@ require_relative 'ast/node/break_node'
28
29
  require_relative 'ast/node/case_match_node'
29
30
  require_relative 'ast/node/case_node'
30
31
  require_relative 'ast/node/class_node'
32
+ require_relative 'ast/node/const_node'
31
33
  require_relative 'ast/node/def_node'
32
34
  require_relative 'ast/node/defined_node'
33
35
  require_relative 'ast/node/ensure_node'
@@ -36,15 +38,19 @@ require_relative 'ast/node/forward_args_node'
36
38
  require_relative 'ast/node/float_node'
37
39
  require_relative 'ast/node/hash_node'
38
40
  require_relative 'ast/node/if_node'
41
+ require_relative 'ast/node/index_node'
42
+ require_relative 'ast/node/indexasgn_node'
39
43
  require_relative 'ast/node/int_node'
40
44
  require_relative 'ast/node/keyword_splat_node'
45
+ require_relative 'ast/node/lambda_node'
41
46
  require_relative 'ast/node/module_node'
47
+ require_relative 'ast/node/next_node'
42
48
  require_relative 'ast/node/or_node'
43
49
  require_relative 'ast/node/pair_node'
44
50
  require_relative 'ast/node/range_node'
45
51
  require_relative 'ast/node/regexp_node'
52
+ require_relative 'ast/node/rescue_node'
46
53
  require_relative 'ast/node/resbody_node'
47
- require_relative 'ast/node/retry_node'
48
54
  require_relative 'ast/node/return_node'
49
55
  require_relative 'ast/node/self_class_node'
50
56
  require_relative 'ast/node/send_node'
@@ -56,8 +62,7 @@ require_relative 'ast/node/when_node'
56
62
  require_relative 'ast/node/while_node'
57
63
  require_relative 'ast/node/yield_node'
58
64
  require_relative 'ast/builder'
65
+ require_relative 'ast/processed_source'
66
+ require_relative 'ast/token'
59
67
  require_relative 'ast/traversal'
60
68
  require_relative 'ast/version'
61
-
62
- require_relative 'token'
63
- require_relative 'processed_source'
@@ -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,
@@ -35,15 +38,19 @@ module RuboCop
35
38
  hash: HashNode,
36
39
  if: IfNode,
37
40
  int: IntNode,
41
+ index: IndexNode,
42
+ indexasgn: IndexasgnNode,
38
43
  irange: RangeNode,
39
44
  erange: RangeNode,
40
45
  kwsplat: KeywordSplatNode,
46
+ lambda: LambdaNode,
41
47
  module: ModuleNode,
48
+ next: NextNode,
42
49
  or: OrNode,
43
50
  pair: PairNode,
44
51
  regexp: RegexpNode,
52
+ rescue: RescueNode,
45
53
  resbody: ResbodyNode,
46
- retry: RetryNode,
47
54
  return: ReturnNode,
48
55
  csend: SendNode,
49
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,12 +38,14 @@ 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
 
45
45
  BASIC_CONDITIONALS = %i[if while until].freeze
46
46
  CONDITIONALS = [*BASIC_CONDITIONALS, :case].freeze
47
+ POST_CONDITION_LOOP_TYPES = %i[while_post until_post].freeze
48
+ LOOP_TYPES = (POST_CONDITION_LOOP_TYPES + %i[while until for]).freeze
47
49
  VARIABLES = %i[ivar gvar cvar lvar].freeze
48
50
  REFERENCES = %i[nth_ref back_ref].freeze
49
51
  KEYWORDS = %i[alias and break case class def defs defined?
@@ -53,6 +55,7 @@ module RuboCop
53
55
  yield].freeze
54
56
  OPERATOR_KEYWORDS = %i[and or].freeze
55
57
  SPECIAL_KEYWORDS = %w[__FILE__ __LINE__ __ENCODING__].freeze
58
+ ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg].freeze
56
59
 
57
60
  # @see https://www.rubydoc.info/gems/ast/AST/Node:initialize
58
61
  def initialize(type, children = [], properties = {})
@@ -113,12 +116,50 @@ module RuboCop
113
116
 
114
117
  # Returns the index of the receiver node in its siblings. (Sibling index
115
118
  # uses zero based numbering.)
119
+ # Use is discouraged, this is a potentially slow method.
116
120
  #
117
- # @return [Integer] the index of the receiver node in its siblings
121
+ # @return [Integer, nil] the index of the receiver node in its siblings
118
122
  def sibling_index
119
123
  parent&.children&.index { |sibling| sibling.equal?(self) }
120
124
  end
121
125
 
126
+ # Use is discouraged, this is a potentially slow method and can lead
127
+ # to even slower algorithms
128
+ # @return [Node, nil] the right (aka next) sibling
129
+ def right_sibling
130
+ return unless parent
131
+
132
+ parent.children[sibling_index + 1].freeze
133
+ end
134
+
135
+ # Use is discouraged, this is a potentially slow method and can lead
136
+ # to even slower algorithms
137
+ # @return [Node, nil] the left (aka previous) sibling
138
+ def left_sibling
139
+ i = sibling_index
140
+ return if i.nil? || i.zero?
141
+
142
+ parent.children[i - 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 [Array<Node>] the left (aka previous) siblings
148
+ def left_siblings
149
+ return [].freeze unless parent
150
+
151
+ parent.children[0...sibling_index].freeze
152
+ end
153
+
154
+ # Use is discouraged, this is a potentially slow method and can lead
155
+ # to even slower algorithms
156
+ # @return [Array<Node>] the right (aka next) siblings
157
+ def right_siblings
158
+ return [].freeze unless parent
159
+
160
+ parent.children[sibling_index + 1..-1].freeze
161
+ end
162
+
122
163
  # Common destructuring method. This can be used to normalize
123
164
  # destructuring for different variations of the node.
124
165
  # Some node types override this with their own custom
@@ -310,8 +351,8 @@ module RuboCop
310
351
  def_node_matcher :defined_module0, <<~PATTERN
311
352
  {(class (const $_ $_) ...)
312
353
  (module (const $_ $_) ...)
313
- (casgn $_ $_ (send (const nil? {:Class :Module}) :new ...))
314
- (casgn $_ $_ (block (send (const nil? {:Class :Module}) :new ...) ...))}
354
+ (casgn $_ $_ (send #global_const?({:Class :Module}) :new ...))
355
+ (casgn $_ $_ (block (send #global_const?({:Class :Module}) :new ...) ...))}
315
356
  PATTERN
316
357
 
317
358
  private :defined_module0
@@ -425,6 +466,15 @@ module RuboCop
425
466
  CONDITIONALS.include?(type)
426
467
  end
427
468
 
469
+ def post_condition_loop?
470
+ POST_CONDITION_LOOP_TYPES.include?(type)
471
+ end
472
+
473
+ # Note: `loop { }` is a normal method call and thus not a loop keyword.
474
+ def loop_keyword?
475
+ LOOP_TYPES.include?(type)
476
+ end
477
+
428
478
  def keyword?
429
479
  return true if special_keyword? || send_type? && prefix_not?
430
480
  return false unless KEYWORDS.include?(type)
@@ -456,6 +506,10 @@ module RuboCop
456
506
  parent&.send_type? && parent.arguments.include?(self)
457
507
  end
458
508
 
509
+ def argument_type?
510
+ ARGUMENT_TYPES.include?(type)
511
+ end
512
+
459
513
  def boolean_type?
460
514
  true_type? || false_type?
461
515
  end
@@ -480,16 +534,33 @@ module RuboCop
480
534
 
481
535
  def_node_matcher :proc?, <<~PATTERN
482
536
  {(block (send nil? :proc) ...)
483
- (block (send (const nil? :Proc) :new) ...)
484
- (send (const nil? :Proc) :new)}
537
+ (block (send #global_const?(:Proc) :new) ...)
538
+ (send #global_const?(:Proc) :new)}
485
539
  PATTERN
486
540
 
487
541
  def_node_matcher :lambda?, '({block numblock} (send nil? :lambda) ...)'
488
542
  def_node_matcher :lambda_or_proc?, '{lambda? proc?}'
489
543
 
544
+ def_node_matcher :global_const?, '(const {nil? cbase} %1)'
545
+
490
546
  def_node_matcher :class_constructor?, <<~PATTERN
491
- { (send (const nil? {:Class :Module}) :new ...)
492
- (block (send (const nil? {:Class :Module}) :new ...) ...)}
547
+ { (send #global_const?({:Class :Module}) :new ...)
548
+ (block (send #global_const?({:Class :Module}) :new ...) ...)}
549
+ PATTERN
550
+
551
+ def_node_matcher :struct_constructor?, <<~PATTERN
552
+ (block (send #global_const?(:Struct) :new ...) _ $_)
553
+ PATTERN
554
+
555
+ def_node_matcher :class_definition?, <<~PATTERN
556
+ {(class _ _ $_)
557
+ (sclass _ $_)
558
+ (block (send #global_const?({:Struct :Class}) :new ...) _ $_)}
559
+ PATTERN
560
+
561
+ def_node_matcher :module_definition?, <<~PATTERN
562
+ {(module _ $_)
563
+ (block (send #global_const?(:Module) :new ...) _ $_)}
493
564
  PATTERN
494
565
 
495
566
  # Some expressions are evaluated for their value, some for their side
@@ -500,7 +571,7 @@ module RuboCop
500
571
  # So, does the return value of this node matter? If we changed it to
501
572
  # `(...; nil)`, might that affect anything?
502
573
  #
503
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
574
+ # rubocop:disable Metrics/MethodLength
504
575
  def value_used?
505
576
  # Be conservative and return true if we're not sure.
506
577
  return false if parent.nil?
@@ -522,7 +593,7 @@ module RuboCop
522
593
  true
523
594
  end
524
595
  end
525
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
596
+ # rubocop:enable Metrics/MethodLength
526
597
 
527
598
  # Some expressions are evaluated for their value, some for their side
528
599
  # effects, and some for both.
@@ -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
 
@@ -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
@@ -15,17 +15,11 @@ 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
23
- def each_in_pattern
18
+ # @deprecated Use `in_pattern_branches.each`
19
+ def each_in_pattern(&block)
24
20
  return in_pattern_branches.to_enum(__method__) unless block_given?
25
21
 
26
- in_pattern_branches.each do |condition|
27
- yield condition
28
- end
22
+ in_pattern_branches.each(&block)
29
23
 
30
24
  self
31
25
  end
@@ -15,17 +15,11 @@ 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
23
- def each_when
18
+ # @deprecated Use `when_branches.each`
19
+ def each_when(&block)
24
20
  return when_branches.to_enum(__method__) unless block_given?
25
21
 
26
- when_branches.each do |condition|
27
- yield condition
28
- end
22
+ when_branches.each(&block)
29
23
 
30
24
  self
31
25
  end
@@ -37,6 +31,16 @@ module RuboCop
37
31
  node_parts[1...-1]
38
32
  end
39
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
+
40
44
  # Returns the else branch of the `case` statement, if any.
41
45
  #
42
46
  # @return [Node] the else branch node of the `case` statement