Pārlūkot izejas kodu

fix(extraction): extract type refs from TS interface property and method signatures (#432)

Types that appeared only in TypeScript interface members — property
signatures like `value?: Partial<IPage>` and method signatures like
`fetchPage(arg: IPage): IOrderField` — were not being captured at
extraction time, so the resolver never built `references` edges for
them. `codegraph_impact`/`codegraph_callers` on the named type missed
every consumer that imported it solely to use it in an interface shape.

Add a `property_signature` / `method_signature` branch in `visitNode`:
when inside a class-like node (which covers interfaces) and the
language supports type annotations, call `extractTypeAnnotations` with
the parent (interface) node ID as the edge source. No property/method
node is created — only unresolved references that the resolver wires
the same way it wires field and parameter type references elsewhere.
TheSunn 3 nedēļas atpakaļ
vecāks
revīzija
7e0d9b9ec0
3 mainītis faili ar 58 papildinājumiem un 0 dzēšanām
  1. 10 0
      CHANGELOG.md
  2. 32 0
      __tests__/extraction.test.ts
  3. 16 0
      src/extraction/tree-sitter.ts

+ 10 - 0
CHANGELOG.md

@@ -196,6 +196,16 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
   `docs/design/mixed-ios-and-react-native-bridging.md`.
 
 ### Fixed
+- **TypeScript interface members now produce `references` edges to their
+  annotated types (#432).** Types that appeared only in an interface's
+  property or method signatures — e.g. `value?: Partial<IPage>` or
+  `fetchPage(arg: IPage): IOrderField` — were being silently dropped at
+  extraction time, so `codegraph_impact`/`codegraph_callers` on the named
+  type missed every consumer that imported it just to use it in an
+  interface shape. The walker now extracts type annotations from both
+  `property_signature` and `method_signature` nodes inside class-like
+  parents (interfaces, classes), and the resolver wires the resulting
+  references the same way it wires field and parameter types elsewhere.
 - **Git worktrees no longer silently borrow another tree's index (#155).**
   When a worktree is nested inside the main checkout — exactly what agent
   tools that place worktrees under gitignored paths like

+ 32 - 0
__tests__/extraction.test.ts

@@ -205,6 +205,38 @@ export interface User {
     });
   });
 
+  it('should extract type references from interface property signatures', () => {
+    const code = `
+import type { IPage } from '../PromoterList';
+import type { IOrderField } from '../types';
+
+interface Hprops {
+  value?: Partial<IPage> & Partial<IOrderField>;
+}
+`;
+    const result = extractFromSource('HeaderFilter.ts', code);
+
+    const refs = result.unresolvedReferences.filter((r) => r.referenceKind === 'references');
+    expect(refs.some((r) => r.referenceName === 'IPage')).toBe(true);
+    expect(refs.some((r) => r.referenceName === 'IOrderField')).toBe(true);
+  });
+
+  it('should extract type references from interface method signatures', () => {
+    const code = `
+import type { IPage } from '../PromoterList';
+import type { IOrderField } from '../types';
+
+interface MethodForm {
+  fetchPage(arg: IPage): IOrderField;
+}
+`;
+    const result = extractFromSource('MethodForm.ts', code);
+
+    const refs = result.unresolvedReferences.filter((r) => r.referenceKind === 'references');
+    expect(refs.some((r) => r.referenceName === 'IPage')).toBe(true);
+    expect(refs.some((r) => r.referenceName === 'IOrderField')).toBe(true);
+  });
+
   it('should track function calls', () => {
     const code = `
 function main() {

+ 16 - 0
src/extraction/tree-sitter.ts

@@ -387,6 +387,22 @@ export class TreeSitterExtractor {
     else if (nodeType === 'impl_item') {
       this.extractRustImplItem(node);
     }
+    // TypeScript interface members: property_signature (`foo: T`, `foo?: T`)
+    // and method_signature (`foo(arg: A): R`) both carry type annotations the
+    // interface walker would otherwise drop. Extract them as `references`
+    // edges from the interface so resolvers can wire callers/impact for
+    // types that only appear in interface members.
+    else if (
+      (nodeType === 'property_signature' || nodeType === 'method_signature') &&
+      this.isInsideClassLikeNode() &&
+      this.TYPE_ANNOTATION_LANGUAGES.has(this.language)
+    ) {
+      const parentId = this.nodeStack[this.nodeStack.length - 1];
+      if (parentId) {
+        this.extractTypeAnnotations(node, parentId);
+      }
+      // don't skipChildren — nested signatures still need traversal
+    }
 
     // Visit children (unless the extract method already visited them)
     if (!skipChildren) {