@@ -1715,23 +1715,41 @@ function extractTypeMapWalk(
17151715 callAssignments ?: CallAssignment [ ] ,
17161716 fnRefBindings ?: FnRefBinding [ ] ,
17171717) : void {
1718- function walk ( node : TreeSitterNode , depth : number ) : void {
1718+ function walk ( node : TreeSitterNode , depth : number , currentClass : string | null ) : void {
17191719 if ( depth >= MAX_WALK_DEPTH ) return ;
17201720 const t = node . type ;
1721+ if ( t === 'class_declaration' || t === 'abstract_class_declaration' ) {
1722+ const nameNode = node . childForFieldName ( 'name' ) ;
1723+ const className = nameNode ?. text ?? null ;
1724+ for ( let i = 0 ; i < node . childCount ; i ++ ) {
1725+ walk ( node . child ( i ) ! , depth + 1 , className ) ;
1726+ }
1727+ return ;
1728+ }
1729+ // Class expressions (e.g. `const Foo = class Bar { ... }`): the expression-internal
1730+ // name (`Bar`) is never visible to the resolver, which derives callerClass from the
1731+ // binding name (`Foo`). Walking with null preserves the pre-fix `this.prop` fallback
1732+ // so the second lookup in resolveByMethodOrGlobal still finds the entry.
1733+ if ( t === 'class' ) {
1734+ for ( let i = 0 ; i < node . childCount ; i ++ ) {
1735+ walk ( node . child ( i ) ! , depth + 1 , null ) ;
1736+ }
1737+ return ;
1738+ }
17211739 if ( t === 'variable_declarator' ) {
17221740 handleVarDeclaratorTypeMap ( node , typeMap , returnTypeMap , callAssignments , fnRefBindings ) ;
17231741 } else if ( t === 'required_parameter' || t === 'optional_parameter' ) {
17241742 handleParamTypeMap ( node , typeMap ) ;
17251743 } else if ( t === 'assignment_expression' ) {
1726- handlePropWriteTypeMap ( node , typeMap ) ;
1744+ handlePropWriteTypeMap ( node , typeMap , currentClass ) ;
17271745 } else if ( t === 'call_expression' ) {
17281746 handleDefinePropertyTypeMap ( node , typeMap ) ;
17291747 }
17301748 for ( let i = 0 ; i < node . childCount ; i ++ ) {
1731- walk ( node . child ( i ) ! , depth + 1 ) ;
1749+ walk ( node . child ( i ) ! , depth + 1 , currentClass ) ;
17321750 }
17331751 }
1734- walk ( rootNode , 0 ) ;
1752+ walk ( rootNode , 0 , null ) ;
17351753}
17361754
17371755/** Extract type info from a variable_declarator: type annotation, constructor, or factory. */
@@ -1931,12 +1949,17 @@ function handleParamTypeMap(node: TreeSitterNode, typeMap: Map<string, TypeMapEn
19311949 * Phase 8.3d: seed the pts map from object property writes.
19321950 *
19331951 * `handlers.auth = authMiddleware` → typeMap.set('handlers.auth', { type: 'authMiddleware', confidence: 0.85 })
1934- * `this.logger = new Logger(...)` → typeMap.set('this.logger', { type: 'Logger', confidence: 1.0 })
1952+ * `this.logger = new Logger(...)` → typeMap.set('UserService.logger', { type: 'Logger', confidence: 1.0 })
1953+ * (keyed as ClassName.prop when currentClass is known, to avoid collisions across classes)
19351954 *
19361955 * Only simple `obj.prop = identifier` and `this.prop = new Ctor()` writes are tracked
19371956 * (not chained `a.b.c = x`). BUILTIN_GLOBALS are skipped (e.g. `console.log = fn`).
19381957 */
1939- function handlePropWriteTypeMap ( node : TreeSitterNode , typeMap : Map < string , TypeMapEntry > ) : void {
1958+ function handlePropWriteTypeMap (
1959+ node : TreeSitterNode ,
1960+ typeMap : Map < string , TypeMapEntry > ,
1961+ currentClass : string | null ,
1962+ ) : void {
19401963 const lhsN = node . childForFieldName ( 'left' ) ;
19411964 const rhsN = node . childForFieldName ( 'right' ) ;
19421965 if ( ! lhsN || ! rhsN ) return ;
@@ -1949,10 +1972,15 @@ function handlePropWriteTypeMap(node: TreeSitterNode, typeMap: Map<string, TypeM
19491972 // computed subscript expressions — consistent with the adjacent fnRefBindings block.
19501973 if ( prop . type !== 'property_identifier' && prop . type !== 'identifier' ) return ;
19511974
1952- // this.prop = new ClassName(...) — constructor-assigned property type
1975+ // this.prop = new ClassName(...) — constructor-assigned property type.
1976+ // Key as ClassName.prop (class-scoped) so two classes with identically-named
1977+ // properties don't overwrite each other's typeMap entry.
19531978 if ( obj . type === 'this' && rhsN . type === 'new_expression' ) {
19541979 const ctorType = extractNewExprTypeName ( rhsN ) ;
1955- if ( ctorType ) setTypeMapEntry ( typeMap , `this.${ prop . text } ` , ctorType , 1.0 ) ;
1980+ if ( ctorType ) {
1981+ const key = currentClass ? `${ currentClass } .${ prop . text } ` : `this.${ prop . text } ` ;
1982+ setTypeMapEntry ( typeMap , key , ctorType , 1.0 ) ;
1983+ }
19561984 return ;
19571985 }
19581986
0 commit comments