test(cpp): characterize CUDA parser limitations

This commit is contained in:
azizur100389 2026-06-15 21:07:37 +01:00
parent 0337d32ab7
commit 12d72a1f54
4 changed files with 76 additions and 2 deletions

View file

@ -30,7 +30,7 @@ import { logger } from '../../logger.js';
/**
* Cross-repo C/C++ `#include` dependency extractor.
*
* **Provider side:** registers every `.h/.hpp/.hxx/.hh` file in the repo
* **Provider side:** registers every `.h/.hpp/.hxx/.hh/.cuh` file in the repo
* as a provider contract with `include::<relative-path>`.
*
* **Consumer side:** parses all C/C++ source/header files for `#include "…"`
@ -307,7 +307,7 @@ function getLanguageForFile(filePath: string): unknown | null {
function isLocalInclude(cleaned: string, suffixIndex: SuffixIndex): boolean {
const candidates = [cleaned];
if (!/\.[a-zA-Z0-9]+$/.test(cleaned)) {
for (const ext of ['.h', '.hpp', '.hxx', '.hh', '.cuh']) candidates.push(cleaned + ext);
for (const ext of HEADER_EXTENSIONS) candidates.push(cleaned + ext);
}
for (const c of candidates) {
if (suffixIndex.get(c) || suffixIndex.getInsensitive(c)) return true;

View file

@ -427,6 +427,8 @@ export const cProvider = defineLanguage({
export const cppProvider = defineLanguage({
id: SupportedLanguages.CPlusPlus,
// CUDA files route through tree-sitter-cpp as a conservative C++-subset parser:
// definitions still extract, but CUDA launch syntax (`<<< >>>`) is not modeled as calls.
extensions: ['.cpp', '.cc', '.cxx', '.h', '.hpp', '.hxx', '.hh', '.cu', '.cuh'],
entryPointPatterns: [
/^main$/,

View file

@ -192,4 +192,39 @@ describe('IncludeExtractor → syncGroup integration (finding #7)', () => {
fs.rmSync(consumerDir, { recursive: true, force: true });
}
});
it('suppresses extensionless local includes that resolve to .cuh headers', async () => {
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gitnexus-include-cuh-'));
try {
fs.mkdirSync(path.join(repoDir, 'include'), { recursive: true });
fs.mkdirSync(path.join(repoDir, 'src'), { recursive: true });
fs.writeFileSync(
path.join(repoDir, 'include/kernel.cuh'),
'#pragma once\n__global__ void kernel();',
);
fs.writeFileSync(
path.join(repoDir, 'src/main.cu'),
'#include "include/kernel"\nvoid host() {}',
);
const extractor = new IncludeExtractor();
const contracts = await extractor.extract(null, repoDir, {
id: 'cuda-repo',
path: 'app/cuda-repo',
repoPath: repoDir,
storagePath: path.join(repoDir, '.gitnexus'),
});
expect(
contracts.some(
(c) => c.role === 'provider' && c.contractId === 'include::include/kernel.cuh',
),
).toBe(true);
expect(
contracts.some((c) => c.role === 'consumer' && c.contractId === 'include::include/kernel'),
).toBe(false);
} finally {
fs.rmSync(repoDir, { recursive: true, force: true });
}
});
});

View file

@ -36,6 +36,16 @@ function extractDefinitions(matches: any[]) {
return defs;
}
function extractCapturedCallNames(matches: any[]) {
const names: string[] = [];
for (const match of matches) {
if (!match.captures.some((c: any) => c.name === 'call')) continue;
const nameCapture = match.captures.find((c: any) => c.name === 'call.name');
if (nameCapture) names.push(nameCapture.node.text);
}
return names;
}
describe('Tree-sitter multi-language parsing', () => {
let parser: Parser;
@ -303,6 +313,33 @@ describe('Tree-sitter multi-language parsing', () => {
expect(names).toContain('launchKernel');
});
it('characterizes CUDA syntax when routed through the C++ parser', async () => {
await loadLanguage(SupportedLanguages.CPlusPlus, 'src/kernels/force.cu');
const code = `
__global__ void axpy(float *x) { x[0] = 1.0f; }
void host() {
axpy<<<1, 32>>>(nullptr);
cudaDeviceSynchronize();
}
`;
const provider = getProvider(SupportedLanguages.CPlusPlus);
const { tree, matches } = parseAndQuery(parser, code, provider.treeSitterQueries);
const defs = extractDefinitions(matches);
const callNames = extractCapturedCallNames(matches);
const ordinaryCalls = extractCapturedCallNames(
parseAndQuery(
parser,
'void host() { cudaDeviceSynchronize(); }',
provider.treeSitterQueries,
).matches,
);
expect(tree.rootNode.hasError).toBe(true);
expect(defs.some((d) => d.name === 'axpy')).toBe(true);
expect(ordinaryCalls).toContain('cudaDeviceSynchronize');
expect(callNames).not.toContain('axpy');
});
it('captures C++ typedef anonymous structs, enums, and enumerators', async () => {
await loadLanguage(SupportedLanguages.CPlusPlus);
const code = `