Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Functional cast with `unsigned` / `signed` / `long` / `short` (e.g. `unsigned(x)`) parsed as a declaration in conditions, producing ERROR

Aperta Adatta ai principianti
#374 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

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 test passes (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 of ERROR with long as sized_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

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di tree-sitter/tree-sitter-cpp

Tutte le issue di tree-sitter/tree-sitter-cpp

Issue simili

Altre issue su JavaScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.