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() 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 parent = DefWithBodyId::FunctionId(self.func.into());
let body = db.body(parent); 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> { pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {

View file

@ -219,9 +219,14 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
let first_param = params let first_param = params
.first() .first()
.ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?; .ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?;
if first_param.as_local(sema.db) != local { match first_param.as_local(sema.db) {
Some(plocal) => {
if plocal != local {
bail!("Only the first parameter may be renamed to self"); bail!("Only the first parameter may be renamed to self");
} }
}
None => bail!("rename_to_self invoked on destructuring parameter"),
}
let assoc_item = fn_def let assoc_item = fn_def
.as_assoc_item(sema.db) .as_assoc_item(sema.db)

View file

@ -317,7 +317,10 @@ fn inline(
if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) { if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {
return Vec::new(); return Vec::new();
} }
usages_for_locals(param.as_local(sema.db)) // 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 { .map(|FileReference { name, range, .. }| match name {
ast::NameLike::NameRef(_) => body ast::NameLike::NameRef(_) => body
.syntax() .syntax()
@ -328,12 +331,15 @@ fn inline(
_ => None, _ => None,
}) })
.collect::<Option<Vec<_>>>() .collect::<Option<Vec<_>>>()
.unwrap_or_default() .unwrap_or_default(),
None => Vec::new(),
}
}) })
.collect(); .collect();
if function.self_param(sema.db).is_some() { if function.self_param(sema.db).is_some() {
let this = || make::name_ref("this").syntax().clone_for_update(); let this = || make::name_ref("this").syntax().clone_for_update();
usages_for_locals(params[0].2.as_local(sema.db)) if let Some(self_local) = params[0].2.as_local(sema.db) {
usages_for_locals(self_local)
.flat_map(|FileReference { name, range, .. }| match name { .flat_map(|FileReference { name, range, .. }| match name {
ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)), ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
_ => None, _ => None,
@ -342,6 +348,7 @@ fn inline(
ted::replace(it, &this()); ted::replace(it, &this());
}) })
} }
}
// Inline parameter expressions or generate `let` statements depending on whether inlining works or not. // 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() { for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() {
let inline_direct = |usage, replacement: &ast::Expr| { let inline_direct = |usage, replacement: &ast::Expr| {