diff options
Diffstat (limited to 'crates/ra_analysis/src/descriptors')
-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 |
4 files changed, 227 insertions, 111 deletions
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 | } | ||