Unnamed repository; edit this file 'description' to name the repository.
fix: consider path used in pattern destructing assignments
fix: assign mutable for variables in pattern destructing assignments
dfireBird 4 months ago
parent 8434be8 · commit 2171300
-rw-r--r--crates/hir/src/semantics.rs53
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs62
-rw-r--r--crates/ide-db/src/search.rs2
3 files changed, 99 insertions, 18 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index a9af26aa3f..ccbfd45e30 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -10,6 +10,7 @@ use std::{
ops::{self, ControlFlow, Not},
};
+use base_db::FxIndexSet;
use either::Either;
use hir_def::{
DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId,
@@ -2197,7 +2198,7 @@ impl<'db> SemanticsImpl<'db> {
&self,
element: Either<&ast::Expr, &ast::StmtList>,
text_range: TextRange,
- ) -> Option<Vec<Local>> {
+ ) -> Option<FxIndexSet<Local>> {
let sa = self.analyze(element.either(|e| e.syntax(), |s| s.syntax()))?;
let store = sa.store()?;
let mut resolver = sa.resolver.clone();
@@ -2245,31 +2246,49 @@ impl<'db> SemanticsImpl<'db> {
let mut exprs: Vec<_> =
exprs.into_iter().filter_map(|e| sa.expr_id(e).and_then(|e| e.as_expr())).collect();
- let mut locals: Vec<Local> = Vec::new();
- let mut add_to_locals_used = |expr_id| {
- if let Expr::Path(path) = &store[expr_id]
+ let mut locals: FxIndexSet<Local> = FxIndexSet::default();
+ let mut add_to_locals_used = |id, parent_expr| {
+ let path = match id {
+ ExprOrPatId::ExprId(expr_id) => {
+ if let Expr::Path(path) = &store[expr_id] {
+ Some(path)
+ } else {
+ None
+ }
+ }
+ ExprOrPatId::PatId(pat_id) => {
+ if let Pat::Path(path) = &store[pat_id] {
+ Some(path)
+ } else {
+ None
+ }
+ }
+ };
+
+ if let Some(path) = path
&& is_not_generated(path)
{
- let _ = resolver.update_to_inner_scope(self.db, def, expr_id);
- resolver
- .resolve_path_in_value_ns_fully(self.db, path, store.expr_path_hygiene(expr_id))
- .inspect(|value| {
- if let ValueNs::LocalBinding(id) = value {
- locals.push((def, *id).into());
- }
- });
+ let _ = resolver.update_to_inner_scope(self.db, def, parent_expr);
+ let hygiene = store.expr_or_pat_path_hygiene(id);
+ resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).inspect(|value| {
+ if let ValueNs::LocalBinding(id) = value {
+ locals.insert((def, *id).into());
+ }
+ });
}
};
while let Some(expr_id) = exprs.pop() {
- let mut has_child = false;
+ if let Expr::Assignment { target, .. } = store[expr_id] {
+ store.walk_pats(target, &mut |id| {
+ add_to_locals_used(ExprOrPatId::PatId(id), expr_id)
+ });
+ };
store.walk_child_exprs(expr_id, |id| {
- has_child = true;
exprs.push(id);
});
- if !has_child {
- add_to_locals_used(expr_id)
- }
+
+ add_to_locals_used(ExprOrPatId::ExprId(expr_id), expr_id)
}
Some(locals)
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 19ded49b18..231df9b5b3 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -6313,4 +6313,66 @@ fn $0fun_name(v1: u32, v2: u32) -> u32 {
}"#,
);
}
+
+ #[test]
+ fn pattern_assignment() {
+ check_assist(
+ extract_function,
+ r#"
+struct Point {x: u32, y: u32};
+
+fn point() -> Point {
+ Point { x: 45, y: 50 };
+}
+
+fn foo() {
+ let mut a = 1;
+ let mut b = 3;
+ $0Point { x: a, y: b } = point();$0
+}
+"#,
+ r#"
+struct Point {x: u32, y: u32};
+
+fn point() -> Point {
+ Point { x: 45, y: 50 };
+}
+
+fn foo() {
+ let mut a = 1;
+ let mut b = 3;
+ fun_name(a, b);
+}
+
+fn $0fun_name(mut a: u32, mut b: u32) {
+ Point { x: a, y: b } = point();
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn tuple_assignment() {
+ check_assist(
+ extract_function,
+ r#"
+fn foo() {
+ let mut a = 3;
+ let mut b = 4;
+ $0(a, b) = (b, a);$0
+}
+"#,
+ r#"
+fn foo() {
+ let mut a = 3;
+ let mut b = 4;
+ fun_name(a, b);
+}
+
+fn $0fun_name(mut a: i32, mut b: i32) {
+ (a, b) = (b, a);
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index a48438cfa8..1d865892a2 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -1345,7 +1345,7 @@ impl ReferenceCategory {
// If the variable or field ends on the LHS's end then it's a Write
// (covers fields and locals). FIXME: This is not terribly accurate.
if let Some(lhs) = expr.lhs()
- && lhs.syntax().text_range().end() == r.syntax().text_range().end() {
+ && lhs.syntax().text_range().contains_range(r.syntax().text_range()) {
return Some(ReferenceCategory::WRITE)
}
}