rubocop-ast 0.0.3 → 0.4.1

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 +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