Skip to content

C++: a 16-character raw-string delimiter (the standard maximum) collapses the whole file to one ERROR node — every symbol silently dropped, index reports success #1522

Description

@inth3shadows

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions