Functional cast with `unsigned` / `signed` / `long` / `short` (e.g. `unsigned(x)`) parsed as a declaration in conditions, producing ERROR
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 82/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- javascript
- Ambito
- compilers
Direzione di ricerca
Inizia in grammar.js, nella regola call_expression (l’issue contiene il collegamento alle righe pertinenti), quindi esamina come il suo campo function gestisce primitive_type. Esegui tree-sitter generate e tree-sitter test, e analizza l’esempio minimo if (unsigned(id) < 8). Il lavoro è completato quando l’esempio e while (long(n) > 0) vengono analizzati senza errori, mentre gli esempi di dichiarazione elencati mantengono i loro alberi esistenti.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Description
A functional-notation cast whose type is a sized_type_specifier (unsigned(x), signed(x), long(x), short(x)) is not
parsed as an expression wherever a declaration could also start: at the beginning of a statement or in the condition of
if / while / switch. The parser commits to a declaration (unsigned as the type, (x) as a parenthesized declarator),
and the following operator (<, >=, ==, ...) produces an ERROR node.
The same code with a primitive_type (int(x)) parses fine, because call_expression accepts primitive_type as the
function field, but not sized_type_specifier. In positions where a declaration is impossible (e.g. return unsigned(id) < 8;)
there is no error only because unsigned is lexed as an identifier and the cast becomes an ordinary call.
Real-world example: fmt, include/fmt/core.h (basic_format_args::get):
if (unsigned(id) < unsigned(max_size())) arg = args_[id];
In larger files the error recovery after this ERROR can swallow much more than the condition itself (in our runs on fmt
headers it absorbed the enclosing class body).
Checked with tree-sitter CLI 0.26.3 on:
12bd6f7(v0.23.4-37, 2025-09-27);- current master
c009222(v0.23.4-41, 2026-09-17).
Both produce the same tree.
Minimal example
bool f(int id) {
if (unsigned(id) < 8) return true;
return false;
}
Actual (tree-sitter parse, identical on 12bd6f7 and c009222)
(translation_unit [0, 0] - [4, 0]
(function_definition [0, 0] - [3, 1]
type: (primitive_type [0, 0] - [0, 4])
declarator: (function_declarator [0, 5] - [0, 14]
declarator: (identifier [0, 5] - [0, 6])
parameters: (parameter_list [0, 6] - [0, 14]
(parameter_declaration [0, 7] - [0, 13]
type: (primitive_type [0, 7] - [0, 10])
declarator: (identifier [0, 11] - [0, 13]))))
body: (compound_statement [0, 15] - [3, 1]
(if_statement [1, 2] - [1, 36]
condition: (condition_clause [1, 5] - [1, 23]
(ERROR [1, 6] - [1, 20]
type: (sized_type_specifier [1, 6] - [1, 14])
(parenthesized_declarator [1, 14] - [1, 18]
(identifier [1, 15] - [1, 17])))
value: (number_literal [1, 21] - [1, 22]))
consequence: (return_statement [1, 24] - [1, 36]
(true [1, 31] - [1, 35])))
(return_statement [2, 2] - [2, 15]
(false [2, 9] - [2, 14])))))
ex.cc Parse: 0.77 ms 93 bytes/ms (ERROR [1, 6] - [1, 20])
Expected
The same shape as for if (int(id) < 8), with sized_type_specifier as the function of the call_expression
(output of a local build with the change proposed below):
(translation_unit [0, 0] - [4, 0]
(function_definition [0, 0] - [3, 1]
type: (primitive_type [0, 0] - [0, 4])
declarator: (function_declarator [0, 5] - [0, 14]
declarator: (identifier [0, 5] - [0, 6])
parameters: (parameter_list [0, 6] - [0, 14]
(parameter_declaration [0, 7] - [0, 13]
type: (primitive_type [0, 7] - [0, 10])
declarator: (identifier [0, 11] - [0, 13]))))
body: (compound_statement [0, 15] - [3, 1]
(if_statement [1, 2] - [1, 36]
condition: (condition_clause [1, 5] - [1, 23]
value: (binary_expression [1, 6] - [1, 22]
left: (call_expression [1, 6] - [1, 18]
function: (sized_type_specifier [1, 6] - [1, 14])
arguments: (argument_list [1, 14] - [1, 18]
(identifier [1, 15] - [1, 17])))
right: (number_literal [1, 21] - [1, 22])))
consequence: (return_statement [1, 24] - [1, 36]
(true [1, 31] - [1, 35])))
(return_statement [2, 2] - [2, 15]
(false [2, 9] - [2, 14])))))
Cause
call_expression only allows primitive_type (or a splice type) as the function of a functional cast:
https://github.com/tree-sitter/tree-sitter-cpp/blob/c009222808634c1014f82438d4883753516a2c24/grammar.js#L1080-L1086
call_expression: ($, original) => prec.dynamic(1, choice(original, seq(
field('function', choice($.primitive_type, seq(
optional('typename'),
$.splice_type_specifier,
))),
field('arguments', $.argument_list),
))),
Proposed fix (one line)
call_expression: ($, original) => prec.dynamic(1, choice(original, seq(
- field('function', choice($.primitive_type, seq(
+ field('function', choice($.primitive_type, $.sized_type_specifier, seq(
optional('typename'),
$.splice_type_specifier,
))),
With this change on master c009222:
tree-sitter generate(CLI 0.26.3) succeeds without new conflicts;tree-sitter testpasses (200 of 200, same as without the change);- the example above parses without errors (tree above), and so does
while (long(n) > 0) --n;
(on master it gives the same kind ofERRORwithlongassized_type_specifier); - declarations stay declarations:
unsigned (*fp)(int);,std::function<unsigned(int)> h;,
unsigned long y = (unsigned long)(x);give the same trees as before.
One visible side effect: the statement unsigned (x); becomes an expression_statement with a call_expression instead of a
declaration. This matches how master already parses int (x); (also a call_expression), so the two are now consistent;
if the declaration reading is preferred for both, that is a separate question about prec.dynamic(1, ...) on this rule.
- 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à 1/5 Meno di un'ora Idoneità per principianti 90/100
tree-sitter/tree-sitter-cpp#371 · 1 commento ·
-
`tree_sitter_cpp_external_scanner_deserialize` doesn't bound `length` against the size of the delimiter bufferForse già presa @dekobon l’ha presa 84 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
-
self-driving
Difficoltà 1/5 1-3 ore Idoneità per principianti 86/100
I maintainer di solito rispondono entro 1 giorno
-
kind:bug needs-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
I maintainer di solito rispondono entro 1 giorno
-
status: needs triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
mastra-ai/mastra#28500 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
fossasia/scrum_helper#949 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
Foundryborne/daggerheart#2531 ·
I maintainer di solito rispondono entro 1 giorno