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,8 +219,13 @@ 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) {
bail!("Only the first parameter may be renamed to self"); 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 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()) { 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
.map(|FileReference { name, range, .. }| match name { // not only the local if it is a simple binding
ast::NameLike::NameRef(_) => body match param.as_local(sema.db) {
.syntax() Some(l) => usages_for_locals(l)
.covering_element(range) .map(|FileReference { name, range, .. }| match name {
.ancestors() ast::NameLike::NameRef(_) => body
.nth(3) .syntax()
.and_then(ast::PathExpr::cast), .covering_element(range)
_ => None, .ancestors()
}) .nth(3)
.collect::<Option<Vec<_>>>() .and_then(ast::PathExpr::cast),
.unwrap_or_default() _ => None,
})
.collect::<Option<Vec<_>>>()
.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) {
.flat_map(|FileReference { name, range, .. }| match name { usages_for_locals(self_local)
ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)), .flat_map(|FileReference { name, range, .. }| match name {
_ => None, ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
}) _ => None,
.for_each(|it| { })
ted::replace(it, &this()); .for_each(|it| {
}) 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() {