fix: Check whether a parameter can be converted to a local

This commit is contained in:
Lukas Wirth 2022-04-09 00:55:45 +02:00
parent 399559e597
commit 15e7112da3
3 changed files with 41 additions and 24 deletions

View file

@ -1506,10 +1506,15 @@ impl Param {
db.function_data(self.func.id).params[self.idx].0.clone()
}
pub fn as_local(&self, db: &dyn HirDatabase) -> Local {
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
let parent = DefWithBodyId::FunctionId(self.func.into());
let body = db.body(parent);
Local { parent, pat_id: body.params[self.idx] }
let pat_id = body.params[self.idx];
if let Pat::Bind { .. } = &body[pat_id] {
Some(Local { parent, pat_id: body.params[self.idx] })
} else {
None
}
}
pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {

View file

@ -219,8 +219,13 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
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");
match first_param.as_local(sema.db) {
Some(plocal) => {
if plocal != local {
bail!("Only the first parameter may be renamed to self");
}
}
None => bail!("rename_to_self invoked on destructuring parameter"),
}
let assoc_item = fn_def

View file

@ -317,30 +317,37 @@ fn inline(
if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {
return Vec::new();
}
usages_for_locals(param.as_local(sema.db))
.map(|FileReference { name, range, .. }| match name {
ast::NameLike::NameRef(_) => body
.syntax()
.covering_element(range)
.ancestors()
.nth(3)
.and_then(ast::PathExpr::cast),
_ => None,
})
.collect::<Option<Vec<_>>>()
.unwrap_or_default()
// FIXME: we need to fetch all locals declared in the parameter here
// not only the local if it is a simple binding
match param.as_local(sema.db) {
Some(l) => usages_for_locals(l)
.map(|FileReference { name, range, .. }| match name {
ast::NameLike::NameRef(_) => body
.syntax()
.covering_element(range)
.ancestors()
.nth(3)
.and_then(ast::PathExpr::cast),
_ => None,
})
.collect::<Option<Vec<_>>>()
.unwrap_or_default(),
None => Vec::new(),
}
})
.collect();
if function.self_param(sema.db).is_some() {
let this = || make::name_ref("this").syntax().clone_for_update();
usages_for_locals(params[0].2.as_local(sema.db))
.flat_map(|FileReference { name, range, .. }| match name {
ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
_ => None,
})
.for_each(|it| {
ted::replace(it, &this());
})
if let Some(self_local) = params[0].2.as_local(sema.db) {
usages_for_locals(self_local)
.flat_map(|FileReference { name, range, .. }| match name {
ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
_ => None,
})
.for_each(|it| {
ted::replace(it, &this());
})
}
}
// Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() {