• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! This modules implements a function to resolve a path `foo::bar::baz` to a
2 //! def, which is used within the name resolution.
3 //!
4 //! When name resolution is finished, the result of resolving a path is either
5 //! `Some(def)` or `None`. However, when we are in process of resolving imports
6 //! or macros, there's a third possibility:
7 //!
8 //!   I can't resolve this path right now, but I might be resolve this path
9 //!   later, when more macros are expanded.
10 //!
11 //! `ReachedFixedPoint` signals about this.
12 
13 use base_db::Edition;
14 use hir_expand::name::Name;
15 
16 use crate::{
17     db::DefDatabase,
18     item_scope::BUILTIN_SCOPE,
19     nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
20     path::{ModPath, PathKind},
21     per_ns::PerNs,
22     visibility::{RawVisibility, Visibility},
23     AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
24 };
25 
26 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
27 pub(super) enum ResolveMode {
28     Import,
29     Other,
30 }
31 
32 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
33 pub(super) enum ReachedFixedPoint {
34     Yes,
35     No,
36 }
37 
38 #[derive(Debug, Clone)]
39 pub(super) struct ResolvePathResult {
40     pub(super) resolved_def: PerNs,
41     pub(super) segment_index: Option<usize>,
42     pub(super) reached_fixedpoint: ReachedFixedPoint,
43     pub(super) krate: Option<CrateId>,
44 }
45 
46 impl ResolvePathResult {
empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult47     fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
48         ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
49     }
50 
with( resolved_def: PerNs, reached_fixedpoint: ReachedFixedPoint, segment_index: Option<usize>, krate: Option<CrateId>, ) -> ResolvePathResult51     fn with(
52         resolved_def: PerNs,
53         reached_fixedpoint: ReachedFixedPoint,
54         segment_index: Option<usize>,
55         krate: Option<CrateId>,
56     ) -> ResolvePathResult {
57         ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, krate }
58     }
59 }
60 
61 impl PerNs {
filter_macro( mut self, db: &dyn DefDatabase, expected: Option<MacroSubNs>, ) -> Self62     pub(super) fn filter_macro(
63         mut self,
64         db: &dyn DefDatabase,
65         expected: Option<MacroSubNs>,
66     ) -> Self {
67         self.macros = self.macros.filter(|&(id, _)| {
68             let this = MacroSubNs::from_id(db, id);
69             sub_namespace_match(Some(this), expected)
70         });
71 
72         self
73     }
74 }
75 
76 impl DefMap {
resolve_visibility( &self, db: &dyn DefDatabase, original_module: LocalModuleId, visibility: &RawVisibility, within_impl: bool, ) -> Option<Visibility>77     pub(crate) fn resolve_visibility(
78         &self,
79         db: &dyn DefDatabase,
80         // module to import to
81         original_module: LocalModuleId,
82         // pub(path)
83         //     ^^^^ this
84         visibility: &RawVisibility,
85         within_impl: bool,
86     ) -> Option<Visibility> {
87         let mut vis = match visibility {
88             RawVisibility::Module(path) => {
89                 let (result, remaining) =
90                     self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None);
91                 if remaining.is_some() {
92                     return None;
93                 }
94                 let types = result.take_types()?;
95                 match types {
96                     ModuleDefId::ModuleId(m) => Visibility::Module(m),
97                     _ => {
98                         // error: visibility needs to refer to module
99                         return None;
100                     }
101                 }
102             }
103             RawVisibility::Public => Visibility::Public,
104         };
105 
106         // In block expressions, `self` normally refers to the containing non-block module, and
107         // `super` to its parent (etc.). However, visibilities must only refer to a module in the
108         // DefMap they're written in, so we restrict them when that happens.
109         if let Visibility::Module(m) = vis {
110             // ...unless we're resolving visibility for an associated item in an impl.
111             if self.block_id() != m.block && !within_impl {
112                 cov_mark::hit!(adjust_vis_in_block_def_map);
113                 vis = Visibility::Module(self.module_id(Self::ROOT));
114                 tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
115             }
116         }
117 
118         Some(vis)
119     }
120 
121     // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
122     // the result.
resolve_path_fp_with_macro( &self, db: &dyn DefDatabase, mode: ResolveMode, mut original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, expected_macro_subns: Option<MacroSubNs>, ) -> ResolvePathResult123     pub(super) fn resolve_path_fp_with_macro(
124         &self,
125         db: &dyn DefDatabase,
126         mode: ResolveMode,
127         // module to import to
128         mut original_module: LocalModuleId,
129         path: &ModPath,
130         shadow: BuiltinShadowMode,
131         // Pass `MacroSubNs` if we know we're resolving macro names and which kind of macro we're
132         // resolving them to. Pass `None` otherwise, e.g. when we're resolving import paths.
133         expected_macro_subns: Option<MacroSubNs>,
134     ) -> ResolvePathResult {
135         let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
136 
137         let mut arc;
138         let mut current_map = self;
139         loop {
140             let new = current_map.resolve_path_fp_with_macro_single(
141                 db,
142                 mode,
143                 original_module,
144                 path,
145                 shadow,
146                 expected_macro_subns,
147             );
148 
149             // Merge `new` into `result`.
150             result.resolved_def = result.resolved_def.or(new.resolved_def);
151             if result.reached_fixedpoint == ReachedFixedPoint::No {
152                 result.reached_fixedpoint = new.reached_fixedpoint;
153             }
154             // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates?
155             result.krate = result.krate.or(new.krate);
156             result.segment_index = match (result.segment_index, new.segment_index) {
157                 (Some(idx), None) => Some(idx),
158                 (Some(old), Some(new)) => Some(old.max(new)),
159                 (None, new) => new,
160             };
161 
162             match &current_map.block {
163                 Some(block) => {
164                     original_module = block.parent.local_id;
165                     arc = block.parent.def_map(db, current_map.krate);
166                     current_map = &*arc;
167                 }
168                 None => return result,
169             }
170         }
171     }
172 
resolve_path_fp_with_macro_single( &self, db: &dyn DefDatabase, mode: ResolveMode, original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, expected_macro_subns: Option<MacroSubNs>, ) -> ResolvePathResult173     pub(super) fn resolve_path_fp_with_macro_single(
174         &self,
175         db: &dyn DefDatabase,
176         mode: ResolveMode,
177         original_module: LocalModuleId,
178         path: &ModPath,
179         shadow: BuiltinShadowMode,
180         expected_macro_subns: Option<MacroSubNs>,
181     ) -> ResolvePathResult {
182         let graph = db.crate_graph();
183         let _cx = stdx::panic_context::enter(format!(
184             "DefMap {:?} crate_name={:?} block={:?} path={}",
185             self.krate,
186             graph[self.krate].display_name,
187             self.block,
188             path.display(db.upcast())
189         ));
190 
191         let mut segments = path.segments().iter().enumerate();
192         let mut curr_per_ns: PerNs = match path.kind {
193             PathKind::DollarCrate(krate) => {
194                 if krate == self.krate {
195                     cov_mark::hit!(macro_dollar_crate_self);
196                     PerNs::types(self.crate_root().into(), Visibility::Public)
197                 } else {
198                     let def_map = db.crate_def_map(krate);
199                     let module = def_map.module_id(Self::ROOT);
200                     cov_mark::hit!(macro_dollar_crate_other);
201                     PerNs::types(module.into(), Visibility::Public)
202                 }
203             }
204             PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public),
205             // plain import or absolute path in 2015: crate-relative with
206             // fallback to extern prelude (with the simplification in
207             // rust-lang/rust#57745)
208             // FIXME there must be a nicer way to write this condition
209             PathKind::Plain | PathKind::Abs
210                 if self.data.edition == Edition::Edition2015
211                     && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
212             {
213                 let (_, segment) = match segments.next() {
214                     Some((idx, segment)) => (idx, segment),
215                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
216                 };
217                 tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
218                 self.resolve_name_in_crate_root_or_extern_prelude(db, segment)
219             }
220             PathKind::Plain => {
221                 let (_, segment) = match segments.next() {
222                     Some((idx, segment)) => (idx, segment),
223                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
224                 };
225                 // The first segment may be a builtin type. If the path has more
226                 // than one segment, we first try resolving it as a module
227                 // anyway.
228                 // FIXME: If the next segment doesn't resolve in the module and
229                 // BuiltinShadowMode wasn't Module, then we need to try
230                 // resolving it as a builtin.
231                 let prefer_module =
232                     if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
233 
234                 tracing::debug!("resolving {:?} in module", segment);
235                 self.resolve_name_in_module(
236                     db,
237                     original_module,
238                     segment,
239                     prefer_module,
240                     expected_macro_subns,
241                 )
242             }
243             PathKind::Super(lvl) => {
244                 let mut module = original_module;
245                 for i in 0..lvl {
246                     match self.modules[module].parent {
247                         Some(it) => module = it,
248                         None => match &self.block {
249                             Some(block) => {
250                                 // Look up remaining path in parent `DefMap`
251                                 let new_path = ModPath::from_segments(
252                                     PathKind::Super(lvl - i),
253                                     path.segments().to_vec(),
254                                 );
255                                 tracing::debug!(
256                                     "`super` path: {} -> {} in parent map",
257                                     path.display(db.upcast()),
258                                     new_path.display(db.upcast())
259                                 );
260                                 return block
261                                     .parent
262                                     .def_map(db, self.krate)
263                                     .resolve_path_fp_with_macro(
264                                         db,
265                                         mode,
266                                         block.parent.local_id,
267                                         &new_path,
268                                         shadow,
269                                         expected_macro_subns,
270                                     );
271                             }
272                             None => {
273                                 tracing::debug!("super path in root module");
274                                 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
275                             }
276                         },
277                     }
278                 }
279 
280                 // Resolve `self` to the containing crate-rooted module if we're a block
281                 self.with_ancestor_maps(db, module, &mut |def_map, module| {
282                     if def_map.block.is_some() {
283                         None // keep ascending
284                     } else {
285                         Some(PerNs::types(def_map.module_id(module).into(), Visibility::Public))
286                     }
287                 })
288                 .expect("block DefMap not rooted in crate DefMap")
289             }
290             PathKind::Abs => {
291                 // 2018-style absolute path -- only extern prelude
292                 let segment = match segments.next() {
293                     Some((_, segment)) => segment,
294                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
295                 };
296                 if let Some(&def) = self.data.extern_prelude.get(segment) {
297                     tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
298                     PerNs::types(def.into(), Visibility::Public)
299                 } else {
300                     return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
301                 }
302             }
303         };
304 
305         for (i, segment) in segments {
306             let (curr, vis) = match curr_per_ns.take_types_vis() {
307                 Some(r) => r,
308                 None => {
309                     // we still have path segments left, but the path so far
310                     // didn't resolve in the types namespace => no resolution
311                     // (don't break here because `curr_per_ns` might contain
312                     // something in the value namespace, and it would be wrong
313                     // to return that)
314                     return ResolvePathResult::empty(ReachedFixedPoint::No);
315                 }
316             };
317             // resolve segment in curr
318 
319             curr_per_ns = match curr {
320                 ModuleDefId::ModuleId(module) => {
321                     if module.krate != self.krate {
322                         let path = ModPath::from_segments(
323                             PathKind::Super(0),
324                             path.segments()[i..].iter().cloned(),
325                         );
326                         tracing::debug!("resolving {:?} in other crate", path);
327                         let defp_map = module.def_map(db);
328                         // Macro sub-namespaces only matter when resolving single-segment paths
329                         // because `macro_use` and other preludes should be taken into account. At
330                         // this point, we know we're resolving a multi-segment path so macro kind
331                         // expectation is discarded.
332                         let (def, s) =
333                             defp_map.resolve_path(db, module.local_id, &path, shadow, None);
334                         return ResolvePathResult::with(
335                             def,
336                             ReachedFixedPoint::Yes,
337                             s.map(|s| s + i),
338                             Some(module.krate),
339                         );
340                     }
341 
342                     let def_map;
343                     let module_data = if module.block == self.block_id() {
344                         &self[module.local_id]
345                     } else {
346                         def_map = module.def_map(db);
347                         &def_map[module.local_id]
348                     };
349 
350                     // Since it is a qualified path here, it should not contains legacy macros
351                     module_data.scope.get(segment)
352                 }
353                 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
354                     // enum variant
355                     cov_mark::hit!(can_import_enum_variant);
356                     let enum_data = db.enum_data(e);
357                     match enum_data.variant(segment) {
358                         Some(local_id) => {
359                             let variant = EnumVariantId { parent: e, local_id };
360                             match &*enum_data.variants[local_id].variant_data {
361                                 crate::data::adt::VariantData::Record(_) => {
362                                     PerNs::types(variant.into(), Visibility::Public)
363                                 }
364                                 crate::data::adt::VariantData::Tuple(_)
365                                 | crate::data::adt::VariantData::Unit => {
366                                     PerNs::both(variant.into(), variant.into(), Visibility::Public)
367                                 }
368                             }
369                         }
370                         None => {
371                             return ResolvePathResult::with(
372                                 PerNs::types(e.into(), vis),
373                                 ReachedFixedPoint::Yes,
374                                 Some(i),
375                                 Some(self.krate),
376                             );
377                         }
378                     }
379                 }
380                 s => {
381                     // could be an inherent method call in UFCS form
382                     // (`Struct::method`), or some other kind of associated item
383                     tracing::debug!(
384                         "path segment {:?} resolved to non-module {:?}, but is not last",
385                         segment,
386                         curr,
387                     );
388 
389                     return ResolvePathResult::with(
390                         PerNs::types(s, vis),
391                         ReachedFixedPoint::Yes,
392                         Some(i),
393                         Some(self.krate),
394                     );
395                 }
396             };
397 
398             curr_per_ns = curr_per_ns
399                 .filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module));
400         }
401 
402         ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
403     }
404 
resolve_name_in_module( &self, db: &dyn DefDatabase, module: LocalModuleId, name: &Name, shadow: BuiltinShadowMode, expected_macro_subns: Option<MacroSubNs>, ) -> PerNs405     fn resolve_name_in_module(
406         &self,
407         db: &dyn DefDatabase,
408         module: LocalModuleId,
409         name: &Name,
410         shadow: BuiltinShadowMode,
411         expected_macro_subns: Option<MacroSubNs>,
412     ) -> PerNs {
413         // Resolve in:
414         //  - legacy scope of macro
415         //  - current module / scope
416         //  - extern prelude / macro_use prelude
417         //  - std prelude
418         let from_legacy_macro = self[module]
419             .scope
420             .get_legacy_macro(name)
421             // FIXME: shadowing
422             .and_then(|it| it.last())
423             .copied()
424             .filter(|&id| {
425                 sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns)
426             })
427             .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
428         let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
429         let from_builtin = match self.block {
430             Some(_) => {
431                 // Only resolve to builtins in the root `DefMap`.
432                 PerNs::none()
433             }
434             None => BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none),
435         };
436         let from_scope_or_builtin = match shadow {
437             BuiltinShadowMode::Module => from_scope.or(from_builtin),
438             BuiltinShadowMode::Other => match from_scope.take_types() {
439                 Some(ModuleDefId::ModuleId(_)) => from_builtin.or(from_scope),
440                 Some(_) | None => from_scope.or(from_builtin),
441             },
442         };
443 
444         let extern_prelude = || {
445             if self.block.is_some() {
446                 // Don't resolve extern prelude in block `DefMap`s.
447                 return PerNs::none();
448             }
449             self.data
450                 .extern_prelude
451                 .get(name)
452                 .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
453         };
454         let macro_use_prelude = || {
455             self.macro_use_prelude
456                 .get(name)
457                 .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
458         };
459         let prelude = || self.resolve_in_prelude(db, name);
460 
461         from_legacy_macro
462             .or(from_scope_or_builtin)
463             .or_else(extern_prelude)
464             .or_else(macro_use_prelude)
465             .or_else(prelude)
466     }
467 
resolve_name_in_crate_root_or_extern_prelude( &self, db: &dyn DefDatabase, name: &Name, ) -> PerNs468     fn resolve_name_in_crate_root_or_extern_prelude(
469         &self,
470         db: &dyn DefDatabase,
471         name: &Name,
472     ) -> PerNs {
473         let from_crate_root = match self.block {
474             Some(_) => {
475                 let def_map = self.crate_root().def_map(db);
476                 def_map[Self::ROOT].scope.get(name)
477             }
478             None => self[Self::ROOT].scope.get(name),
479         };
480         let from_extern_prelude = || {
481             if self.block.is_some() {
482                 // Don't resolve extern prelude in block `DefMap`s.
483                 return PerNs::none();
484             }
485             self.data
486                 .extern_prelude
487                 .get(name)
488                 .copied()
489                 .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
490         };
491 
492         from_crate_root.or_else(from_extern_prelude)
493     }
494 
resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs495     fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
496         if let Some(prelude) = self.prelude {
497             let keep;
498             let def_map = if prelude.krate == self.krate {
499                 self
500             } else {
501                 // Extend lifetime
502                 keep = prelude.def_map(db);
503                 &keep
504             };
505             def_map[prelude.local_id].scope.get(name)
506         } else {
507             PerNs::none()
508         }
509     }
510 }
511