Class: Rigor::Inference::CoverageScanner
- Inherits:
-
Object
- Object
- Rigor::Inference::CoverageScanner
- Defined in:
- lib/rigor/inference/coverage_scanner.rb
Overview
Walks an AST and reports per-node-class coverage of ‘Rigor::Scope#type_of`.
For every visited node the scanner runs ‘type_of` with a fresh `FallbackTracer` and inspects the first recorded event:
-
If the first event’s ‘node_class` matches the visited node’s class, the engine entered the fallback (else) branch *for this very node* —the node is counted as **directly unrecognized**.
-
Otherwise the typer either succeeded outright or recursed into a child that itself was unrecognized; the visited node is counted as recognized so pass-through wrappers (‘ProgramNode`, `StatementsNode`, `ParenthesesNode`, …) are not double-counted along with their leaves.
This class is intended for tooling probes and CI gates rather than the hot inference path: it allocates a tracer per visited node and discards the inferred type values.
Defined Under Namespace
Classes: Result
Instance Method Summary collapse
-
#initialize(scope: nil) ⇒ CoverageScanner
constructor
A new instance of CoverageScanner.
- #scan(root) ⇒ Result
Constructor Details
#initialize(scope: nil) ⇒ CoverageScanner
Returns a new instance of CoverageScanner.
48 49 50 |
# File 'lib/rigor/inference/coverage_scanner.rb', line 48 def initialize(scope: nil) @scope = scope || Scope.empty end |
Instance Method Details
#scan(root) ⇒ Result
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/rigor/inference/coverage_scanner.rb', line 54 def scan(root) visits = Hash.new(0) unrecognized = Hash.new(0) events = [] # Build the per-node scope index once per scan so locals bound # earlier in the program flow into the scope used to type every # later node. The indexer walks the program with a tracer-less # StatementEvaluator and propagates the recorded scope down to # expression-interior nodes the evaluator does not visit. The # second pass below re-types each node with its own tracer so # the per-class fallback statistics stay attributable. scope_index = ScopeIndexer.index(root, default_scope: @scope) Source::NodeWalker.each(root) do |node| visits[node.class] += 1 tracer = FallbackTracer.new scope_index[node].type_of(node, tracer: tracer) first_event = tracer.events.first next unless first_event && first_event.node_class == node.class unrecognized[node.class] += 1 events << first_event end Result.new(visits: visits, unrecognized: unrecognized, events: events) end |