A C++ raw-string delimiter of exactly 16 characters — the longest the standard allows — makes the file's entire parse collapse into one ERROR node, so codegraph indexes the file as successful with zero symbols. No warning, no error, no diagnostic. 15 characters is fine.
Repro
// min.cpp — 6 lines, compiles cleanly with `g++ -std=c++17 -fsyntax-only`
const char* kTemplate = R"FILE_TEMPLATE_V1(
struct Ignored { int v; };
)FILE_TEMPLATE_V1";
int after_the_raw_string(int x) {
return x + 1;
}
FILE_TEMPLATE_V1 is 16 characters.
$ codegraph init
◆ Indexed 1 files
● 1 nodes, 0 edges in 395ms
$ codegraph files
└── min.cpp (cpp, 1 symbols) # the file node — that's all
$ codegraph query after_the_raw_string
ℹ No results found for "after_the_raw_string"
Shorten the delimiter by one character (FILE_TEMPLATE_V, 15) and the same file indexes correctly and the query returns the function.
The boundary is exactly the standard's limit
[lex.string] says the d-char-sequence "shall consist of at most 16 characters", so 16 is legal and 17 is not. The grammar accepts up to 15:
| delimiter length |
g++ -std=c++17 |
codegraph |
| 15 |
compiles |
✅ both functions indexed |
| 16 |
compiles |
❌ 0 function nodes, no warning |
| 17 |
rejected — "raw string delimiter longer than 16 characters" |
❌ 0 function nodes |
So the one delimiter length that is legal-but-unhandled is 16. Verified by bisecting lengths 1–18: clean break, 15 passes, 16 onward fails.
Where it breaks
The parse produces a single ERROR node that spans from the raw string to EOF, swallowing every following top-level sibling. Dumping the tree with codegraph's own bundled grammar and runtime:
taglen=15 → hasError = false, function_definition nodes = 2, ERROR nodes = 0
taglen=16 → hasError = true, function_definition nodes = 0, ERROR nodes = 10
top-level child [1] = ERROR, lines 3-17
This is upstream in tree-sitter-cpp, not a stale vendored grammar — it reproduces identically on all three:
- the vendored
src/extraction/wasm/tree-sitter-cpp.wasm
tree-sitter-wasms@0.1.13 (latest published)
- native
tree-sitter-cpp@0.23.4 (latest published), via tree-sitter node bindings
Affected routes (same repro, renamed): .cpp / .hpp / .cc / .cxx (cpp grammar), .h (c grammar), .mm (objc grammar) — the function is missing in every one.
The codegraph-side half
Even with the grammar bug upstream, codegraph currently has no way to notice. src/extraction/tree-sitter.ts parses and walks without ever consulting tree.rootNode.hasError — the only 'ERROR' reference in the extraction path is a narrow child-type check at tree-sitter.ts:4334, not a diagnostic. A file whose parse collapsed entirely is recorded as a successful index carrying just a file node.
That part is language-agnostic: any grammar gap, in any language, silently zeroes a file rather than surfacing anything. A hasError check that warns (or flags the file in status) would turn this class of failure from invisible into reportable, independent of whether the delimiter bug itself is ever fixed upstream.
Why it bites in practice
The files most likely to carry long descriptive raw-string delimiters are code generators, scaffold/template files, embedded shader or SQL blobs — exactly the files where a delimiter like FILE_TEMPLATE_V1, CMAKE_TEMPLATE_1, or SHADER_SOURCE_V2 reads as natural. When it happens, every symbol in the file disappears from the graph with no signal that anything went wrong.
@colbymchenry — if you can confirm the boundary on your side, this looks like a one-line off-by-one in tree-sitter-cpp's raw-string scanner and is probably best reported upstream to tree-sitter/tree-sitter-cpp from the project. The hasError gap above is separable and lands entirely on this side.
Possibly related
#1505 reports the same observable (file node + #includes, zero functions) for C++ files whose top matter is large anonymous namespaces containing raw strings. I could not reproduce that one from its description across 20 variants, and the reporter states native tree-sitter-cpp gives hasError === false on their file — which would rule this delimiter case out for them. Noting it only because the signature is identical; they may be two separate causes.
Environment: released 1.5.0 (linux-x64 bundle) for all codegraph runs; source read against main @ d6d1728. g++ 13.3.0, Ubuntu 24.04.
A C++ raw-string delimiter of exactly 16 characters — the longest the standard allows — makes the file's entire parse collapse into one
ERRORnode, so codegraph indexes the file as successful with zero symbols. No warning, no error, no diagnostic. 15 characters is fine.Repro
FILE_TEMPLATE_V1is 16 characters.Shorten the delimiter by one character (
FILE_TEMPLATE_V, 15) and the same file indexes correctly and the query returns the function.The boundary is exactly the standard's limit
[lex.string]says the d-char-sequence "shall consist of at most 16 characters", so 16 is legal and 17 is not. The grammar accepts up to 15:g++ -std=c++17So the one delimiter length that is legal-but-unhandled is 16. Verified by bisecting lengths 1–18: clean break, 15 passes, 16 onward fails.
Where it breaks
The parse produces a single
ERRORnode that spans from the raw string to EOF, swallowing every following top-level sibling. Dumping the tree with codegraph's own bundled grammar and runtime:This is upstream in tree-sitter-cpp, not a stale vendored grammar — it reproduces identically on all three:
src/extraction/wasm/tree-sitter-cpp.wasmtree-sitter-wasms@0.1.13(latest published)tree-sitter-cpp@0.23.4(latest published), viatree-sitternode bindingsAffected routes (same repro, renamed):
.cpp/.hpp/.cc/.cxx(cpp grammar),.h(c grammar),.mm(objc grammar) — the function is missing in every one.The codegraph-side half
Even with the grammar bug upstream, codegraph currently has no way to notice.
src/extraction/tree-sitter.tsparses and walks without ever consultingtree.rootNode.hasError— the only'ERROR'reference in the extraction path is a narrow child-type check attree-sitter.ts:4334, not a diagnostic. A file whose parse collapsed entirely is recorded as a successful index carrying just a file node.That part is language-agnostic: any grammar gap, in any language, silently zeroes a file rather than surfacing anything. A
hasErrorcheck that warns (or flags the file instatus) would turn this class of failure from invisible into reportable, independent of whether the delimiter bug itself is ever fixed upstream.Why it bites in practice
The files most likely to carry long descriptive raw-string delimiters are code generators, scaffold/template files, embedded shader or SQL blobs — exactly the files where a delimiter like
FILE_TEMPLATE_V1,CMAKE_TEMPLATE_1, orSHADER_SOURCE_V2reads as natural. When it happens, every symbol in the file disappears from the graph with no signal that anything went wrong.@colbymchenry — if you can confirm the boundary on your side, this looks like a one-line off-by-one in tree-sitter-cpp's raw-string scanner and is probably best reported upstream to tree-sitter/tree-sitter-cpp from the project. The
hasErrorgap above is separable and lands entirely on this side.Possibly related
#1505 reports the same observable (file node +
#includes, zero functions) for C++ files whose top matter is large anonymous namespaces containing raw strings. I could not reproduce that one from its description across 20 variants, and the reporter states native tree-sitter-cpp giveshasError === falseon their file — which would rule this delimiter case out for them. Noting it only because the signature is identical; they may be two separate causes.Environment: released
1.5.0(linux-x64 bundle) for allcodegraphruns; source read againstmain@d6d1728. g++ 13.3.0, Ubuntu 24.04.