All files / transpiler/state AdrProvenance.ts

100% Statements 17/17
100% Branches 7/7
100% Functions 4/4
100% Lines 17/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70                                                40x 40x       1407x                       577x 62x   515x         308x 308x 308x 201x 201x 15x   186x 186x   308x       309x 309x          
/**
 * Issue #1241: records where an ADR's rule fired during transpilation.
 *
 * Occupancy of the scope-context matrix is derived from source POSITIONS. A
 * diagnostic supplies one; a codegen decision did not, so ADRs governing
 * successful resolution (ADR-016, ADR-057) had fixtures that could never occupy
 * a cell -- the gate read `warn` for seventeen cells that eleven fixtures were
 * already exercising. That is an observability gap, not a coverage gap, and
 * writing more fixtures could not have closed it.
 *
 * Deliberately a plain sink, not a decision-maker: it records that a rule fired
 * at a position and answers nothing about which cell that is. Classification
 * stays in the matrix tooling, walking the parse tree, so provenance cannot
 * drift from the fixture the way a declared cell can.
 *
 * The current file lives here rather than being passed at every call. A
 * recording site is a one-liner inside a decision it is already making, and
 * threading a path through helpers that deliberately take injected deps
 * (TypeGenerationHelper) would have made those helpers know about global state
 * purely to report on themselves.
 */
import type IRecordedAdrSite from "../types/IRecordedAdrSite";
 
class AdrProvenance {
  private static sites: IRecordedAdrSite[] = [];
  private static currentSourcePath: string | null = null;
 
  /** Attribute subsequent recordings to `sourcePath`. */
  static beginFile(sourcePath: string | null): void {
    this.currentSourcePath = sourcePath;
  }
 
  /**
   * Note that `adr`'s rule fired at `line` of the current file.
   *
   * Silently ignores a call with no current file or a non-positive line: a
   * position that cannot be resolved must not become occupancy for whatever
   * declaration happens to start the file, which is the same reason
   * FixtureContext refuses the synthetic 1:0 placeholder.
   */
  static record(adr: string, line: number | null | undefined): void {
    if (!this.currentSourcePath || line == null || line <= 0) {
      return;
    }
    this.sites.push({ adr, sourcePath: this.currentSourcePath, line });
  }
 
  /** Every site recorded since the last reset, deduplicated. */
  static collect(): readonly IRecordedAdrSite[] {
    const seen = new Set<string>();
    const unique: IRecordedAdrSite[] = [];
    for (const site of this.sites) {
      const key = `${site.adr} ${site.sourcePath} ${site.line}`;
      if (seen.has(key)) {
        continue;
      }
      seen.add(key);
      unique.push(site);
    }
    return unique;
  }
 
  static reset(): void {
    this.sites = [];
    this.currentSourcePath = null;
  }
}
 
export default AdrProvenance;