rust-lang/rust-analyzer · error
Can't rename local that is defined in a macro declaration
Error message
Can't rename local that is defined in a macro declaration
What it means
In `source_edit_from_def`, rust_analyzer must find the textual source of the definition being renamed to place the edit. When the local is declared inside a macro (the pattern comes from macro expansion and has no mappable original syntax node), no source range exists and it bails with "Can't rename local that is defined in a macro declaration".
Source
Thrown at crates/ide-db/src/rename.rs:725
.and_then(|x| x.name())
.and_then(|x| sema.original_range_opt(x.syntax()))
.or_else(|| {
source
.source
.syntax()
.original_file_range_opt(sema.db)
.map(TupleExt::head)
}) {
Some(FileRange { file_id: file_id2, range }) => {
file_id = Some(file_id2);
edit.replace(
range,
new_name.display(sema.db, file_id2.edition(sema.db)).to_string(),
);
continue;
}
None => {
bail!("Can't rename local that is defined in a macro declaration")
}
}
}
};
file_id = Some(source.file_id);
if let Either::Left(pat) = source.value {
let name_range = pat.name().unwrap().syntax().text_range();
// special cases required for renaming fields/locals in Record patterns
if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
if let Some(name_ref) = pat_field.name_ref() {
if new_name.as_str() == name_ref.text().trim_start_matches("r#")
&& pat.at_token().is_none()
{
// Foo { field: ref mut local } -> Foo { ref mut field }
// ^^^^^^ delete this
// ^^^^^ replace this with `field`
cov_mark::hit!(test_rename_local_put_init_shorthand_pat);View on GitHub (pinned to e8f7e90aa3)
Solutions
- Rename the local at its real (non-macro) definition site, or inline the macro expansion and rename there.
- Restructure the code so the variable is bound outside the macro.
- In tooling, detect that the definition's source is inside a macro file and disable the rename action upfront.
Example fix
// before
macro_rules! m { ($v:ident) => { let x = 1; let _ = $v; } }
// renaming `x` (declared inside the macro) fails
// after: declare the local outside the macro
let x = 1;
macro_rules! m { ($v:ident) => { let _ = $v + x; } }
rename(x_local, "y")?; // ok Defensive patterns
Strategy: try-catch
Validate before calling
fn is_in_macro_file(def: &Definition, sema: &Semantics<'_, RootDatabase>) -> bool {
def.source(sema)
.map(|s| s.file_id.is_macro())
.unwrap_or(true)
} Type guard
fn has_real_source(def: &Definition, sema: &Semantics<'_, RootDatabase>) -> bool {
def.source(sema).map(|s| !s.file_id.is_macro()).unwrap_or(false)
} Try / catch
match def.rename(sema, new_name, ...) {
Err(e) if e.to_string().contains("defined in a macro declaration") => eprintln!("expand the macro and rename there"),
other => other?,
} Prevention
- Disable rename actions when the definition's source maps into a macro expansion file.
- Bind variables outside macros when their names need to change.
- Fall back to textual find/replace in the macro definition for such locals.
When it happens
Trigger: Renaming a `Definition::Local` whose binding pattern was produced by a macro expansion — e.g. a variable introduced by `macro_rules!` or `vec![]` destructuring — such that `source.value` pattern resolution returns `None` at crates/ide-db/src/rename.rs:725.
Common situations: User places the cursor on a variable generated by a macro (`let (a, b) = tuple;` from a macro, or identifiers emitted by declarative macros) and invokes rename; the IDE cannot produce text edits inside macro input.
Related errors
- Cannot rename builtin type
- Cannot rename a builtin attr.
- Cannot rename `Self`
- Invalid name `{0}`: cannot rename module to {0}
- Invalid name `{}`: not a lifetime identifier
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/be23892ae4e88391.
Report an issue: GitHub.