mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-09 03:17:54 +00:00
fix: drop redundant TypeAlias pairs and keep bare identifiers off class members
Two regressions the full suite caught after the review fixes, both real.
`schema-pair-coverage` failed with eleven hand-declared pairs that a rule now
generates. Adding `TypeAlias` to `LINKABLE_LABELS` — needed so
`resolveDefGraphId` can bridge an alias def to its node — also makes it a
SCOPE_BRIDGE source and target, so the cross-product produces `File|TypeAlias`,
`TypeAlias|Property` and nine others that round 1 had declared by hand. Removed;
the invariant is that no pair is both generated and hand-declared.
This also changes what the structural-pair sentinel means, and the comment is
corrected rather than left overstating it: `TypeAlias|Property` is no longer
load-bearing because the label is off the generated grid — it is load-bearing
because it now depends on `TypeAlias` being IN `LINKABLE_LABELS`. Remove it and
the pair stops being generated while the hand declaration is gone, which is the
same state that silently breaks alias consumer edges.
`block-scope-shadowing` failed because a bare identifier resolved to a class
`Property`. `class Box { baseUrl = '...'; pick() { const baseUrl = ...; return
baseUrl; } }` linked the block-local read to `Box.baseUrl`, duplicating the
legitimate `this.baseUrl` edge. A bare identifier is not a member access: with
no receiver there is no object whose property it could be, and in JS/TS a field
read needs `this.`. Receiver-less read/write sites no longer accept `Property`
hits; callables stay reachable, so `cb = save` naming a top-level function is
unaffected.
That defect PREDATES this branch's TypeScript captures — JavaScript has emitted
bare-identifier reads since A2 and no class fixture exercised the shadow. The
TS parity added here is what surfaced it.
Golden snapshot regenerated after verifying the drift line by line: exactly
+5 USES from type annotations in the mini-repo, every pre-existing count
unchanged, so nothing was rewired.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
411cac9b90
commit
6ce853acf0
4 changed files with 41 additions and 26 deletions
|
|
@ -220,7 +220,25 @@ function lookupForSite(
|
|||
...(site.explicitReceiver !== undefined ? { explicitReceiver: site.explicitReceiver } : {}),
|
||||
};
|
||||
const fieldHits = fieldRegistry.lookup(site.name, site.inScope, fieldOpts);
|
||||
if (fieldHits.length > 0) return fieldHits;
|
||||
// A BARE IDENTIFIER is not a member access. With no receiver there is no
|
||||
// object whose `Property` this could be, so a hit on one is a false edge:
|
||||
// in JS/TS/Python/Ruby a field read needs `this.` / `self.` / `@`, and the
|
||||
// bare name means the nearest lexical binding instead.
|
||||
//
|
||||
// Observed: `class Box { baseUrl = '...'; pick() { const baseUrl = ...;
|
||||
// return baseUrl; } }` linked the block-local read to `Box.baseUrl`,
|
||||
// duplicating the legitimate `this.baseUrl` edge. That predates the
|
||||
// TypeScript captures added here — JavaScript has emitted bare-identifier
|
||||
// reads since A2 and no class fixture exercised the shadow.
|
||||
//
|
||||
// Callables are deliberately still reachable: `cb = save` naming a
|
||||
// top-level function is a real bare-name reference, which is why the
|
||||
// method/class fallbacks below are untouched.
|
||||
const receiverlessFieldHits =
|
||||
site.explicitReceiver === undefined
|
||||
? fieldHits.filter((hit) => hit.def?.type !== 'Property')
|
||||
: fieldHits;
|
||||
if (receiverlessFieldHits.length > 0) return receiverlessFieldHits;
|
||||
const methodHits = methodRegistry.lookup(site.name, site.inScope);
|
||||
if (methodHits.length > 0) return methodHits;
|
||||
return classRegistry.lookup(site.name, site.inScope);
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ const ATTACHMENT_TARGET_LABELS: readonly NodeTableName[] = [
|
|||
*
|
||||
* What survives here is characteristic, not arbitrary. Almost all of it is a
|
||||
* TARGET no rule reaches — `CodeElement`, `Impl`, `Namespace`, `Template`,
|
||||
* `TypeAlias`, `Typedef`, `Union`, `Static`, `Section`, `Folder` are in neither
|
||||
* `Typedef`, `Union`, `Static`, `Section`, `Folder` are in neither
|
||||
* `SCOPE_BRIDGE_TARGET_LABELS` nor {@link ATTACHMENT_TARGET_LABELS} — plus the
|
||||
* `Impl|*` and `Template|*` member rows (Rust `impl`/`trait` bodies, C++
|
||||
* templates), the two `Route|Process` / `Tool|Process` entry points whose
|
||||
|
|
@ -437,7 +437,6 @@ export const STRUCTURAL_PAIR_DDL = ` FROM File TO Folder,
|
|||
FROM File TO \`Union\`,
|
||||
FROM File TO \`Namespace\`,
|
||||
FROM File TO \`Impl\`,
|
||||
FROM File TO \`TypeAlias\`,
|
||||
FROM File TO \`Static\`,
|
||||
FROM File TO \`Template\`,
|
||||
FROM File TO Section,
|
||||
|
|
@ -445,20 +444,17 @@ export const STRUCTURAL_PAIR_DDL = ` FROM File TO Folder,
|
|||
FROM Folder TO File,
|
||||
FROM Function TO \`Template\`,
|
||||
FROM Function TO \`Namespace\`,
|
||||
FROM Function TO \`TypeAlias\`,
|
||||
FROM Function TO \`Impl\`,
|
||||
FROM Function TO \`Typedef\`,
|
||||
FROM Function TO \`Union\`,
|
||||
FROM Function TO CodeElement,
|
||||
FROM Class TO \`Template\`,
|
||||
FROM Class TO \`TypeAlias\`,
|
||||
FROM Class TO \`Impl\`,
|
||||
FROM Class TO \`Union\`,
|
||||
FROM Class TO \`Namespace\`,
|
||||
FROM Class TO \`Typedef\`,
|
||||
FROM Class TO CodeElement,
|
||||
FROM Method TO \`Template\`,
|
||||
FROM Method TO \`TypeAlias\`,
|
||||
FROM Method TO \`Namespace\`,
|
||||
FROM Method TO \`Impl\`,
|
||||
FROM Method TO CodeElement,
|
||||
|
|
@ -480,8 +476,6 @@ export const STRUCTURAL_PAIR_DDL = ` FROM File TO Folder,
|
|||
FROM CodeElement TO \`Property\`,
|
||||
FROM Section TO Section,
|
||||
FROM Interface TO CodeElement,
|
||||
FROM Interface TO \`TypeAlias\`,
|
||||
FROM \`Enum\` TO \`TypeAlias\`,
|
||||
FROM \`Namespace\` TO \`Struct\`,
|
||||
FROM \`Impl\` TO Method,
|
||||
FROM \`Impl\` TO Function,
|
||||
|
|
@ -490,15 +484,10 @@ export const STRUCTURAL_PAIR_DDL = ` FROM File TO Folder,
|
|||
FROM \`Impl\` TO \`Trait\`,
|
||||
FROM \`Impl\` TO \`Struct\`,
|
||||
FROM \`Impl\` TO \`Impl\`,
|
||||
FROM \`TypeAlias\` TO \`Trait\`,
|
||||
FROM \`TypeAlias\` TO Class,
|
||||
FROM \`TypeAlias\` TO \`Property\`,
|
||||
FROM \`TypeAlias\` TO Method,
|
||||
FROM \`Record\` TO Method,
|
||||
FROM \`Record\` TO \`Constructor\`,
|
||||
FROM \`Record\` TO \`Property\`,
|
||||
FROM \`Constructor\` TO \`Template\`,
|
||||
FROM \`Constructor\` TO \`TypeAlias\`,
|
||||
FROM \`Constructor\` TO \`Impl\`,
|
||||
FROM \`Constructor\` TO \`Namespace\`,
|
||||
FROM \`Constructor\` TO \`Typedef\`,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"fixture": "mini-repo",
|
||||
"totalFileCount": 7,
|
||||
"symbols": 41,
|
||||
"relationships": 80,
|
||||
"relationships": 85,
|
||||
"processes": 4,
|
||||
"byType": {
|
||||
"Class": 1,
|
||||
|
|
@ -25,7 +25,8 @@
|
|||
"HAS_PROPERTY": 8,
|
||||
"IMPORTS": 12,
|
||||
"MEMBER_OF": 12,
|
||||
"STEP_IN_PROCESS": 12
|
||||
"STEP_IN_PROCESS": 12,
|
||||
"USES": 5
|
||||
},
|
||||
"edgeDigest": "d617ad94554d760eb0497bf5365b7b0a2e21842ab5b16568def721b954aa1594"
|
||||
"edgeDigest": "c55bd8307a5fccbfd23d5aa93f241e26ef0f45c1a9e57691f2bf4f6602e8e4ab"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,21 +117,28 @@ const NON_BRIDGE_CORPUS = [
|
|||
// died on any repo containing `type X = { ... }`. Every resolver test still
|
||||
// passed, because they build an in-memory graph and never write to the DB —
|
||||
// this suite is the only place that difference shows up.
|
||||
// `TypeAlias|Property` is the LOAD-BEARING sentinel: `TypeAlias` is on the
|
||||
// eleven-table list this suite exists for, so no cross-product generates it.
|
||||
// `Interface|Property` was listed alongside it and is tautological — both
|
||||
// labels are in the SCOPE_BRIDGE cross-product, so that pair is emitted by
|
||||
// construction and the sentinel cannot fail. Dropped rather than left to
|
||||
// read as coverage.
|
||||
// `TypeAlias` USED to be off the generated grid, which is why round 1 hand-
|
||||
// declared its pairs. It is now in `LINKABLE_LABELS` (the def→graph-node
|
||||
// bridge needs it), which makes it a SCOPE_BRIDGE source and target, so the
|
||||
// cross-product generates these pairs and the hand declarations were
|
||||
// removed as redundant.
|
||||
//
|
||||
// The sentinel is still load-bearing, for a different reason than before:
|
||||
// it now depends on `TypeAlias` being in `LINKABLE_LABELS`. Take it out and
|
||||
// the pair stops being generated AND the hand declaration is gone, so this
|
||||
// fails — which is exactly the state that also silently breaks alias
|
||||
// consumer edges. `Interface|Property` was dropped from this entry because
|
||||
// it is tautological in the ordinary way: both labels were always in the
|
||||
// cross-product, so nothing about it could ever fail.
|
||||
fixture: 'typescript-alias-fields',
|
||||
emitter: 'object-type alias HAS_PROPERTY',
|
||||
sentinels: ['TypeAlias|Property'],
|
||||
},
|
||||
{
|
||||
// `TypeAlias|Method` is declared in `schema.ts` but no fixture contained a
|
||||
// method-shaped alias member, so nothing proved it was the right pair for
|
||||
// what is actually emitted — a declared pair that no emitter exercises is
|
||||
// indistinguishable from a missing one until an analyze aborts on it.
|
||||
// Nothing in the corpus contained a method-shaped alias member, so nothing
|
||||
// proved `TypeAlias|Method` was the right pair for what is actually
|
||||
// emitted — a pair no emitter exercises is indistinguishable from a missing
|
||||
// one until an analyze aborts on a real repo.
|
||||
fixture: 'typescript-alias-methods',
|
||||
emitter: 'object-type alias HAS_METHOD',
|
||||
sentinels: ['TypeAlias|Method'],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue