Class: FiberAudit::Static::SemanticIndex

Inherits:
Object
  • Object
show all
Defined in:
lib/fiber_audit/static/semantic_index.rb

Overview

Semantic index backed by Rubydex 0.2.9.

Line/column conventions (FiberAudit standard)

  • Lines are one-based (1, 2, 3, ...).
  • Columns are zero-based (0, 1, 2, ...).

Rubydex 0.2.9 reports start_line as zero-based and start_column as zero-based. The normalize_location helper adds 1 to start_line so that all returned locations use FiberAudit's one-based line convention.

Defined Under Namespace

Classes: Constant, Declaration, Reference, RubydexGap

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root:) ⇒ SemanticIndex

Returns a new instance of SemanticIndex.



26
27
28
29
30
# File 'lib/fiber_audit/static/semantic_index.rb', line 26

def initialize(root:)
  @root = Pathname.new(root).expand_path.cleanpath
  @graph = nil
  @gaps = []
end

Instance Attribute Details

#gapsObject (readonly)

Returns the value of attribute gaps.



24
25
26
# File 'lib/fiber_audit/static/semantic_index.rb', line 24

def gaps
  @gaps
end

#rootObject (readonly)

Returns the value of attribute root.



24
25
26
# File 'lib/fiber_audit/static/semantic_index.rb', line 24

def root
  @root
end

Instance Method Details

#ancestors_of(name) ⇒ Object

Returns Array of ancestor class/module names. Uses Rubydex's ancestors method which considers ALL class/module declarations (including external/framework ancestors) while the target declaration itself remains workspace-owned (via find_declaration).



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/fiber_audit/static/semantic_index.rb', line 86

def ancestors_of(name)
  return [] unless @graph

  decl = find_declaration(name)
  return [] unless decl
  return [] unless decl.respond_to?(:ancestors)

  ancestors = decl.ancestors
  return [] unless ancestors

  ancestors.select { |ancestor| class_or_module?(ancestor) && ancestor.name != name }
           .map(&:name)
rescue StandardError
  []
end

#buildObject

Build (or rebuild) the index. Clears and deduplicates gaps on every call.



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/fiber_audit/static/semantic_index.rb', line 33

def build
  @gaps = []
  @graph = ::Rubydex::Graph.new
  @graph.workspace_path = @root.to_s
  @graph.index_workspace
  @graph.resolve
  record_gaps
  self
rescue StandardError => e
  @gaps << RubydexGap.new(method: 'build', reason: e.message)
  self
end

#declarationsObject

Returns Array for classes/modules/methods in the workspace. Returns one Declaration per workspace definition site so that reopened classes expose both (or more) definition sites.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/fiber_audit/static/semantic_index.rb', line 49

def declarations
  return [] unless @graph

  @graph.declarations.select { |d| workspace_declaration?(d) }.filter_map do |decl|
    kind = declaration_kind(decl)
    workspace_definition_locations(decl).filter_map do |loc|
      path, line = normalize_location(loc)
      next unless path

      Declaration.new(name: decl.name, kind: kind, path: path, line: line)
    end
  end.flatten
rescue StandardError
  []
end

#descendants_of(name) ⇒ Object

Returns Array of descendant class/module names. Gracefully returns [] if Rubydex cannot compute descendants.



104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/fiber_audit/static/semantic_index.rb', line 104

def descendants_of(name)
  return [] unless @graph

  decl = find_declaration(name)
  return [] unless decl
  return [] unless decl.respond_to?(:descendants)

  descendants = decl.descendants
  return [] unless descendants

  descendants.map(&:name).uniq - [name]
rescue StandardError
  []
end

#references_to(name) ⇒ Object

Returns Array for references to a given constant. Filters to workspace and rescues non-file URIs per reference.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/fiber_audit/static/semantic_index.rb', line 121

def references_to(name)
  return [] unless @graph

  @graph.constant_references.filter_map do |ref|
    next unless ref.respond_to?(:declaration) && ref.declaration&.name == name

    loc = ref.location
    next unless workspace_location?(loc)

    path, line, column = normalize_location_full(loc)
    Reference.new(name: name, path: path, line: line, column: column, context: nil)
  rescue StandardError
    nil
  end
rescue StandardError
  []
end

#resolve_constant(name, nesting:) ⇒ Object

Resolves a constant name to a Constant object. Returns nil if the constant cannot be resolved or is outside the workspace.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/fiber_audit/static/semantic_index.rb', line 67

def resolve_constant(name, nesting:)
  return nil unless @graph

  result = @graph.resolve_constant(name.to_s, nesting || [])
  return nil unless result

  loc = first_workspace_location(result)
  return nil unless loc

  path, line = normalize_location(loc)
  Constant.new(name: result.name, path: path, line: line)
rescue StandardError
  nil
end