fix(extraction): warn when parse errors leave no symbols (#1522) (#1799)

Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
This commit is contained in:
Colby Mchenry
2026-09-08 16:17:28 -05:00
committed by GitHub
co-authored by Colby McHenry
parent 4fd816b610
commit 7b339373b4
5 changed files with 159 additions and 0 deletions
@@ -0,0 +1,77 @@
import { beforeAll, describe, expect, it } from 'vitest';
import { extractFromSource } from '../src/extraction';
import { getParser, initGrammars, loadGrammarsForLanguages } from '../src/extraction/grammars';
function rawStringSource(delimiter: string): string {
return `const char* kTemplate = R"${delimiter}(
struct Ignored { int v; };
)${delimiter}";
int after_the_raw_string(int x) {
return x + 1;
}
`;
}
describe('C++ raw-string delimiter parse collapse (#1522)', () => {
beforeAll(async () => {
await initGrammars();
await loadGrammarsForLanguages(['cpp', 'c']);
});
it('warns when a legal 16-character delimiter swallows every symbol', () => {
const result = extractFromSource('min.cpp', rawStringSource('FILE_TEMPLATE_V1'));
// The vendored tree-sitter-cpp scanner currently rejects the standard's
// maximum delimiter length, consuming the following function as ERROR.
expect(result.nodes.filter((n) => n.kind === 'function')).toEqual([]);
expect(result.nodes.map((n) => n.kind)).toEqual(['file']);
expect(result.errors).toEqual([
{
message:
'min.cpp: parse produced no symbols (tree has errors) — ' +
'the file is indexed but contributes nothing to the graph',
severity: 'warning',
code: 'parse_error',
},
]);
});
it('extracts the function after a 15-character delimiter without warning', () => {
const result = extractFromSource('min.cpp', rawStringSource('FILE_TEMPLATE_V'));
expect(result.nodes.filter((n) => n.kind === 'function').map((n) => n.name))
.toEqual(['after_the_raw_string']);
expect(result.errors).toEqual([]);
});
it.each(['min.cpp', 'min.c', 'min.h'])('does not warn on a healthy include-only %s', (filePath) => {
const result = extractFromSource(filePath, '#include <stdio.h>\n#include <stdlib.h>\n');
expect(result.nodes.filter((n) => n.kind !== 'file' && n.kind !== 'import')).toEqual([]);
expect(result.errors).toEqual([]);
});
it('does not warn on a healthy empty file with zero symbols', () => {
const result = extractFromSource('empty.cpp', '');
expect(result.nodes.map((n) => n.kind)).toEqual(['file']);
expect(result.errors).toEqual([]);
});
it('does not warn on parse errors when a function survives', () => {
const source = 'int before_the_raw_string() { return 0; }\n' + rawStringSource('FILE_TEMPLATE_V1');
const tree = getParser('cpp')!.parse(source)!;
try {
expect(tree.rootNode.hasError).toBe(true);
} finally {
tree.delete();
}
const result = extractFromSource('min.cpp', source);
expect(result.nodes.filter((n) => n.kind === 'function').map((n) => n.name))
.toEqual(['before_the_raw_string']);
expect(result.errors).toEqual([]);
});
});