]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir-def / src / nameres / path_resolution.rs
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::{BuiltinShadowMode, DefMap},
20 path::{ModPath, PathKind},
21 per_ns::PerNs,
22 visibility::{RawVisibility, Visibility},
23 AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
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 {
47 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
48 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
49 }
50
51 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 DefMap {
62 pub(super) fn resolve_name_in_extern_prelude(
63 &self,
64 db: &dyn DefDatabase,
65 name: &Name,
66 ) -> Option<ModuleId> {
67 match self.block {
68 Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(),
69 None => self.extern_prelude.get(name).copied(),
70 }
71 }
72
73 pub(crate) fn resolve_visibility(
74 &self,
75 db: &dyn DefDatabase,
76 // module to import to
77 original_module: LocalModuleId,
78 // pub(path)
79 // ^^^^ this
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() {
87 return None;
88 }
89 let types = result.take_types()?;
90 match types {
91 ModuleDefId::ModuleId(m) => Visibility::Module(m),
92 _ => {
93 // error: visibility needs to refer to module
94 return None;
95 }
96 }
97 }
98 RawVisibility::Public => Visibility::Public,
99 };
100
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);
109 }
110 }
111
112 Some(vis)
113 }
114
115 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
116 // the result.
117 pub(super) fn resolve_path_fp_with_macro(
118 &self,
119 db: &dyn DefDatabase,
120 mode: ResolveMode,
121 // module to import to
122 mut original_module: LocalModuleId,
123 path: &ModPath,
124 shadow: BuiltinShadowMode,
125 ) -> ResolvePathResult {
126 let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
127
128 let mut arc;
129 let mut current_map = self;
130 loop {
131 let new = current_map.resolve_path_fp_with_macro_single(
132 db,
133 mode,
134 original_module,
135 path,
136 shadow,
137 );
138
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;
143 }
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)),
149 (None, new) => new,
150 };
151
152 match &current_map.block {
153 Some(block) => {
154 original_module = block.parent.local_id;
155 arc = block.parent.def_map(db);
156 current_map = &*arc;
157 }
158 None => return result,
159 }
160 }
161 }
162
163 pub(super) fn resolve_path_fp_with_macro_single(
164 &self,
165 db: &dyn DefDatabase,
166 mode: ResolveMode,
167 original_module: LocalModuleId,
168 path: &ModPath,
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
175 ));
176
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)
183 } else {
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)
188 }
189 }
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) =>
198 {
199 let (_, segment) = match segments.next() {
200 Some((idx, segment)) => (idx, segment),
201 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
202 };
203 tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
204 self.resolve_name_in_crate_root_or_extern_prelude(db, segment)
205 }
206 PathKind::Plain => {
207 let (_, segment) = match segments.next() {
208 Some((idx, segment)) => (idx, segment),
209 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
210 };
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
213 // anyway.
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.
217 let prefer_module =
218 if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
219
220 tracing::debug!("resolving {:?} in module", segment);
221 self.resolve_name_in_module(db, original_module, segment, prefer_module)
222 }
223 PathKind::Super(lvl) => {
224 let mut module = original_module;
225 for i in 0..lvl {
226 match self.modules[module].parent {
227 Some(it) => module = it,
228 None => match &self.block {
229 Some(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(),
234 );
235 tracing::debug!(
236 "`super` path: {} -> {} in parent map",
237 path,
238 new_path
239 );
240 return block.parent.def_map(db).resolve_path_fp_with_macro(
241 db,
242 mode,
243 block.parent.local_id,
244 &new_path,
245 shadow,
246 );
247 }
248 None => {
249 tracing::debug!("super path in root module");
250 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
251 }
252 },
253 }
254 }
255
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
260 } else {
261 Some(PerNs::types(def_map.module_id(module).into(), Visibility::Public))
262 }
263 })
264 .expect("block DefMap not rooted in crate DefMap")
265 }
266 PathKind::Abs => {
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),
271 };
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)
275 } else {
276 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
277 }
278 }
279 };
280
281 for (i, segment) in segments {
282 let (curr, vis) = match curr_per_ns.take_types_vis() {
283 Some(r) => r,
284 None => {
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
289 // to return that)
290 return ResolvePathResult::empty(ReachedFixedPoint::No);
291 }
292 };
293 // resolve segment in curr
294
295 curr_per_ns = match curr {
296 ModuleDefId::ModuleId(module) => {
297 if module.krate != self.krate {
298 let path = ModPath::from_segments(
299 PathKind::Super(0),
300 path.segments()[i..].iter().cloned(),
301 );
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(
306 def,
307 ReachedFixedPoint::Yes,
308 s.map(|s| s + i),
309 Some(module.krate),
310 );
311 }
312
313 let def_map;
314 let module_data = if module.block == self.block_id() {
315 &self[module.local_id]
316 } else {
317 def_map = module.def_map(db);
318 &def_map[module.local_id]
319 };
320
321 // Since it is a qualified path here, it should not contains legacy macros
322 module_data.scope.get(segment)
323 }
324 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
325 // enum variant
326 cov_mark::hit!(can_import_enum_variant);
327 let enum_data = db.enum_data(e);
328 match enum_data.variant(segment) {
329 Some(local_id) => {
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)
334 }
335 crate::adt::VariantData::Tuple(_)
336 | crate::adt::VariantData::Unit => {
337 PerNs::both(variant.into(), variant.into(), Visibility::Public)
338 }
339 }
340 }
341 None => {
342 return ResolvePathResult::with(
343 PerNs::types(e.into(), vis),
344 ReachedFixedPoint::Yes,
345 Some(i),
346 Some(self.krate),
347 );
348 }
349 }
350 }
351 s => {
352 // could be an inherent method call in UFCS form
353 // (`Struct::method`), or some other kind of associated item
354 tracing::debug!(
355 "path segment {:?} resolved to non-module {:?}, but is not last",
356 segment,
357 curr,
358 );
359
360 return ResolvePathResult::with(
361 PerNs::types(s, vis),
362 ReachedFixedPoint::Yes,
363 Some(i),
364 Some(self.krate),
365 );
366 }
367 };
368
369 curr_per_ns = curr_per_ns
370 .filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module));
371 }
372
373 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
374 }
375
376 fn resolve_name_in_module(
377 &self,
378 db: &dyn DefDatabase,
379 module: LocalModuleId,
380 name: &Name,
381 shadow: BuiltinShadowMode,
382 ) -> PerNs {
383 // Resolve in:
384 // - legacy scope of macro
385 // - current module / scope
386 // - extern prelude
387 // - std prelude
388 let from_legacy_macro = self[module]
389 .scope
390 .get_legacy_macro(name)
391 // FIXME: shadowing
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 {
396 Some(_) => {
397 // Only resolve to builtins in the root `DefMap`.
398 PerNs::none()
399 }
400 None => BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none),
401 };
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),
407 },
408 };
409
410 let extern_prelude = || {
411 self.extern_prelude
412 .get(name)
413 .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
414 };
415 let prelude = || self.resolve_in_prelude(db, name);
416
417 from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude)
418 }
419
420 fn resolve_name_in_crate_root_or_extern_prelude(
421 &self,
422 db: &dyn DefDatabase,
423 name: &Name,
424 ) -> PerNs {
425 let from_crate_root = match self.block {
426 Some(_) => {
427 let def_map = self.crate_root(db).def_map(db);
428 def_map[def_map.root].scope.get(name)
429 }
430 None => self[self.root].scope.get(name),
431 };
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))
435 };
436
437 from_crate_root.or_else(from_extern_prelude)
438 }
439
440 fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
441 if let Some(prelude) = self.prelude {
442 let keep;
443 let def_map = if prelude.krate == self.krate {
444 self
445 } else {
446 // Extend lifetime
447 keep = prelude.def_map(db);
448 &keep
449 };
450 def_map[prelude.local_id].scope.get(name)
451 } else {
452 PerNs::none()
453 }
454 }
455 }