about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2022-04-09 00:55:45 +0200
committerLukas Wirth <lukastw97@gmail.com>2022-04-09 00:55:45 +0200
commit15e7112da31b8ffd246620831b3dc2b70a858af5 (patch)
treee4ef4e314eb2cae34e7815b8b7635929683bf938
parent399559e5973a16372b686e05844d930ec4c314c5 (diff)
downloadrust-15e7112da31b8ffd246620831b3dc2b70a858af5.tar.gz
rust-15e7112da31b8ffd246620831b3dc2b70a858af5.zip
fix: Check whether a parameter can be converted to a local
-rw-r--r--crates/hir/src/lib.rs9
-rw-r--r--crates/ide/src/rename.rs9
-rw-r--r--crates/ide_assists/src/handlers/inline_call.rs47
3 files changed, 41 insertions, 24 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 8bab7c1f3ea..37bb2e1997d 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -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> {
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 83bc299adcc..1bb3cdb9073 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -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
diff --git a/crates/ide_assists/src/handlers/inline_call.rs b/crates/ide_assists/src/handlers/inline_call.rs
index af2cb332e5a..c857adf876e 100644
--- a/crates/ide_assists/src/handlers/inline_call.rs
+++ b/crates/ide_assists/src/handlers/inline_call.rs
@@ -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() {