C++: a 16-character raw-string delimiter (the standard maximum) fails to parse — capacity guard is checked before the '(' terminator
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 1/5
- Tempo stimato
- Meno di un'ora
- Idoneità per principianti
- 90/100
Direzione di ricerca
Inizia in src/scanner.c, in scan_raw_string_delimiter, e analizza il ciclo del delimitatore di apertura e il relativo controllo della capacità. Verifica che il parser accetti un delimitatore di 16 caratteri continuando a rifiutare i delimitatori più lunghi, quindi esegui i controlli disponibili del parser e aggiungi o esegui un caso di regressione del corpus, se supportato; la scansione differenziale esistente fornisce il comportamento previsto.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
A raw-string delimiter of exactly 16 characters — the maximum [lex.string] allows — fails to parse. The whole translation unit after it collapses into ERROR nodes. 15 characters is fine, so the one length that is legal-but-rejected is exactly the spec's limit.
The cause is a check ordering in scan_raw_string_delimiter, and the fix is a one-line reorder. I have built and tested it; details and a differential sweep below.
Repro
// compiles cleanly with `g++ -std=c++17 -fsyntax-only`
const char* kTemplate = R"FFFFFFFFFFFFFFFF(
struct Ignored { int v; };
)FFFFFFFFFFFFFFFF";
int after_the_raw_string(int x) {
return x + 1;
}
FFFFFFFFFFFFFFFF is 16 chars.
Parsed with the C API against this repo at HEAD (src/parser.c + src/scanner.c, linked with the tree-sitter runtime — no editor or downstream tool involved):
delim=14 hasError=false function_definition=1 ERROR=0
delim=15 hasError=false function_definition=1 ERROR=0
delim=16 hasError=true function_definition=0 ERROR=3 <-- legal, rejected
delim=17 hasError=true function_definition=0 ERROR=3
delim=18 hasError=true function_definition=0 ERROR=3
after_the_raw_string disappears entirely at 16.
Cause
src/scanner.c, opening-delimiter loop:
for (;;) {
if (scanner->delimiter_length >= MAX_DELIMITER_LENGTH || lexer->eof(lexer) || lexer->lookahead == '\\' ||
iswspace(lexer->lookahead)) {
return false; // (1) capacity guard
}
if (lexer->lookahead == '(') {
return scanner->delimiter_length > 0; // (2) legal terminator
}
scanner->delimiter[scanner->delimiter_length++] = lexer->lookahead;
advance(lexer);
}
With a 16-char delimiter, the 16 d-chars are consumed and delimiter_length == 16. On the next iteration lookahead is ( — the legal terminator, check (2) — but check (1) runs first, sees the buffer is full, and returns false. The delimiter is never accepted even though nothing is wrong with it: the buffer is full, not overflowing.
15 works only because the loop still has a free slot when it reaches (.
Fix
Test the terminator before the capacity guard. A full buffer followed by ( is a complete, legal delimiter; a full buffer followed by another d-char is the genuine overflow.
for (;;) {
- if (scanner->delimiter_length >= MAX_DELIMITER_LENGTH || lexer->eof(lexer) || lexer->lookahead == '\\' ||
- iswspace(lexer->lookahead)) {
- return false;
- }
if (lexer->lookahead == '(') {
// Rather than create a token for an empty delimiter, we fail and
// let the grammar fall back to a delimiter-less rule.
return scanner->delimiter_length > 0;
}
+ if (scanner->delimiter_length >= MAX_DELIMITER_LENGTH || lexer->eof(lexer) || lexer->lookahead == '\\' ||
+ iswspace(lexer->lookahead)) {
+ return false;
+ }
scanner->delimiter[scanner->delimiter_length++] = lexer->lookahead;
advance(lexer);
}
No bounds change: delimiter[] is still only written when delimiter_length < MAX_DELIMITER_LENGTH, so the array cannot overflow.
Verification
Built stock and patched scanners from the same sources and swept delimiter lengths 0-18, comparing function_definition counts:
| length | stock | patched | |
|---|---|---|---|
| 0-15 | 1 | 1 | identical |
| 16 | 0 | 1 | fixed |
| 17, 18 | 0 | 0 | identical — still rejected |
The patch changes behaviour at exactly one length, the one that is legal today and rejected. 17+ stays rejected, so the spec limit is preserved, and length 0 (the empty-delimiter fallback the comment describes) is unchanged.
What I did not do: I could not run the corpus suite — npx tree-sitter-cli did not finish downloading in my environment — so this is verified by differential parsing rather than by tree-sitter test. Worth running before merging.
Happy to open a PR with the patch and a corpus case if useful.
Why it matters downstream
Files most likely to carry long descriptive delimiters — code generators, template/scaffold files, embedded shader or SQL blobs — are exactly where FILE_TEMPLATE_V1, SHADER_SOURCE_V2 or CMAKE_TEMPLATE_1 read as natural, and all are 16. When it happens the file yields no symbols at all, with no diagnostic.
Distinct from #245 (error-recovery synthesising a raw_string_delimiter, fixed in 3d8d510a) — nothing here is in error recovery; the input is well-formed C++.
Environment: this repo at HEAD, tree-sitter runtime from tree-sitter/tree-sitter HEAD, gcc 13.3.0, Ubuntu 24.04.
- Lingua principale
- JavaScript
- Stelle
- 463
- Fork
- 190
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di tree-sitter/tree-sitter-cpp
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
tree-sitter/tree-sitter-cpp#374 ·
-
`tree_sitter_cpp_external_scanner_deserialize` doesn't bound `length` against the size of the delimiter bufferForse già presa @dekobon l’ha presa 85 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
tree-sitter/tree-sitter-cpp#368 ·
-
bug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 70/100
tree-sitter/tree-sitter-cpp#377 · 1 commento ·
-
bug
Difficoltà 3/5 Mezza giornata Idoneità per principianti 55/100
tree-sitter/tree-sitter-cpp#376 ·
-
enhancement
Difficoltà 3/5 1-2 giorni Idoneità per principianti 45/100
tree-sitter/tree-sitter-cpp#365 · 1 reazione ·
Tutte le issue di tree-sitter/tree-sitter-cpp
Issue simili
-
enhancement javascript python
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
jlab-sensing/ENTS-backend#851 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 1/5 1-3 ore Idoneità per principianti 86/100
guilhermerodz/input-otp#157 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
davidjerleke/embla-carousel#1414 ·
I maintainer di solito rispondono entro 4 giorni
-
0. Needs triage bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 67/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno