diff --git a/gitnexus/src/core/ingestion/languages/typescript/scope-resolver.ts b/gitnexus/src/core/ingestion/languages/typescript/scope-resolver.ts index 09301de71..91c147535 100644 --- a/gitnexus/src/core/ingestion/languages/typescript/scope-resolver.ts +++ b/gitnexus/src/core/ingestion/languages/typescript/scope-resolver.ts @@ -322,6 +322,26 @@ function hasLocalBindingInScopeChain( return true; } + // Type-annotated function parameters — and other value-space type facts + // such as `self` and variable annotations — live in `scope.typeBindings`, + // not `scope.bindings`. A parameter named like a composable genuinely + // shadows the auto-import, and typeBindings never holds a pure type that + // belongs to callable space, so a same-file presence check here cannot + // over-suppress a legitimate auto-import. + // + // Residual (known limitation): this catches parameters whose annotation the + // TS scope query records as a type-binding (`p: Named`, generics, unions, + // predefined, arrays). Function-typed params (`p: () => void`), untyped + // params, destructured locals (`const { x } = …`), and catch-clause vars + // are captured by NEITHER map — the scope query emits no `@declaration` / + // `@type-binding` for them — so those shadow forms still leak an edge. + // Closing that needs shared TS scope-query/extractor changes that alter call + // resolution beyond Nuxt, so it is deferred to a follow-up rather than fixed + // here. + if (scope.filePath === filePath && scope.typeBindings.has(name)) { + return true; + } + cursor = scope.parent; } diff --git a/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/pages/param-typed.ts b/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/pages/param-typed.ts new file mode 100644 index 000000000..f84d8fd10 --- /dev/null +++ b/gitnexus/test/fixtures/lang-resolution/nuxt-auto-imports/pages/param-typed.ts @@ -0,0 +1,3 @@ +export function renderTyped(validate: ValidateFn) { + validate(); +} diff --git a/gitnexus/test/integration/resolvers/nuxt-scope.test.ts b/gitnexus/test/integration/resolvers/nuxt-scope.test.ts index ad86d83b8..518a638f8 100644 --- a/gitnexus/test/integration/resolvers/nuxt-scope.test.ts +++ b/gitnexus/test/integration/resolvers/nuxt-scope.test.ts @@ -103,11 +103,24 @@ describe('Nuxt/Nitro auto-import scope resolution', () => { it('does not emit auto-import edges for local shadowing or lexical noise', () => { const calls = nuxtCalls(); + // Guard against a vacuous pass: the feature must have emitted edges elsewhere. + expect(calls.length).toBeGreaterThan(0); expect(calls.filter((edge) => edge.sourceFilePath.endsWith('pages/local.ts'))).toHaveLength(0); expect(calls.filter((edge) => edge.sourceFilePath.endsWith('pages/noise.ts'))).toHaveLength(0); }); + it('does not emit an auto-import edge when a typed parameter shadows the name', () => { + const calls = nuxtCalls(); + expect(calls.length).toBeGreaterThan(0); + // pages/param-typed.ts has `function renderTyped(validate: ValidateFn)` and + // calls validate() — the type-annotated parameter (in scope.typeBindings) + // shadows the composable, so no nuxt edge is emitted. + expect( + calls.filter((edge) => edge.sourceFilePath.endsWith('pages/param-typed.ts')), + ).toHaveLength(0); + }); + it('allows type-only local declarations to coexist with value auto-import calls', () => { const calls = nuxtCalls();