diff --git a/gitnexus/src/core/ingestion/languages/zig/query.ts b/gitnexus/src/core/ingestion/languages/zig/query.ts index fb16a3432..cf0ee3ca5 100644 --- a/gitnexus/src/core/ingestion/languages/zig/query.ts +++ b/gitnexus/src/core/ingestion/languages/zig/query.ts @@ -56,9 +56,13 @@ const ZIG_SCOPE_QUERY = ` (container_field name: (identifier) @declaration.name) @declaration.field -;; Declarations — const/var bindings (import/container groups filtered in TS) +;; Declarations — const/var bindings (import/container groups filtered in TS). +;; The \`.\` anchor pins the FIRST named child: without it the pattern also +;; matched the initializer of \`const first = target;\`, minting a phantom +;; local named \`target\` that shadowed the real callee for every later +;; reference in the block. (variable_declaration - (identifier) @declaration.name) @declaration.variable + . (identifier) @declaration.name) @declaration.variable ;; Imports — const x = @import("...") (variable_declaration diff --git a/gitnexus/test/integration/resolvers/zig.test.ts b/gitnexus/test/integration/resolvers/zig.test.ts index 7267dbd28..6f8bb7e15 100644 --- a/gitnexus/test/integration/resolvers/zig.test.ts +++ b/gitnexus/test/integration/resolvers/zig.test.ts @@ -72,3 +72,28 @@ describe('Zig basic resolution', () => { expect(edgeSet(calls)).toContain('main → tick'); }); }); + +describe('Zig scope captures — variable bindings', () => { + it('binds only the declared name, never the initializer identifier', async () => { + // `(variable_declaration (identifier) @declaration.name)` without a + // first-child anchor ALSO matches the RHS identifier of `const h = helper;` + // and mints a phantom local named `helper` in the enclosing block. That + // phantom shadows the real function for every later reference in the + // block, so `helper()` below silently lost its CALLS edge — and the + // callable-value-flow seed for `h` had nothing to resolve against. + const { emitZigScopeCaptures } = + await import('../../../src/core/ingestion/languages/zig/captures.js'); + const source = [ + 'fn helper() void {}', + 'pub fn main() void {', + ' const h = helper;', + ' helper();', + '}', + '', + ].join('\n'); + const variableNames = emitZigScopeCaptures(source, 'main.zig') + .filter((m) => m['@declaration.variable'] !== undefined) + .map((m) => m['@declaration.name']?.text); + expect(variableNames).toEqual(['h']); + }); +});