rubocop-ast 0.0.1 → 0.3.0

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 +23 -4
  3. data/lib/rubocop/ast.rb +10 -7
  4. data/lib/rubocop/ast/builder.rb +6 -1
  5. data/lib/rubocop/ast/ext/range.rb +28 -0
  6. data/lib/rubocop/ast/node.rb +41 -8
  7. data/lib/rubocop/ast/node/array_node.rb +2 -8
  8. data/lib/rubocop/ast/node/break_node.rb +1 -6
  9. data/lib/rubocop/ast/node/case_match_node.rb +3 -9
  10. data/lib/rubocop/ast/node/case_node.rb +13 -9
  11. data/lib/rubocop/ast/node/def_node.rb +5 -24
  12. data/lib/rubocop/ast/node/defined_node.rb +2 -0
  13. data/lib/rubocop/ast/node/float_node.rb +1 -0
  14. data/lib/rubocop/ast/node/forward_args_node.rb +15 -0
  15. data/lib/rubocop/ast/node/hash_node.rb +21 -8
  16. data/lib/rubocop/ast/node/if_node.rb +7 -14
  17. data/lib/rubocop/ast/node/index_node.rb +48 -0
  18. data/lib/rubocop/ast/node/indexasgn_node.rb +50 -0
  19. data/lib/rubocop/ast/node/int_node.rb +1 -0
  20. data/lib/rubocop/ast/node/lambda_node.rb +65 -0
  21. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +2 -8
  22. data/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb +99 -3
  23. data/lib/rubocop/ast/node/mixin/parameterized_node.rb +56 -0
  24. data/lib/rubocop/ast/node/next_node.rb +12 -0
  25. data/lib/rubocop/ast/node/pair_node.rb +2 -2
  26. data/lib/rubocop/ast/node/regexp_node.rb +61 -2
  27. data/lib/rubocop/ast/node/return_node.rb +1 -13
  28. data/lib/rubocop/ast/node/send_node.rb +9 -2
  29. data/lib/rubocop/ast/node/super_node.rb +2 -0
  30. data/lib/rubocop/ast/node/when_node.rb +3 -9
  31. data/lib/rubocop/ast/node/yield_node.rb +2 -0
  32. data/lib/rubocop/ast/node_pattern.rb +952 -0
  33. data/lib/rubocop/ast/processed_source.rb +246 -0
  34. data/lib/rubocop/ast/token.rb +116 -0
  35. data/lib/rubocop/ast/traversal.rb +5 -3
  36. data/lib/rubocop/ast/version.rb +1 -1
  37. metadata +16 -13
  38. data/lib/rubocop/ast/node/retry_node.rb +0 -17
  39. data/lib/rubocop/error.rb +0 -34
  40. data/lib/rubocop/node_pattern.rb +0 -881
  41. data/lib/rubocop/processed_source.rb +0 -211
  42. data/lib/rubocop/token.rb +0 -114
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4c380d9edb3f0bdbdb8d7d9822b25cd96f9d23b7428bf8e8c56e4e19cf5628b
4
- data.tar.gz: 3568dc3fabb7c2d6b0575c9dc07341b4bb0b5daf6d65e284fdd524000a7de019
3
+ metadata.gz: 918c6017183db74497fc68166973904312bac7583f7752c2e55d1829574cb248
4
+ data.tar.gz: 1d04eb24fcbaec96b4bf0f17694018c31034fce4b3d81a9446ca98b3085e05a4
5
5
  SHA512:
6
- metadata.gz: 228b11a44b85a2bb3467afad8d28aadd65d7864ee7a5639773b007bcb2b9eef37ef2daff14d923730ec1b50cf3b7bfd96556ba3573e179615b9c7aba7d787480
7
- data.tar.gz: 061d70e99b00be1f84ab91d18cab96a97829e1ee5a62ef615013eae5994b72f67aa0b6101b64bf0715de6b863ebf60022849b1930ed7e306ff841d86bff39d60
6
+ metadata.gz: 54f6882ebf057c76a9c97212a62b295d03baea85ec7c44e606b412e74edce01b6c4bba93dfafa341756009a8bfa2352c4b2b3b22bdbf689dac08ddd9395a9aff
7
+ data.tar.gz: '08e60b802e83dba688bf1a31be417f9880b640194cf3b966849dd2c211de5a1f3797c8a37be3f2d3aa466d6c994b10c31277b0ed3dc708b12fe1c2f7a0f15552'
data/README.md CHANGED
@@ -1,11 +1,17 @@
1
1
  # RuboCop AST
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rubocop-ast.svg)](https://badge.fury.io/rb/rubocop-ast)
4
- [![CircleCI](https://circleci.com/gh/rubocop-hq/rubocop-ast.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop-ast)
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`
9
+
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.
9
15
 
10
16
  ## Installation
11
17
 
@@ -23,7 +29,20 @@ gem 'rubocop-ast'
23
29
 
24
30
  ## Usage
25
31
 
26
- 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
+ ```
27
46
 
28
47
  ## Contributing
29
48
 
@@ -2,10 +2,10 @@
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/node_pattern'
9
9
  require_relative 'ast/sexp'
10
10
  require_relative 'ast/node'
11
11
  require_relative 'ast/node/mixin/method_identifier_predicates'
@@ -36,15 +36,18 @@ require_relative 'ast/node/forward_args_node'
36
36
  require_relative 'ast/node/float_node'
37
37
  require_relative 'ast/node/hash_node'
38
38
  require_relative 'ast/node/if_node'
39
+ require_relative 'ast/node/index_node'
40
+ require_relative 'ast/node/indexasgn_node'
39
41
  require_relative 'ast/node/int_node'
40
42
  require_relative 'ast/node/keyword_splat_node'
43
+ require_relative 'ast/node/lambda_node'
41
44
  require_relative 'ast/node/module_node'
45
+ require_relative 'ast/node/next_node'
42
46
  require_relative 'ast/node/or_node'
43
47
  require_relative 'ast/node/pair_node'
44
48
  require_relative 'ast/node/range_node'
45
49
  require_relative 'ast/node/regexp_node'
46
50
  require_relative 'ast/node/resbody_node'
47
- require_relative 'ast/node/retry_node'
48
51
  require_relative 'ast/node/return_node'
49
52
  require_relative 'ast/node/self_class_node'
50
53
  require_relative 'ast/node/send_node'
@@ -56,7 +59,7 @@ require_relative 'ast/node/when_node'
56
59
  require_relative 'ast/node/while_node'
57
60
  require_relative 'ast/node/yield_node'
58
61
  require_relative 'ast/builder'
62
+ require_relative 'ast/processed_source'
63
+ require_relative 'ast/token'
59
64
  require_relative 'ast/traversal'
60
-
61
- require_relative 'token'
62
- require_relative 'processed_source'
65
+ require_relative 'ast/version'
@@ -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,
@@ -35,15 +37,18 @@ module RuboCop
35
37
  hash: HashNode,
36
38
  if: IfNode,
37
39
  int: IntNode,
40
+ index: IndexNode,
41
+ indexasgn: IndexasgnNode,
38
42
  irange: RangeNode,
39
43
  erange: RangeNode,
40
44
  kwsplat: KeywordSplatNode,
45
+ lambda: LambdaNode,
41
46
  module: ModuleNode,
47
+ next: NextNode,
42
48
  or: OrNode,
43
49
  pair: PairNode,
44
50
  regexp: RegexpNode,
45
51
  resbody: ResbodyNode,
46
- retry: RetryNode,
47
52
  return: ReturnNode,
48
53
  csend: SendNode,
49
54
  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
@@ -44,6 +44,8 @@ module RuboCop
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 = {})
@@ -310,8 +313,8 @@ module RuboCop
310
313
  def_node_matcher :defined_module0, <<~PATTERN
311
314
  {(class (const $_ $_) ...)
312
315
  (module (const $_ $_) ...)
313
- (casgn $_ $_ (send (const nil? {:Class :Module}) :new ...))
314
- (casgn $_ $_ (block (send (const nil? {:Class :Module}) :new ...) ...))}
316
+ (casgn $_ $_ (send #global_const?({:Class :Module}) :new ...))
317
+ (casgn $_ $_ (block (send #global_const?({:Class :Module}) :new ...) ...))}
315
318
  PATTERN
316
319
 
317
320
  private :defined_module0
@@ -425,6 +428,15 @@ module RuboCop
425
428
  CONDITIONALS.include?(type)
426
429
  end
427
430
 
431
+ def post_condition_loop?
432
+ POST_CONDITION_LOOP_TYPES.include?(type)
433
+ end
434
+
435
+ # Note: `loop { }` is a normal method call and thus not a loop keyword.
436
+ def loop_keyword?
437
+ LOOP_TYPES.include?(type)
438
+ end
439
+
428
440
  def keyword?
429
441
  return true if special_keyword? || send_type? && prefix_not?
430
442
  return false unless KEYWORDS.include?(type)
@@ -456,6 +468,10 @@ module RuboCop
456
468
  parent&.send_type? && parent.arguments.include?(self)
457
469
  end
458
470
 
471
+ def argument_type?
472
+ ARGUMENT_TYPES.include?(type)
473
+ end
474
+
459
475
  def boolean_type?
460
476
  true_type? || false_type?
461
477
  end
@@ -480,16 +496,33 @@ module RuboCop
480
496
 
481
497
  def_node_matcher :proc?, <<~PATTERN
482
498
  {(block (send nil? :proc) ...)
483
- (block (send (const nil? :Proc) :new) ...)
484
- (send (const nil? :Proc) :new)}
499
+ (block (send #global_const?(:Proc) :new) ...)
500
+ (send #global_const?(:Proc) :new)}
485
501
  PATTERN
486
502
 
487
503
  def_node_matcher :lambda?, '({block numblock} (send nil? :lambda) ...)'
488
504
  def_node_matcher :lambda_or_proc?, '{lambda? proc?}'
489
505
 
506
+ def_node_matcher :global_const?, '(const {nil? cbase} %1)'
507
+
490
508
  def_node_matcher :class_constructor?, <<~PATTERN
491
- { (send (const nil? {:Class :Module}) :new ...)
492
- (block (send (const nil? {:Class :Module}) :new ...) ...)}
509
+ { (send #global_const?({:Class :Module}) :new ...)
510
+ (block (send #global_const?({:Class :Module}) :new ...) ...)}
511
+ PATTERN
512
+
513
+ def_node_matcher :struct_constructor?, <<~PATTERN
514
+ (block (send #global_const?(:Struct) :new ...) _ $_)
515
+ PATTERN
516
+
517
+ def_node_matcher :class_definition?, <<~PATTERN
518
+ {(class _ _ $_)
519
+ (sclass _ $_)
520
+ (block (send #global_const?({:Struct :Class}) :new ...) _ $_)}
521
+ PATTERN
522
+
523
+ def_node_matcher :module_definition?, <<~PATTERN
524
+ {(module _ $_)
525
+ (block (send #global_const?(:Module) :new ...) _ $_)}
493
526
  PATTERN
494
527
 
495
528
  # Some expressions are evaluated for their value, some for their side
@@ -500,7 +533,7 @@ module RuboCop
500
533
  # So, does the return value of this node matter? If we changed it to
501
534
  # `(...; nil)`, might that affect anything?
502
535
  #
503
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
536
+ # rubocop:disable Metrics/MethodLength
504
537
  def value_used?
505
538
  # Be conservative and return true if we're not sure.
506
539
  return false if parent.nil?
@@ -522,7 +555,7 @@ module RuboCop
522
555
  true
523
556
  end
524
557
  end
525
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
558
+ # rubocop:enable Metrics/MethodLength
526
559
 
527
560
  # Some expressions are evaluated for their value, some for their side
528
561
  # 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
 
@@ -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
@@ -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