Skip to content

Commit a8be5f7

Browse files
claude[bot]claude
andauthored
fix(scripts): resolve override members in the symbol-anchor accept set (#16895)
* wip: add override to symbol-anchors member modifier accept set Refs #16821 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU * wip: self-test cases + battery pin Refs #16821 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU * fix(scripts): resolve `override` members in the symbol-anchor accept set The member matcher in scripts/symbol-anchors.mjs carried a hand-enumerated nine modifiers -- readonly, static, public, private, protected, abstract, declare, async and the generator star -- and `override` was the one TS member modifier absent. A symbol anchor naming a real `override` declaration returned `unresolved-symbol`, and the gate's own remedy text left the author only a weaker, file- or class-level anchor. Adds `override ` to that alternation, a rationale comment recording that the enumeration is hand-made and has now been demonstrated incomplete once, and twelve self-test cases -- one per member spelling, each paired with its own negative control -- with the battery floor moved 69 to 93. Purely additive: nothing that resolved before stops resolving. All three corpora that register against this resolver were re-measured before and after and their output is byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU --------- Co-authored-by: claude <noreply@anthropic.com>
1 parent 0939ba0 commit a8be5f7

1 file changed

Lines changed: 75 additions & 3 deletions

File tree

scripts/symbol-anchors.mjs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,44 @@ function scriptSymbolClass(source, symbol) {
332332
new RegExp(`\\b(?:export\\s+)?(?:default\\s+)?(?:declare\\s+)?(?:abstract\\s+)?(?:async\\s+)?(?:function\\s*\\*?|class|interface|type|enum|namespace|module)\\s+${n}\\b`),
333333
// value binding
334334
new RegExp(`\\b(?:export\\s+)?(?:declare\\s+)?(?:const|let|var)\\s+${n}\\b`),
335-
// a member or object-literal key written at the start of a line
336-
new RegExp(`^[ \\t]*(?:readonly |static |public |private |protected |abstract |declare |async |\\* )*(?:get |set )?(?:${n}|'${n}'|"${n}"|\\[${n}\\]|\\['${n}'\\]|\\["${n}"\\])\\s*[?!]?\\s*[:(<=]`, 'm'),
335+
/* A member or object-literal key written at the start of a line.
336+
*
337+
* ⚠️ The modifier alternation below is HAND-ENUMERATED, and it has been
338+
* demonstrated INCOMPLETE once (#16821): `override` was absent, so a
339+
* symbol anchor naming any `override` member -- a dated reading: 276 of
340+
* them under packages/ when this was measured, 207 of those the
341+
* `static override` metadata on CLI command classes -- returned
342+
* `unresolved-symbol` for a declaration that was really there. The gate's
343+
* own remedy text ("name the real symbol, or drop to a file-level
344+
* anchor") then left an author only the WEAKER anchor, because the real
345+
* symbol was already named correctly.
346+
*
347+
* ⭐ And the shape being refused was the one the anchor rule exists for:
348+
* an `override` member is where a subclass restates a base contract, so
349+
* it is exactly where a citation most needs to be checkable. The failure
350+
* was loud per author and silent in aggregate -- the corpus censuses read
351+
* like coverage while being structurally unable to contain an `override`
352+
* member.
353+
*
354+
* ⇒ Widening this list is ADDITIVE: it makes a real declaration resolve
355+
* and refuses nothing that resolved before. But this list is the SHARED
356+
* one -- every registered corpus resolves through it -- so a widening
357+
* moves every census in the same stroke, and a PR that widens it re-takes
358+
* them all and says what moved.
359+
*
360+
* ⛔ Whether this set should be DERIVED rather than enumerated is a live
361+
* question about this resolver and is NOT settled here. It is a
362+
* maintainer's call, because the ruling this module implements is that a
363+
* corpus joins by REGISTRATION and there is to be no second
364+
* implementation -- so a rewrite of the rule is a change to every corpus
365+
* at once, not a local cleanup.
366+
*
367+
* ⛔ The alternation stays a free-order `*` group on purpose. TS fixes the
368+
* written order (accessibility, `static`, `override`, `readonly`,
369+
* `abstract`), but pinning that order here would refuse a spelling for
370+
* being unidiomatic rather than for being ABSENT, and judging style is not
371+
* this rule's job. */
372+
new RegExp(`^[ \\t]*(?:readonly |static |public |private |protected |override |abstract |declare |async |\\* )*(?:get |set )?(?:${n}|'${n}'|"${n}"|\\[${n}\\]|\\['${n}'\\]|\\["${n}"\\])\\s*[?!]?\\s*[:(<=]`, 'm'),
337373
// named re-export
338374
new RegExp(`\\bexport\\s*\\{[^}]*\\b${n}\\b[^}]*\\}`),
339375
// destructured binding
@@ -734,8 +770,11 @@ function assert(cond, msg) { if (!cond) { console.error(`❌ symbol-anchors --se
734770
// to find what stopped registering.
735771
// 63 → 67 when `declinedShape` gained a case per arm (#15809).
736772
// 67 → 69 when the sweep's git child gained an explicit environment (#16624).
773+
// 69 → 93 when the member-modifier spellings gained a case EACH, every one of
774+
// them paired with its own negative control, after `override` was found
775+
// missing from the hand-enumerated accept set (#16821).
737776
const SELF_TEST_BATTERIES = Object.freeze({
738-
'symbol-anchors self-test': 69,
777+
'symbol-anchors self-test': 93,
739778
});
740779

741780
// DELETING an entry silences that battery's floor exactly as effectively as
@@ -801,6 +840,39 @@ export function selfTest() {
801840
check(symbolResolutionClass(ts, 'x.ts', 'sys_metadata') === 'literal', 'a quoted data identifier resolves as `literal`, not `declaration`');
802841
check(symbolResolutionClass(ts, 'x.ts', 'notPresentAnywhere') === null, 'an absent symbol must NOT resolve');
803842

843+
// 1b. ⭐ MEMBER MODIFIERS, one case per spelling, each with its own control.
844+
// The modifier alternation in `scriptSymbolClass` is hand-enumerated and
845+
// has been demonstrated incomplete once (#16821: `override` was missing,
846+
// so no `override` member in the tree could carry a symbol anchor and the
847+
// only remedy on offer was a weaker anchor). ⛔ One token per finding is
848+
// NOT the repair -- an enumeration is worth exactly what it is provoked
849+
// with, so every spelling is driven by name here rather than trusted to
850+
// the one that happened to be reported.
851+
// ⭐ The negative control rides the SAME source as each positive row: a
852+
// matcher that answered `declaration` to everything would satisfy every
853+
// row above and this battery would never be able to fail.
854+
const memberSpellings = [
855+
['plain', 'initObjects(o) {}'],
856+
['async', 'async initObjects(o) {}'],
857+
['public async', 'public async initObjects(o) {}'],
858+
['protected async', 'protected async initObjects(o) {}'],
859+
['private', 'private initObjects(o) {}'],
860+
['override', 'override initObjects(o) {}'],
861+
['override async', 'override async initObjects(o) {}'],
862+
['protected override async', 'protected override async initObjects(o) {}'],
863+
['public override', 'public override initObjects(o) {}'],
864+
['static override', 'static override initObjects(o) {}'],
865+
['override get', 'override get initObjects() { return 1; }'],
866+
['override readonly property', 'override readonly initObjects: number = 1;'],
867+
];
868+
for (const [spelling, member] of memberSpellings) {
869+
const cls = `class Subclass extends Base {\n ${member}\n}\n`;
870+
check(symbolResolutionClass(cls, 'x.ts', 'initObjects') === 'declaration',
871+
`a member declared \`${spelling}\` must resolve as a declaration`);
872+
check(symbolResolutionClass(cls, 'x.ts', 'notPresentAnywhere') === null,
873+
`CONTROL: an absent name must NOT resolve against a \`${spelling}\` member`);
874+
}
875+
804876
// 2. ⭐ The census caveat, enforced: prose is not resolution. A symbol named
805877
// only in a comment is exactly the false green that made 72.1% a LOWER
806878
// bound, so it is provoked directly.

0 commit comments

Comments
 (0)