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
-
.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. -
.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 (orclass << self), outside any def. -
.each_method_definition(root) ⇒ Object
Yields every method definition with its [cpath, singleton, name] key: the receiver form folded in (
def self.xand defs insideclass << selfare singleton;def Foo.xdefines on Foo wherever it lexically sits), so every collection computes method identity the same way. -
.path_segments(constant_path) ⇒ Object
The written segments of a qualified constant — a class/module name, an assignment target, a def receiver.
- .walk(node, cpath, singleton, in_def) {|node, cpath, singleton, in_def| ... } ⇒ Object
Instance Method Summary collapse
- #self?.each {|arg0, arg1, arg2, arg3| ... } ⇒ void
- #self?.each_meta_call {|arg0, arg1, arg2| ... } ⇒ void
- #self?.each_method_definition {|arg0, arg1| ... } ⇒ void
- #self?.path_segments ⇒ [Array[Symbol], bool]?
- #self?.walk {|arg0, arg1, arg2, arg3| ... } ⇒ void
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 (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
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.
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.
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.
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]?
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.
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
|