rubocop-ast 0.0.3 → 0.4.1

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 (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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b0c196be6c58699fcb84209860508c96e69e8122adb8e14d6641134009802712
4
- data.tar.gz: a2d50c781f612349b4aa7e24a4ea4008c359ce766a6f5e0f7e925ac78d57dbb7
3
+ metadata.gz: 4bf5651a5da2be43d773d152c2f2260dfd1850ce1075aca7c4d21c08dd31a92a
4
+ data.tar.gz: c9addcd67e29e797109dc2f523c9c2094f94908b40c2e60c56061d6a8bf0e0f5
5
5
  SHA512:
6
- metadata.gz: 2d263c6a0ab978cb1b81dde253ce752c2b64af9e94c59ac6a6a364aef3cec37c03028e4d7c00086c11b8b46abae9373edad636528782f84ad914ea527b644ec0
7
- data.tar.gz: edeefb4a74f678920907e130ea6a7fea86b4c67cf9a9bbaefa21ef231738c0de541d61de5d9c234384e46b3243a74ffaac84288b2c68c49e3178cb39d2d96b37
6
+ metadata.gz: 1aa33edeacd95a65468093b80248a5908c69d30bf7d19afd6141044d00830f31c12aa489caf8ef705364d63771cade73651d9697635ab22941137df467ce4e92
7
+ data.tar.gz: fb820b52d69df72ab17a5ee06d06bdf7256b3318d8094b96467f79d314bad1b52bcb3938cb2ef98ca07b300f56132ac14921ef24aadf299d4c5754bb118b119f
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::AST::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::AST::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,7 +2,10 @@
2
2
 
3
3
  require 'parser'
4
4
  require 'forwardable'
5
+ require 'set'
5
6
 
7
+ require_relative 'ast/ext/range'
8
+ require_relative 'ast/ext/set'
6
9
  require_relative 'ast/node_pattern'
7
10
  require_relative 'ast/sexp'
8
11
  require_relative 'ast/node'
@@ -26,6 +29,7 @@ require_relative 'ast/node/break_node'
26
29
  require_relative 'ast/node/case_match_node'
27
30
  require_relative 'ast/node/case_node'
28
31
  require_relative 'ast/node/class_node'
32
+ require_relative 'ast/node/const_node'
29
33
  require_relative 'ast/node/def_node'
30
34
  require_relative 'ast/node/defined_node'
31
35
  require_relative 'ast/node/ensure_node'
@@ -34,15 +38,19 @@ require_relative 'ast/node/forward_args_node'
34
38
  require_relative 'ast/node/float_node'
35
39
  require_relative 'ast/node/hash_node'
36
40
  require_relative 'ast/node/if_node'
41
+ require_relative 'ast/node/index_node'
42
+ require_relative 'ast/node/indexasgn_node'
37
43
  require_relative 'ast/node/int_node'
38
44
  require_relative 'ast/node/keyword_splat_node'
45
+ require_relative 'ast/node/lambda_node'
39
46
  require_relative 'ast/node/module_node'
47
+ require_relative 'ast/node/next_node'
40
48
  require_relative 'ast/node/or_node'
41
49
  require_relative 'ast/node/pair_node'
42
50
  require_relative 'ast/node/range_node'
43
51
  require_relative 'ast/node/regexp_node'
52
+ require_relative 'ast/node/rescue_node'
44
53
  require_relative 'ast/node/resbody_node'
45
- require_relative 'ast/node/retry_node'
46
54
  require_relative 'ast/node/return_node'
47
55
  require_relative 'ast/node/self_class_node'
48
56
  require_relative 'ast/node/send_node'
@@ -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
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # A node extension for `const` nodes.
6
+ class ConstNode < Node
7
+ # The `send` node associated with this block.
8
+ #
9
+ # @return [Node, nil] the node associated with the scope (e.g. cbase, const, ...)
10
+ def namespace
11
+ children[0]
12
+ end
13
+
14
+ # @return [Symbol] the demodulized name of the constant: "::Foo::Bar" => :Bar
15
+ def short_name
16
+ children[1]
17
+ end
18
+
19
+ # The body of this block.
20
+ #
21
+ # @return [Boolean] if the constant is a Module / Class, according to the standard convention.
22
+ # Note: some classes might have uppercase in which case this method
23
+ # returns false
24
+ def module_name?
25
+ short_name.match?(/[[:lower:]]/)
26
+ end
27
+ alias class_name? module_name?
28
+
29
+ # @return [Boolean] if the constant starts with `::` (aka s(:cbase))
30
+ def absolute?
31
+ return false unless namespace
32
+
33
+ each_path.first.cbase_type?
34
+ end
35
+
36
+ # @return [Boolean] if the constant does not start with `::` (aka s(:cbase))
37
+ def relative?
38
+ !absolute?
39
+ end
40
+
41
+ # Yield nodes for the namespace
42
+ #
43
+ # For `::Foo::Bar::BAZ` => yields:
44
+ # s(:cbase), then
45
+ # s(:const, :Foo), then
46
+ # s(:const, s(:const, :Foo), :Bar)
47
+ def each_path(&block)
48
+ return to_enum(__method__) unless block_given?
49
+
50
+ descendants = []
51
+ last = self
52
+ loop do
53
+ last = last.children.first
54
+ break if last.nil?
55
+
56
+ descendants << last
57
+ break unless last.const_type?
58
+ end
59
+ descendants.reverse_each(&block)
60
+
61
+ self
62
+ end
63
+ end
64
+ end
65
+ end