Module: Ryac::Nesting

Defined in:
lib/ryac/analysis/nesting.rb,
sig/ryac/analysis/nesting.rbs

Overview

Closed-world walk of the Prism tree with the class/module nesting tracked.

The nesting is a lexical fact: class B::C under [:M, :A] defines [:M, :A, :B, :C] — verified against TypeProf, which concatenates the written path onto the enclosing nesting exactly like this rather than resolving B first. Defs and class << self do not change it. The one thing that cannot be computed here is a dynamic path (class self::X, class << other); those bodies are not descended into, which errs on the side of leaving their contents unrenamed.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.each(root, &block) ⇒ Object

Yields every node together with the enclosing [cpath, singleton, in_def] — singleton is true inside class << self, in_def inside a method body.



18
19
20
# File 'lib/ryac/analysis/nesting.rb', line 18

def each(root, &block)
  walk(root, [], false, false, &block)
end

.each_meta_call(root, names, loose: false) ⇒ Object

Yields meta-style declarations (attr_reader :a / include M) under the conditions type analysis recognizes them: a bare call in statement position directly in a class or module body (or class << self), outside any def. Passing loose: true drops everything but the bare-call requirement — for consumers where over-matching is the safe direction.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/ryac/analysis/nesting.rb', line 27

def each_meta_call(root, names, loose: false)
  if loose
    each(root) do |node, cpath, singleton, _in_def|
      next unless node.is_a?(Prism::CallNode) && node.receiver.nil? && names.include?(node.name)

      yield node, cpath, singleton
    end
  else
    each(root) do |node, cpath, singleton, in_def|
      next unless node.is_a?(Prism::StatementsNode)
      next if in_def
      # any? rather than empty?: the type-dependent empty? transform does
      # not reach a fixed point on this code under self-hosting.
      next unless cpath.any? || singleton

      node.body.each do |child|
        next unless child.is_a?(Prism::CallNode) && child.receiver.nil? && names.include?(child.name)

        yield child, cpath, singleton
      end
    end
  end
end

.each_method_definition(root) ⇒ Object

Yields every method definition with its [cpath, singleton, name] key: the receiver form folded in (def self.x and defs inside class << self are singleton; def Foo.x defines on Foo wherever it lexically sits), so every collection computes method identity the same way.



55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/ryac/analysis/nesting.rb', line 55

def each_method_definition(root)
  each(root) do |node, cpath, sclass_singleton, _in_def|
    next unless node.is_a?(Prism::DefNode)

    singleton = sclass_singleton || !node.receiver.nil?
    key_cpath = cpath
    receiver = node.receiver
    if receiver && !receiver.is_a?(Prism::SelfNode)
      segments, _absolute = path_segments(receiver)
      key_cpath = segments if segments
    end
    yield node, [key_cpath, singleton, node.name].freeze # steep:ignore ArgumentTypeMismatch
  end
end

.path_segments(constant_path) ⇒ Object

The written segments of a qualified constant — a class/module name, an assignment target, a def receiver. Returns [segments, absolute] — absolute when written ::X — or nil when the path is not statically known (self::X, expr::X).



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/ryac/analysis/nesting.rb', line 74

def path_segments(constant_path)
  segments = [] #: Array[Symbol]
  current = constant_path
  while current.is_a?(Prism::ConstantPathNode) || current.is_a?(Prism::ConstantPathTargetNode)
    # Prism types #name as Symbol? only for parse-degenerate paths.
    segments.unshift(current.name) # steep:ignore ArgumentTypeMismatch
    current = current.parent
  end

  case current
  when Prism::ConstantReadNode
    [segments.unshift(current.name), false]
  when nil
    [segments, true]
  end
end

.walk(node, cpath, singleton, in_def) {|node, cpath, singleton, in_def| ... } ⇒ Object

Yields:

  • (node, cpath, singleton, in_def)


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/ryac/analysis/nesting.rb', line 91

def walk(node, cpath, singleton, in_def, &block)
  return unless node.is_a?(Prism::Node)

  yield node, cpath, singleton, in_def

  case node
  when Prism::ClassNode, Prism::ModuleNode
    # The name path is walked too: its segments are constant references
    # in their own right (counted and aliased like any other).
    walk(node.constant_path, cpath, singleton, in_def, &block)
    walk(node.superclass, cpath, singleton, in_def, &block) if node.is_a?(Prism::ClassNode) && node.superclass
    segments, absolute = path_segments(node.constant_path)
    return unless segments

    inner = absolute ? segments : cpath + segments
    walk(node.body, inner, false, false, &block)
  when Prism::SingletonClassNode
    walk(node.expression, cpath, singleton, in_def, &block)
    return unless node.expression.is_a?(Prism::SelfNode)

    walk(node.body, cpath, true, in_def, &block)
  when Prism::DefNode
    # The receiver of `def Foo.bar` is deliberately not walked: type
    # analysis has no nodes there, and every renamer that cares reads
    # node.receiver off the def itself.
    walk(node.parameters, cpath, singleton, true, &block)
    walk(node.body, cpath, singleton, true, &block)
  else
    node.compact_child_nodes.each { |child| walk(child, cpath, singleton, in_def, &block) }
  end
end

Instance Method Details

#self?.each {|arg0, arg1, arg2, arg3| ... } ⇒ void

This method returns an undefined value.

Parameters:

  • root (Prism::Node)

Yields:

Yield Parameters:

  • arg0 (Prism::Node)
  • arg1 (Array[Symbol])
  • arg2 (Boolean)
  • arg3 (Boolean)

Yield Returns:

  • (void)


3
# File 'sig/ryac/analysis/nesting.rbs', line 3

def self?.each: (Prism::Node root) { (Prism::Node, Array[Symbol], bool, bool) -> void } -> void

#self?.each_meta_call {|arg0, arg1, arg2| ... } ⇒ void

This method returns an undefined value.

Parameters:

  • root (Prism::Node)
  • names (Array[Symbol])
  • loose: (Boolean)

Yields:

Yield Parameters:

  • arg0 (Prism::CallNode)
  • arg1 (Array[Symbol])
  • arg2 (Boolean)

Yield Returns:

  • (void)


4
# File 'sig/ryac/analysis/nesting.rbs', line 4

def self?.each_meta_call: (Prism::Node root, Array[Symbol] names, ?loose: bool) { (Prism::CallNode, Array[Symbol], bool) -> void } -> void

#self?.each_method_definition {|arg0, arg1| ... } ⇒ void

This method returns an undefined value.

Parameters:

  • root (Prism::Node)

Yields:

Yield Parameters:

  • arg0 (Prism::DefNode)
  • arg1 ([Array[Symbol], bool, Symbol])

Yield Returns:

  • (void)


5
# File 'sig/ryac/analysis/nesting.rbs', line 5

def self?.each_method_definition: (Prism::Node root) { (Prism::DefNode, [Array[Symbol], bool, Symbol]) -> void } -> void

#self?.path_segments ⇒ [Array[Symbol], bool]?

Parameters:

  • constant_path (Prism::Node)

Returns:

  • ([Array[Symbol], bool], nil)


6
# File 'sig/ryac/analysis/nesting.rbs', line 6

def self?.path_segments: (Prism::Node constant_path) -> [Array[Symbol], bool]?

#self?.walk {|arg0, arg1, arg2, arg3| ... } ⇒ void

This method returns an undefined value.

Parameters:

  • node (Prism::Node, nil)
  • cpath (Array[Symbol])
  • singleton (Boolean)
  • in_def (Boolean)

Yields:

Yield Parameters:

  • arg0 (Prism::Node)
  • arg1 (Array[Symbol])
  • arg2 (Boolean)
  • arg3 (Boolean)

Yield Returns:

  • (void)


7
# File 'sig/ryac/analysis/nesting.rbs', line 7

def self?.walk: (Prism::Node? node, Array[Symbol] cpath, bool singleton, bool in_def) { (Prism::Node, Array[Symbol], bool, bool) -> void } -> void