rust-lang/rust-analyzer · error

{punct:#?} is not a valid punct

Error message

{punct:#?} is not a valid punct

What it means

This panic fires inside `to_parser_input` in syntax-bridge when converting macro-expansion `tt` token trees into parser `Input`. For every `tt::Leaf::Punct` it calls `SyntaxKind::from_char(punct.char)`; if the punctuation character has no corresponding Rust `SyntaxKind`, the code panics with a debug dump of the punct. It indicates a punct token outside the Rust punctuation set reached the converter.

Source

Thrown at crates/syntax-bridge/src/to_parser_input.rs:81

                        let edition = ctx_edition(ident.span.ctx);
                        match ident.sym.as_str() {
                            "_" => res.push(T![_], edition),
                            i if i.starts_with('\'') => res.push(LIFETIME_IDENT, edition),
                            _ if ident.is_raw.yes() => res.push(IDENT, edition),
                            text => match SyntaxKind::from_keyword(text, edition) {
                                Some(kind) => res.push(kind, edition),
                                None => {
                                    let contextual_keyword =
                                        SyntaxKind::from_contextual_keyword(text, edition)
                                            .unwrap_or(SyntaxKind::IDENT);
                                    res.push_ident(contextual_keyword, edition);
                                }
                            },
                        }
                    }
                    tt::Leaf::Punct(punct) => {
                        let kind = SyntaxKind::from_char(punct.char)
                            .unwrap_or_else(|| panic!("{punct:#?} is not a valid punct"));
                        res.push(kind, ctx_edition(punct.span.ctx));
                        if punct.spacing == tt::Spacing::Joint {
                            res.was_joint();
                        }
                    }
                }
                current.bump();
            }
            Some(tt::TokenTree::Subtree(subtree)) => {
                if let Some(kind) = match subtree.delimiter.kind {
                    tt::DelimiterKind::Parenthesis => Some(T!['(']),
                    tt::DelimiterKind::Brace => Some(T!['{']),
                    tt::DelimiterKind::Bracket => Some(T!['[']),
                    tt::DelimiterKind::Invisible => None,
                } {
                    res.push(kind, ctx_edition(subtree.delimiter.open.ctx));
                }
                current.bump();

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Inspect the `{punct:#?}` dump in the panic message to identify the offending `char` and trace the proc-macro or code that produced the token tree.
  2. Fix the producing proc-macro so it only emits valid Rust punctuation characters.
  3. If the char is a legitimate new Rust punct, add a mapping for it in `SyntaxKind::from_char`.
  4. When processing untrusted token trees, pre-filter puncts via `SyntaxKind::from_char(punct.char).is_some()` and skip/error on invalid ones instead of panicking.

Example fix

// before (panics)
let kind = SyntaxKind::from_char(punct.char)
    .unwrap_or_else(|| panic!("{punct:#?} is not a valid punct"));
// after (caller-side guard)
if SyntaxKind::from_char(punct.char).is_none() {
    return Err(format!("invalid punct {:?}", punct.char));
}
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_punct(p: &tt::Punct) -> bool {
    syntax::SyntaxKind::from_char(p.char).is_some()
}
// check every Punct leaf in the TokenTreesView before calling to_parser_input

Type guard

fn valid_punct_kind(p: &tt::Punct) -> Option<syntax::SyntaxKind> {
    syntax::SyntaxKind::from_char(p.char)
}

Prevention

When it happens

Trigger: Calling `to_parser_input(buffer, span_to_edition)` with a `tt::TokenTreesView` containing a `Punct` leaf whose `char` is not a recognized Rust punctuation character (e.g. a non-ASCII or invented punct produced by a buggy proc-macro or corrupted token tree), so `SyntaxKind::from_char` returns None.

Common situations: Buggy or malicious procedural macros emitting synthesized punct characters; version mismatch between the `tt` crate representation and `SyntaxKind::from_char` (a new punct added on one side only); hand-crafted token trees in tests using non-Rust punctuation characters.

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/a35c45eec125a8aa. Report an issue: GitHub.