aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/nameres.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/nameres.rs')
-rw-r--r--crates/ra_hir_def/src/nameres.rs270
1 files changed, 14 insertions, 256 deletions
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 433bdde48..d3ecabb9b 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -14,10 +14,10 @@
14//! 14//!
15//! ## Collecting RawItems 15//! ## Collecting RawItems
16//! 16//!
17//! This happens in the `raw` module, which parses a single source file into a 17//! This happens in the `raw` module, which parses a single source file into a
18//! set of top-level items. Nested imports are desugared to flat imports in 18//! set of top-level items. Nested imports are desugared to flat imports in this
19//! this phase. Macro calls are represented as a triple of (Path, Option<Name>, 19//! phase. Macro calls are represented as a triple of (Path, Option<Name>,
20//! TokenTree). 20//! TokenTree).
21//! 21//!
22//! ## Collecting Modules 22//! ## Collecting Modules
23//! 23//!
@@ -44,14 +44,14 @@
44//! Macros from other crates (including proc-macros) can be used with 44//! Macros from other crates (including proc-macros) can be used with
45//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of 45//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
46//! unexpanded macros. On every iteration, we try to resolve each macro call 46//! unexpanded macros. On every iteration, we try to resolve each macro call
47//! path and, upon success, we run macro expansion and "collect module" phase 47//! path and, upon success, we run macro expansion and "collect module" phase on
48//! on the result 48//! the result
49 49
50// FIXME: review privacy of submodules
51pub mod raw; 50pub mod raw;
52pub mod per_ns; 51pub mod per_ns;
53pub mod collector; 52mod collector;
54pub mod mod_resolution; 53mod mod_resolution;
54mod path_resolution;
55 55
56#[cfg(test)] 56#[cfg(test)]
57mod tests; 57mod tests;
@@ -65,14 +65,15 @@ use ra_db::{CrateId, Edition, FileId};
65use ra_prof::profile; 65use ra_prof::profile;
66use ra_syntax::ast; 66use ra_syntax::ast;
67use rustc_hash::{FxHashMap, FxHashSet}; 67use rustc_hash::{FxHashMap, FxHashSet};
68use test_utils::tested_by;
69 68
70use crate::{ 69use crate::{
71 builtin_type::BuiltinType, 70 builtin_type::BuiltinType,
72 db::DefDatabase2, 71 db::DefDatabase2,
73 nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId}, 72 nameres::{
74 path::{Path, PathKind}, 73 diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId,
75 AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId, 74 },
75 path::Path,
76 AstId, CrateModuleId, ModuleDefId, ModuleId, TraitId,
76}; 77};
77 78
78/// Contains all top-level defs from a macro-expanded crate 79/// Contains all top-level defs from a macro-expanded crate
@@ -195,45 +196,6 @@ pub struct Resolution {
195 pub import: Option<ImportId>, 196 pub import: Option<ImportId>,
196} 197}
197 198
198impl Resolution {
199 pub(crate) fn from_macro(macro_: MacroDefId) -> Self {
200 Resolution { def: PerNs::macros(macro_), import: None }
201 }
202}
203
204#[derive(Debug, Clone)]
205struct ResolvePathResult {
206 resolved_def: PerNs,
207 segment_index: Option<usize>,
208 reached_fixedpoint: ReachedFixedPoint,
209}
210
211impl ResolvePathResult {
212 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
213 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
214 }
215
216 fn with(
217 resolved_def: PerNs,
218 reached_fixedpoint: ReachedFixedPoint,
219 segment_index: Option<usize>,
220 ) -> ResolvePathResult {
221 ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226enum ResolveMode {
227 Import,
228 Other,
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232enum ReachedFixedPoint {
233 Yes,
234 No,
235}
236
237impl CrateDefMap { 199impl CrateDefMap {
238 pub(crate) fn crate_def_map_query( 200 pub(crate) fn crate_def_map_query(
239 // Note that this doesn't have `+ AstDatabase`! 201 // Note that this doesn't have `+ AstDatabase`!
@@ -296,210 +258,6 @@ impl CrateDefMap {
296 let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path); 258 let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
297 (res.resolved_def, res.segment_index) 259 (res.resolved_def, res.segment_index)
298 } 260 }
299
300 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
301 // the result.
302 fn resolve_path_fp_with_macro(
303 &self,
304 db: &impl DefDatabase2,
305 mode: ResolveMode,
306 original_module: CrateModuleId,
307 path: &Path,
308 ) -> ResolvePathResult {
309 let mut segments = path.segments.iter().enumerate();
310 let mut curr_per_ns: PerNs = match path.kind {
311 PathKind::DollarCrate(krate) => {
312 if krate == self.krate {
313 tested_by!(macro_dollar_crate_self);
314 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
315 } else {
316 let def_map = db.crate_def_map(krate);
317 let module = ModuleId { krate, module_id: def_map.root };
318 tested_by!(macro_dollar_crate_other);
319 PerNs::types(module.into())
320 }
321 }
322 PathKind::Crate => {
323 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
324 }
325 PathKind::Self_ => {
326 PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
327 }
328 // plain import or absolute path in 2015: crate-relative with
329 // fallback to extern prelude (with the simplification in
330 // rust-lang/rust#57745)
331 // FIXME there must be a nicer way to write this condition
332 PathKind::Plain | PathKind::Abs
333 if self.edition == Edition::Edition2015
334 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
335 {
336 let segment = match segments.next() {
337 Some((_, segment)) => segment,
338 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
339 };
340 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
341 self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
342 }
343 PathKind::Plain => {
344 let segment = match segments.next() {
345 Some((_, segment)) => segment,
346 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
347 };
348 log::debug!("resolving {:?} in module", segment);
349 self.resolve_name_in_module(db, original_module, &segment.name)
350 }
351 PathKind::Super => {
352 if let Some(p) = self.modules[original_module].parent {
353 PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
354 } else {
355 log::debug!("super path in root module");
356 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
357 }
358 }
359 PathKind::Abs => {
360 // 2018-style absolute path -- only extern prelude
361 let segment = match segments.next() {
362 Some((_, segment)) => segment,
363 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
364 };
365 if let Some(def) = self.extern_prelude.get(&segment.name) {
366 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
367 PerNs::types(*def)
368 } else {
369 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
370 }
371 }
372 PathKind::Type(_) => {
373 // This is handled in `infer::infer_path_expr`
374 // The result returned here does not matter
375 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
376 }
377 };
378
379 for (i, segment) in segments {
380 let curr = match curr_per_ns.take_types() {
381 Some(r) => r,
382 None => {
383 // we still have path segments left, but the path so far
384 // didn't resolve in the types namespace => no resolution
385 // (don't break here because `curr_per_ns` might contain
386 // something in the value namespace, and it would be wrong
387 // to return that)
388 return ResolvePathResult::empty(ReachedFixedPoint::No);
389 }
390 };
391 // resolve segment in curr
392
393 curr_per_ns = match curr {
394 ModuleDefId::ModuleId(module) => {
395 if module.krate != self.krate {
396 let path =
397 Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
398 log::debug!("resolving {:?} in other crate", path);
399 let defp_map = db.crate_def_map(module.krate);
400 let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
401 return ResolvePathResult::with(
402 def,
403 ReachedFixedPoint::Yes,
404 s.map(|s| s + i),
405 );
406 }
407
408 // Since it is a qualified path here, it should not contains legacy macros
409 match self[module.module_id].scope.get(&segment.name) {
410 Some(res) => res.def,
411 _ => {
412 log::debug!("path segment {:?} not found", segment.name);
413 return ResolvePathResult::empty(ReachedFixedPoint::No);
414 }
415 }
416 }
417 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
418 // enum variant
419 tested_by!(can_import_enum_variant);
420 let enum_data = db.enum_data(e);
421 match enum_data.variant(&segment.name) {
422 Some(local_id) => {
423 let variant = EnumVariantId { parent: e, local_id };
424 PerNs::both(variant.into(), variant.into())
425 }
426 None => {
427 return ResolvePathResult::with(
428 PerNs::types(e.into()),
429 ReachedFixedPoint::Yes,
430 Some(i),
431 );
432 }
433 }
434 }
435 s => {
436 // could be an inherent method call in UFCS form
437 // (`Struct::method`), or some other kind of associated item
438 log::debug!(
439 "path segment {:?} resolved to non-module {:?}, but is not last",
440 segment.name,
441 curr,
442 );
443
444 return ResolvePathResult::with(
445 PerNs::types(s),
446 ReachedFixedPoint::Yes,
447 Some(i),
448 );
449 }
450 };
451 }
452 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
453 }
454
455 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
456 let from_crate_root =
457 self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
458 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
459
460 from_crate_root.or(from_extern_prelude)
461 }
462
463 pub(crate) fn resolve_name_in_module(
464 &self,
465 db: &impl DefDatabase2,
466 module: CrateModuleId,
467 name: &Name,
468 ) -> PerNs {
469 // Resolve in:
470 // - legacy scope of macro
471 // - current module / scope
472 // - extern prelude
473 // - std prelude
474 let from_legacy_macro =
475 self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
476 let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
477 let from_extern_prelude =
478 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
479 let from_prelude = self.resolve_in_prelude(db, name);
480
481 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
482 }
483
484 fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
485 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
486 }
487
488 fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
489 if let Some(prelude) = self.prelude {
490 let keep;
491 let def_map = if prelude.krate == self.krate {
492 self
493 } else {
494 // Extend lifetime
495 keep = db.crate_def_map(prelude.krate);
496 &keep
497 };
498 def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
499 } else {
500 PerNs::none()
501 }
502 }
503} 261}
504 262
505mod diagnostics { 263mod diagnostics {