diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index d9495d36c0..86eac6e97c 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -23,12 +23,29 @@ pub fn find_path( db: &dyn DefDatabase, item: ItemInNs, from: ModuleId, - prefix_kind: PrefixKind, + mut prefix_kind: PrefixKind, ignore_local_imports: bool, - cfg: ImportPathConfig, + mut cfg: ImportPathConfig, ) -> Option { let _p = tracing::span!(tracing::Level::INFO, "find_path").entered(); - find_path_inner(FindPathCtx { db, prefix: prefix_kind, cfg, ignore_local_imports }, item, from) + + // - if the item is a builtin, it's in scope + if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item { + return Some(ModPath::from_segments(PathKind::Plain, iter::once(builtin.as_name()))); + } + + // within block modules, forcing a `self` or `crate` prefix will not allow using inner items, so + // default to plain paths. + if item.module(db).is_some_and(ModuleId::is_within_block) { + prefix_kind = PrefixKind::Plain; + } + cfg.prefer_no_std = cfg.prefer_no_std || db.crate_supports_no_std(from.krate()); + + find_path_inner( + &FindPathCtx { db, prefix: prefix_kind, cfg, ignore_local_imports, from }, + item, + MAX_PATH_LEN, + ) } #[derive(Copy, Clone, Debug)] @@ -63,79 +80,52 @@ impl PrefixKind { #[inline] fn path_kind(self) -> PathKind { match self { - PrefixKind::BySelf => PathKind::Super(0), + PrefixKind::BySelf => PathKind::SELF, PrefixKind::Plain => PathKind::Plain, PrefixKind::ByCrate => PathKind::Crate, } } } -#[derive(Copy, Clone)] struct FindPathCtx<'db> { db: &'db dyn DefDatabase, prefix: PrefixKind, cfg: ImportPathConfig, ignore_local_imports: bool, + from: ModuleId, } /// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId -fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Option { - // - if the item is a builtin, it's in scope - if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item { - return Some(ModPath::from_segments(PathKind::Plain, iter::once(builtin.as_name()))); - } - - let def_map = from.def_map(ctx.db); - let crate_root = from.derive_crate_root(); +fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option { + let def_map = ctx.from.def_map(ctx.db); // - if the item is a module, jump straight to module search if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { let mut visited_modules = FxHashSet::default(); - return find_path_for_module( - FindPathCtx { - cfg: ImportPathConfig { - prefer_no_std: ctx.cfg.prefer_no_std - || ctx.db.crate_supports_no_std(crate_root.krate), - ..ctx.cfg - }, - ..ctx - }, - &def_map, - &mut visited_modules, - from, - module_id, - MAX_PATH_LEN, - ) - .map(|(item, _)| item); + return find_path_for_module(ctx, &def_map, &mut visited_modules, module_id, max_len) + .map(|(item, _)| item); } - let prefix = if item.module(ctx.db).is_some_and(|it| it.is_within_block()) { - PrefixKind::Plain - } else { - ctx.prefix - }; - let may_be_in_scope = match prefix { + let may_be_in_scope = match ctx.prefix { PrefixKind::Plain | PrefixKind::BySelf => true, - PrefixKind::ByCrate => from.is_crate_root(), + PrefixKind::ByCrate => ctx.from.is_crate_root(), }; if may_be_in_scope { // - if the item is already in scope, return the name under which it is - let scope_name = find_in_scope(ctx.db, &def_map, from, item, ctx.ignore_local_imports); + let scope_name = find_in_scope(ctx.db, &def_map, ctx.from, item, ctx.ignore_local_imports); if let Some(scope_name) = scope_name { - return Some(ModPath::from_segments(prefix.path_kind(), iter::once(scope_name))); + return Some(ModPath::from_segments(ctx.prefix.path_kind(), iter::once(scope_name))); } } // - if the item is in the prelude, return the name from there - if let value @ Some(_) = - find_in_prelude(ctx.db, &crate_root.def_map(ctx.db), &def_map, item, from) - { - return value; + if let Some(value) = find_in_prelude(ctx.db, &def_map, item, ctx.from) { + return Some(value); } if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { // - if the item is an enum variant, refer to it via the enum if let Some(mut path) = - find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), from) + find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len) { path.push_segment(ctx.db.enum_variant_data(variant).name.clone()); return Some(path); @@ -147,30 +137,14 @@ fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Opti let mut visited_modules = FxHashSet::default(); - calculate_best_path( - FindPathCtx { - cfg: ImportPathConfig { - prefer_no_std: ctx.cfg.prefer_no_std - || ctx.db.crate_supports_no_std(crate_root.krate), - ..ctx.cfg - }, - ..ctx - }, - &def_map, - &mut visited_modules, - MAX_PATH_LEN, - item, - from, - ) - .map(|(item, _)| item) + calculate_best_path(ctx, &def_map, &mut visited_modules, item, max_len).map(|(item, _)| item) } #[tracing::instrument(skip_all)] fn find_path_for_module( - ctx: FindPathCtx<'_>, + ctx: &FindPathCtx<'_>, def_map: &DefMap, visited_modules: &mut FxHashSet, - from: ModuleId, module_id: ModuleId, max_len: usize, ) -> Option<(ModPath, Stability)> { @@ -180,20 +154,20 @@ fn find_path_for_module( let is_crate_root = module_id.as_crate_root(); // - if the item is the crate root, return `crate` - if is_crate_root.is_some_and(|it| it == from.derive_crate_root()) { + if is_crate_root == Some(ctx.from.derive_crate_root()) { return Some((ModPath::from_segments(PathKind::Crate, None), Stable)); } - let root_def_map = from.derive_crate_root().def_map(ctx.db); // - if the item is the crate root of a dependency crate, return the name from the extern prelude if let Some(crate_root) = is_crate_root { + let root_def_map = ctx.from.derive_crate_root().def_map(ctx.db); // rev here so we prefer looking at renamed extern decls first for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude().rev() { if crate_root != def_id { continue; } let name_already_occupied_in_type_ns = def_map - .with_ancestor_maps(ctx.db, from.local_id, &mut |def_map, local_id| { + .with_ancestor_maps(ctx.db, ctx.from.local_id, &mut |def_map, local_id| { def_map[local_id] .scope .type_(name) @@ -209,30 +183,30 @@ fn find_path_for_module( return Some((ModPath::from_segments(kind, iter::once(name.clone())), Stable)); } } - let prefix = if module_id.is_within_block() { PrefixKind::Plain } else { ctx.prefix }; - let may_be_in_scope = match prefix { + + let may_be_in_scope = match ctx.prefix { PrefixKind::Plain | PrefixKind::BySelf => true, - PrefixKind::ByCrate => from.is_crate_root(), + PrefixKind::ByCrate => ctx.from.is_crate_root(), }; if may_be_in_scope { let scope_name = find_in_scope( ctx.db, def_map, - from, + ctx.from, ItemInNs::Types(module_id.into()), ctx.ignore_local_imports, ); if let Some(scope_name) = scope_name { // - if the item is already in scope, return the name under which it is return Some(( - ModPath::from_segments(prefix.path_kind(), iter::once(scope_name)), + ModPath::from_segments(ctx.prefix.path_kind(), iter::once(scope_name)), Stable, )); } } // - if the module can be referenced as self, super or crate, do that - if let Some(mod_path) = is_kw_kind_relative_to_from(def_map, module_id, from) { + if let Some(mod_path) = is_kw_kind_relative_to_from(def_map, module_id, ctx.from) { if ctx.prefix != PrefixKind::ByCrate || mod_path.kind == PathKind::Crate { return Some((mod_path, Stable)); } @@ -240,18 +214,11 @@ fn find_path_for_module( // - if the module is in the prelude, return it by that path if let Some(mod_path) = - find_in_prelude(ctx.db, &root_def_map, def_map, ItemInNs::Types(module_id.into()), from) + find_in_prelude(ctx.db, def_map, ItemInNs::Types(module_id.into()), ctx.from) { return Some((mod_path, Stable)); } - calculate_best_path( - ctx, - def_map, - visited_modules, - max_len, - ItemInNs::Types(module_id.into()), - from, - ) + calculate_best_path(ctx, def_map, visited_modules, ItemInNs::Types(module_id.into()), max_len) } // FIXME: Do we still need this now that we record import origins, and hence aliases? @@ -274,12 +241,11 @@ fn find_in_scope( /// name doesn't clash in current scope. fn find_in_prelude( db: &dyn DefDatabase, - root_def_map: &DefMap, local_def_map: &DefMap, item: ItemInNs, from: ModuleId, ) -> Option { - let (prelude_module, _) = root_def_map.prelude()?; + let (prelude_module, _) = local_def_map.prelude()?; // Preludes in block DefMaps are ignored, only the crate DefMap is searched let prelude_def_map = prelude_module.def_map(db); let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; @@ -319,7 +285,7 @@ fn is_kw_kind_relative_to_from( let from = from.local_id; if item == from { // - if the item is the module we're in, use `self` - Some(ModPath::from_segments(PathKind::Super(0), None)) + Some(ModPath::from_segments(PathKind::SELF, None)) } else if let Some(parent_id) = def_map[from].parent { if item == parent_id { // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) @@ -337,12 +303,11 @@ fn is_kw_kind_relative_to_from( #[tracing::instrument(skip_all)] fn calculate_best_path( - ctx: FindPathCtx<'_>, + ctx: &FindPathCtx<'_>, def_map: &DefMap, visited_modules: &mut FxHashSet, - max_len: usize, item: ItemInNs, - from: ModuleId, + max_len: usize, ) -> Option<(ModPath, Stability)> { if max_len <= 1 { return None; @@ -356,24 +321,21 @@ fn calculate_best_path( } None => *best_path = Some(new_path), }; - // Recursive case: - // - otherwise, look for modules containing (reexporting) it and import it from one of those - if item.krate(ctx.db) == Some(from.krate) { + + if item.krate(ctx.db) == Some(ctx.from.krate) { let mut best_path_len = max_len; // Item was defined in the same crate that wants to import it. It cannot be found in any // dependency in this case. - for (module_id, name) in find_local_import_locations(ctx.db, item, from) { + // FIXME: cache the `find_local_import_locations` output? + for (module_id, name) in find_local_import_locations(ctx.db, item, ctx.from) { if !visited_modules.insert(module_id) { continue; } - if let Some(mut path) = find_path_for_module( - ctx, - def_map, - visited_modules, - from, - module_id, - best_path_len - 1, - ) { + // we are looking for paths of length up to best_path_len, any longer will make it be + // less optimal. The -1 is due to us pushing name onto it afterwards. + if let Some(mut path) = + find_path_for_module(ctx, def_map, visited_modules, module_id, best_path_len - 1) + { path.0.push_segment(name); let new_path = match best_path.take() { @@ -389,7 +351,7 @@ fn calculate_best_path( // too (unless we can't name it at all). It could *also* be (re)exported by the same crate // that wants to import it here, but we always prefer to use the external path here. - for dep in &ctx.db.crate_graph()[from.krate].dependencies { + for dep in &ctx.db.crate_graph()[ctx.from.krate].dependencies { let import_map = ctx.db.import_map(dep.crate_id); let Some(import_info_for) = import_map.import_info_for(item) else { continue }; for info in import_info_for { @@ -400,14 +362,15 @@ fn calculate_best_path( // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? - let Some((mut path, path_stability)) = find_path_for_module( + let path = find_path_for_module( ctx, def_map, visited_modules, - from, info.container, max_len - 1, - ) else { + // fixme shouldnt we consider the best path length here? + ); + let Some((mut path, path_stability)) = path else { continue; }; cov_mark::hit!(partially_imported); @@ -633,15 +596,13 @@ mod tests { .into_iter() .cartesian_product([false, true]) { - let found_path = find_path_inner( - FindPathCtx { - db: &db, - prefix, - cfg: ImportPathConfig { prefer_no_std: false, prefer_prelude }, - ignore_local_imports, - }, + let found_path = find_path( + &db, resolved, module, + prefix, + ignore_local_imports, + ImportPathConfig { prefer_no_std: false, prefer_prelude }, ); format_to!( res, diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index acda64c41f..52147e4a54 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -242,11 +242,11 @@ impl ItemVisibilities { match &vis { RawVisibility::Public => RawVisibilityId::PUB, RawVisibility::Module(path, explicitiy) if path.segments().is_empty() => { - match (&path.kind, explicitiy) { - (PathKind::Super(0), VisibilityExplicitness::Explicit) => { + match (path.kind, explicitiy) { + (PathKind::SELF, VisibilityExplicitness::Explicit) => { RawVisibilityId::PRIV_EXPLICIT } - (PathKind::Super(0), VisibilityExplicitness::Implicit) => { + (PathKind::SELF, VisibilityExplicitness::Implicit) => { RawVisibilityId::PRIV_IMPLICIT } (PathKind::Crate, _) => RawVisibilityId::PUB_CRATE, @@ -586,11 +586,11 @@ impl Index for ItemTree { fn index(&self, index: RawVisibilityId) -> &Self::Output { static VIS_PUB: RawVisibility = RawVisibility::Public; static VIS_PRIV_IMPLICIT: RawVisibility = RawVisibility::Module( - ModPath::from_kind(PathKind::Super(0)), + ModPath::from_kind(PathKind::SELF), VisibilityExplicitness::Implicit, ); static VIS_PRIV_EXPLICIT: RawVisibility = RawVisibility::Module( - ModPath::from_kind(PathKind::Super(0)), + ModPath::from_kind(PathKind::SELF), VisibilityExplicitness::Explicit, ); static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module( @@ -928,7 +928,7 @@ impl UseTree { _ => None, } } - (Some(prefix), PathKind::Super(0)) if path.segments().is_empty() => { + (Some(prefix), PathKind::SELF) if path.segments().is_empty() => { // `some::path::self` == `some::path` Some((prefix, ImportKind::TypeOnly)) } diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 682d169adb..b86703c3cb 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -396,6 +396,23 @@ impl PartialEq for CrateRootModuleId { other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate } } +impl PartialEq for ModuleId { + fn eq(&self, other: &CrateRootModuleId) -> bool { + other == self + } +} + +impl From for ModuleId { + fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self { + ModuleId { krate, block: None, local_id: DefMap::ROOT } + } +} + +impl From for ModuleDefId { + fn from(value: CrateRootModuleId) -> Self { + ModuleDefId::ModuleId(value.into()) + } +} impl From for CrateRootModuleId { fn from(krate: CrateId) -> Self { @@ -472,6 +489,7 @@ impl ModuleId { self.block.is_some() } + /// Returns the [`CrateRootModuleId`] for this module if it is the crate root module. pub fn as_crate_root(&self) -> Option { if self.local_id == DefMap::ROOT && self.block.is_none() { Some(CrateRootModuleId { krate: self.krate }) @@ -480,33 +498,17 @@ impl ModuleId { } } + /// Returns the [`CrateRootModuleId`] for this module. pub fn derive_crate_root(&self) -> CrateRootModuleId { CrateRootModuleId { krate: self.krate } } + /// Whether this module represents the crate root module fn is_crate_root(&self) -> bool { self.local_id == DefMap::ROOT && self.block.is_none() } } -impl PartialEq for ModuleId { - fn eq(&self, other: &CrateRootModuleId) -> bool { - other == self - } -} - -impl From for ModuleId { - fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self { - ModuleId { krate, block: None, local_id: DefMap::ROOT } - } -} - -impl From for ModuleDefId { - fn from(value: CrateRootModuleId) -> Self { - ModuleDefId::ModuleId(value.into()) - } -} - /// An ID of a module, **local** to a `DefMap`. pub type LocalModuleId = Idx; diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index d621f3a360..863ccec885 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -283,7 +283,7 @@ impl DefMap { // If we have a different `DefMap` from `self` (the original `DefMap` we started // with), resolve the remaining path segments in that `DefMap`. let path = - ModPath::from_segments(PathKind::Super(0), path.segments().iter().cloned()); + ModPath::from_segments(PathKind::SELF, path.segments().iter().cloned()); return def_map.resolve_path_fp_with_macro( db, mode, @@ -333,7 +333,7 @@ impl DefMap { ModuleDefId::ModuleId(module) => { if module.krate != self.krate { let path = ModPath::from_segments( - PathKind::Super(0), + PathKind::SELF, path.segments()[i..].iter().cloned(), ); tracing::debug!("resolving {:?} in other crate", path); diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs index 6af5261411..2b555b3998 100644 --- a/crates/hir-def/src/path/lower.rs +++ b/crates/hir-def/src/path/lower.rs @@ -122,7 +122,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option, mut path: ast::Path) -> Option match path.kind() { PathKind::Plain => {} - PathKind::Super(0) => write!(buf, "self")?, + &PathKind::SELF => write!(buf, "self")?, PathKind::Super(n) => { for i in 0..*n { if i == 0 { diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs index 1ef8fa772a..e08718fc83 100644 --- a/crates/hir-def/src/visibility.rs +++ b/crates/hir-def/src/visibility.rs @@ -27,10 +27,7 @@ pub enum RawVisibility { impl RawVisibility { pub(crate) const fn private() -> RawVisibility { - RawVisibility::Module( - ModPath::from_kind(PathKind::Super(0)), - VisibilityExplicitness::Implicit, - ) + RawVisibility::Module(ModPath::from_kind(PathKind::SELF), VisibilityExplicitness::Implicit) } pub(crate) fn from_ast( @@ -60,7 +57,7 @@ impl RawVisibility { } ast::VisibilityKind::PubCrate => ModPath::from_kind(PathKind::Crate), ast::VisibilityKind::PubSuper => ModPath::from_kind(PathKind::Super(1)), - ast::VisibilityKind::PubSelf => ModPath::from_kind(PathKind::Super(0)), + ast::VisibilityKind::PubSelf => ModPath::from_kind(PathKind::SELF), ast::VisibilityKind::Pub => return RawVisibility::Public, }; RawVisibility::Module(path, VisibilityExplicitness::Explicit) diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 46f8c2b9d8..12fdf88a2a 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -44,6 +44,10 @@ pub enum PathKind { DollarCrate(CrateId), } +impl PathKind { + pub const SELF: PathKind = PathKind::Super(0); +} + impl ModPath { pub fn from_src( db: &dyn ExpandDatabase, @@ -96,7 +100,7 @@ impl ModPath { pub fn textual_len(&self) -> usize { let base = match self.kind { PathKind::Plain => 0, - PathKind::Super(0) => "self".len(), + PathKind::SELF => "self".len(), PathKind::Super(i) => "super".len() * i as usize, PathKind::Crate => "crate".len(), PathKind::Abs => 0, @@ -113,7 +117,7 @@ impl ModPath { } pub fn is_self(&self) -> bool { - self.kind == PathKind::Super(0) && self.segments.is_empty() + self.kind == PathKind::SELF && self.segments.is_empty() } #[allow(non_snake_case)] @@ -193,7 +197,7 @@ fn display_fmt_path( }; match path.kind { PathKind::Plain => {} - PathKind::Super(0) => add_segment("self")?, + PathKind::SELF => add_segment("self")?, PathKind::Super(n) => { for _ in 0..n { add_segment("super")?; @@ -316,7 +320,7 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option { resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate) } - tt::Leaf::Ident(tt::Ident { text, .. }) if text == "self" => PathKind::Super(0), + tt::Leaf::Ident(tt::Ident { text, .. }) if text == "self" => PathKind::SELF, tt::Leaf::Ident(tt::Ident { text, .. }) if text == "super" => { let mut deg = 1; while let Some(tt::Leaf::Ident(tt::Ident { text, .. })) = leaves.next() { diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 0027eb56a8..f035dd11e1 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1942,7 +1942,7 @@ impl HirDisplay for Path { (_, PathKind::Plain) => {} (_, PathKind::Abs) => {} (_, PathKind::Crate) => write!(f, "crate")?, - (_, PathKind::Super(0)) => write!(f, "self")?, + (_, &PathKind::SELF) => write!(f, "self")?, (_, PathKind::Super(n)) => { for i in 0..*n { if i > 0 { diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index c7502890ef..7b3ff7b064 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -307,7 +307,7 @@ fn doc_modpath_from_str(link: &str) -> Option { let kind = match parts.next()? { "" => PathKind::Abs, "crate" => PathKind::Crate, - "self" => PathKind::Super(0), + "self" => PathKind::SELF, "super" => { let mut deg = 1; for segment in parts.by_ref() { diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index db44b1e723..063e366d4b 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs @@ -41,7 +41,7 @@ pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { let mut is_abs = false; match path.kind { hir::PathKind::Plain => {} - hir::PathKind::Super(0) => segments.push(make::path_segment_self()), + hir::PathKind::SELF => segments.push(make::path_segment_self()), hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())), hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { segments.push(make::path_segment_crate())