mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Gitleaks / gitleaks (push) Has been cancelled
Publish / Classify release event (push) Has been cancelled
Scorecard / Scorecard analysis (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-cli) (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-web) (push) Has been cancelled
Publish / RC guard (marker + release-PR skip) (push) Has been cancelled
Publish / ci (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
Publish / Build & Push RC Docker images (push) Has been cancelled
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
203 lines
7.6 KiB
TypeScript
203 lines
7.6 KiB
TypeScript
/**
|
|
* Unit tests for C++ arity compatibility and metadata.
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { cppArityCompatibility } from '../../../../src/core/ingestion/languages/cpp/arity.js';
|
|
import {
|
|
computeCppDeclarationArity,
|
|
computeCppCallArity,
|
|
classifyCppParameterType,
|
|
} from '../../../../src/core/ingestion/languages/cpp/arity-metadata.js';
|
|
import { getCppParser } from '../../../../src/core/ingestion/languages/cpp/query.js';
|
|
import type { SyntaxNode } from '../../../../src/core/ingestion/utils/ast-helpers.js';
|
|
import type { SymbolDefinition, Callsite } from 'gitnexus-shared';
|
|
|
|
function parseFuncDef(src: string): SyntaxNode | null {
|
|
const tree = getCppParser().parse(src);
|
|
for (let i = 0; i < tree.rootNode.namedChildCount; i++) {
|
|
const child = tree.rootNode.namedChild(i);
|
|
if (child?.type === 'function_definition') return child as SyntaxNode;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function parseCallExpr(src: string): SyntaxNode | null {
|
|
const tree = getCppParser().parse(src);
|
|
const walk = (node: SyntaxNode): SyntaxNode | null => {
|
|
if (node.type === 'call_expression') return node;
|
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
const found = walk(node.namedChild(i) as SyntaxNode);
|
|
if (found) return found;
|
|
}
|
|
return null;
|
|
};
|
|
return walk(tree.rootNode as SyntaxNode);
|
|
}
|
|
|
|
function mkDef(overrides: Partial<SymbolDefinition> = {}): SymbolDefinition {
|
|
return {
|
|
nodeId: 'test-def',
|
|
qualifiedName: 'test',
|
|
filePath: 'test.cpp',
|
|
type: 'Function',
|
|
...overrides,
|
|
} as SymbolDefinition;
|
|
}
|
|
|
|
function mkCallsite(arity: number): Callsite {
|
|
return { arity } as Callsite;
|
|
}
|
|
|
|
// ── Declaration arity ───────────────────────────────────────────────────────
|
|
|
|
describe('computeCppDeclarationArity', () => {
|
|
it('computes arity for zero-parameter function', () => {
|
|
const node = parseFuncDef('void foo() {}');
|
|
expect(node).not.toBeNull();
|
|
const arity = computeCppDeclarationArity(node!);
|
|
expect(arity.parameterCount).toBe(0);
|
|
expect(arity.requiredParameterCount).toBe(0);
|
|
});
|
|
|
|
it('computes arity for (void) parameter', () => {
|
|
const node = parseFuncDef('void foo(void) {}');
|
|
expect(node).not.toBeNull();
|
|
const arity = computeCppDeclarationArity(node!);
|
|
expect(arity.parameterCount).toBe(0);
|
|
expect(arity.requiredParameterCount).toBe(0);
|
|
});
|
|
|
|
it('computes arity for multiple parameters', () => {
|
|
const node = parseFuncDef('void foo(int x, int y, int z) {}');
|
|
expect(node).not.toBeNull();
|
|
const arity = computeCppDeclarationArity(node!);
|
|
expect(arity.parameterCount).toBe(3);
|
|
expect(arity.requiredParameterCount).toBe(3);
|
|
});
|
|
|
|
it('computes arity with default parameters', () => {
|
|
const node = parseFuncDef('void foo(int x, int y = 5, int z = 10) {}');
|
|
expect(node).not.toBeNull();
|
|
const arity = computeCppDeclarationArity(node!);
|
|
expect(arity.parameterCount).toBe(3);
|
|
expect(arity.requiredParameterCount).toBe(1);
|
|
});
|
|
|
|
it('detects variadic function', () => {
|
|
const node = parseFuncDef('void foo(int x, ...) {}');
|
|
expect(node).not.toBeNull();
|
|
const arity = computeCppDeclarationArity(node!);
|
|
expect(arity.parameterCount).toBeUndefined(); // variadic → undefined max
|
|
expect(arity.requiredParameterCount).toBe(1);
|
|
expect(arity.parameterTypes).toContain('...');
|
|
});
|
|
|
|
it('handles pointer return type', () => {
|
|
const node = parseFuncDef('int* create(int size) {}');
|
|
expect(node).not.toBeNull();
|
|
const arity = computeCppDeclarationArity(node!);
|
|
expect(arity.parameterCount).toBe(1);
|
|
});
|
|
|
|
it('keeps coarse parameterTypes stable while preserving pointer/reference sidecar classes', () => {
|
|
const node = parseFuncDef('void f(int value, const int* ptr, int& ref, int&& move) {}');
|
|
expect(node).not.toBeNull();
|
|
const arity = computeCppDeclarationArity(node!);
|
|
expect(arity.parameterTypes).toEqual(['int', 'int', 'int', 'int']);
|
|
expect(arity.parameterTypeClasses).toEqual([
|
|
{ base: 'int', cv: 'none', indirection: 'value', pointerDepth: 0 },
|
|
{ base: 'int', cv: 'const', indirection: 'pointer', pointerDepth: 1 },
|
|
{ base: 'int', cv: 'none', indirection: 'lvalue-ref', pointerDepth: 0 },
|
|
{ base: 'int', cv: 'none', indirection: 'rvalue-ref', pointerDepth: 0 },
|
|
]);
|
|
});
|
|
|
|
it('classifies int, int*, and int& as distinct sidecar shapes for future is_same_v consumers', () => {
|
|
expect(classifyCppParameterType('int')).toEqual({
|
|
base: 'int',
|
|
cv: 'none',
|
|
indirection: 'value',
|
|
pointerDepth: 0,
|
|
});
|
|
expect(classifyCppParameterType('int', '* p')).toEqual({
|
|
base: 'int',
|
|
cv: 'none',
|
|
indirection: 'pointer',
|
|
pointerDepth: 1,
|
|
});
|
|
expect(classifyCppParameterType('int', '& r')).toEqual({
|
|
base: 'int',
|
|
cv: 'none',
|
|
indirection: 'lvalue-ref',
|
|
pointerDepth: 0,
|
|
});
|
|
});
|
|
});
|
|
|
|
// ── Call-site arity ─────────────────────────────────────────────────────────
|
|
|
|
describe('computeCppCallArity', () => {
|
|
it('computes arity for no-argument call', () => {
|
|
const node = parseCallExpr('void f() { foo(); }');
|
|
expect(node).not.toBeNull();
|
|
expect(computeCppCallArity(node!)).toBe(0);
|
|
});
|
|
|
|
it('computes arity for multi-argument call', () => {
|
|
const node = parseCallExpr('void f() { foo(1, 2, 3); }');
|
|
expect(node).not.toBeNull();
|
|
expect(computeCppCallArity(node!)).toBe(3);
|
|
});
|
|
|
|
it('computes arity for single-argument call', () => {
|
|
const node = parseCallExpr('void f() { foo(42); }');
|
|
expect(node).not.toBeNull();
|
|
expect(computeCppCallArity(node!)).toBe(1);
|
|
});
|
|
});
|
|
|
|
// ── Arity compatibility ─────────────────────────────────────────────────────
|
|
|
|
describe('cppArityCompatibility', () => {
|
|
it('returns compatible for exact match', () => {
|
|
const def = mkDef({ parameterCount: 2, requiredParameterCount: 2 });
|
|
expect(cppArityCompatibility(def, mkCallsite(2))).toBe('compatible');
|
|
});
|
|
|
|
it('returns compatible when call uses default params', () => {
|
|
const def = mkDef({ parameterCount: 3, requiredParameterCount: 1 });
|
|
expect(cppArityCompatibility(def, mkCallsite(1))).toBe('compatible');
|
|
expect(cppArityCompatibility(def, mkCallsite(2))).toBe('compatible');
|
|
expect(cppArityCompatibility(def, mkCallsite(3))).toBe('compatible');
|
|
});
|
|
|
|
it('returns incompatible for too few args', () => {
|
|
const def = mkDef({ parameterCount: 3, requiredParameterCount: 2 });
|
|
expect(cppArityCompatibility(def, mkCallsite(1))).toBe('incompatible');
|
|
});
|
|
|
|
it('returns incompatible for too many args (non-variadic)', () => {
|
|
const def = mkDef({ parameterCount: 2, requiredParameterCount: 2 });
|
|
expect(cppArityCompatibility(def, mkCallsite(5))).toBe('incompatible');
|
|
});
|
|
|
|
it('returns compatible for variadic with extra args', () => {
|
|
const def = mkDef({
|
|
parameterCount: undefined,
|
|
requiredParameterCount: 1,
|
|
parameterTypes: ['int', '...'],
|
|
});
|
|
expect(cppArityCompatibility(def, mkCallsite(5))).toBe('compatible');
|
|
});
|
|
|
|
it('returns unknown when no metadata', () => {
|
|
const def = mkDef({});
|
|
expect(cppArityCompatibility(def, mkCallsite(2))).toBe('unknown');
|
|
});
|
|
|
|
it('returns unknown for negative arity', () => {
|
|
const def = mkDef({ parameterCount: 2, requiredParameterCount: 2 });
|
|
expect(cppArityCompatibility(def, mkCallsite(-1))).toBe('unknown');
|
|
});
|
|
});
|