rust-lang/rust-analyzer · error

No file available to rename

Error message

No file available to rename

What it means

rust-analyzer's rename implementation (`source_edit_from_def` in crates/ide-db/src/rename.rs) builds text edits for every occurrence of the definition, tracking which file each occurrence lives in. This bail fires when, after collecting all edits, no file id was ever recorded (the local `file_id` Option stayed None), so there is no file on which to anchor the rename. It means the definition's textual source could not be resolved to a concrete file, typically because the definition comes from a macro or generated code whose origin file is unavailable.

Source

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

                            new_name.display(sema.db, source.file_id.edition(sema.db)).to_string(),
                        );
                    }
                } else {
                    edit.replace(
                        name_range,
                        new_name.display(sema.db, source.file_id.edition(sema.db)).to_string(),
                    );
                }
            }
        }
        let mut edit = edit.finish();

        for (edit, _) in source_change.source_file_edits.values_mut() {
            edit.set_annotation(conflict_annotation);
        }
        edit.set_annotation(conflict_annotation);

        let Some(file_id) = file_id else { bail!("No file available to rename") };
        return Ok((file_id.file_id(sema.db), edit));
    }
    let FileRange { file_id, range } = def
        .range_for_rename(sema)
        .ok_or_else(|| format_err!("No identifier available to rename"))?;
    let (range, new_name) = match def {
        Definition::ExternCrateDecl(decl) if decl.alias(sema.db).is_none() => (
            TextRange::empty(range.end()),
            format!(" as {}", new_name.display(sema.db, file_id.edition(sema.db)),),
        ),
        _ => (range, new_name.display(sema.db, file_id.edition(sema.db)).to_string()),
    };
    edit.replace(range, new_name);
    Ok((file_id.file_id(sema.db), edit.finish()))
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum IdentifierKind {

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Rename the symbol at its real (non-macro) definition site instead of the expanded/macro-introduced occurrence.
  2. Manually edit the macro definition text where the binding is declared, then rebuild.
  3. If the symbol genuinely has no file-anchored source, the rename is unsupported — do not attempt it; fall back to textual search-and-replace.
  4. Check for a rust-analyzer update; macro-defile mapping support improves between versions.

Example fix

// before: rename invoked on a local bound inside a macro body
macro_rules! m { () => { let tmp = 1; tmp } }
m!(); // cursor on `tmp` -> rename fails

// after: rename the identifier in the macro definition itself
macro_rules! m { () => { let value = 1; value } }
Defensive patterns

Strategy: try-catch

Validate before calling

// Client-side: only send rename for symbols whose definition is plain code.
// Heuristic check on the symbol under the cursor before calling rename:
fn is_plain_definition(def: &lsp_types::DocumentSymbol) -> bool {
    // skip symbols inside macro expansions / generated ranges
    !def.name.starts_with("macro")
}

Type guard

fn is_renameable(def_name: &str, kind_supported: bool) -> bool {
    kind_supported && !def_name.is_empty()
}

Try / catch

// Wrap the LSP rename request and surface a friendly message.
match rename(symbol, new_name) {
    Ok(edit) => apply_workspace_edit(edit),
    Err(e) if e.to_string().contains("No file available to rename") => {
        notify("Cannot rename: symbol is defined inside a macro; edit the macro definition manually.");
    }
    Err(e) => notify(&format!("Rename failed: {e}")),
}

Prevention

When it happens

Trigger: Calling the rename request (textDocument/rename) on a definition whose source is produced inside a macro declaration or expansion such that no `FileRange` for the identifier can be obtained; `rename_reference` -> `source_edit_from_def` runs with `file_id == None` at crates/ide-db/src/rename.rs:790. Related sibling error: `range_for_rename` returning None yields 'No identifier available to rename'.

Common situations: Renaming a local variable or binding introduced by a declarative macro (macro_rules!) whose defining site is in a macro body rust-analyzer cannot map back to a file; renaming symbols in heavily macro-generated code; renaming when the definition comes from a desugared/anonymous binding.

Related errors


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