33use crate :: errors:: error;
44use crate :: { parsing, SsrError } ;
55use parsing:: Placeholder ;
6+ use ra_db:: FilePosition ;
67use ra_syntax:: { ast, SmolStr , SyntaxKind , SyntaxNode , SyntaxToken } ;
78use rustc_hash:: { FxHashMap , FxHashSet } ;
89use test_utils:: mark;
910
11+ pub ( crate ) struct ResolutionScope < ' db > {
12+ scope : hir:: SemanticsScope < ' db > ,
13+ hygiene : hir:: Hygiene ,
14+ }
15+
1016pub ( crate ) struct ResolvedRule {
1117 pub ( crate ) pattern : ResolvedPattern ,
1218 pub ( crate ) template : Option < ResolvedPattern > ,
@@ -30,12 +36,11 @@ pub(crate) struct ResolvedPath {
3036impl ResolvedRule {
3137 pub ( crate ) fn new (
3238 rule : parsing:: ParsedRule ,
33- scope : & hir:: SemanticsScope ,
34- hygiene : & hir:: Hygiene ,
39+ resolution_scope : & ResolutionScope ,
3540 index : usize ,
3641 ) -> Result < ResolvedRule , SsrError > {
3742 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 } ;
3944 let resolved_template = if let Some ( template) = rule. template {
4045 Some ( resolver. resolve_pattern_tree ( template) ?)
4146 } else {
@@ -57,8 +62,7 @@ impl ResolvedRule {
5762}
5863
5964struct Resolver < ' a , ' db > {
60- scope : & ' a hir:: SemanticsScope < ' db > ,
61- hygiene : & ' a hir:: Hygiene ,
65+ resolution_scope : & ' a ResolutionScope < ' db > ,
6266 placeholders_by_stand_in : FxHashMap < SmolStr , parsing:: Placeholder > ,
6367}
6468
@@ -104,6 +108,7 @@ impl Resolver<'_, '_> {
104108 && !self . path_contains_placeholder ( & path)
105109 {
106110 let resolution = self
111+ . resolution_scope
107112 . resolve_path ( & path)
108113 . ok_or_else ( || error ! ( "Failed to resolve path `{}`" , node. text( ) ) ) ?;
109114 resolved_paths. insert ( node, ResolvedPath { resolution, depth } ) ;
@@ -131,9 +136,32 @@ impl Resolver<'_, '_> {
131136 }
132137 false
133138 }
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+ }
134162
135163 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 ) ?;
137165 // First try resolving the whole path. This will work for things like
138166 // `std::collections::HashMap`, but will fail for things like
139167 // `std::collections::HashMap::new`.
@@ -158,6 +186,33 @@ impl Resolver<'_, '_> {
158186 }
159187}
160188
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+
161216/// Returns whether `path` or any of its qualifiers contains type arguments.
162217fn path_contains_type_arguments ( path : Option < ast:: Path > ) -> bool {
163218 if let Some ( path) = path {
0 commit comments