All files / types UNSET_SOURCE_SPAN.ts

100% Statements 1/1
100% Branches 0/0
100% Functions 0/0
100% Lines 1/1

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                                                                              160x                
import type ISourceSpan from "./ISourceSpan";
 
/**
 * Issue #1318: the span a scope carries before any declaring block has been
 * collected.
 *
 * A scope is the one symbol built before its position is known.
 * `SymbolRegistry.getOrCreateScope` mints it on first reference, which may be a
 * member collector reaching for its enclosing scope, and only
 * `ScopeCollector` -- running on the `scope` block itself -- can say where the
 * scope was written. So there is a window in which the symbol exists and its
 * position does not.
 *
 * Line 0 is what marks that window. It cannot collide with a real position
 * because ANTLR lines are 1-based, which is what lets `ScopeCollector` keep the
 * FIRST declaring block rather than the last: ADR-016 permits a scope to be
 * reopened, and before #1334 the position was assigned unconditionally, so a
 * scope declared in four files reported only the fourth and a conflict naming
 * two definitions printed one location twice.
 *
 * It is a named constant rather than an inline literal because the writer
 * (`ScopeUtils.createScope`) and the reader (`ScopeCollector`) are in different
 * layers. Spelling the sentinel twice is how the two come to disagree about
 * which value means "unset" -- the shape #1300 records, where two places held
 * one fact and the header believed the wrong one.
 */
/*
 * Frozen because every scope created before its declaring block is collected
 * shares this ONE object. `readonly` on `ISourceSpan` is compile-time only, and
 * `ScopeCollector` deliberately casts around it (`scope as unknown as {...}`),
 * so a single `mutableScope.span.line = n` written in place of
 * `mutableScope.span = span` would rewrite the sentinel for every unset scope
 * in the run -- and the `span.line === UNSET_SOURCE_SPAN.line` check would then
 * read "already set" for all of them. The old sentinel was the primitive `0`,
 * where that was not representable. Freezing restores that property.
 *
 * The comparison stays a VALUE comparison (`span.line === ...`), never
 * `span === UNSET_SOURCE_SPAN`: identity does not survive a cache round-trip.
 */
const UNSET_SOURCE_SPAN: ISourceSpan = Object.freeze({
  line: 0,
  column: 0,
  endLine: 0,
  endColumn: 0,
});
 
export default UNSET_SOURCE_SPAN;