aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/descriptors/module/nameres.rs
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-11-21 11:57:32 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-11-21 11:57:32 +0000
commit031bc868293539714157e3d93cc338b011f5661d (patch)
tree624843cfddada855a6a80380e817bb188596c441 /crates/ra_analysis/src/descriptors/module/nameres.rs
parent713c3ea30b38199e05e1f70094d85177f16915f8 (diff)
parent5a61b218f8869532ccb6be9dccff9e7d8641bf49 (diff)
Merge #236
236: WIP: Module name resolution r=matklad a=matklad work towards #231 Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_analysis/src/descriptors/module/nameres.rs')
-rw-r--r--crates/ra_analysis/src/descriptors/module/nameres.rs453
1 files changed, 453 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/descriptors/module/nameres.rs b/crates/ra_analysis/src/descriptors/module/nameres.rs
new file mode 100644
index 000000000..c5bf467ca
--- /dev/null
+++ b/crates/ra_analysis/src/descriptors/module/nameres.rs
@@ -0,0 +1,453 @@
1//! Name resolution algorithm
2use std::{
3 sync::Arc,
4 time::Instant,
5};
6
7use rustc_hash::FxHashMap;
8
9use ra_syntax::{
10 SmolStr, SyntaxKind::{self, *},
11 ast::{self, AstNode, ModuleItemOwner}
12};
13
14use crate::{
15 Cancelable,
16 loc2id::{DefId, DefLoc},
17 descriptors::{
18 DescriptorDatabase,
19 module::{ModuleId, ModuleTree, ModuleSourceNode},
20 },
21 syntax_ptr::{LocalSyntaxPtr},
22 input::SourceRootId,
23};
24
25/// Item map is the result of the name resolution. Item map contains, for each
26/// module, the set of visible items.
27#[derive(Default, Debug, PartialEq, Eq)]
28pub(crate) struct ItemMap {
29 pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>,
30}
31
32#[derive(Debug, Default, PartialEq, Eq, Clone)]
33pub(crate) struct ModuleScope {
34 pub(crate) items: FxHashMap<SmolStr, Resolution>,
35 pub(crate) import_resolutions: FxHashMap<LocalSyntaxPtr, DefId>,
36}
37
38/// A set of items and imports declared inside a module, without relation to
39/// other modules.
40///
41/// This stands in-between raw syntax and name resolution and alow us to avoid
42/// recomputing name res: if `InputModuleItems` are the same, we can avoid
43/// running name resolution.
44#[derive(Debug, Default, PartialEq, Eq)]
45pub(crate) struct InputModuleItems {
46 items: Vec<ModuleItem>,
47 glob_imports: Vec<Path>,
48 imports: Vec<Path>,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52struct Path {
53 kind: PathKind,
54 segments: Vec<(LocalSyntaxPtr, SmolStr)>,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58enum PathKind {
59 Abs,
60 Self_,
61 Super,
62 Crate,
63}
64
65pub(crate) fn input_module_items(
66 db: &impl DescriptorDatabase,
67 source_root: SourceRootId,
68 module_id: ModuleId,
69) -> Cancelable<Arc<InputModuleItems>> {
70 let module_tree = db._module_tree(source_root)?;
71 let source = module_id.source(&module_tree);
72 let res = match source.resolve(db) {
73 ModuleSourceNode::SourceFile(it) => {
74 let items = it.borrowed().items();
75 InputModuleItems::new(items)
76 }
77 ModuleSourceNode::Module(it) => {
78 let items = it
79 .borrowed()
80 .item_list()
81 .into_iter()
82 .flat_map(|it| it.items());
83 InputModuleItems::new(items)
84 }
85 };
86 Ok(Arc::new(res))
87}
88
89pub(crate) fn item_map(
90 db: &impl DescriptorDatabase,
91 source_root: SourceRootId,
92) -> Cancelable<Arc<ItemMap>> {
93 let start = Instant::now();
94 let module_tree = db._module_tree(source_root)?;
95 let input = module_tree
96 .modules()
97 .map(|id| {
98 let items = db._input_module_items(source_root, id)?;
99 Ok((id, items))
100 })
101 .collect::<Cancelable<FxHashMap<_, _>>>()?;
102
103 let mut resolver = Resolver {
104 db: db,
105 input: &input,
106 source_root,
107 module_tree,
108 result: ItemMap::default(),
109 };
110 resolver.resolve()?;
111 let res = resolver.result;
112 let elapsed = start.elapsed();
113 log::info!("item_map: {:?}", elapsed);
114 Ok(Arc::new(res))
115}
116
117/// Resolution is basically `DefId` atm, but it should account for stuff like
118/// multiple namespaces, ambiguity and errors.
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub(crate) struct Resolution {
121 /// None for unresolved
122 pub(crate) def_id: Option<DefId>,
123 /// ident by whitch this is imported into local scope.
124 /// TODO: make this offset-independent.
125 pub(crate) import_name: Option<LocalSyntaxPtr>,
126}
127
128// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
129// enum Namespace {
130// Types,
131// Values,
132// }
133
134// #[derive(Debug)]
135// struct PerNs<T> {
136// types: Option<T>,
137// values: Option<T>,
138// }
139
140#[derive(Debug, PartialEq, Eq)]
141struct ModuleItem {
142 ptr: LocalSyntaxPtr,
143 name: SmolStr,
144 kind: SyntaxKind,
145 vis: Vis,
146}
147
148#[derive(Debug, PartialEq, Eq)]
149enum Vis {
150 // Priv,
151 Other,
152}
153
154impl InputModuleItems {
155 fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> InputModuleItems {
156 let mut res = InputModuleItems::default();
157 for item in items {
158 res.add_item(item);
159 }
160 res
161 }
162
163 fn add_item(&mut self, item: ast::ModuleItem) -> Option<()> {
164 match item {
165 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(it)?),
166 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(it)?),
167 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(it)?),
168 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(it)?),
169 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(it)?),
170 ast::ModuleItem::ImplItem(_) => {
171 // impls don't define items
172 }
173 ast::ModuleItem::UseItem(it) => self.add_use_item(it),
174 ast::ModuleItem::ExternCrateItem(_) => {
175 // TODO
176 }
177 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(it)?),
178 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(it)?),
179 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(it)?),
180 }
181 Some(())
182 }
183
184 fn add_use_item(&mut self, item: ast::UseItem) {
185 if let Some(tree) = item.use_tree() {
186 self.add_use_tree(None, tree);
187 }
188 }
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 };
199 for tree in use_tree_list.use_trees() {
200 self.add_use_tree(prefix.clone(), tree);
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 }
215}
216
217fn 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
265impl ModuleItem {
266 fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
267 let name = item.name()?.text();
268 let ptr = LocalSyntaxPtr::new(item.syntax());
269 let kind = item.syntax().kind();
270 let vis = Vis::Other;
271 let res = ModuleItem {
272 ptr,
273 name,
274 kind,
275 vis,
276 };
277 Some(res)
278 }
279}
280
281struct Resolver<'a, DB> {
282 db: &'a DB,
283 input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
284 source_root: SourceRootId,
285 module_tree: Arc<ModuleTree>,
286 result: ItemMap,
287}
288
289impl<'a, DB> Resolver<'a, DB>
290where
291 DB: DescriptorDatabase,
292{
293 fn resolve(&mut self) -> Cancelable<()> {
294 for (&module_id, items) in self.input.iter() {
295 self.populate_module(module_id, items)
296 }
297
298 for &module_id in self.input.keys() {
299 crate::db::check_canceled(self.db)?;
300 self.resolve_imports(module_id);
301 }
302 Ok(())
303 }
304
305 fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
306 let file_id = module_id.source(&self.module_tree).file_id();
307
308 let mut module_items = ModuleScope::default();
309
310 for import in input.imports.iter() {
311 if let Some((ptr, name)) = import.segments.last() {
312 module_items.items.insert(
313 name.clone(),
314 Resolution {
315 def_id: None,
316 import_name: Some(*ptr),
317 },
318 );
319 }
320 }
321
322 for item in input.items.iter() {
323 if item.kind == MODULE {
324 // handle submodules separatelly
325 continue;
326 }
327 let ptr = item.ptr.into_global(file_id);
328 let def_loc = DefLoc::Item { ptr };
329 let def_id = self.db.id_maps().def_id(def_loc);
330 let resolution = Resolution {
331 def_id: Some(def_id),
332 import_name: None,
333 };
334 module_items.items.insert(item.name.clone(), resolution);
335 }
336
337 for (name, mod_id) in module_id.children(&self.module_tree) {
338 let def_loc = DefLoc::Module {
339 id: mod_id,
340 source_root: self.source_root,
341 };
342 let def_id = self.db.id_maps().def_id(def_loc);
343 let resolution = Resolution {
344 def_id: Some(def_id),
345 import_name: None,
346 };
347 module_items.items.insert(name, resolution);
348 }
349
350 self.result.per_module.insert(module_id, module_items);
351 }
352
353 fn resolve_imports(&mut self, module_id: ModuleId) {
354 for import in self.input[&module_id].imports.iter() {
355 self.resolve_import(module_id, import);
356 }
357 }
358
359 fn resolve_import(&mut self, module_id: ModuleId, import: &Path) {
360 let mut curr = match import.kind {
361 // TODO: handle extern crates
362 PathKind::Abs => return,
363 PathKind::Self_ => module_id,
364 PathKind::Super => {
365 match module_id.parent(&self.module_tree) {
366 Some(it) => it,
367 // TODO: error
368 None => return,
369 }
370 }
371 PathKind::Crate => module_id.crate_root(&self.module_tree),
372 };
373
374 for (i, (ptr, name)) in import.segments.iter().enumerate() {
375 let is_last = i == import.segments.len() - 1;
376
377 let def_id = match self.result.per_module[&curr].items.get(name) {
378 None => return,
379 Some(res) => match res.def_id {
380 Some(it) => it,
381 None => return,
382 },
383 };
384
385 self.update(module_id, |items| {
386 items.import_resolutions.insert(*ptr, def_id);
387 });
388
389 if !is_last {
390 curr = match self.db.id_maps().def_loc(def_id) {
391 DefLoc::Module { id, .. } => id,
392 _ => return,
393 }
394 } else {
395 self.update(module_id, |items| {
396 let res = Resolution {
397 def_id: Some(def_id),
398 import_name: Some(*ptr),
399 };
400 items.items.insert(name.clone(), res);
401 })
402 }
403 }
404 }
405
406 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
407 let module_items = self.result.per_module.get_mut(&module_id).unwrap();
408 f(module_items)
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use crate::{
415 mock_analysis::analysis_and_position,
416 descriptors::{DescriptorDatabase, module::ModuleDescriptor},
417 input::FilesDatabase,
418};
419 use super::*;
420
421 fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
422 let (analysis, pos) = analysis_and_position(fixture);
423 let db = analysis.imp.db;
424 let source_root = db.file_source_root(pos.file_id);
425 let descr = ModuleDescriptor::guess_from_position(&*db, pos)
426 .unwrap()
427 .unwrap();
428 let module_id = descr.module_id;
429 (db._item_map(source_root).unwrap(), module_id)
430 }
431
432 #[test]
433 fn test_item_map() {
434 let (item_map, module_id) = item_map(
435 "
436 //- /lib.rs
437 mod foo;
438
439 use crate::foo::bar::Baz;
440 <|>
441
442 //- /foo/mod.rs
443 pub mod bar;
444
445 //- /foo/bar.rs
446 pub struct Baz;
447 ",
448 );
449 let name = SmolStr::from("Baz");
450 let resolution = &item_map.per_module[&module_id].items[&name];
451 assert!(resolution.def_id.is_some());
452 }
453}