1use base_db::SourceDatabase;
14use either::Either;
15use hir_expand::{
16 mod_path::{ModPath, PathKind},
17 name::Name,
18};
19use span::Edition;
20use stdx::TupleExt;
21
22use crate::{
23 AdtId, ModuleDefId, ModuleId,
24 item_scope::{BUILTIN_SCOPE, ImportOrExternCrate},
25 nameres::{
26 BlockInfo, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, assoc::TraitItems,
27 crate_def_map, sub_namespace_match,
28 },
29 per_ns::PerNs,
30 visibility::{RawVisibility, Visibility},
31};
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub(super) enum ResolveMode {
35 Import,
36 Other,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub(super) enum ReachedFixedPoint {
41 Yes,
42 No,
43}
44
45#[derive(Debug, Clone)]
46pub(super) struct ResolvePathResult {
47 pub(super) resolved_def: PerNs,
48 pub(super) segment_index: Option<usize>,
50 pub(super) reached_fixedpoint: ReachedFixedPoint,
51 pub(super) prefix_info: ResolvePathResultPrefixInfo,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
55pub struct ResolvePathResultPrefixInfo {
56 pub(crate) differing_crate: bool,
57 pub enum_variant: bool,
59}
60
61impl ResolvePathResult {
62 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
63 ResolvePathResult::new(
64 PerNs::none(),
65 reached_fixedpoint,
66 None,
67 ResolvePathResultPrefixInfo::default(),
68 )
69 }
70
71 fn new(
72 resolved_def: PerNs,
73 reached_fixedpoint: ReachedFixedPoint,
74 segment_index: Option<usize>,
75 prefix_info: ResolvePathResultPrefixInfo,
76 ) -> ResolvePathResult {
77 ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, prefix_info }
78 }
79}
80
81impl PerNs {
82 pub(super) fn filter_macro(
83 mut self,
84 db: &dyn SourceDatabase,
85 expected: Option<MacroSubNs>,
86 ) -> Self {
87 self.macros = self.macros.filter(|def| sub_namespace_match(db, def.def, expected));
88
89 self
90 }
91}
92
93impl DefMap {
94 pub(crate) fn resolve_visibility(
95 &self,
96 local_def_map: &LocalDefMap,
97 db: &dyn SourceDatabase,
98 original_module: ModuleId,
100 visibility: &RawVisibility,
103 within_impl: bool,
104 ) -> Option<Visibility> {
105 let vis = match visibility {
106 RawVisibility::Module(path, explicitness) => {
107 let (result, remaining) = self.resolve_path(
108 local_def_map,
109 db,
110 original_module,
111 path,
112 BuiltinShadowMode::Module,
113 None,
114 );
115 if remaining.is_some() {
116 return None;
117 }
118 let types = result.take_types()?;
119 let mut vis = match types {
120 ModuleDefId::ModuleId(m) => Visibility::Module(m, *explicitness),
121 _ => {
123 return None;
124 }
125 };
126
127 if let Visibility::Module(m, mv) = vis {
131 if self.block_id() != m.block(db) && !within_impl {
133 vis = Visibility::Module(self.root, mv);
134 tracing::debug!(
135 "visibility {:?} points outside DefMap, adjusting to {:?}",
136 m,
137 vis
138 );
139 }
140 }
141 vis
142 }
143 RawVisibility::PubSelf(explicitness) => {
144 Visibility::Module(original_module, *explicitness)
145 }
146 RawVisibility::Public => Visibility::Public,
147 RawVisibility::PubCrate => Visibility::PubCrate(self.krate),
148 };
149 Some(vis)
150 }
151
152 pub(super) fn resolve_path_fp_with_macro(
155 &self,
156 local_def_map: &LocalDefMap,
157 db: &dyn SourceDatabase,
158 mode: ResolveMode,
159 mut original_module: ModuleId,
161 path: &ModPath,
162 shadow: BuiltinShadowMode,
163 expected_macro_subns: Option<MacroSubNs>,
166 ) -> ResolvePathResult {
167 let mut result = self.resolve_path_fp_with_macro_single(
168 local_def_map,
169 db,
170 mode,
171 original_module,
172 path,
173 shadow,
174 expected_macro_subns,
175 );
176
177 if self.block.is_none() {
178 return result;
180 }
181
182 let mut current_map = self;
183
184 let mut merge = |new: ResolvePathResult| {
185 result.resolved_def = result.resolved_def.or(new.resolved_def);
186 if result.reached_fixedpoint == ReachedFixedPoint::No {
187 result.reached_fixedpoint = new.reached_fixedpoint;
188 }
189 result.prefix_info.differing_crate |= new.prefix_info.differing_crate;
190 result.prefix_info.enum_variant |= new.prefix_info.enum_variant;
191 result.segment_index = match (result.segment_index, new.segment_index) {
192 (Some(idx), None) => Some(idx),
193 (Some(old), Some(new)) => Some(old.max(new)),
194 (None, new) => new,
195 };
196 };
197
198 loop {
199 match current_map.block {
200 Some(block) if original_module == current_map.root => {
201 original_module = block.parent;
203 current_map = block.parent.def_map(db);
204 }
205 _ => {
207 if original_module != current_map.root && current_map.block.is_some() {
208 original_module = current_map.root;
211 current_map = crate_def_map(db, self.krate);
212
213 let new = current_map.resolve_path_fp_in_all_preludes(
214 local_def_map,
215 db,
216 mode,
217 original_module,
218 path,
219 shadow,
220 );
221 merge(new);
222 }
223
224 return result;
225 }
226 }
227
228 let new = current_map.resolve_path_fp_with_macro_single(
229 local_def_map,
230 db,
231 mode,
232 original_module,
233 path,
234 shadow,
235 expected_macro_subns,
236 );
237
238 merge(new);
239 }
240 }
241
242 pub(super) fn resolve_path_fp_with_macro_single(
243 &self,
244 local_def_map: &LocalDefMap,
245 db: &dyn SourceDatabase,
246 mode: ResolveMode,
247 original_module: ModuleId,
248 path: &ModPath,
249 shadow: BuiltinShadowMode,
250 expected_macro_subns: Option<MacroSubNs>,
251 ) -> ResolvePathResult {
252 let mut segments = path.segments().iter().enumerate();
253 let curr_per_ns = match path.kind {
254 PathKind::DollarCrate(krate) => {
255 if krate == self.krate {
256 cov_mark::hit!(macro_dollar_crate_self);
257 PerNs::types(self.crate_root(db).into(), Visibility::Public, None)
258 } else {
259 let def_map = crate_def_map(db, krate);
260 let module = def_map.root;
261 cov_mark::hit!(macro_dollar_crate_other);
262 PerNs::types(module.into(), Visibility::Public, None)
263 }
264 }
265 PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public, None),
266 PathKind::Plain | PathKind::Abs
270 if self.data.edition == Edition::Edition2015
271 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
272 {
273 let (_, segment) = match segments.next() {
274 Some((idx, segment)) => (idx, segment),
275 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
276 };
277 tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
278 self.resolve_name_in_crate_root_or_extern_prelude(
279 local_def_map,
280 db,
281 original_module,
282 segment,
283 )
284 }
285 PathKind::Plain => {
286 let (_, segment) = match segments.next() {
287 Some((idx, segment)) => (idx, segment),
288 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
289 };
290 let prefer_module =
297 if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
298
299 tracing::debug!("resolving {:?} in module", segment);
300 self.resolve_name_in_module(
301 local_def_map,
302 db,
303 original_module,
304 segment,
305 prefer_module,
306 expected_macro_subns,
307 )
308 }
309 PathKind::Super(lvl) => {
310 let mut local_id = original_module;
311 let mut def_map = self;
312
313 (def_map, local_id) = adjust_to_nearest_non_block_module(db, def_map, local_id);
315
316 for _ in 0..lvl {
319 if let Some(parent) = def_map.modules[local_id].parent {
322 (def_map, local_id) =
323 adjust_to_nearest_non_block_module(db, def_map, parent);
324 } else {
325 stdx::always!(def_map.block.is_none());
326 tracing::debug!("super path in root module");
327 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
328 }
329 }
330
331 if self.block != def_map.block {
332 let path =
335 ModPath::from_segments(PathKind::SELF, path.segments().iter().cloned());
336 return def_map.resolve_path_fp_with_macro(
338 local_def_map,
339 db,
340 mode,
341 local_id,
342 &path,
343 shadow,
344 expected_macro_subns,
345 );
346 }
347
348 PerNs::types(local_id.into(), Visibility::Public, None)
349 }
350 PathKind::Abs => match self.resolve_path_abs(local_def_map, &mut segments, path) {
351 Either::Left(it) => it,
352 Either::Right(reached_fixed_point) => {
353 return ResolvePathResult::empty(reached_fixed_point);
354 }
355 },
356 };
357
358 self.resolve_remaining_segments(
359 db,
360 mode,
361 segments,
362 curr_per_ns,
363 path,
364 shadow,
365 original_module,
366 )
367 }
368
369 pub(super) fn resolve_path_fp_in_all_preludes(
371 &self,
372 local_def_map: &LocalDefMap,
373 db: &dyn SourceDatabase,
374 mode: ResolveMode,
375 original_module: ModuleId,
376 path: &ModPath,
377 shadow: BuiltinShadowMode,
378 ) -> ResolvePathResult {
379 let mut segments = path.segments().iter().enumerate();
380 let curr_per_ns = match path.kind {
381 PathKind::Plain | PathKind::Abs
385 if self.data.edition == Edition::Edition2015
386 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
387 {
388 let (_, segment) = match segments.next() {
389 Some((idx, segment)) => (idx, segment),
390 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
391 };
392 tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
393 self.resolve_name_in_extern_prelude(local_def_map, segment)
394 }
395 PathKind::Plain => {
396 let (_, segment) = match segments.next() {
397 Some((idx, segment)) => (idx, segment),
398 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
399 };
400 tracing::debug!("resolving {:?} in module", segment);
401 self.resolve_name_in_all_preludes(local_def_map, db, segment)
402 }
403 PathKind::Abs => match self.resolve_path_abs(local_def_map, &mut segments, path) {
404 Either::Left(it) => it,
405 Either::Right(reached_fixed_point) => {
406 return ResolvePathResult::empty(reached_fixed_point);
407 }
408 },
409 PathKind::DollarCrate(_) | PathKind::Crate | PathKind::Super(_) => {
410 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
411 }
412 };
413
414 self.resolve_remaining_segments(
415 db,
416 mode,
417 segments,
418 curr_per_ns,
419 path,
420 shadow,
421 original_module,
422 )
423 }
424
425 fn resolve_path_abs<'a>(
427 &self,
428 local_def_map: &LocalDefMap,
429 segments: &mut impl Iterator<Item = (usize, &'a Name)>,
430 path: &ModPath,
431 ) -> Either<PerNs, ReachedFixedPoint> {
432 let segment = match segments.next() {
433 Some((_, segment)) => segment,
434 None => return Either::Right(ReachedFixedPoint::Yes),
435 };
436 if let Some(&(def, extern_crate)) = local_def_map.extern_prelude.get(segment) {
437 tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
438 Either::Left(PerNs::types(
439 def.into(),
440 Visibility::Public,
441 extern_crate.map(ImportOrExternCrate::ExternCrate),
442 ))
443 } else {
444 Either::Right(ReachedFixedPoint::No) }
446 }
447
448 fn resolve_remaining_segments<'a>(
449 &self,
450 db: &dyn SourceDatabase,
451 mode: ResolveMode,
452 mut segments: impl Iterator<Item = (usize, &'a Name)>,
453 mut curr_per_ns: PerNs,
454 path: &ModPath,
455 shadow: BuiltinShadowMode,
456 original_module: ModuleId,
457 ) -> ResolvePathResult {
458 while let Some((i, segment)) = segments.next() {
459 let curr = match curr_per_ns.take_types_full() {
460 Some(r) => r,
461 None => {
462 return ResolvePathResult::empty(ReachedFixedPoint::No);
468 }
469 };
470 curr_per_ns = match curr.def {
473 ModuleDefId::ModuleId(module) => {
474 if module.krate(db) != self.krate {
475 let path = ModPath::from_segments(
477 PathKind::SELF,
478 path.segments()[i..].iter().cloned(),
479 );
480 tracing::debug!("resolving {:?} in other crate", path);
481 let defp_map = module.def_map(db);
482 let resolution = defp_map.resolve_path_fp_with_macro(
487 LocalDefMap::EMPTY,
488 db,
489 mode,
490 module,
491 &path,
492 shadow,
493 None,
494 );
495 return ResolvePathResult::new(
496 resolution.resolved_def,
497 ReachedFixedPoint::Yes,
498 resolution.segment_index.map(|s| s + i),
499 ResolvePathResultPrefixInfo {
500 differing_crate: true,
501 enum_variant: resolution.prefix_info.enum_variant,
502 },
503 );
504 }
505
506 let module_data = if module.block(db) == self.block_id() {
507 &self[module]
508 } else {
509 &module.def_map(db)[module]
510 };
511
512 module_data.scope.get(segment)
514 }
515 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
516 cov_mark::hit!(can_import_enum_variant);
518
519 let res = e.enum_variants(db).variants.get(segment).map(|&(variant, shape)| {
520 if shape.has_value_ns_ctor() {
521 PerNs::both(variant.into(), variant.into(), curr.vis, None)
522 } else {
523 PerNs::types(variant.into(), curr.vis, None)
524 }
525 });
526 return match res {
528 Some(res) => {
529 if segments.next().is_some() {
530 ResolvePathResult::empty(ReachedFixedPoint::No)
532 } else {
533 ResolvePathResult::new(
534 res,
535 ReachedFixedPoint::Yes,
536 None,
537 ResolvePathResultPrefixInfo {
538 enum_variant: true,
539 ..ResolvePathResultPrefixInfo::default()
540 },
541 )
542 }
543 }
544 None => ResolvePathResult::new(
545 PerNs::types(e.into(), curr.vis, curr.import),
546 ReachedFixedPoint::Yes,
547 Some(i),
548 ResolvePathResultPrefixInfo::default(),
549 ),
550 };
551 }
552 def @ ModuleDefId::TraitId(t) if mode == ResolveMode::Import => {
553 let item = if true {
560 None
561 } else {
562 TraitItems::query(db, t).assoc_item_by_name(segment)
563 };
564 return match item {
565 Some(item) => ResolvePathResult::new(
566 match item {
567 crate::AssocItemId::FunctionId(function_id) => PerNs::values(
568 function_id.into(),
569 curr.vis,
570 curr.import.and_then(|it| it.import_or_glob()),
571 ),
572 crate::AssocItemId::ConstId(const_id) => PerNs::values(
573 const_id.into(),
574 curr.vis,
575 curr.import.and_then(|it| it.import_or_glob()),
576 ),
577 crate::AssocItemId::TypeAliasId(type_alias_id) => {
578 PerNs::types(type_alias_id.into(), curr.vis, curr.import)
579 }
580 },
581 ReachedFixedPoint::Yes,
582 segments.next().map(TupleExt::head),
583 ResolvePathResultPrefixInfo::default(),
584 ),
585 None => ResolvePathResult::new(
586 PerNs::types(def, curr.vis, curr.import),
587 ReachedFixedPoint::Yes,
588 Some(i),
589 ResolvePathResultPrefixInfo::default(),
590 ),
591 };
592 }
593 s => {
594 tracing::debug!(
597 "path segment {:?} resolved to non-module {:?}, but is not last",
598 segment,
599 curr,
600 );
601
602 return ResolvePathResult::new(
603 PerNs::types(s, curr.vis, curr.import),
604 ReachedFixedPoint::Yes,
605 Some(i),
606 ResolvePathResultPrefixInfo::default(),
607 );
608 }
609 };
610
611 curr_per_ns = curr_per_ns
612 .filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module));
613 }
614
615 ResolvePathResult::new(
616 curr_per_ns,
617 ReachedFixedPoint::Yes,
618 None,
619 ResolvePathResultPrefixInfo::default(),
620 )
621 }
622
623 fn resolve_name_in_module(
624 &self,
625 local_def_map: &LocalDefMap,
626 db: &dyn SourceDatabase,
627 module: ModuleId,
628 name: &Name,
629 shadow: BuiltinShadowMode,
630 expected_macro_subns: Option<MacroSubNs>,
631 ) -> PerNs {
632 let from_legacy_macro = self[module]
638 .scope
639 .get_legacy_macro(name)
640 .and_then(|it| it.last())
642 .copied()
643 .filter(|&id| sub_namespace_match(db, id, expected_macro_subns))
644 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public, None));
645 let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
646 let from_builtin = match self.block {
647 Some(_) => {
648 PerNs::none()
650 }
651 None => BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none),
652 };
653 let from_scope_or_builtin = match shadow {
654 BuiltinShadowMode::Module => from_scope.or(from_builtin),
655 BuiltinShadowMode::Other => match from_scope.take_types() {
656 Some(ModuleDefId::ModuleId(_)) => from_builtin.or(from_scope),
657 Some(_) | None => from_scope.or(from_builtin),
658 },
659 };
660
661 let extern_prelude = || {
662 if self.block.is_some() && module == self.root {
663 return PerNs::none();
666 }
667 self.resolve_name_in_extern_prelude(local_def_map, name)
668 };
669 let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
670 let prelude = || {
671 if self.block.is_some() && module == self.root {
672 return PerNs::none();
673 }
674 self.resolve_in_prelude(db, name)
675 };
676
677 from_legacy_macro
678 .or(from_scope_or_builtin)
679 .or_else(extern_prelude)
680 .or_else(macro_use_prelude)
681 .or_else(prelude)
682 }
683
684 fn resolve_name_in_all_preludes(
685 &self,
686 local_def_map: &LocalDefMap,
687 db: &dyn SourceDatabase,
688 name: &Name,
689 ) -> PerNs {
690 let extern_prelude = self.resolve_name_in_extern_prelude(local_def_map, name);
694 let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
695 let prelude = || self.resolve_in_prelude(db, name);
696
697 extern_prelude.or_else(macro_use_prelude).or_else(prelude)
698 }
699
700 fn resolve_name_in_extern_prelude(&self, local_def_map: &LocalDefMap, name: &Name) -> PerNs {
701 local_def_map.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
702 PerNs::types(
703 it.into(),
704 Visibility::Public,
705 extern_crate.map(ImportOrExternCrate::ExternCrate),
706 )
707 })
708 }
709
710 fn resolve_in_macro_use_prelude(&self, name: &Name) -> PerNs {
711 self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
712 PerNs::macros(
713 it,
714 Visibility::Public,
715 extern_crate.map(ImportOrExternCrate::ExternCrate),
716 )
717 })
718 }
719
720 fn resolve_name_in_crate_root_or_extern_prelude(
721 &self,
722 local_def_map: &LocalDefMap,
723 db: &dyn SourceDatabase,
724 module: ModuleId,
725 name: &Name,
726 ) -> PerNs {
727 let from_crate_root = match self.block {
728 Some(_) => {
729 let def_map = self.crate_root(db).def_map(db);
730 def_map[def_map.root].scope.get(name)
731 }
732 None => self[self.root].scope.get(name),
733 };
734 let from_extern_prelude = || {
735 if self.block.is_some() && module == self.root {
736 return PerNs::none();
738 }
739 self.resolve_name_in_extern_prelude(local_def_map, name)
740 };
741
742 from_crate_root.or_else(from_extern_prelude)
743 }
744
745 fn resolve_in_prelude(&self, db: &dyn SourceDatabase, name: &Name) -> PerNs {
746 if let Some((prelude, _use)) = self.prelude {
747 let def_map = if prelude.krate(db) == self.krate { self } else { prelude.def_map(db) };
748 def_map[prelude].scope.get(name)
749 } else {
750 PerNs::none()
751 }
752 }
753}
754
755#[inline]
757fn adjust_to_nearest_non_block_module<'db>(
758 db: &'db dyn SourceDatabase,
759 mut def_map: &'db DefMap,
760 mut local_id: ModuleId,
761) -> (&'db DefMap, ModuleId) {
762 if def_map.root_module_id() != local_id {
763 return (def_map, local_id);
766 }
767 while let Some(BlockInfo { parent, .. }) = def_map.block {
768 def_map = parent.def_map(db);
769 local_id = parent;
770 if def_map.root_module_id() != local_id {
771 return (def_map, local_id);
772 }
773 }
774 (def_map, local_id)
775}