fix: consider path used in pattern destructing assignments
fix: assign mutable for variables in pattern destructing assignments
This commit is contained in:
parent
594623cada
commit
a44de58d4b
3 changed files with 99 additions and 18 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue