1 //! This modules implements a function to resolve a path `foo::bar::baz` to a
2 //! def, which is used within the name resolution.
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:
8 //! I can't resolve this path right now, but I might be resolve this path
9 //! later, when more macros are expanded.
11 //! `ReachedFixedPoint` signals about this.
14 use hir_expand
::name
::Name
;
18 item_scope
::BUILTIN_SCOPE
,
19 nameres
::{BuiltinShadowMode, DefMap}
,
20 path
::{ModPath, PathKind}
,
22 visibility
::{RawVisibility, Visibility}
,
23 AdtId
, CrateId
, EnumVariantId
, LocalModuleId
, ModuleDefId
, ModuleId
,
26 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
27 pub(super) enum ResolveMode
{
32 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
33 pub(super) enum ReachedFixedPoint
{
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
>,
46 impl ResolvePathResult
{
47 fn empty(reached_fixedpoint
: ReachedFixedPoint
) -> ResolvePathResult
{
48 ResolvePathResult
::with(PerNs
::none(), reached_fixedpoint
, None
, None
)
53 reached_fixedpoint
: ReachedFixedPoint
,
54 segment_index
: Option
<usize>,
55 krate
: Option
<CrateId
>,
56 ) -> ResolvePathResult
{
57 ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, krate }
62 pub(super) fn resolve_name_in_extern_prelude(
66 ) -> Option
<ModuleId
> {
68 Some(_
) => self.crate_root(db
).def_map(db
).extern_prelude
.get(name
).copied(),
69 None
=> self.extern_prelude
.get(name
).copied(),
73 pub(crate) fn resolve_visibility(
76 // module to import to
77 original_module
: LocalModuleId
,
80 visibility
: &RawVisibility
,
81 ) -> Option
<Visibility
> {
82 let mut vis
= match visibility
{
83 RawVisibility
::Module(path
) => {
84 let (result
, remaining
) =
85 self.resolve_path(db
, original_module
, path
, BuiltinShadowMode
::Module
);
86 if remaining
.is_some() {
89 let types
= result
.take_types()?
;
91 ModuleDefId
::ModuleId(m
) => Visibility
::Module(m
),
93 // error: visibility needs to refer to module
98 RawVisibility
::Public
=> Visibility
::Public
,
101 // In block expressions, `self` normally refers to the containing non-block module, and
102 // `super` to its parent (etc.). However, visibilities must only refer to a module in the
103 // DefMap they're written in, so we restrict them when that happens.
104 if let Visibility
::Module(m
) = vis
{
105 if self.block_id() != m
.block
{
106 cov_mark
::hit
!(adjust_vis_in_block_def_map
);
107 vis
= Visibility
::Module(self.module_id(self.root()));
108 tracing
::debug
!("visibility {:?} points outside DefMap, adjusting to {:?}", m
, vis
);
115 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
117 pub(super) fn resolve_path_fp_with_macro(
119 db
: &dyn DefDatabase
,
121 // module to import to
122 mut original_module
: LocalModuleId
,
124 shadow
: BuiltinShadowMode
,
125 ) -> ResolvePathResult
{
126 let mut result
= ResolvePathResult
::empty(ReachedFixedPoint
::No
);
129 let mut current_map
= self;
131 let new
= current_map
.resolve_path_fp_with_macro_single(
139 // Merge `new` into `result`.
140 result
.resolved_def
= result
.resolved_def
.or(new
.resolved_def
);
141 if result
.reached_fixedpoint
== ReachedFixedPoint
::No
{
142 result
.reached_fixedpoint
= new
.reached_fixedpoint
;
144 // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates?
145 result
.krate
= result
.krate
.or(new
.krate
);
146 result
.segment_index
= match (result
.segment_index
, new
.segment_index
) {
147 (Some(idx
), None
) => Some(idx
),
148 (Some(old
), Some(new
)) => Some(old
.max(new
)),
152 match ¤t_map
.block
{
154 original_module
= block
.parent
.local_id
;
155 arc
= block
.parent
.def_map(db
);
158 None
=> return result
,
163 pub(super) fn resolve_path_fp_with_macro_single(
165 db
: &dyn DefDatabase
,
167 original_module
: LocalModuleId
,
169 shadow
: BuiltinShadowMode
,
170 ) -> ResolvePathResult
{
171 let graph
= db
.crate_graph();
172 let _cx
= stdx
::panic_context
::enter(format
!(
173 "DefMap {:?} crate_name={:?} block={:?} path={path}",
174 self.krate
, graph
[self.krate
].display_name
, self.block
177 let mut segments
= path
.segments().iter().enumerate();
178 let mut curr_per_ns
: PerNs
= match path
.kind
{
179 PathKind
::DollarCrate(krate
) => {
180 if krate
== self.krate
{
181 cov_mark
::hit
!(macro_dollar_crate_self
);
182 PerNs
::types(self.crate_root(db
).into(), Visibility
::Public
)
184 let def_map
= db
.crate_def_map(krate
);
185 let module
= def_map
.module_id(def_map
.root
);
186 cov_mark
::hit
!(macro_dollar_crate_other
);
187 PerNs
::types(module
.into(), Visibility
::Public
)
190 PathKind
::Crate
=> PerNs
::types(self.crate_root(db
).into(), Visibility
::Public
),
191 // plain import or absolute path in 2015: crate-relative with
192 // fallback to extern prelude (with the simplification in
193 // rust-lang/rust#57745)
194 // FIXME there must be a nicer way to write this condition
195 PathKind
::Plain
| PathKind
::Abs
196 if self.edition
== Edition
::Edition2015
197 && (path
.kind
== PathKind
::Abs
|| mode
== ResolveMode
::Import
) =>
199 let (_
, segment
) = match segments
.next() {
200 Some((idx
, segment
)) => (idx
, segment
),
201 None
=> return ResolvePathResult
::empty(ReachedFixedPoint
::Yes
),
203 tracing
::debug
!("resolving {:?} in crate root (+ extern prelude)", segment
);
204 self.resolve_name_in_crate_root_or_extern_prelude(db
, segment
)
207 let (_
, segment
) = match segments
.next() {
208 Some((idx
, segment
)) => (idx
, segment
),
209 None
=> return ResolvePathResult
::empty(ReachedFixedPoint
::Yes
),
211 // The first segment may be a builtin type. If the path has more
212 // than one segment, we first try resolving it as a module
214 // FIXME: If the next segment doesn't resolve in the module and
215 // BuiltinShadowMode wasn't Module, then we need to try
216 // resolving it as a builtin.
218 if path
.segments().len() == 1 { shadow }
else { BuiltinShadowMode::Module }
;
220 tracing
::debug
!("resolving {:?} in module", segment
);
221 self.resolve_name_in_module(db
, original_module
, segment
, prefer_module
)
223 PathKind
::Super(lvl
) => {
224 let mut module
= original_module
;
226 match self.modules
[module
].parent
{
227 Some(it
) => module
= it
,
228 None
=> match &self.block
{
230 // Look up remaining path in parent `DefMap`
231 let new_path
= ModPath
::from_segments(
232 PathKind
::Super(lvl
- i
),
233 path
.segments().to_vec(),
236 "`super` path: {} -> {} in parent map",
240 return block
.parent
.def_map(db
).resolve_path_fp_with_macro(
243 block
.parent
.local_id
,
249 tracing
::debug
!("super path in root module");
250 return ResolvePathResult
::empty(ReachedFixedPoint
::Yes
);
256 // Resolve `self` to the containing crate-rooted module if we're a block
257 self.with_ancestor_maps(db
, module
, &mut |def_map
, module
| {
258 if def_map
.block
.is_some() {
259 None
// keep ascending
261 Some(PerNs
::types(def_map
.module_id(module
).into(), Visibility
::Public
))
264 .expect("block DefMap not rooted in crate DefMap")
267 // 2018-style absolute path -- only extern prelude
268 let segment
= match segments
.next() {
269 Some((_
, segment
)) => segment
,
270 None
=> return ResolvePathResult
::empty(ReachedFixedPoint
::Yes
),
272 if let Some(&def
) = self.extern_prelude
.get(segment
) {
273 tracing
::debug
!("absolute path {:?} resolved to crate {:?}", path
, def
);
274 PerNs
::types(def
.into(), Visibility
::Public
)
276 return ResolvePathResult
::empty(ReachedFixedPoint
::No
); // extern crate declarations can add to the extern prelude
281 for (i
, segment
) in segments
{
282 let (curr
, vis
) = match curr_per_ns
.take_types_vis() {
285 // we still have path segments left, but the path so far
286 // didn't resolve in the types namespace => no resolution
287 // (don't break here because `curr_per_ns` might contain
288 // something in the value namespace, and it would be wrong
290 return ResolvePathResult
::empty(ReachedFixedPoint
::No
);
293 // resolve segment in curr
295 curr_per_ns
= match curr
{
296 ModuleDefId
::ModuleId(module
) => {
297 if module
.krate
!= self.krate
{
298 let path
= ModPath
::from_segments(
300 path
.segments()[i
..].iter().cloned(),
302 tracing
::debug
!("resolving {:?} in other crate", path
);
303 let defp_map
= module
.def_map(db
);
304 let (def
, s
) = defp_map
.resolve_path(db
, module
.local_id
, &path
, shadow
);
305 return ResolvePathResult
::with(
307 ReachedFixedPoint
::Yes
,
314 let module_data
= if module
.block
== self.block_id() {
315 &self[module
.local_id
]
317 def_map
= module
.def_map(db
);
318 &def_map
[module
.local_id
]
321 // Since it is a qualified path here, it should not contains legacy macros
322 module_data
.scope
.get(segment
)
324 ModuleDefId
::AdtId(AdtId
::EnumId(e
)) => {
326 cov_mark
::hit
!(can_import_enum_variant
);
327 let enum_data
= db
.enum_data(e
);
328 match enum_data
.variant(segment
) {
330 let variant
= EnumVariantId { parent: e, local_id }
;
331 match &*enum_data
.variants
[local_id
].variant_data
{
332 crate::adt
::VariantData
::Record(_
) => {
333 PerNs
::types(variant
.into(), Visibility
::Public
)
335 crate::adt
::VariantData
::Tuple(_
)
336 | crate::adt
::VariantData
::Unit
=> {
337 PerNs
::both(variant
.into(), variant
.into(), Visibility
::Public
)
342 return ResolvePathResult
::with(
343 PerNs
::types(e
.into(), vis
),
344 ReachedFixedPoint
::Yes
,
352 // could be an inherent method call in UFCS form
353 // (`Struct::method`), or some other kind of associated item
355 "path segment {:?} resolved to non-module {:?}, but is not last",
360 return ResolvePathResult
::with(
361 PerNs
::types(s
, vis
),
362 ReachedFixedPoint
::Yes
,
369 curr_per_ns
= curr_per_ns
370 .filter_visibility(|vis
| vis
.is_visible_from_def_map(db
, self, original_module
));
373 ResolvePathResult
::with(curr_per_ns
, ReachedFixedPoint
::Yes
, None
, Some(self.krate
))
376 fn resolve_name_in_module(
378 db
: &dyn DefDatabase
,
379 module
: LocalModuleId
,
381 shadow
: BuiltinShadowMode
,
384 // - legacy scope of macro
385 // - current module / scope
388 let from_legacy_macro
= self[module
]
390 .get_legacy_macro(name
)
392 .and_then(|it
| it
.last())
393 .map_or_else(PerNs
::none
, |&m
| PerNs
::macros(m
, Visibility
::Public
));
394 let from_scope
= self[module
].scope
.get(name
);
395 let from_builtin
= match self.block
{
397 // Only resolve to builtins in the root `DefMap`.
400 None
=> BUILTIN_SCOPE
.get(name
).copied().unwrap_or_else(PerNs
::none
),
402 let from_scope_or_builtin
= match shadow
{
403 BuiltinShadowMode
::Module
=> from_scope
.or(from_builtin
),
404 BuiltinShadowMode
::Other
=> match from_scope
.take_types() {
405 Some(ModuleDefId
::ModuleId(_
)) => from_builtin
.or(from_scope
),
406 Some(_
) | None
=> from_scope
.or(from_builtin
),
410 let extern_prelude
= || {
413 .map_or(PerNs
::none(), |&it
| PerNs
::types(it
.into(), Visibility
::Public
))
415 let prelude
= || self.resolve_in_prelude(db
, name
);
417 from_legacy_macro
.or(from_scope_or_builtin
).or_else(extern_prelude
).or_else(prelude
)
420 fn resolve_name_in_crate_root_or_extern_prelude(
422 db
: &dyn DefDatabase
,
425 let from_crate_root
= match self.block
{
427 let def_map
= self.crate_root(db
).def_map(db
);
428 def_map
[def_map
.root
].scope
.get(name
)
430 None
=> self[self.root
].scope
.get(name
),
432 let from_extern_prelude
= || {
433 self.resolve_name_in_extern_prelude(db
, name
)
434 .map_or(PerNs
::none(), |it
| PerNs
::types(it
.into(), Visibility
::Public
))
437 from_crate_root
.or_else(from_extern_prelude
)
440 fn resolve_in_prelude(&self, db
: &dyn DefDatabase
, name
: &Name
) -> PerNs
{
441 if let Some(prelude
) = self.prelude
{
443 let def_map
= if prelude
.krate
== self.krate
{
447 keep
= prelude
.def_map(db
);
450 def_map
[prelude
.local_id
].scope
.get(name
)