aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/descriptors/module/nameres.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/descriptors/module/nameres.rs')
-rw-r--r--crates/ra_analysis/src/descriptors/module/nameres.rs162
1 files changed, 54 insertions, 108 deletions
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.
2use std::{ 17use std::{
3 sync::Arc, 18 sync::Arc,
4 time::Instant, 19 time::Instant,
@@ -8,13 +23,14 @@ use rustc_hash::FxHashMap;
8 23
9use ra_syntax::{ 24use ra_syntax::{
10 SmolStr, SyntaxKind::{self, *}, 25 SmolStr, SyntaxKind::{self, *},
11 ast::{self, AstNode, ModuleItemOwner} 26 ast::{self, ModuleItemOwner}
12}; 27};
13 28
14use crate::{ 29use 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)]
33pub(crate) struct ModuleScope { 49pub(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)]
45pub(crate) struct InputModuleItems { 60pub(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)]
52struct Path { 66struct 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)]
58enum PathKind { 72enum ImportKind {
59 Abs, 73 Glob,
60 Self_, 74 // TODO: make offset independent
61 Super, 75 Named(LocalSyntaxPtr),
62 Crate,
63} 76}
64 77
65pub(crate) fn input_module_items( 78pub(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
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 { 208impl 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 })