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