From 48924fcfa8f1f59c02df25fb4637db762e6b9869 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 11 May 2026 07:06:57 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20address=204=20findings=20=E2=80=94=20K&R?= =?UTF-8?q?=20arity,=20function-pointer=20docs,=20prototype=20docs,=20=5Ff?= =?UTF-8?q?ilePath=20rename?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finding 1: K&R empty parameter list `int foo()` now returns unknown arity `{}` instead of `{parameterCount:0}`. Distinguishes from explicit `int foo(void)`. 3 unit tests cover K&R definition, prototype, and void comparison. Finding 2: Added code comment documenting function-pointer-variable call capture as known architectural trade-off (same as Go resolver). Finding 3: Added code comment documenting prototype/definition duplication as graph-quality concern (no false CALLS edges). Finding 4: Renamed `_filePath` → `filePath` in captures.ts since it is actively used in markStaticName(). Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/ab2164f7-972f-4ea8-81fa-a14ce20d7cce Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --- .../ingestion/languages/c/arity-metadata.ts | 7 ++++ .../core/ingestion/languages/c/captures.ts | 4 +-- .../src/core/ingestion/languages/c/query.ts | 11 ++++++ .../unit/scope-resolution/c/c-arity.test.ts | 34 +++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/gitnexus/src/core/ingestion/languages/c/arity-metadata.ts b/gitnexus/src/core/ingestion/languages/c/arity-metadata.ts index 2ebdab3a0..013d78ea8 100644 --- a/gitnexus/src/core/ingestion/languages/c/arity-metadata.ts +++ b/gitnexus/src/core/ingestion/languages/c/arity-metadata.ts @@ -26,6 +26,13 @@ export function computeCDeclarationArity(node: SyntaxNode): CArityInfo { } } + // K&R old-style declaration: `int foo()` has an empty parameter_list with + // no parameter_declaration or variadic_parameter children. Per C89/C99, + // this means the function accepts an unspecified number/types of arguments — + // NOT zero arguments. Return unknown arity to avoid false 'incompatible'. + // `int foo(void)` is the explicit zero-parameter form and is handled below. + if (params.length === 0) return {}; + // (void) means zero parameters if (params.length === 1 && params[0].type === 'parameter_declaration') { const typeNode = params[0].childForFieldName('type'); diff --git a/gitnexus/src/core/ingestion/languages/c/captures.ts b/gitnexus/src/core/ingestion/languages/c/captures.ts index 6173d9155..836eb3eac 100644 --- a/gitnexus/src/core/ingestion/languages/c/captures.ts +++ b/gitnexus/src/core/ingestion/languages/c/captures.ts @@ -14,7 +14,7 @@ import { markStaticName } from './static-linkage.js'; export function emitCScopeCaptures( sourceText: string, - _filePath: string, + filePath: string, cachedTree?: unknown, ): readonly CaptureMatch[] { let tree = cachedTree as ReturnType['parse']> | undefined; @@ -102,7 +102,7 @@ export function emitCScopeCaptures( if (hasStaticStorageClass(fnNode)) { const nameText = grouped['@declaration.name']?.text; if (nameText !== undefined) { - markStaticName(_filePath, nameText); + markStaticName(filePath, nameText); } } } diff --git a/gitnexus/src/core/ingestion/languages/c/query.ts b/gitnexus/src/core/ingestion/languages/c/query.ts index 653bd0567..da49a1a6a 100644 --- a/gitnexus/src/core/ingestion/languages/c/query.ts +++ b/gitnexus/src/core/ingestion/languages/c/query.ts @@ -53,6 +53,11 @@ const C_SCOPE_QUERY = ` declarator: (identifier) @declaration.name))) @declaration.function ;; Declarations — function declaration (prototype) +;; Note: Both prototypes and definitions are captured as @declaration.function. +;; This may produce duplicate Function nodes in the knowledge graph when a +;; function is declared in a header and defined in a .c file. CALLS edges +;; resolve correctly through scope-based wildcard import chains; the +;; duplication is a graph-quality concern only (no false edges). (declaration declarator: (function_declarator declarator: (identifier) @declaration.name)) @declaration.function @@ -114,6 +119,12 @@ const C_SCOPE_QUERY = ` declarator: (identifier) @type-binding.name)) @type-binding.assignment ;; References — free calls +;; Note: This also captures calls through function pointer variables (e.g. fp(x)) +;; since tree-sitter-c produces structurally identical AST nodes for both direct +;; function calls and function-pointer-variable calls. A type-based guard to +;; distinguish variable-calls from function-calls is not implemented — this is a +;; known architectural trade-off shared with the Go resolver. The uniqueness +;; constraint in pickUniqueGlobalCallable limits false edge exposure. (call_expression function: (identifier) @reference.name) @reference.call.free diff --git a/gitnexus/test/unit/scope-resolution/c/c-arity.test.ts b/gitnexus/test/unit/scope-resolution/c/c-arity.test.ts index 36a3ba4a8..fddf1b932 100644 --- a/gitnexus/test/unit/scope-resolution/c/c-arity.test.ts +++ b/gitnexus/test/unit/scope-resolution/c/c-arity.test.ts @@ -101,6 +101,40 @@ describe('computeCDeclarationArity', () => { expect(arity.parameterCount).toBe(1); expect(arity.requiredParameterCount).toBe(1); }); + + it('returns unknown arity for K&R empty parameter list int foo()', () => { + const node = parseFunctionNode('int foo() { return 0; }'); + expect(node).not.toBeNull(); + const arity = computeCDeclarationArity(node!); + // K&R old-style: unspecified parameters, NOT zero parameters + expect(arity.parameterCount).toBeUndefined(); + expect(arity.requiredParameterCount).toBeUndefined(); + expect(arity.parameterTypes).toBeUndefined(); + }); + + it('distinguishes K&R int foo() from explicit int foo(void)', () => { + const knrNode = parseFunctionNode('int foo() { return 0; }'); + const voidNode = parseFunctionNode('int foo(void) { return 0; }'); + expect(knrNode).not.toBeNull(); + expect(voidNode).not.toBeNull(); + + const knrArity = computeCDeclarationArity(knrNode!); + const voidArity = computeCDeclarationArity(voidNode!); + + // K&R: unknown arity + expect(knrArity.parameterCount).toBeUndefined(); + // Explicit void: zero params + expect(voidArity.parameterCount).toBe(0); + expect(voidArity.requiredParameterCount).toBe(0); + }); + + it('returns unknown arity for K&R prototype int foo();', () => { + const node = parseFunctionNode('int foo();'); + expect(node).not.toBeNull(); + const arity = computeCDeclarationArity(node!); + expect(arity.parameterCount).toBeUndefined(); + expect(arity.requiredParameterCount).toBeUndefined(); + }); }); describe('computeCCallArity', () => {