8169: Make more use of the HIR in rename::rename_to_self r=Veykril a=Veykril

bors r+

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-03-23 15:55:04 +00:00 committed by GitHub
commit 065a8e87cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 44 deletions

View File

@ -852,6 +852,7 @@ impl Function {
}) })
.collect() .collect()
} }
pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> { pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
if self.self_param(db).is_none() { if self.self_param(db).is_none() {
return None; return None;
@ -909,7 +910,7 @@ impl From<hir_ty::Mutability> for Access {
} }
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct Param { pub struct Param {
func: Function, func: Function,
/// The index in parameter list, including self parameter. /// The index in parameter list, including self parameter.
@ -922,13 +923,25 @@ impl Param {
&self.ty &self.ty
} }
pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> { pub fn as_local(&self, db: &dyn HirDatabase) -> Local {
let params = self.func.source(db)?.value.param_list()?; let parent = DefWithBodyId::FunctionId(self.func.into());
if params.self_param().is_some() { let body = db.body(parent);
params.params().nth(self.idx.checked_sub(1)?)?.pat() Local { parent, pat_id: body.params[self.idx] }
} else {
params.params().nth(self.idx)?.pat()
} }
pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {
self.source(db).and_then(|p| p.value.pat())
}
pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::Param>> {
let InFile { file_id, value } = self.func.source(db)?;
let params = value.param_list()?;
if params.self_param().is_some() {
params.params().nth(self.idx.checked_sub(1)?)
} else {
params.params().nth(self.idx)
}
.map(|value| InFile { file_id, value })
} }
} }

View File

@ -1,8 +1,11 @@
//! FIXME: write short doc here //! Renaming functionality
//!
//! All reference and file rename requests go through here where the corresponding [`SourceChange`]s
//! will be calculated.
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use either::Either; use either::Either;
use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; use hir::{AsAssocItem, InFile, Module, ModuleDef, ModuleSource, Semantics};
use ide_db::{ use ide_db::{
base_db::{AnchoredPathBuf, FileId}, base_db::{AnchoredPathBuf, FileId},
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
@ -196,7 +199,7 @@ fn rename_mod(
file_id, file_id,
TextEdit::replace(name.syntax().text_range(), new_name.to_string()), TextEdit::replace(name.syntax().text_range(), new_name.to_string()),
), ),
_ => unreachable!(), _ => never!("Module source node is missing a name"),
} }
} }
let def = Definition::ModuleDef(ModuleDef::Module(module)); let def = Definition::ModuleDef(ModuleDef::Module(module));
@ -275,46 +278,32 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
let fn_def = match local.parent(sema.db) { let fn_def = match local.parent(sema.db) {
hir::DefWithBody::Function(func) => func, hir::DefWithBody::Function(func) => func,
_ => bail!("Cannot rename non-param local to self"), _ => bail!("Cannot rename local to self outside of function"),
}; };
// FIXME: reimplement this on the hir instead if let Some(_) = fn_def.self_param(sema.db) {
// as of the time of this writing params in hir don't keep their names
let fn_ast = fn_def
.source(sema.db)
.ok_or_else(|| format_err!("Cannot rename non-param local to self"))?
.value;
let first_param_range = fn_ast
.param_list()
.and_then(|p| p.params().next())
.ok_or_else(|| format_err!("Method has no parameters"))?
.syntax()
.text_range();
let InFile { file_id, value: local_source } = local.source(sema.db);
match local_source {
either::Either::Left(pat)
if !first_param_range.contains_range(pat.syntax().text_range()) =>
{
bail!("Only the first parameter can be self");
}
_ => (),
}
let impl_block = fn_ast
.syntax()
.ancestors()
.find_map(|node| ast::Impl::cast(node))
.and_then(|def| sema.to_def(&def))
.ok_or_else(|| format_err!("No impl block found for function"))?;
if fn_def.self_param(sema.db).is_some() {
bail!("Method already has a self parameter"); bail!("Method already has a self parameter");
} }
let params = fn_def.assoc_fn_params(sema.db); let params = fn_def.assoc_fn_params(sema.db);
let first_param = params.first().ok_or_else(|| format_err!("Method has no parameters"))?; let first_param = params
.first()
.ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?;
if first_param.as_local(sema.db) != local {
bail!("Only the first parameter may be renamed to self");
}
let assoc_item = fn_def
.as_assoc_item(sema.db)
.ok_or_else(|| format_err!("Cannot rename parameter to self for free function"))?;
let impl_ = match assoc_item.container(sema.db) {
hir::AssocItemContainer::Trait(_) => {
bail!("Cannot rename parameter to self for trait functions");
}
hir::AssocItemContainer::Impl(impl_) => impl_,
};
let first_param_ty = first_param.ty(); let first_param_ty = first_param.ty();
let impl_ty = impl_block.target_ty(sema.db); let impl_ty = impl_.target_ty(sema.db);
let (ty, self_param) = if impl_ty.remove_ref().is_some() { let (ty, self_param) = if impl_ty.remove_ref().is_some() {
// if the impl is a ref to the type we can just match the `&T` with self directly // if the impl is a ref to the type we can just match the `&T` with self directly
(first_param_ty.clone(), "self") (first_param_ty.clone(), "self")
@ -328,6 +317,9 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
bail!("Parameter type differs from impl block type"); bail!("Parameter type differs from impl block type");
} }
let InFile { file_id, value: param_source } =
first_param.source(sema.db).ok_or_else(|| format_err!("No source for parameter found"))?;
let def = Definition::Local(local); let def = Definition::Local(local);
let usages = def.usages(sema).all(); let usages = def.usages(sema).all();
let mut source_change = SourceChange::default(); let mut source_change = SourceChange::default();
@ -336,9 +328,8 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
})); }));
source_change.insert_source_edit( source_change.insert_source_edit(
file_id.original_file(sema.db), file_id.original_file(sema.db),
TextEdit::replace(first_param_range, String::from(self_param)), TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)),
); );
Ok(source_change) Ok(source_change)
} }
@ -1361,7 +1352,7 @@ fn f(foo$0: &mut Foo) -> i32 {
foo.i foo.i
} }
"#, "#,
"error: No impl block found for function", "error: Cannot rename parameter to self for free function",
); );
check( check(
"self", "self",
@ -1391,7 +1382,7 @@ impl Foo {
} }
} }
"#, "#,
"error: Only the first parameter can be self", "error: Only the first parameter may be renamed to self",
); );
} }