3
3
use crate :: errors:: error;
4
4
use crate :: { parsing, SsrError } ;
5
5
use parsing:: Placeholder ;
6
+ use ra_db:: FilePosition ;
6
7
use ra_syntax:: { ast, SmolStr , SyntaxKind , SyntaxNode , SyntaxToken } ;
7
8
use rustc_hash:: { FxHashMap , FxHashSet } ;
8
9
use test_utils:: mark;
9
10
11
+ pub ( crate ) struct ResolutionScope < ' db > {
12
+ scope : hir:: SemanticsScope < ' db > ,
13
+ hygiene : hir:: Hygiene ,
14
+ }
15
+
10
16
pub ( crate ) struct ResolvedRule {
11
17
pub ( crate ) pattern : ResolvedPattern ,
12
18
pub ( crate ) template : Option < ResolvedPattern > ,
@@ -30,12 +36,11 @@ pub(crate) struct ResolvedPath {
30
36
impl ResolvedRule {
31
37
pub ( crate ) fn new (
32
38
rule : parsing:: ParsedRule ,
33
- scope : & hir:: SemanticsScope ,
34
- hygiene : & hir:: Hygiene ,
39
+ resolution_scope : & ResolutionScope ,
35
40
index : usize ,
36
41
) -> Result < ResolvedRule , SsrError > {
37
42
let resolver =
38
- Resolver { scope , hygiene , placeholders_by_stand_in : rule. placeholders_by_stand_in } ;
43
+ Resolver { resolution_scope , placeholders_by_stand_in : rule. placeholders_by_stand_in } ;
39
44
let resolved_template = if let Some ( template) = rule. template {
40
45
Some ( resolver. resolve_pattern_tree ( template) ?)
41
46
} else {
@@ -57,8 +62,7 @@ impl ResolvedRule {
57
62
}
58
63
59
64
struct Resolver < ' a , ' db > {
60
- scope : & ' a hir:: SemanticsScope < ' db > ,
61
- hygiene : & ' a hir:: Hygiene ,
65
+ resolution_scope : & ' a ResolutionScope < ' db > ,
62
66
placeholders_by_stand_in : FxHashMap < SmolStr , parsing:: Placeholder > ,
63
67
}
64
68
@@ -104,6 +108,7 @@ impl Resolver<'_, '_> {
104
108
&& !self . path_contains_placeholder ( & path)
105
109
{
106
110
let resolution = self
111
+ . resolution_scope
107
112
. resolve_path ( & path)
108
113
. ok_or_else ( || error ! ( "Failed to resolve path `{}`" , node. text( ) ) ) ?;
109
114
resolved_paths. insert ( node, ResolvedPath { resolution, depth } ) ;
@@ -131,9 +136,32 @@ impl Resolver<'_, '_> {
131
136
}
132
137
false
133
138
}
139
+ }
140
+
141
+ impl < ' db > ResolutionScope < ' db > {
142
+ pub ( crate ) fn new (
143
+ sema : & hir:: Semantics < ' db , ra_ide_db:: RootDatabase > ,
144
+ lookup_context : FilePosition ,
145
+ ) -> ResolutionScope < ' db > {
146
+ use ra_syntax:: ast:: AstNode ;
147
+ let file = sema. parse ( lookup_context. file_id ) ;
148
+ // Find a node at the requested position, falling back to the whole file.
149
+ let node = file
150
+ . syntax ( )
151
+ . token_at_offset ( lookup_context. offset )
152
+ . left_biased ( )
153
+ . map ( |token| token. parent ( ) )
154
+ . unwrap_or_else ( || file. syntax ( ) . clone ( ) ) ;
155
+ let node = pick_node_for_resolution ( node) ;
156
+ let scope = sema. scope ( & node) ;
157
+ ResolutionScope {
158
+ scope,
159
+ hygiene : hir:: Hygiene :: new ( sema. db , lookup_context. file_id . into ( ) ) ,
160
+ }
161
+ }
134
162
135
163
fn resolve_path ( & self , path : & ast:: Path ) -> Option < hir:: PathResolution > {
136
- let hir_path = hir:: Path :: from_src ( path. clone ( ) , self . hygiene ) ?;
164
+ let hir_path = hir:: Path :: from_src ( path. clone ( ) , & self . hygiene ) ?;
137
165
// First try resolving the whole path. This will work for things like
138
166
// `std::collections::HashMap`, but will fail for things like
139
167
// `std::collections::HashMap::new`.
@@ -158,6 +186,33 @@ impl Resolver<'_, '_> {
158
186
}
159
187
}
160
188
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
+
161
216
/// Returns whether `path` or any of its qualifiers contains type arguments.
162
217
fn path_contains_type_arguments ( path : Option < ast:: Path > ) -> bool {
163
218
if let Some ( path) = path {
0 commit comments