{"record":{"id":"b5e649ae5d586448","repo":"rust-lang/rust-analyzer","slug":"invalid-escape-in-token-literal","errorCode":null,"errorMessage":"invalid escape in token literal","messagePattern":"invalid escape in token literal","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/ungrammar/src/lexer.rs","lineNumber":91,"sourceCode":"fn advance(input: &mut &str) -> Result<TokenKind> {\n    let mut chars = input.chars();\n    let c = chars.next().unwrap();\n    let res = match c {\n        '=' => TokenKind::Eq,\n        '*' => TokenKind::Star,\n        '?' => TokenKind::QMark,\n        '(' => TokenKind::LParen,\n        ')' => TokenKind::RParen,\n        '|' => TokenKind::Pipe,\n        ':' => TokenKind::Colon,\n        '\\'' => {\n            let mut buf = String::new();\n            loop {\n                match chars.next() {\n                    None => bail!(\"unclosed token literal\"),\n                    Some('\\\\') => match chars.next() {\n                        Some(c) if is_escapable(c) => buf.push(c),\n                        _ => bail!(\"invalid escape in token literal\"),\n                    },\n                    Some('\\'') => break,\n                    Some(c) => buf.push(c),\n                }\n            }\n            TokenKind::Token(buf)\n        }\n        c if is_ident_char(c) => {\n            let mut buf = String::new();\n            buf.push(c);\n            loop {\n                match chars.clone().next() {\n                    Some(c) if is_ident_char(c) => {\n                        chars.next();\n                        buf.push(c);\n                    }\n                    _ => break,\n                }","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/lib/ungrammar/src/lexer.rs#L73-L109","documentation":"Inside a `'...'` token literal, a backslash must be followed by an escapable character (checked by `is_escapable`). Any other escape sequence, or a trailing lone backslash at end of input, bails with this error.","triggerScenarios":"Writing an unsupported escape such as `'\\n'`, `'\\t'`, or `\\` immediately before the end of input inside a token literal in a `.ungrammar` file.","commonSituations":"Authors assuming C-style escapes (\\n, \\t, \\u...) work in ungrammar; regex-style escapes copied from other DSLs.","solutions":["Remove the backslash and use the literal character directly if it needs no escaping","Only escape characters accepted by `is_escapable` (e.g. `\\'` and `\\\\`)","Replace control characters with the grammar's own notation or restructure the token"],"exampleFix":"// before\nToken = 'line\\nend'\n// after\nToken = 'line\\nend'  // or drop the escape: 'line end'","handlingStrategy":"validation","validationCode":"// reject escapes not accepted by ungrammar's is_escapable\nfn has_bad_escape(src: &str) -> bool {\n    let esc: Vec<char> = vec!['\\'', '\\\\'];\n    let mut it = src.chars().peekable();\n    while let Some(c) = it.next() {\n        if c == '\\\\' {\n            match it.peek() {\n                None => return true,\n                Some(&n) if !esc.contains(&n) => return true,\n                _ => {}\n            }\n        }\n    }\n    false\n}","typeGuard":null,"tryCatchPattern":"match ungrammar::Grammar::parse(src) {\n    Err(e) if e.to_string().contains(\"invalid escape\") => {\n        eprintln!(\"unsupported escape in token literal: {e}\")\n    }\n    r => r?,\n}","preventionTips":["Do not assume C-style (\\n, \\t, \\u) escapes work in ungrammar","Only escape quotes and backslashes inside '...' literals","Lint grammar files for backslashes before parsing"],"tags":["lexer","ungrammar","escape","parse-error"],"backgroundTag":"invalid-escape-sequence","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"}