mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-05 02:43:32 +00:00
test(cpp): characterize CUDA parser limitations
This commit is contained in:
parent
0337d32ab7
commit
12d72a1f54
4 changed files with 76 additions and 2 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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$/,
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 = `
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue