Skip to content

Commit b3ca36b

Browse files
SSR: Fix for path resolution of locals
It seems that Semantics::scope, if given a statement node, won't resolve locals that were defined in the current scope, only in parent scopes. Not sure if this is intended / expected behavior, but we work around it for now by finding another nearby node to use as the scope (e.g. the expression inside the EXPR_STMT).
1 parent bb587fa commit b3ca36b

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

crates/ra_ssr/src/resolving.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ impl<'db> ResolutionScope<'db> {
152152
.left_biased()
153153
.map(|token| token.parent())
154154
.unwrap_or_else(|| file.syntax().clone());
155+
let node = pick_node_for_resolution(node);
155156
let scope = sema.scope(&node);
156157
ResolutionScope {
157158
scope,
@@ -185,6 +186,33 @@ impl<'db> ResolutionScope<'db> {
185186
}
186187
}
187188

189+
/// Returns a suitable node for resolving paths in the current scope. If we create a scope based on
190+
/// a statement node, then we can't resolve local variables that were defined in the current scope
191+
/// (only in parent scopes). So we find another node, ideally a child of the statement where local
192+
/// variable resolution is permitted.
193+
fn pick_node_for_resolution(node: SyntaxNode) -> SyntaxNode {
194+
match node.kind() {
195+
SyntaxKind::EXPR_STMT => {
196+
if let Some(n) = node.first_child() {
197+
mark::hit!(cursor_after_semicolon);
198+
return n;
199+
}
200+
}
201+
SyntaxKind::LET_STMT | SyntaxKind::BIND_PAT => {
202+
if let Some(next) = node.next_sibling() {
203+
return pick_node_for_resolution(next);
204+
}
205+
}
206+
SyntaxKind::NAME => {
207+
if let Some(parent) = node.parent() {
208+
return pick_node_for_resolution(parent);
209+
}
210+
}
211+
_ => {}
212+
}
213+
node
214+
}
215+
188216
/// Returns whether `path` or any of its qualifiers contains type arguments.
189217
fn path_contains_type_arguments(path: Option<ast::Path>) -> bool {
190218
if let Some(path) = path {

crates/ra_ssr/src/tests.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,3 +885,40 @@ fn ufcs_matches_method_call() {
885885
"#]],
886886
);
887887
}
888+
889+
#[test]
890+
fn replace_local_variable_reference() {
891+
// The pattern references a local variable `foo` in the block containing the cursor. We should
892+
// only replace references to this variable `foo`, not other variables that just happen to have
893+
// the same name.
894+
mark::check!(cursor_after_semicolon);
895+
assert_ssr_transform(
896+
"foo + $a ==>> $a - foo",
897+
r#"
898+
fn bar1() -> i32 {
899+
let mut res = 0;
900+
let foo = 5;
901+
res += foo + 1;
902+
let foo = 10;
903+
res += foo + 2;<|>
904+
res += foo + 3;
905+
let foo = 15;
906+
res += foo + 4;
907+
res
908+
}
909+
"#,
910+
expect![[r#"
911+
fn bar1() -> i32 {
912+
let mut res = 0;
913+
let foo = 5;
914+
res += foo + 1;
915+
let foo = 10;
916+
res += 2 - foo;
917+
res += 3 - foo;
918+
let foo = 15;
919+
res += foo + 4;
920+
res
921+
}
922+
"#]],
923+
)
924+
}

0 commit comments

Comments
 (0)