rust-lang/rust-analyzer · error

unexpected `\r`, only Unix-style line endings allowed

Error message

unexpected `\r`, only Unix-style line endings allowed

What it means

The ungrammar lexer rejects carriage-return characters outright: any `\r` in the input bails, because grammar files are expected to use Unix (LF) line endings only. This keeps position tracking and the parser simple and deterministic.

Solutions

  1. Convert the file to LF line endings (dos2unix or editor setting)
  2. Add/update `.gitattributes` to force `*.ungrammar text eol=lf`
  3. Set git `core.autocrlf=input` for this repo
  4. Configure your editor to save LF for .ungrammar files

Example fix

# before (CRLF bytes)
Expr = 'foo'
# after (LF)
Expr = 'foo'
Defensive patterns

Strategy: validation

Validate before calling

// normalize CRLF before parsing
let src = if src.contains('\r') {
    src.replace("\r\n", "\n").replace('\r', "\n")
} else { src.to_string() };

Try / catch

match ungrammar::Grammar::parse(&src) {
    Err(e) if e.to_string().contains('\\r') => {
        eprintln!("convert the grammar file to LF line endings: {e}")
    }
    r => r?,
}

Prevention

When it happens

Trigger: Loading a `.ungrammar` file saved with CRLF (Windows) line endings; a tool or editor converting line endings; a git checkout with `core.autocrlf=true` on Windows.

Common situations: Cloning the repo on Windows with autocrlf enabled; editing grammar files in an editor configured for CRLF; committing files reformatted on Windows.

Related errors


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

Appendix: source

Thrown at lib/ungrammar/src/lexer.rs:113

                }
            }
            TokenKind::Token(buf)
        }
        c if is_ident_char(c) => {
            let mut buf = String::new();
            buf.push(c);
            loop {
                match chars.clone().next() {
                    Some(c) if is_ident_char(c) => {
                        chars.next();
                        buf.push(c);
                    }
                    _ => break,
                }
            }
            TokenKind::Node(buf)
        }
        '\r' => bail!("unexpected `\\r`, only Unix-style line endings allowed"),
        c => bail!("unexpected character: `{}`", c),
    };

    *input = chars.as_str();
    Ok(res)
}

fn is_escapable(c: char) -> bool {
    matches!(c, '\\' | '\'')
}
fn is_whitespace(c: char) -> bool {
    matches!(c, ' ' | '\t' | '\n')
}
fn is_ident_char(c: char) -> bool {
    matches!(c, 'a'..='z' | 'A'..='Z' | '_')
}

View on GitHub (pinned to e8f7e90aa3)