{"id":"f6dca02c96aa023d","repo":"rust-lang/rust","slug":"failed-to-unescape-char-literal","errorCode":null,"errorMessage":"failed to unescape char literal","messagePattern":"failed to unescape char literal","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_ast/src/util/literal.rs","lineNumber":84,"sourceCode":"        }\n\n        // For byte/char/string literals, chars and escapes have already been\n        // checked in the lexer (in `cook_lexer_literal`). So we can assume all\n        // chars and escapes are valid here.\n        Ok(match kind {\n            token::Bool => {\n                assert!(symbol.is_bool_lit());\n                LitKind::Bool(symbol == kw::True)\n            }\n            token::Byte => {\n                return unescape_byte(symbol.as_str())\n                    .map(LitKind::Byte)\n                    .map_err(|_| panic!(\"failed to unescape byte literal\"));\n            }\n            token::Char => {\n                return unescape_char(symbol.as_str())\n                    .map(LitKind::Char)\n                    .map_err(|_| panic!(\"failed to unescape char literal\"));\n            }\n\n            // There are some valid suffixes for integer and float literals,\n            // so all the handling is done internally.\n            token::Integer => return integer_lit(symbol, suffix),\n            token::Float => return float_lit(symbol, suffix),\n\n            token::Str => {\n                // If there are no characters requiring special treatment we can\n                // reuse the symbol from the token. Otherwise, we must generate a\n                // new symbol because the string in the LitKind is different to the\n                // string in the token.\n                let s = symbol.as_str();\n                // Vanilla strings are so common we optimize for the common case where no chars\n                // requiring special behaviour are present.\n                let symbol = if s.contains('\\\\') {\n                    let mut buf = String::with_capacity(s.len());\n                    // Force-inlining here is aggressive but the closure is","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_ast/src/util/literal.rs#L66-L102","documentation":"Panic in LitKind::from_token_lit when unescape_char returns Err for a token::Char literal. As with the byte case, the lexer is contractually expected to have validated char escapes beforehand, so this panic signals a violation of that invariant — a malformed char literal reached semantic conversion. An internal-only path that should be unreachable from user source.","triggerScenarios":"Reached when from_token_lit is called on a token::Char whose symbol is not a valid char literal (e.g. '\\xZZ') without prior lexer validation. Possible via hand-built token streams in proc-macros, fuzzing the parser boundary, or a compiler change that bypasses cook_lexer_literal.","commonSituations":"Nightly rustc regressions in the lexer; proc-macro tooling that fabricates char literal tokens; fuzz targets. Normal user code with a bad char literal emits a diagnostic at parse time.","solutions":["Report the ICE to rust-lang/rust with the literal and rustc version hash.","If generating char literal tokens programmatically, ensure symbols are valid char literals or route through the lexer.","Pin to a known-good nightly to confirm the regression window.","Run the failing input through rustc_lexer directly to see whether validation is skipped."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Same shape as byte literals but for '...' char literals.\nfn valid_char_literal(sym: &str) -> bool {\n    let inner = sym.strip_prefix(\"'\").and_then(|s| s.strip_suffix(\"'\"));\n    let Some(inner) = inner else { return false; };\n    rustc_lexer::unescape::unescape_char(inner).is_ok()\n}\nif !valid_char_literal(symbol.as_str()) {\n    return Err(format!(\"invalid char literal: {}\", symbol));\n}","typeGuard":"fn is_valid_char_lit(kind: token::LitKind, sym: Symbol) -> bool {\n    matches!(kind, token::Char) && rustc_lexer::unescape::unescape_char(sym.as_str()).is_ok()\n}","tryCatchPattern":"let lit = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| LitKind::from_token_lit(raw_lit)));\nmatch lit {\n    Ok(Ok(LitKind::Char(c))) => c,\n    Ok(Ok(other)) => other,\n    Ok(Err(e)) => return Err(format!(\"char literal error: {:?}\", e)),\n    Err(_) => return Err(format!(\"char literal failed to unescape: {}\", raw_lit.symbol)),\n}","preventionTips":["Char literals must hold exactly one Unicode scalar value: a single source char or a \\u{...} escape in 0..=0x10FFFF excluding surrogates (0xD800..=0xDFFF).","Avoid bare \\x in '...' — use \\u{..}; \\xHH is only valid in byte literals.","When generating char literals from arbitrary code points, gate on char::from_u32(cp).is_some() before emitting the literal.","In proc-macro output, build LitKind::Char(c) directly instead of generating source text that must be re-lexed."],"tags":["rustc","ice","lexer","literal","char"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}