aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/nameres.rs')
-rw-r--r--crates/ra_hir/src/nameres.rs260
1 files changed, 143 insertions, 117 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index a3bc98958..639726b5e 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -19,6 +19,8 @@ pub(crate) mod lower;
19use std::sync::Arc; 19use std::sync::Arc;
20 20
21use ra_db::CrateId; 21use ra_db::CrateId;
22use ra_arena::map::ArenaMap;
23use test_utils::tested_by;
22use rustc_hash::{FxHashMap, FxHashSet}; 24use rustc_hash::{FxHashMap, FxHashSet};
23 25
24use crate::{ 26use crate::{
@@ -27,16 +29,21 @@ use crate::{
27 HirDatabase, Crate, 29 HirDatabase, Crate,
28 Name, 30 Name,
29 module_tree::{ModuleId, ModuleTree}, 31 module_tree::{ModuleId, ModuleTree},
30//FIXME: deglobify 32 nameres::lower::{ImportId, LoweredModule, ImportData},
31 nameres::lower::*,
32}; 33};
33 34
34/// `ItemMap` is the result of name resolution. It contains, for each 35/// `ItemMap` is the result of name resolution. It contains, for each
35/// module, the set of visible items. 36/// module, the set of visible items.
36// FIXME: currenty we compute item map per source-root. We should do it per crate instead.
37#[derive(Default, Debug, PartialEq, Eq)] 37#[derive(Default, Debug, PartialEq, Eq)]
38pub struct ItemMap { 38pub struct ItemMap {
39 pub per_module: FxHashMap<ModuleId, ModuleScope>, 39 per_module: ArenaMap<ModuleId, ModuleScope>,
40}
41
42impl std::ops::Index<ModuleId> for ItemMap {
43 type Output = ModuleScope;
44 fn index(&self, id: ModuleId) -> &ModuleScope {
45 &self.per_module[id]
46 }
40} 47}
41 48
42#[derive(Debug, Default, PartialEq, Eq, Clone)] 49#[derive(Debug, Default, PartialEq, Eq, Clone)]
@@ -58,7 +65,7 @@ impl ModuleScope {
58#[derive(Debug, Clone, PartialEq, Eq)] 65#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct Resolution { 66pub struct Resolution {
60 /// None for unresolved 67 /// None for unresolved
61 pub def_id: PerNs<ModuleDef>, 68 pub def: PerNs<ModuleDef>,
62 /// ident by which this is imported into local scope. 69 /// ident by which this is imported into local scope.
63 pub import: Option<ImportId>, 70 pub import: Option<ImportId>,
64} 71}
@@ -210,11 +217,11 @@ where
210 let krate = Crate::new(crate_id); 217 let krate = Crate::new(crate_id);
211 for dep in krate.dependencies(self.db) { 218 for dep in krate.dependencies(self.db) {
212 if let Some(module) = dep.krate.root_module(self.db) { 219 if let Some(module) = dep.krate.root_module(self.db) {
213 let def_id = module.into(); 220 let def = module.into();
214 self.add_module_item( 221 self.add_module_item(
215 &mut module_items, 222 &mut module_items,
216 dep.name.clone(), 223 dep.name.clone(),
217 PerNs::types(def_id), 224 PerNs::types(def),
218 ); 225 );
219 } 226 }
220 } 227 }
@@ -226,7 +233,7 @@ where
226 module_items.items.insert( 233 module_items.items.insert(
227 segment.name.clone(), 234 segment.name.clone(),
228 Resolution { 235 Resolution {
229 def_id: PerNs::none(), 236 def: PerNs::none(),
230 import: Some(import_id), 237 import: Some(import_id),
231 }, 238 },
232 ); 239 );
@@ -234,11 +241,8 @@ where
234 } 241 }
235 } 242 }
236 // Populate explicitly declared items, except modules 243 // Populate explicitly declared items, except modules
237 for (name, &def_id) in input.declarations.iter() { 244 for (name, &def) in input.declarations.iter() {
238 let resolution = Resolution { 245 let resolution = Resolution { def, import: None };
239 def_id,
240 import: None,
241 };
242 module_items.items.insert(name.clone(), resolution); 246 module_items.items.insert(name.clone(), resolution);
243 } 247 }
244 248
@@ -254,16 +258,8 @@ where
254 self.result.per_module.insert(module_id, module_items); 258 self.result.per_module.insert(module_id, module_items);
255 } 259 }
256 260
257 fn add_module_item( 261 fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def: PerNs<ModuleDef>) {
258 &self, 262 let resolution = Resolution { def, import: None };
259 module_items: &mut ModuleScope,
260 name: Name,
261 def_id: PerNs<ModuleDef>,
262 ) {
263 let resolution = Resolution {
264 def_id,
265 import: None,
266 };
267 module_items.items.insert(name, resolution); 263 module_items.items.insert(name, resolution);
268 } 264 }
269 265
@@ -273,7 +269,7 @@ where
273 // already done 269 // already done
274 continue; 270 continue;
275 } 271 }
276 if self.resolve_import(module_id, import_id, import_data) { 272 if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes {
277 log::debug!("import {:?} resolved (or definite error)", import_id); 273 log::debug!("import {:?} resolved (or definite error)", import_id);
278 self.processed_imports.insert((module_id, import_id)); 274 self.processed_imports.insert((module_id, import_id));
279 } 275 }
@@ -285,116 +281,146 @@ where
285 module_id: ModuleId, 281 module_id: ModuleId,
286 import_id: ImportId, 282 import_id: ImportId,
287 import: &ImportData, 283 import: &ImportData,
288 ) -> bool { 284 ) -> ReachedFixedPoint {
289 log::debug!("resolving import: {:?}", import); 285 log::debug!("resolving import: {:?}", import);
290 if import.is_glob { 286 if import.is_glob {
291 return false; 287 return ReachedFixedPoint::Yes;
292 }; 288 };
289 let original_module = Module {
290 krate: self.krate,
291 module_id,
292 };
293 let (def, reached_fixedpoint) =
294 self.result
295 .resolve_path_fp(self.db, original_module, &import.path);
296
297 if reached_fixedpoint == ReachedFixedPoint::Yes {
298 let last_segment = import.path.segments.last().unwrap();
299 self.update(module_id, |items| {
300 let res = Resolution {
301 def,
302 import: Some(import_id),
303 };
304 items.items.insert(last_segment.name.clone(), res);
305 });
306 log::debug!(
307 "resolved import {:?} ({:?}) cross-source root to {:?}",
308 last_segment.name,
309 import,
310 def,
311 );
312 }
313 reached_fixedpoint
314 }
315
316 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
317 let module_items = self.result.per_module.get_mut(module_id).unwrap();
318 f(module_items)
319 }
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Eq)]
323enum ReachedFixedPoint {
324 Yes,
325 No,
326}
293 327
294 let mut curr: ModuleId = match import.path.kind { 328impl ItemMap {
295 PathKind::Plain | PathKind::Self_ => module_id, 329 pub(crate) fn resolve_path(
330 &self,
331 db: &impl HirDatabase,
332 original_module: Module,
333 path: &Path,
334 ) -> PerNs<ModuleDef> {
335 self.resolve_path_fp(db, original_module, path).0
336 }
337
338 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
339 // the result.
340 fn resolve_path_fp(
341 &self,
342 db: &impl HirDatabase,
343 original_module: Module,
344 path: &Path,
345 ) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
346 let mut curr_per_ns: PerNs<ModuleDef> = PerNs::types(match path.kind {
347 PathKind::Crate => original_module.crate_root(db).into(),
348 PathKind::Self_ | PathKind::Plain => original_module.into(),
296 PathKind::Super => { 349 PathKind::Super => {
297 match module_id.parent(&self.module_tree) { 350 if let Some(p) = original_module.parent(db) {
298 Some(it) => it, 351 p.into()
299 None => { 352 } else {
300 // TODO: error 353 log::debug!("super path in root module");
301 log::debug!("super path in root module"); 354 return (PerNs::none(), ReachedFixedPoint::Yes);
302 return true; // this can't suddenly resolve if we just resolve some other imports
303 }
304 } 355 }
305 } 356 }
306 PathKind::Crate => module_id.crate_root(&self.module_tree),
307 PathKind::Abs => { 357 PathKind::Abs => {
308 // TODO: absolute use is not supported for now 358 // TODO: absolute use is not supported
309 return false; 359 return (PerNs::none(), ReachedFixedPoint::Yes);
310 } 360 }
311 }; 361 });
312 362
313 for (i, segment) in import.path.segments.iter().enumerate() { 363 for (i, segment) in path.segments.iter().enumerate() {
314 let is_last = i == import.path.segments.len() - 1; 364 let curr = match curr_per_ns.as_ref().take_types() {
315 365 Some(r) => r,
316 let def_id = match self.result.per_module[&curr].items.get(&segment.name) { 366 None => {
317 Some(res) if !res.def_id.is_none() => res.def_id, 367 // we still have path segments left, but the path so far
318 _ => { 368 // didn't resolve in the types namespace => no resolution
319 log::debug!("path segment {:?} not found", segment.name); 369 // (don't break here because curr_per_ns might contain
320 return false; 370 // something in the value namespace, and it would be wrong
371 // to return that)
372 return (PerNs::none(), ReachedFixedPoint::No);
321 } 373 }
322 }; 374 };
375 // resolve segment in curr
376
377 curr_per_ns = match curr {
378 ModuleDef::Module(module) => {
379 if module.krate != original_module.krate {
380 let path = Path {
381 segments: path.segments[i..].iter().cloned().collect(),
382 kind: PathKind::Crate,
383 };
384 log::debug!("resolving {:?} in other crate", path);
385 let def = module.resolve_path(db, &path);
386 return (def, ReachedFixedPoint::Yes);
387 }
323 388
324 if !is_last { 389 match self[module.module_id].items.get(&segment.name) {
325 let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { 390 Some(res) if !res.def.is_none() => res.def,
326 d 391 _ => {
327 } else { 392 log::debug!("path segment {:?} not found", segment.name);
328 log::debug!( 393 return (PerNs::none(), ReachedFixedPoint::No);
329 "path segment {:?} resolved to value only, but is not last",
330 segment.name
331 );
332 return false;
333 };
334 curr = match type_def_id {
335 ModuleDef::Module(module) => {
336 if module.krate == self.krate {
337 module.module_id
338 } else {
339 let path = Path {
340 segments: import.path.segments[i + 1..].iter().cloned().collect(),
341 kind: PathKind::Crate,
342 };
343 log::debug!("resolving {:?} in other source root", path);
344 let def_id = module.resolve_path(self.db, &path);
345 if !def_id.is_none() {
346 let last_segment = path.segments.last().unwrap();
347 self.update(module_id, |items| {
348 let res = Resolution {
349 def_id,
350 import: Some(import_id),
351 };
352 items.items.insert(last_segment.name.clone(), res);
353 });
354 log::debug!(
355 "resolved import {:?} ({:?}) cross-source root to {:?}",
356 last_segment.name,
357 import,
358 def_id,
359 );
360 return true;
361 } else {
362 log::debug!("rest of path did not resolve in other source root");
363 return true;
364 }
365 } 394 }
366 } 395 }
367 _ => { 396 }
368 log::debug!( 397 ModuleDef::Enum(e) => {
369 "path segment {:?} resolved to non-module {:?}, but is not last", 398 // enum variant
370 segment.name, 399 tested_by!(item_map_enum_importing);
371 type_def_id, 400 let matching_variant = e
372 ); 401 .variants(db)
373 return true; // this resolved to a non-module, so the path won't ever resolve 402 .into_iter()
403 .find(|(n, _variant)| n == &segment.name);
404
405 match matching_variant {
406 Some((_n, variant)) => PerNs::both(variant.into(), (*e).into()),
407 None => PerNs::none(),
374 } 408 }
375 } 409 }
376 } else { 410 _ => {
377 log::debug!( 411 // could be an inherent method call in UFCS form
378 "resolved import {:?} ({:?}) within source root to {:?}", 412 // (`Struct::method`), or some other kind of associated
379 segment.name, 413 // item... Which we currently don't handle (TODO)
380 import, 414 log::debug!(
381 def_id, 415 "path segment {:?} resolved to non-module {:?}, but is not last",
382 ); 416 segment.name,
383 self.update(module_id, |items| { 417 curr,
384 let res = Resolution { 418 );
385 def_id, 419 return (PerNs::none(), ReachedFixedPoint::Yes);
386 import: Some(import_id), 420 }
387 }; 421 };
388 items.items.insert(segment.name.clone(), res);
389 })
390 }
391 } 422 }
392 true 423 (curr_per_ns, ReachedFixedPoint::Yes)
393 }
394
395 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
396 let module_items = self.result.per_module.get_mut(&module_id).unwrap();
397 f(module_items)
398 } 424 }
399} 425}
400 426