{"record":{"id":"a35c45eec125a8aa","repo":"rust-lang/rust-analyzer","slug":"punct-is-not-a-valid-punct","errorCode":null,"errorMessage":"{punct:#?} is not a valid punct","messagePattern":"(.+?) is not a valid punct","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/syntax-bridge/src/to_parser_input.rs","lineNumber":81,"sourceCode":"                        let edition = ctx_edition(ident.span.ctx);\n                        match ident.sym.as_str() {\n                            \"_\" => res.push(T![_], edition),\n                            i if i.starts_with('\\'') => res.push(LIFETIME_IDENT, edition),\n                            _ if ident.is_raw.yes() => res.push(IDENT, edition),\n                            text => match SyntaxKind::from_keyword(text, edition) {\n                                Some(kind) => res.push(kind, edition),\n                                None => {\n                                    let contextual_keyword =\n                                        SyntaxKind::from_contextual_keyword(text, edition)\n                                            .unwrap_or(SyntaxKind::IDENT);\n                                    res.push_ident(contextual_keyword, edition);\n                                }\n                            },\n                        }\n                    }\n                    tt::Leaf::Punct(punct) => {\n                        let kind = SyntaxKind::from_char(punct.char)\n                            .unwrap_or_else(|| panic!(\"{punct:#?} is not a valid punct\"));\n                        res.push(kind, ctx_edition(punct.span.ctx));\n                        if punct.spacing == tt::Spacing::Joint {\n                            res.was_joint();\n                        }\n                    }\n                }\n                current.bump();\n            }\n            Some(tt::TokenTree::Subtree(subtree)) => {\n                if let Some(kind) = match subtree.delimiter.kind {\n                    tt::DelimiterKind::Parenthesis => Some(T!['(']),\n                    tt::DelimiterKind::Brace => Some(T!['{']),\n                    tt::DelimiterKind::Bracket => Some(T!['[']),\n                    tt::DelimiterKind::Invisible => None,\n                } {\n                    res.push(kind, ctx_edition(subtree.delimiter.open.ctx));\n                }\n                current.bump();","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/syntax-bridge/src/to_parser_input.rs#L63-L99","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Fix the producing proc-macro so it only emits valid Rust punctuation characters.","If the char is a legitimate new Rust punct, add a mapping for it in `SyntaxKind::from_char`.","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."],"exampleFix":"// before (panics)\nlet kind = SyntaxKind::from_char(punct.char)\n    .unwrap_or_else(|| panic!(\"{punct:#?} is not a valid punct\"));\n// after (caller-side guard)\nif SyntaxKind::from_char(punct.char).is_none() {\n    return Err(format!(\"invalid punct {:?}\", punct.char));\n}","handlingStrategy":"validation","validationCode":"fn is_valid_punct(p: &tt::Punct) -> bool {\n    syntax::SyntaxKind::from_char(p.char).is_some()\n}\n// check every Punct leaf in the TokenTreesView before calling to_parser_input","typeGuard":"fn valid_punct_kind(p: &tt::Punct) -> Option<syntax::SyntaxKind> {\n    syntax::SyntaxKind::from_char(p.char)\n}","tryCatchPattern":null,"preventionTips":["Check `SyntaxKind::from_char` before feeding puncts into `to_parser_input`.","Fuzz proc-macro outputs with arbitrary punct characters.","Keep the `tt` crate and `syntax` SyntaxKind punctuation mappings in sync when adding new puncts.","Never hand-craft token trees with non-Rust punctuation in tests."],"tags":["proc-macro","token-tree","panic","parser"],"backgroundTag":"invalid-punctuation-token","analyzedSha":"e8f7e90aa3e7b26aa9a000200f606c1078da99ec","analyzedAt":"2026-09-03T21:08:06.959Z","contentChangedAt":"2026-09-03T21:08:06.959Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}