mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-07 02:58:02 +00:00
feat(typescript): make object-type aliases own their members
A TS object-type alias declares the same `property_signature` members as the interface beside it and answers the same question, but was not a member owner: its fields were minted with bare ids and no owner edge, so two aliases in one file sharing a field name collapsed onto one node, while the identical interface resolved normally. `type_alias_declaration` joins CLASS_CONTAINER_TYPES (and CONTAINER_TYPE_TO_LABEL, as that set's invariant requires — a container missing there gets orphaned member edges or a wrong owner label). Aliases with no object type (`type Id = string`) declare no members, so they own nothing and are unaffected. This also lands the INTERFACE field -> consumer edges, verified on the mini-repo fixture rather than only on a purpose-built one: `saveToDb` now links to `ValidationResult.value`, and `formatLogEntry` to `LogEntry.level` and `LogEntry.message` — three real contract-field reads that previously had no graph path at all. Golden updated: +3 ACCESSES, no node changes. The ALIAS field -> consumer edge is still not linked and is recorded as a todo with the exact blocker: resolving a receiver typed as the alias needs the NAME to resolve to a class-like def, and `isClassLike` is Class|Interface|Struct|Record|Enum|Trait. That predicate is read from ~12 sites including MRO and heritage, and every language mints TypeAlias, so widening it would enrol aliases in linearizations where they do not belong. Widening only the scope index was tried and reverted — the type-name walkers gate on it independently, so it fixed nothing and left dead code. That needs a deliberate "shape-like" concept, not more call-site widening. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6ec8dfc3cd
commit
448868f891
3 changed files with 29 additions and 5 deletions
|
|
@ -335,6 +335,15 @@ export const CLASS_CONTAINER_TYPES = new Set([
|
|||
'class_declaration',
|
||||
'abstract_class_declaration',
|
||||
'interface_declaration',
|
||||
// A TypeScript object-type alias owns its members exactly as the interface
|
||||
// beside it does — same `property_signature` members, same "who reads this
|
||||
// contract field?" question. Without it an alias member is minted with a
|
||||
// bare id and no owner, so two aliases in one file sharing a field name
|
||||
// collapse onto one node and nothing links the field to its consumers,
|
||||
// while the identical interface resolves. Aliases with no object type
|
||||
// (`type Id = string`) declare no members, so they own nothing and are
|
||||
// unaffected.
|
||||
'type_alias_declaration',
|
||||
'struct_declaration',
|
||||
'record_declaration',
|
||||
'class_specifier',
|
||||
|
|
@ -398,6 +407,9 @@ export const CONTAINER_TYPE_TO_LABEL: Record<string, string> = {
|
|||
class_declaration: 'Class',
|
||||
abstract_class_declaration: 'Class',
|
||||
interface_declaration: 'Interface',
|
||||
// Required by the CLASS_CONTAINER_TYPES invariant above: a container missing
|
||||
// here gets orphaned member edges or a wrong owner label.
|
||||
type_alias_declaration: 'TypeAlias',
|
||||
struct_declaration: 'Struct',
|
||||
struct_specifier: 'Struct',
|
||||
class_specifier: 'Class',
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"fixture": "mini-repo",
|
||||
"totalFileCount": 7,
|
||||
"symbols": 41,
|
||||
"relationships": 77,
|
||||
"relationships": 80,
|
||||
"processes": 4,
|
||||
"byType": {
|
||||
"Class": 1,
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
"Property": 8
|
||||
},
|
||||
"byRelType": {
|
||||
"ACCESSES": 3,
|
||||
"CALLS": 9,
|
||||
"CONTAINS": 7,
|
||||
"DEFINES": 16,
|
||||
|
|
@ -26,5 +27,5 @@
|
|||
"MEMBER_OF": 12,
|
||||
"STEP_IN_PROCESS": 12
|
||||
},
|
||||
"edgeDigest": "1ccfc9e5de04d7e71430bcfeb3ce61fe85135b826f33aad2b1bdd2ee193e4728"
|
||||
"edgeDigest": "d617ad94554d760eb0497bf5365b7b0a2e21842ab5b16568def721b954aa1594"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,8 +73,19 @@ describe('TypeScript type-alias and interface members (A4)', () => {
|
|||
// `fieldFallbackOnMethodLookup: false` (scope-resolver.ts) because name
|
||||
// matching over-connects in a typed language, and the unique-name pass
|
||||
// honors that opt-out. The precise path is the only route for TS, by design.
|
||||
it.todo('links an alias field to its consumer');
|
||||
it.todo('links an interface field to its consumer');
|
||||
it('links an interface field to its consumer', () => {
|
||||
expect(readersOf('ifaceSlots')).toContain('renderIface');
|
||||
});
|
||||
|
||||
void readersOf;
|
||||
// The ALIAS half still does not link, and the remaining blocker is now
|
||||
// exact: resolving `cfg: LiveModeConfig` to its members requires the name
|
||||
// `LiveModeConfig` to resolve to a CLASS-LIKE def, and `isClassLike` is
|
||||
// Class|Interface|Struct|Record|Enum|Trait — no TypeAlias. That predicate is
|
||||
// consulted from ~12 sites including MRO and heritage, and every language
|
||||
// mints TypeAlias (Rust type_item, Kotlin/Swift/Dart typealias, C typedef),
|
||||
// so widening it would enrol aliases in linearization where they do not
|
||||
// belong. Widening only the scope index was tried and is NOT sufficient —
|
||||
// the type-name walkers gate on it independently. Needs a deliberate
|
||||
// "shape-like" concept rather than more call-site widening.
|
||||
it.todo('links an alias field to its consumer');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue