rust-lang/rust-analyzer · error

Invalid name `{}`: cannot rename to a keyword

Error message

Invalid name `{}`: cannot rename to a keyword

What it means

`IdentifierKind::classify` (crates/ide-db/src/rename.rs:829) validates a proposed new name by lexing it as a single Rust token. If the name lexes to a keyword, only a small set is permitted: `self` (LowercaseSelf) and path keywords that become valid idents via raw-name representation (`_ => Ok(...Ident)`). The reserved path keywords `crate`, `super`, and `Self` can never be binding names, so the method bails with 'cannot rename to a keyword'.

Source

Thrown at crates/ide-db/src/rename.rs:829

    Underscore,
    LowercaseSelf,
}

impl IdentifierKind {
    pub fn classify(edition: Edition, new_name: &str) -> Result<(Name, IdentifierKind)> {
        match parser::LexedStr::single_token(edition, new_name) {
            Some(res) => match res {
                (SyntaxKind::IDENT, _) => Ok((Name::new_root(new_name), IdentifierKind::Ident)),
                (T![_], _) => {
                    Ok((Name::new_symbol_root(sym::underscore), IdentifierKind::Underscore))
                }
                (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
                    Ok((Name::new_lifetime(new_name), IdentifierKind::Lifetime))
                }
                _ if SyntaxKind::from_keyword(new_name, edition).is_some() => match new_name {
                    "self" => Ok((Name::new_root(new_name), IdentifierKind::LowercaseSelf)),
                    "crate" | "super" | "Self" => {
                        bail!("Invalid name `{}`: cannot rename to a keyword", new_name)
                    }
                    _ => Ok((Name::new_root(new_name), IdentifierKind::Ident)),
                },
                (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
                (_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
            },
            None => bail!("Invalid name `{}`: not an identifier", new_name),
        }
    }
}

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Choose a legal identifier that is not one of `crate`, `super`, or `Self` (e.g. `self_param` instead of `self`-like keywords).
  2. If you wanted path-component semantics, rename the module/item to a normal name and update `use` paths instead.
  3. For a type alias intent, keep the original name and add `type SelfAlias = ...;` rather than renaming.

Example fix

// before: LSP rename request
{ "newName": "Self" }

// after
{ "newName": "SelfType" }
Defensive patterns

Strategy: validation

Validate before calling

const RESERVED: [&str; 3] = ["crate", "super", "Self"];
fn validate_rename_name(new_name: &str) -> Result<(), String> {
    if RESERVED.contains(&new_name) {
        return Err(format!("cannot rename to keyword `{new_name}`"));
    }
    Ok(())
}

Type guard

fn is_legal_rust_ident(s: &str) -> bool {
    let mut cs = s.chars();
    matches!(cs.next(), Some(c) if c.is_alphabetic() || c == '_')
        && cs.all(|c| c.is_alphanumeric() || c == '_')
        && !matches!(s, "crate" | "super" | "Self")
}

Prevention

When it happens

Trigger: Calling rename (textDocument/rename) with newName equal to `crate`, `super`, or `Self` on any item; the name passes the single-token check, matches `SyntaxKind::from_keyword`, and hits the explicit bail arm at rename.rs:829.

Common situations: User (or an LLM/refactoring script driving the LSP) types `Self` intending a struct-like alias, or `super`/`crate` intending a path segment rename — none of which are legal identifier positions in Rust.

Related errors


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