diff options
Diffstat (limited to 'crates/ra_analysis')
-rw-r--r-- | crates/ra_analysis/src/completion/mod.rs | 14 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/reference_completion.rs | 65 | ||||
-rw-r--r-- | crates/ra_analysis/src/descriptors/mod.rs | 5 | ||||
-rw-r--r-- | crates/ra_analysis/src/descriptors/module/mod.rs | 18 | ||||
-rw-r--r-- | crates/ra_analysis/src/descriptors/module/nameres.rs | 162 | ||||
-rw-r--r-- | crates/ra_analysis/src/descriptors/path.rs | 153 |
6 files changed, 259 insertions, 158 deletions
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs index a8a752fc7..c7717ab61 100644 --- a/crates/ra_analysis/src/completion/mod.rs +++ b/crates/ra_analysis/src/completion/mod.rs | |||
@@ -221,6 +221,20 @@ mod tests { | |||
221 | } | 221 | } |
222 | 222 | ||
223 | #[test] | 223 | #[test] |
224 | fn test_completion_self_path() { | ||
225 | check_scope_completion( | ||
226 | r" | ||
227 | use self::m::<|>; | ||
228 | |||
229 | mod m { | ||
230 | struct Bar; | ||
231 | } | ||
232 | ", | ||
233 | r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }]"#, | ||
234 | ); | ||
235 | } | ||
236 | |||
237 | #[test] | ||
224 | fn test_completion_mod_scope_nested() { | 238 | fn test_completion_mod_scope_nested() { |
225 | check_scope_completion( | 239 | check_scope_completion( |
226 | r" | 240 | r" |
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs index 84383b547..d301a3c02 100644 --- a/crates/ra_analysis/src/completion/reference_completion.rs +++ b/crates/ra_analysis/src/completion/reference_completion.rs | |||
@@ -10,8 +10,11 @@ use ra_syntax::{ | |||
10 | use crate::{ | 10 | use crate::{ |
11 | db::RootDatabase, | 11 | db::RootDatabase, |
12 | completion::CompletionItem, | 12 | completion::CompletionItem, |
13 | descriptors::module::{ModuleDescriptor}, | 13 | descriptors::{ |
14 | descriptors::function::FnScopes, | 14 | module::{ModuleDescriptor}, |
15 | function::FnScopes, | ||
16 | Path, | ||
17 | }, | ||
15 | Cancelable | 18 | Cancelable |
16 | }; | 19 | }; |
17 | 20 | ||
@@ -55,7 +58,7 @@ pub(super) fn completions( | |||
55 | }), | 58 | }), |
56 | ); | 59 | ); |
57 | } | 60 | } |
58 | NameRefKind::CratePath(path) => complete_path(acc, db, module, path)?, | 61 | NameRefKind::Path(path) => complete_path(acc, db, module, path)?, |
59 | NameRefKind::BareIdentInMod => { | 62 | NameRefKind::BareIdentInMod => { |
60 | let name_range = name_ref.syntax().range(); | 63 | let name_range = name_ref.syntax().range(); |
61 | let top_node = name_ref | 64 | let top_node = name_ref |
@@ -79,8 +82,8 @@ enum NameRefKind<'a> { | |||
79 | LocalRef { | 82 | LocalRef { |
80 | enclosing_fn: Option<ast::FnDef<'a>>, | 83 | enclosing_fn: Option<ast::FnDef<'a>>, |
81 | }, | 84 | }, |
82 | /// NameRef is the last segment in crate:: path | 85 | /// NameRef is the last segment in some path |
83 | CratePath(Vec<ast::NameRef<'a>>), | 86 | Path(Path), |
84 | /// NameRef is bare identifier at the module's root. | 87 | /// NameRef is bare identifier at the module's root. |
85 | /// Used for keyword completion | 88 | /// Used for keyword completion |
86 | BareIdentInMod, | 89 | BareIdentInMod, |
@@ -102,8 +105,10 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> { | |||
102 | let parent = name_ref.syntax().parent()?; | 105 | let parent = name_ref.syntax().parent()?; |
103 | if let Some(segment) = ast::PathSegment::cast(parent) { | 106 | if let Some(segment) = ast::PathSegment::cast(parent) { |
104 | let path = segment.parent_path(); | 107 | let path = segment.parent_path(); |
105 | if let Some(crate_path) = crate_path(path) { | 108 | if let Some(path) = Path::from_ast(path) { |
106 | return Some(NameRefKind::CratePath(crate_path)); | 109 | if !path.is_ident() { |
110 | return Some(NameRefKind::Path(path)); | ||
111 | } | ||
107 | } | 112 | } |
108 | if path.qualifier().is_none() { | 113 | if path.qualifier().is_none() { |
109 | let enclosing_fn = name_ref | 114 | let enclosing_fn = name_ref |
@@ -117,32 +122,6 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> { | |||
117 | None | 122 | None |
118 | } | 123 | } |
119 | 124 | ||
120 | fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> { | ||
121 | let mut res = Vec::new(); | ||
122 | loop { | ||
123 | let segment = path.segment()?; | ||
124 | match segment.kind()? { | ||
125 | ast::PathSegmentKind::Name(name) => res.push(name), | ||
126 | ast::PathSegmentKind::CrateKw => break, | ||
127 | ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None, | ||
128 | } | ||
129 | path = qualifier(path)?; | ||
130 | } | ||
131 | res.reverse(); | ||
132 | return Some(res); | ||
133 | |||
134 | fn qualifier(path: ast::Path) -> Option<ast::Path> { | ||
135 | if let Some(q) = path.qualifier() { | ||
136 | return Some(q); | ||
137 | } | ||
138 | // TODO: this bottom up traversal is not too precise. | ||
139 | // Should we handle do a top-down analysiss, recording results? | ||
140 | let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; | ||
141 | let use_tree = use_tree_list.parent_use_tree(); | ||
142 | use_tree.path() | ||
143 | } | ||
144 | } | ||
145 | |||
146 | fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { | 125 | fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { |
147 | let mut shadowed = FxHashSet::default(); | 126 | let mut shadowed = FxHashSet::default(); |
148 | acc.extend( | 127 | acc.extend( |
@@ -169,9 +148,13 @@ fn complete_path( | |||
169 | acc: &mut Vec<CompletionItem>, | 148 | acc: &mut Vec<CompletionItem>, |
170 | db: &RootDatabase, | 149 | db: &RootDatabase, |
171 | module: &ModuleDescriptor, | 150 | module: &ModuleDescriptor, |
172 | crate_path: Vec<ast::NameRef>, | 151 | mut path: Path, |
173 | ) -> Cancelable<()> { | 152 | ) -> Cancelable<()> { |
174 | let target_module = match find_target_module(module, crate_path) { | 153 | if path.segments.is_empty() { |
154 | return Ok(()); | ||
155 | } | ||
156 | path.segments.pop(); | ||
157 | let target_module = match module.resolve_path(path) { | ||
175 | None => return Ok(()), | 158 | None => return Ok(()), |
176 | Some(it) => it, | 159 | Some(it) => it, |
177 | }; | 160 | }; |
@@ -188,18 +171,6 @@ fn complete_path( | |||
188 | Ok(()) | 171 | Ok(()) |
189 | } | 172 | } |
190 | 173 | ||
191 | fn find_target_module( | ||
192 | module: &ModuleDescriptor, | ||
193 | mut crate_path: Vec<ast::NameRef>, | ||
194 | ) -> Option<ModuleDescriptor> { | ||
195 | crate_path.pop(); | ||
196 | let mut target_module = module.crate_root(); | ||
197 | for name in crate_path { | ||
198 | target_module = target_module.child(name.text().as_str())?; | ||
199 | } | ||
200 | Some(target_module) | ||
201 | } | ||
202 | |||
203 | fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { | 174 | fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { |
204 | acc.push(CompletionItem { | 175 | acc.push(CompletionItem { |
205 | label: "tfn".to_string(), | 176 | label: "tfn".to_string(), |
diff --git a/crates/ra_analysis/src/descriptors/mod.rs b/crates/ra_analysis/src/descriptors/mod.rs index 6b56d92e1..97750ea64 100644 --- a/crates/ra_analysis/src/descriptors/mod.rs +++ b/crates/ra_analysis/src/descriptors/mod.rs | |||
@@ -1,10 +1,11 @@ | |||
1 | pub(crate) mod function; | 1 | pub(crate) mod function; |
2 | pub(crate) mod module; | 2 | pub(crate) mod module; |
3 | mod path; | ||
3 | 4 | ||
4 | use std::sync::Arc; | 5 | use std::sync::Arc; |
5 | 6 | ||
6 | use ra_syntax::{ | 7 | use ra_syntax::{ |
7 | ast::{self, AstNode, FnDefNode}, | 8 | ast::{self, FnDefNode, AstNode}, |
8 | TextRange, | 9 | TextRange, |
9 | }; | 10 | }; |
10 | 11 | ||
@@ -18,6 +19,8 @@ use crate::{ | |||
18 | Cancelable, | 19 | Cancelable, |
19 | }; | 20 | }; |
20 | 21 | ||
22 | pub(crate) use self::path::{Path, PathKind}; | ||
23 | |||
21 | salsa::query_group! { | 24 | salsa::query_group! { |
22 | pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase { | 25 | pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase { |
23 | fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> { | 26 | fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> { |
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_analysis/src/descriptors/module/mod.rs index cfdffcdbc..acc6c1c5a 100644 --- a/crates/ra_analysis/src/descriptors/module/mod.rs +++ b/crates/ra_analysis/src/descriptors/module/mod.rs | |||
@@ -14,11 +14,11 @@ use relative_path::RelativePathBuf; | |||
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable, | 16 | db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable, |
17 | descriptors::DescriptorDatabase, | 17 | descriptors::{Path, PathKind, DescriptorDatabase}, |
18 | input::SourceRootId | 18 | input::SourceRootId |
19 | }; | 19 | }; |
20 | 20 | ||
21 | pub(crate) use self::{nameres::ModuleScope}; | 21 | pub(crate) use self::nameres::ModuleScope; |
22 | 22 | ||
23 | /// `ModuleDescriptor` is API entry point to get all the information | 23 | /// `ModuleDescriptor` is API entry point to get all the information |
24 | /// about a particular module. | 24 | /// about a particular module. |
@@ -110,6 +110,7 @@ impl ModuleDescriptor { | |||
110 | } | 110 | } |
111 | 111 | ||
112 | /// `name` is `None` for the crate's root module | 112 | /// `name` is `None` for the crate's root module |
113 | #[allow(unused)] | ||
113 | pub fn name(&self) -> Option<SmolStr> { | 114 | pub fn name(&self) -> Option<SmolStr> { |
114 | let link = self.module_id.parent_link(&self.tree)?; | 115 | let link = self.module_id.parent_link(&self.tree)?; |
115 | Some(link.name(&self.tree)) | 116 | Some(link.name(&self.tree)) |
@@ -131,6 +132,19 @@ impl ModuleDescriptor { | |||
131 | Ok(res) | 132 | Ok(res) |
132 | } | 133 | } |
133 | 134 | ||
135 | pub(crate) fn resolve_path(&self, path: Path) -> Option<ModuleDescriptor> { | ||
136 | let mut curr = match path.kind { | ||
137 | PathKind::Crate => self.crate_root(), | ||
138 | PathKind::Self_ | PathKind::Plain => self.clone(), | ||
139 | PathKind::Super => self.parent()?, | ||
140 | }; | ||
141 | let segments = path.segments; | ||
142 | for name in segments { | ||
143 | curr = curr.child(&name)?; | ||
144 | } | ||
145 | Some(curr) | ||
146 | } | ||
147 | |||
134 | pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> { | 148 | pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> { |
135 | self.module_id.problems(&self.tree, db) | 149 | self.module_id.problems(&self.tree, db) |
136 | } | 150 | } |
diff --git a/crates/ra_analysis/src/descriptors/module/nameres.rs b/crates/ra_analysis/src/descriptors/module/nameres.rs index c5bf467ca..4c555421d 100644 --- a/crates/ra_analysis/src/descriptors/module/nameres.rs +++ b/crates/ra_analysis/src/descriptors/module/nameres.rs | |||
@@ -1,4 +1,19 @@ | |||
1 | //! Name resolution algorithm | 1 | //! Name resolution algorithm. The end result of the algorithm is `ItemMap`: a |
2 | //! map with maps each module to it's scope: the set of items, visible in the | ||
3 | //! module. That is, we only resolve imports here, name resolution of item | ||
4 | //! bodies will be done in a separate step. | ||
5 | //! | ||
6 | //! Like Rustc, we use an interative per-crate algorithm: we start with scopes | ||
7 | //! containing only directly defined items, and then iteratively resolve | ||
8 | //! imports. | ||
9 | //! | ||
10 | //! To make this work nicely in the IDE scenarios, we place `InputModuleItems` | ||
11 | //! in between raw syntax and name resolution. `InputModuleItems` are computed | ||
12 | //! using only the module's syntax, and it is all directly defined items plus | ||
13 | //! imports. The plain is to make `InputModuleItems` independent of local | ||
14 | //! modifications (that is, typing inside a function shold not change IMIs), | ||
15 | //! such that the results of name resolution can be preserved unless the module | ||
16 | //! structure itself is modified. | ||
2 | use std::{ | 17 | use std::{ |
3 | sync::Arc, | 18 | sync::Arc, |
4 | time::Instant, | 19 | time::Instant, |
@@ -8,13 +23,14 @@ use rustc_hash::FxHashMap; | |||
8 | 23 | ||
9 | use ra_syntax::{ | 24 | use ra_syntax::{ |
10 | SmolStr, SyntaxKind::{self, *}, | 25 | SmolStr, SyntaxKind::{self, *}, |
11 | ast::{self, AstNode, ModuleItemOwner} | 26 | ast::{self, ModuleItemOwner} |
12 | }; | 27 | }; |
13 | 28 | ||
14 | use crate::{ | 29 | use crate::{ |
15 | Cancelable, | 30 | Cancelable, |
16 | loc2id::{DefId, DefLoc}, | 31 | loc2id::{DefId, DefLoc}, |
17 | descriptors::{ | 32 | descriptors::{ |
33 | Path, PathKind, | ||
18 | DescriptorDatabase, | 34 | DescriptorDatabase, |
19 | module::{ModuleId, ModuleTree, ModuleSourceNode}, | 35 | module::{ModuleId, ModuleTree, ModuleSourceNode}, |
20 | }, | 36 | }, |
@@ -32,7 +48,6 @@ pub(crate) struct ItemMap { | |||
32 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | 48 | #[derive(Debug, Default, PartialEq, Eq, Clone)] |
33 | pub(crate) struct ModuleScope { | 49 | pub(crate) struct ModuleScope { |
34 | pub(crate) items: FxHashMap<SmolStr, Resolution>, | 50 | pub(crate) items: FxHashMap<SmolStr, Resolution>, |
35 | pub(crate) import_resolutions: FxHashMap<LocalSyntaxPtr, DefId>, | ||
36 | } | 51 | } |
37 | 52 | ||
38 | /// A set of items and imports declared inside a module, without relation to | 53 | /// A set of items and imports declared inside a module, without relation to |
@@ -44,22 +59,20 @@ pub(crate) struct ModuleScope { | |||
44 | #[derive(Debug, Default, PartialEq, Eq)] | 59 | #[derive(Debug, Default, PartialEq, Eq)] |
45 | pub(crate) struct InputModuleItems { | 60 | pub(crate) struct InputModuleItems { |
46 | items: Vec<ModuleItem>, | 61 | items: Vec<ModuleItem>, |
47 | glob_imports: Vec<Path>, | 62 | imports: Vec<Import>, |
48 | imports: Vec<Path>, | ||
49 | } | 63 | } |
50 | 64 | ||
51 | #[derive(Debug, Clone, PartialEq, Eq)] | 65 | #[derive(Debug, Clone, PartialEq, Eq)] |
52 | struct Path { | 66 | struct Import { |
53 | kind: PathKind, | 67 | path: Path, |
54 | segments: Vec<(LocalSyntaxPtr, SmolStr)>, | 68 | kind: ImportKind, |
55 | } | 69 | } |
56 | 70 | ||
57 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 71 | #[derive(Debug, Clone, PartialEq, Eq)] |
58 | enum PathKind { | 72 | enum ImportKind { |
59 | Abs, | 73 | Glob, |
60 | Self_, | 74 | // TODO: make offset independent |
61 | Super, | 75 | Named(LocalSyntaxPtr), |
62 | Crate, | ||
63 | } | 76 | } |
64 | 77 | ||
65 | pub(crate) fn input_module_items( | 78 | pub(crate) fn input_module_items( |
@@ -182,86 +195,16 @@ impl InputModuleItems { | |||
182 | } | 195 | } |
183 | 196 | ||
184 | fn add_use_item(&mut self, item: ast::UseItem) { | 197 | fn add_use_item(&mut self, item: ast::UseItem) { |
185 | if let Some(tree) = item.use_tree() { | 198 | Path::expand_use_item(item, |path, ptr| { |
186 | self.add_use_tree(None, tree); | 199 | let kind = match ptr { |
187 | } | 200 | None => ImportKind::Glob, |
188 | } | 201 | Some(ptr) => ImportKind::Named(ptr), |
189 | |||
190 | fn add_use_tree(&mut self, prefix: Option<Path>, tree: ast::UseTree) { | ||
191 | if let Some(use_tree_list) = tree.use_tree_list() { | ||
192 | let prefix = match tree.path() { | ||
193 | None => prefix, | ||
194 | Some(path) => match convert_path(prefix, path) { | ||
195 | Some(it) => Some(it), | ||
196 | None => return, // TODO: report errors somewhere | ||
197 | }, | ||
198 | }; | 202 | }; |
199 | for tree in use_tree_list.use_trees() { | 203 | self.imports.push(Import { kind, path }) |
200 | self.add_use_tree(prefix.clone(), tree); | 204 | }) |
201 | } | ||
202 | } else { | ||
203 | if let Some(path) = tree.path() { | ||
204 | if let Some(path) = convert_path(prefix, path) { | ||
205 | if tree.has_star() { | ||
206 | &mut self.glob_imports | ||
207 | } else { | ||
208 | &mut self.imports | ||
209 | } | ||
210 | .push(path); | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | } | 205 | } |
215 | } | 206 | } |
216 | 207 | ||
217 | fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> { | ||
218 | let prefix = if let Some(qual) = path.qualifier() { | ||
219 | Some(convert_path(prefix, qual)?) | ||
220 | } else { | ||
221 | None | ||
222 | }; | ||
223 | let segment = path.segment()?; | ||
224 | let res = match segment.kind()? { | ||
225 | ast::PathSegmentKind::Name(name) => { | ||
226 | let mut res = prefix.unwrap_or_else(|| Path { | ||
227 | kind: PathKind::Abs, | ||
228 | segments: Vec::with_capacity(1), | ||
229 | }); | ||
230 | let ptr = LocalSyntaxPtr::new(name.syntax()); | ||
231 | res.segments.push((ptr, name.text())); | ||
232 | res | ||
233 | } | ||
234 | ast::PathSegmentKind::CrateKw => { | ||
235 | if prefix.is_some() { | ||
236 | return None; | ||
237 | } | ||
238 | Path { | ||
239 | kind: PathKind::Crate, | ||
240 | segments: Vec::new(), | ||
241 | } | ||
242 | } | ||
243 | ast::PathSegmentKind::SelfKw => { | ||
244 | if prefix.is_some() { | ||
245 | return None; | ||
246 | } | ||
247 | Path { | ||
248 | kind: PathKind::Self_, | ||
249 | segments: Vec::new(), | ||
250 | } | ||
251 | } | ||
252 | ast::PathSegmentKind::SuperKw => { | ||
253 | if prefix.is_some() { | ||
254 | return None; | ||
255 | } | ||
256 | Path { | ||
257 | kind: PathKind::Super, | ||
258 | segments: Vec::new(), | ||
259 | } | ||
260 | } | ||
261 | }; | ||
262 | Some(res) | ||
263 | } | ||
264 | |||
265 | impl ModuleItem { | 208 | impl ModuleItem { |
266 | fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { | 209 | fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { |
267 | let name = item.name()?.text(); | 210 | let name = item.name()?.text(); |
@@ -308,14 +251,16 @@ where | |||
308 | let mut module_items = ModuleScope::default(); | 251 | let mut module_items = ModuleScope::default(); |
309 | 252 | ||
310 | for import in input.imports.iter() { | 253 | for import in input.imports.iter() { |
311 | if let Some((ptr, name)) = import.segments.last() { | 254 | if let Some(name) = import.path.segments.iter().last() { |
312 | module_items.items.insert( | 255 | if let ImportKind::Named(ptr) = import.kind { |
313 | name.clone(), | 256 | module_items.items.insert( |
314 | Resolution { | 257 | name.clone(), |
315 | def_id: None, | 258 | Resolution { |
316 | import_name: Some(*ptr), | 259 | def_id: None, |
317 | }, | 260 | import_name: Some(ptr), |
318 | ); | 261 | }, |
262 | ); | ||
263 | } | ||
319 | } | 264 | } |
320 | } | 265 | } |
321 | 266 | ||
@@ -356,10 +301,15 @@ where | |||
356 | } | 301 | } |
357 | } | 302 | } |
358 | 303 | ||
359 | fn resolve_import(&mut self, module_id: ModuleId, import: &Path) { | 304 | fn resolve_import(&mut self, module_id: ModuleId, import: &Import) { |
360 | let mut curr = match import.kind { | 305 | let ptr = match import.kind { |
306 | ImportKind::Glob => return, | ||
307 | ImportKind::Named(ptr) => ptr, | ||
308 | }; | ||
309 | |||
310 | let mut curr = match import.path.kind { | ||
361 | // TODO: handle extern crates | 311 | // TODO: handle extern crates |
362 | PathKind::Abs => return, | 312 | PathKind::Plain => return, |
363 | PathKind::Self_ => module_id, | 313 | PathKind::Self_ => module_id, |
364 | PathKind::Super => { | 314 | PathKind::Super => { |
365 | match module_id.parent(&self.module_tree) { | 315 | match module_id.parent(&self.module_tree) { |
@@ -371,8 +321,8 @@ where | |||
371 | PathKind::Crate => module_id.crate_root(&self.module_tree), | 321 | PathKind::Crate => module_id.crate_root(&self.module_tree), |
372 | }; | 322 | }; |
373 | 323 | ||
374 | for (i, (ptr, name)) in import.segments.iter().enumerate() { | 324 | for (i, name) in import.path.segments.iter().enumerate() { |
375 | let is_last = i == import.segments.len() - 1; | 325 | let is_last = i == import.path.segments.len() - 1; |
376 | 326 | ||
377 | let def_id = match self.result.per_module[&curr].items.get(name) { | 327 | let def_id = match self.result.per_module[&curr].items.get(name) { |
378 | None => return, | 328 | None => return, |
@@ -382,10 +332,6 @@ where | |||
382 | }, | 332 | }, |
383 | }; | 333 | }; |
384 | 334 | ||
385 | self.update(module_id, |items| { | ||
386 | items.import_resolutions.insert(*ptr, def_id); | ||
387 | }); | ||
388 | |||
389 | if !is_last { | 335 | if !is_last { |
390 | curr = match self.db.id_maps().def_loc(def_id) { | 336 | curr = match self.db.id_maps().def_loc(def_id) { |
391 | DefLoc::Module { id, .. } => id, | 337 | DefLoc::Module { id, .. } => id, |
@@ -395,7 +341,7 @@ where | |||
395 | self.update(module_id, |items| { | 341 | self.update(module_id, |items| { |
396 | let res = Resolution { | 342 | let res = Resolution { |
397 | def_id: Some(def_id), | 343 | def_id: Some(def_id), |
398 | import_name: Some(*ptr), | 344 | import_name: Some(ptr), |
399 | }; | 345 | }; |
400 | items.items.insert(name.clone(), res); | 346 | items.items.insert(name.clone(), res); |
401 | }) | 347 | }) |
diff --git a/crates/ra_analysis/src/descriptors/path.rs b/crates/ra_analysis/src/descriptors/path.rs new file mode 100644 index 000000000..99fca18b1 --- /dev/null +++ b/crates/ra_analysis/src/descriptors/path.rs | |||
@@ -0,0 +1,153 @@ | |||
1 | use ra_syntax::{SmolStr, ast, AstNode}; | ||
2 | |||
3 | use crate::syntax_ptr::LocalSyntaxPtr; | ||
4 | |||
5 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
6 | pub(crate) struct Path { | ||
7 | pub(crate) kind: PathKind, | ||
8 | pub(crate) segments: Vec<SmolStr>, | ||
9 | } | ||
10 | |||
11 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
12 | pub(crate) enum PathKind { | ||
13 | Plain, | ||
14 | Self_, | ||
15 | Super, | ||
16 | Crate, | ||
17 | } | ||
18 | |||
19 | impl Path { | ||
20 | /// Calls `cb` with all paths, represented by this use item. | ||
21 | pub(crate) fn expand_use_item( | ||
22 | item: ast::UseItem, | ||
23 | mut cb: impl FnMut(Path, Option<LocalSyntaxPtr>), | ||
24 | ) { | ||
25 | if let Some(tree) = item.use_tree() { | ||
26 | expand_use_tree(None, tree, &mut cb); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | /// Converts an `ast::Path` to `Path`. Works with use trees. | ||
31 | pub(crate) fn from_ast(mut path: ast::Path) -> Option<Path> { | ||
32 | let mut kind = PathKind::Plain; | ||
33 | let mut segments = Vec::new(); | ||
34 | loop { | ||
35 | let segment = path.segment()?; | ||
36 | match segment.kind()? { | ||
37 | ast::PathSegmentKind::Name(name) => segments.push(name.text()), | ||
38 | ast::PathSegmentKind::CrateKw => { | ||
39 | kind = PathKind::Crate; | ||
40 | break; | ||
41 | } | ||
42 | ast::PathSegmentKind::SelfKw => { | ||
43 | kind = PathKind::Self_; | ||
44 | break; | ||
45 | } | ||
46 | ast::PathSegmentKind::SuperKw => { | ||
47 | kind = PathKind::Super; | ||
48 | break; | ||
49 | } | ||
50 | } | ||
51 | path = match qualifier(path) { | ||
52 | Some(it) => it, | ||
53 | None => break, | ||
54 | }; | ||
55 | } | ||
56 | segments.reverse(); | ||
57 | return Some(Path { kind, segments }); | ||
58 | |||
59 | fn qualifier(path: ast::Path) -> Option<ast::Path> { | ||
60 | if let Some(q) = path.qualifier() { | ||
61 | return Some(q); | ||
62 | } | ||
63 | // TODO: this bottom up traversal is not too precise. | ||
64 | // Should we handle do a top-down analysiss, recording results? | ||
65 | let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; | ||
66 | let use_tree = use_tree_list.parent_use_tree(); | ||
67 | use_tree.path() | ||
68 | } | ||
69 | } | ||
70 | |||
71 | /// `true` is this path is a single identifier, like `foo` | ||
72 | pub(crate) fn is_ident(&self) -> bool { | ||
73 | self.kind == PathKind::Plain && self.segments.len() == 1 | ||
74 | } | ||
75 | } | ||
76 | |||
77 | fn expand_use_tree( | ||
78 | prefix: Option<Path>, | ||
79 | tree: ast::UseTree, | ||
80 | cb: &mut impl FnMut(Path, Option<LocalSyntaxPtr>), | ||
81 | ) { | ||
82 | if let Some(use_tree_list) = tree.use_tree_list() { | ||
83 | let prefix = match tree.path() { | ||
84 | None => prefix, | ||
85 | Some(path) => match convert_path(prefix, path) { | ||
86 | Some(it) => Some(it), | ||
87 | None => return, // TODO: report errors somewhere | ||
88 | }, | ||
89 | }; | ||
90 | for tree in use_tree_list.use_trees() { | ||
91 | expand_use_tree(prefix.clone(), tree, cb); | ||
92 | } | ||
93 | } else { | ||
94 | if let Some(ast_path) = tree.path() { | ||
95 | if let Some(path) = convert_path(prefix, ast_path) { | ||
96 | let ptr = if tree.has_star() { | ||
97 | None | ||
98 | } else { | ||
99 | let ptr = LocalSyntaxPtr::new(ast_path.segment().unwrap().syntax()); | ||
100 | Some(ptr) | ||
101 | }; | ||
102 | cb(path, ptr) | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> { | ||
109 | let prefix = if let Some(qual) = path.qualifier() { | ||
110 | Some(convert_path(prefix, qual)?) | ||
111 | } else { | ||
112 | None | ||
113 | }; | ||
114 | let segment = path.segment()?; | ||
115 | let res = match segment.kind()? { | ||
116 | ast::PathSegmentKind::Name(name) => { | ||
117 | let mut res = prefix.unwrap_or_else(|| Path { | ||
118 | kind: PathKind::Plain, | ||
119 | segments: Vec::with_capacity(1), | ||
120 | }); | ||
121 | res.segments.push(name.text()); | ||
122 | res | ||
123 | } | ||
124 | ast::PathSegmentKind::CrateKw => { | ||
125 | if prefix.is_some() { | ||
126 | return None; | ||
127 | } | ||
128 | Path { | ||
129 | kind: PathKind::Crate, | ||
130 | segments: Vec::new(), | ||
131 | } | ||
132 | } | ||
133 | ast::PathSegmentKind::SelfKw => { | ||
134 | if prefix.is_some() { | ||
135 | return None; | ||
136 | } | ||
137 | Path { | ||
138 | kind: PathKind::Self_, | ||
139 | segments: Vec::new(), | ||
140 | } | ||
141 | } | ||
142 | ast::PathSegmentKind::SuperKw => { | ||
143 | if prefix.is_some() { | ||
144 | return None; | ||
145 | } | ||
146 | Path { | ||
147 | kind: PathKind::Super, | ||
148 | segments: Vec::new(), | ||
149 | } | ||
150 | } | ||
151 | }; | ||
152 | Some(res) | ||
153 | } | ||