Class: Moult::Index
- Inherits:
-
Object
- Object
- Moult::Index
- Defined in:
- lib/moult/index.rb
Overview
The definition/reference index — Moult's adapter over the rubydex gem and
the only file that names Rubydex. Everything downstream consumes the
Moult-owned Definition value object, never a rubydex type, so the
backend is swappable (the "swap, not rewrite" invariant).
rubydex has two quirks this adapter normalises away (see test/test_index.rb):
- Its locations are 0-based; Moult/Prism are 1-based. We add 1 to line numbers so dead-code symbol ids line up with Scoring's hotspot ids.
- Method references are not resolved to their target declaration (only constants are). So a method is considered "referenced" when its bare name appears anywhere in the call-site collection. This is deliberately conservative: a name collision can only hide a dead method, never invent a false positive — the safe direction for a confidence-graded tool.
Defined Under Namespace
Classes: Definition, Edge
Constant Summary collapse
- BUILTIN_SCHEME =
"file:"
Instance Attribute Summary collapse
-
#override_of ⇒ Object
readonly
FQ name of the ancestor whose method this overrides/implements (reachable via that interface), else nil.
-
#owner_hierarchy_reference_paths ⇒ Object
readonly
for methods: root-relative paths referencing the owner type or any descendant, excluding the hierarchy's own definition files.
-
#reference_count ⇒ Object
readonly
resolvable usages of this definition.
-
#reference_paths ⇒ Object
readonly
root-relative paths that use it.
Class Method Summary collapse
-
.available? ⇒ Boolean
Whether the rubydex backend is loadable.
- .build(root:, paths:) ⇒ Index
Instance Method Summary collapse
-
#definitions ⇒ Array<Index::Definition>
Method + constant definition sites defined within the workspace, each with its resolved reference count.
-
#diagnostics ⇒ Array<String>
Human-readable index diagnostics (non-fatal).
-
#file_edges ⇒ Array<Index::Edge>
Unique src->dst file dependencies from resolved constant references (superclass and mixin clauses flow through the same list, so inheritance edges are included).
-
#initialize(graph:, root:) ⇒ Index
constructor
A new instance of Index.
-
#kind(value) ⇒ Object
:method or :constant.
- #resolved? ⇒ Boolean
-
#singleton(value) ⇒ Object
true for Class.method / constants.
-
#visibility(value) ⇒ Object
:public, :private, :protected.
Constructor Details
#initialize(graph:, root:) ⇒ Index
Returns a new instance of Index.
76 77 78 79 80 81 82 83 84 |
# File 'lib/moult/index.rb', line 76 def initialize(graph:, root:) @graph = graph # rubydex reports canonical (symlink-resolved) paths, so the root must be # canonicalised too or workspace filtering misses everything on systems # where e.g. /tmp -> /private/tmp. @root = File.realpath(root.to_s) rescue Errno::ENOENT @root = root.to_s end |
Instance Attribute Details
#override_of ⇒ Object (readonly)
FQ name of the ancestor whose method this overrides/implements (reachable via that interface), else nil
39 40 41 42 43 44 |
# File 'lib/moult/index.rb', line 39 Definition = Struct.new( :symbol_id, :kind, :name, :unqualified_name, :owner, :visibility, :singleton, :span, :path, :reference_count, :reference_paths, :override_of, :owner_hierarchy_reference_paths ) |
#owner_hierarchy_reference_paths ⇒ Object (readonly)
for methods: root-relative paths referencing the owner type or any descendant, excluding the hierarchy's own definition files. nil when the owner is unknown or defined outside the workspace (fact unavailable), and always nil for constants (their own reference_paths already carry the signal)
39 40 41 42 43 44 |
# File 'lib/moult/index.rb', line 39 Definition = Struct.new( :symbol_id, :kind, :name, :unqualified_name, :owner, :visibility, :singleton, :span, :path, :reference_count, :reference_paths, :override_of, :owner_hierarchy_reference_paths ) |
#reference_count ⇒ Object (readonly)
resolvable usages of this definition
39 40 41 42 43 44 |
# File 'lib/moult/index.rb', line 39 Definition = Struct.new( :symbol_id, :kind, :name, :unqualified_name, :owner, :visibility, :singleton, :span, :path, :reference_count, :reference_paths, :override_of, :owner_hierarchy_reference_paths ) |
#reference_paths ⇒ Object (readonly)
root-relative paths that use it
39 40 41 42 43 44 |
# File 'lib/moult/index.rb', line 39 Definition = Struct.new( :symbol_id, :kind, :name, :unqualified_name, :owner, :visibility, :singleton, :span, :path, :reference_count, :reference_paths, :override_of, :owner_hierarchy_reference_paths ) |
Class Method Details
.available? ⇒ Boolean
Whether the rubydex backend is loadable. Always true once the gem is a hard dependency, but kept so live integration tests can skip cleanly.
68 69 70 71 72 73 |
# File 'lib/moult/index.rb', line 68 def available? require "rubydex" true rescue LoadError false end |
Instance Method Details
#definitions ⇒ Array<Index::Definition>
Returns method + constant definition sites defined within the workspace, each with its resolved reference count.
88 89 90 |
# File 'lib/moult/index.rb', line 88 def definitions @definitions ||= method_definitions + constant_definitions end |
#diagnostics ⇒ Array<String>
Returns human-readable index diagnostics (non-fatal).
97 98 99 100 101 |
# File 'lib/moult/index.rb', line 97 def diagnostics @graph.diagnostics.map(&:to_s) rescue [] end |
#file_edges ⇒ Array<Index::Edge>
Returns unique src->dst file dependencies from resolved constant references (superclass and mixin clauses flow through the same list, so inheritance edges are included). A constant reopened in N files yields an edge to every in-workspace definition file; self-edges and qualifier segments are dropped. Sorted by [src, dst] so output is byte-stable regardless of rubydex iteration order.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/moult/index.rb', line 109 def file_edges @file_edges ||= begin edges = {} resolved = @graph.constant_references.select { |r| r.is_a?(Rubydex::ResolvedConstantReference) } qualifiers = qualifier_references(resolved) resolved.each do |ref| next if qualifiers.include?(ref) src = workspace_relative(ref.location) next unless src span = span_from(ref.location) in_workspace_definitions(ref.declaration).each do |_defn, _span, dst| next if dst == src existing = edges[[src, dst]] next if existing && existing.span.start_line <= span.start_line edges[[src, dst]] = Edge.new(src: src, dst: dst, constant: ref.declaration.name, span: span) end end edges.values.sort_by { |e| [e.src, e.dst] } end end |
#kind=(value) ⇒ Object
:method or :constant
39 40 41 42 43 44 |
# File 'lib/moult/index.rb', line 39 Definition = Struct.new( :symbol_id, :kind, :name, :unqualified_name, :owner, :visibility, :singleton, :span, :path, :reference_count, :reference_paths, :override_of, :owner_hierarchy_reference_paths ) |
#resolved? ⇒ Boolean
92 93 94 |
# File 'lib/moult/index.rb', line 92 def resolved? true end |
#singleton=(value) ⇒ Object
true for Class.method / constants
39 40 41 42 43 44 |
# File 'lib/moult/index.rb', line 39 Definition = Struct.new( :symbol_id, :kind, :name, :unqualified_name, :owner, :visibility, :singleton, :span, :path, :reference_count, :reference_paths, :override_of, :owner_hierarchy_reference_paths ) |
#visibility=(value) ⇒ Object
:public, :private, :protected
39 40 41 42 43 44 |
# File 'lib/moult/index.rb', line 39 Definition = Struct.new( :symbol_id, :kind, :name, :unqualified_name, :owner, :visibility, :singleton, :span, :path, :reference_count, :reference_paths, :override_of, :owner_hierarchy_reference_paths ) |