Rename fails on renaming definitions created by macros

This commit is contained in:
Lukas Wirth 2021-08-19 22:53:00 +02:00
parent 320bb72b7f
commit c67ecbebc4
5 changed files with 62 additions and 66 deletions

View file

@ -22,8 +22,7 @@ use either::Either;
pub use mbe::{ExpandError, ExpandResult}; pub use mbe::{ExpandError, ExpandResult};
pub use parser::FragmentKind; pub use parser::FragmentKind;
use std::hash::Hash; use std::{hash::Hash, sync::Arc};
use std::sync::Arc;
use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange}; use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange};
use syntax::{ use syntax::{
@ -32,11 +31,13 @@ use syntax::{
Direction, SyntaxNode, SyntaxToken, TextRange, TextSize, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
}; };
use crate::ast_id_map::FileAstId; use crate::{
use crate::builtin_attr::BuiltinAttrExpander; ast_id_map::FileAstId,
use crate::builtin_derive::BuiltinDeriveExpander; builtin_attr::BuiltinAttrExpander,
use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; builtin_derive::BuiltinDeriveExpander,
use crate::proc_macro::ProcMacroExpander; builtin_macro::{BuiltinFnLikeExpander, EagerExpander},
proc_macro::ProcMacroExpander,
};
#[cfg(test)] #[cfg(test)]
mod test_db; mod test_db;
@ -210,12 +211,12 @@ impl MacroDefId {
pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> { pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
let id = match &self.kind { let id = match &self.kind {
MacroDefKind::Declarative(id) => id,
MacroDefKind::BuiltIn(_, id) => id,
MacroDefKind::BuiltInAttr(_, id) => id,
MacroDefKind::BuiltInDerive(_, id) => id,
MacroDefKind::BuiltInEager(_, id) => id,
MacroDefKind::ProcMacro(.., id) => return Either::Right(*id), MacroDefKind::ProcMacro(.., id) => return Either::Right(*id),
MacroDefKind::Declarative(id)
| MacroDefKind::BuiltIn(_, id)
| MacroDefKind::BuiltInAttr(_, id)
| MacroDefKind::BuiltInDerive(_, id)
| MacroDefKind::BuiltInEager(_, id) => id,
}; };
Either::Left(*id) Either::Left(*id)
} }
@ -464,15 +465,10 @@ impl InFile<SyntaxNode> {
} }
impl<'a> InFile<&'a SyntaxNode> { impl<'a> InFile<&'a SyntaxNode> {
/// Falls back to the macro call range if the node cannot be mapped up fully.
pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange { pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
if let Some(range) = original_range_opt(db, self) { if let Some(res) = self.original_file_range_opt(db) {
let original_file = range.file_id.original_file(db); return res;
if range.file_id == original_file.into() {
return FileRange { file_id: original_file, range: range.value };
}
log::error!("Fail to mapping up more for {:?}", range);
return FileRange { file_id: range.file_id.original_file(db), range: range.value };
} }
// Fall back to whole macro call. // Fall back to whole macro call.
@ -483,8 +479,27 @@ impl<'a> InFile<&'a SyntaxNode> {
let orig_file = node.file_id.original_file(db); let orig_file = node.file_id.original_file(db);
assert_eq!(node.file_id, orig_file.into()); assert_eq!(node.file_id, orig_file.into());
FileRange { file_id: orig_file, range: node.value.text_range() } FileRange { file_id: orig_file, range: node.value.text_range() }
} }
/// Attempts to map the syntax node back up its macro calls.
pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option<FileRange> {
match original_range_opt(db, self) {
Some(range) => {
let original_file = range.file_id.original_file(db);
if range.file_id != original_file.into() {
log::error!("Failed mapping up more for {:?}", range);
}
Some(FileRange { file_id: original_file, range: range.value })
}
_ if !self.file_id.is_macro() => Some(FileRange {
file_id: self.file_id.original_file(db),
range: self.value.text_range(),
}),
_ => None,
}
}
} }
fn original_range_opt( fn original_range_opt(

View file

@ -143,8 +143,11 @@ impl NavigationTarget {
kind: SymbolKind, kind: SymbolKind,
) -> NavigationTarget { ) -> NavigationTarget {
let name = node.value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into()); let name = node.value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
let focus_range = let focus_range = node
node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range); .value
.name()
.and_then(|it| node.with_value(it.syntax()).original_file_range_opt(db))
.map(|it| it.range);
let frange = node.map(|it| it.syntax()).original_file_range(db); let frange = node.map(|it| it.syntax()).original_file_range(db);
NavigationTarget::from_syntax(frange.file_id, name, focus_range, frange.range, kind) NavigationTarget::from_syntax(frange.file_id, name, focus_range, frange.range, kind)

View file

@ -1869,8 +1869,7 @@ fn f() { <()>::BAR$0; }"#,
} }
#[test] #[test]
fn macros_are_broken_lol() { fn defs_from_macros_arent_renamed() {
cov_mark::check!(macros_are_broken_lol);
check( check(
"lol", "lol",
r#" r#"
@ -1878,11 +1877,7 @@ macro_rules! m { () => { fn f() {} } }
m!(); m!();
fn main() { f$0() } fn main() { f$0() }
"#, "#,
r#" "error: No identifier available to rename",
macro_rules! m { () => { fn f() {} } }
lol
fn main() { lol() }
"#,
) )
} }
} }

View file

@ -1446,7 +1446,6 @@ gen2!();
0, 0,
), ),
full_range: 228..236, full_range: 228..236,
focus_range: 228..236,
name: "foo_test2", name: "foo_test2",
kind: Function, kind: Function,
}, },
@ -1467,7 +1466,6 @@ gen2!();
0, 0,
), ),
full_range: 218..225, full_range: 218..225,
focus_range: 218..225,
name: "foo_test", name: "foo_test",
kind: Function, kind: Function,
}, },
@ -1533,7 +1531,6 @@ foo!();
0, 0,
), ),
full_range: 210..217, full_range: 210..217,
focus_range: 210..217,
name: "foo0", name: "foo0",
kind: Function, kind: Function,
}, },
@ -1554,7 +1551,6 @@ foo!();
0, 0,
), ),
full_range: 210..217, full_range: 210..217,
focus_range: 210..217,
name: "foo1", name: "foo1",
kind: Function, kind: Function,
}, },
@ -1575,7 +1571,6 @@ foo!();
0, 0,
), ),
full_range: 210..217, full_range: 210..217,
focus_range: 210..217,
name: "foo2", name: "foo2",
kind: Function, kind: Function,
}, },

View file

@ -81,14 +81,6 @@ impl Definition {
/// `Definition`. Note that some definitions, like buitin types, can't be /// `Definition`. Note that some definitions, like buitin types, can't be
/// renamed. /// renamed.
pub fn range_for_rename(self, sema: &Semantics<RootDatabase>) -> Option<FileRange> { pub fn range_for_rename(self, sema: &Semantics<RootDatabase>) -> Option<FileRange> {
// FIXME: the `original_file_range` calls here are wrong -- they never fail,
// and _fall back_ to the entirety of the macro call. Such fall back is
// incorrect for renames. The safe behavior would be to return an error for
// such cases. The correct behavior would be to return an auxiliary list of
// "can't rename these occurrences in macros" items, and then show some kind
// of a dialog to the user. See:
cov_mark::hit!(macros_are_broken_lol);
let res = match self { let res = match self {
Definition::Macro(mac) => { Definition::Macro(mac) => {
let src = mac.source(sema.db)?; let src = mac.source(sema.db)?;
@ -96,38 +88,35 @@ impl Definition {
Either::Left(it) => it.name()?, Either::Left(it) => it.name()?,
Either::Right(it) => it.name()?, Either::Right(it) => it.name()?,
}; };
src.with_value(name.syntax()).original_file_range(sema.db) src.with_value(name.syntax()).original_file_range_opt(sema.db)
} }
Definition::Field(field) => { Definition::Field(field) => {
let src = field.source(sema.db)?; let src = field.source(sema.db)?;
match &src.value { match &src.value {
FieldSource::Named(record_field) => { FieldSource::Named(record_field) => {
let name = record_field.name()?; let name = record_field.name()?;
src.with_value(name.syntax()).original_file_range(sema.db) src.with_value(name.syntax()).original_file_range_opt(sema.db)
}
FieldSource::Pos(_) => {
return None;
} }
FieldSource::Pos(_) => None,
} }
} }
Definition::ModuleDef(module_def) => match module_def { Definition::ModuleDef(module_def) => match module_def {
hir::ModuleDef::Module(module) => { hir::ModuleDef::Module(module) => {
let src = module.declaration_source(sema.db)?; let src = module.declaration_source(sema.db)?;
let name = src.value.name()?; let name = src.value.name()?;
src.with_value(name.syntax()).original_file_range(sema.db) src.with_value(name.syntax()).original_file_range_opt(sema.db)
} }
hir::ModuleDef::Function(it) => name_range(it, sema)?, hir::ModuleDef::Function(it) => name_range(it, sema),
hir::ModuleDef::Adt(adt) => match adt { hir::ModuleDef::Adt(adt) => match adt {
hir::Adt::Struct(it) => name_range(it, sema)?, hir::Adt::Struct(it) => name_range(it, sema),
hir::Adt::Union(it) => name_range(it, sema)?, hir::Adt::Union(it) => name_range(it, sema),
hir::Adt::Enum(it) => name_range(it, sema)?, hir::Adt::Enum(it) => name_range(it, sema),
}, },
hir::ModuleDef::Variant(it) => name_range(it, sema)?, hir::ModuleDef::Variant(it) => name_range(it, sema),
hir::ModuleDef::Const(it) => name_range(it, sema)?, hir::ModuleDef::Const(it) => name_range(it, sema),
hir::ModuleDef::Static(it) => name_range(it, sema)?, hir::ModuleDef::Static(it) => name_range(it, sema),
hir::ModuleDef::Trait(it) => name_range(it, sema)?, hir::ModuleDef::Trait(it) => name_range(it, sema),
hir::ModuleDef::TypeAlias(it) => name_range(it, sema)?, hir::ModuleDef::TypeAlias(it) => name_range(it, sema),
hir::ModuleDef::BuiltinType(_) => return None, hir::ModuleDef::BuiltinType(_) => return None,
}, },
Definition::SelfType(_) => return None, Definition::SelfType(_) => return None,
@ -137,7 +126,7 @@ impl Definition {
Either::Left(bind_pat) => bind_pat.name()?, Either::Left(bind_pat) => bind_pat.name()?,
Either::Right(_) => return None, Either::Right(_) => return None,
}; };
src.with_value(name.syntax()).original_file_range(sema.db) src.with_value(name.syntax()).original_file_range_opt(sema.db)
} }
Definition::GenericParam(generic_param) => match generic_param { Definition::GenericParam(generic_param) => match generic_param {
hir::GenericParam::TypeParam(type_param) => { hir::GenericParam::TypeParam(type_param) => {
@ -146,22 +135,22 @@ impl Definition {
Either::Left(type_param) => type_param.name()?, Either::Left(type_param) => type_param.name()?,
Either::Right(_trait) => return None, Either::Right(_trait) => return None,
}; };
src.with_value(name.syntax()).original_file_range(sema.db) src.with_value(name.syntax()).original_file_range_opt(sema.db)
} }
hir::GenericParam::LifetimeParam(lifetime_param) => { hir::GenericParam::LifetimeParam(lifetime_param) => {
let src = lifetime_param.source(sema.db)?; let src = lifetime_param.source(sema.db)?;
let lifetime = src.value.lifetime()?; let lifetime = src.value.lifetime()?;
src.with_value(lifetime.syntax()).original_file_range(sema.db) src.with_value(lifetime.syntax()).original_file_range_opt(sema.db)
} }
hir::GenericParam::ConstParam(it) => name_range(it, sema)?, hir::GenericParam::ConstParam(it) => name_range(it, sema),
}, },
Definition::Label(label) => { Definition::Label(label) => {
let src = label.source(sema.db); let src = label.source(sema.db);
let lifetime = src.value.lifetime()?; let lifetime = src.value.lifetime()?;
src.with_value(lifetime.syntax()).original_file_range(sema.db) src.with_value(lifetime.syntax()).original_file_range_opt(sema.db)
} }
}; };
return Some(res); return res;
fn name_range<D>(def: D, sema: &Semantics<RootDatabase>) -> Option<FileRange> fn name_range<D>(def: D, sema: &Semantics<RootDatabase>) -> Option<FileRange>
where where
@ -170,8 +159,7 @@ impl Definition {
{ {
let src = def.source(sema.db)?; let src = def.source(sema.db)?;
let name = src.value.name()?; let name = src.value.name()?;
let res = src.with_value(name.syntax()).original_file_range(sema.db); src.with_value(name.syntax()).original_file_range_opt(sema.db)
Some(res)
} }
} }
} }