Unnamed repository; edit this file 'description' to name the repository.
fix: shadow type by module
Signed-off-by: Hayashi Mikihiro <[email protected]>
Hayashi Mikihiro 2025-04-10
parent 0d64633 · commit 2854ad9
-rw-r--r--crates/hir-def/src/resolver.rs114
-rw-r--r--crates/hir-ty/src/lower/path.rs11
-rw-r--r--crates/hir/src/source_analyzer.rs84
-rw-r--r--crates/ide/src/goto_definition.rs144
4 files changed, 281 insertions, 72 deletions
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 4f1be7285c..10a8192b93 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -107,6 +107,12 @@ pub enum TypeNs {
// ModuleId(ModuleId)
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum ModuleOrTypeNs {
+ ModuleNs(ModuleId),
+ TypeNs(TypeNs),
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ResolveValueResult {
ValueNs(ValueNs, Option<ImportOrGlob>),
@@ -163,22 +169,33 @@ impl Resolver {
self.resolve_module_path(db, path, BuiltinShadowMode::Module)
}
- pub fn resolve_path_in_type_ns(
- &self,
- db: &dyn DefDatabase,
- path: &Path,
- ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
+ pub fn resolve_path_in_type_ns<'a>(
+ &'a self,
+ db: &'a dyn DefDatabase,
+ path: &'a Path,
+ ) -> impl Iterator<Item = (ModuleOrTypeNs, Option<usize>, Option<ImportOrExternCrate>)> + 'a
+ {
self.resolve_path_in_type_ns_with_prefix_info(db, path).map(
- |(resolution, remaining_segments, import, _)| (resolution, remaining_segments, import),
+ move |(resolution, remaining_segments, import, _)| {
+ (resolution, remaining_segments, import)
+ },
)
}
- pub fn resolve_path_in_type_ns_with_prefix_info(
- &self,
- db: &dyn DefDatabase,
- path: &Path,
- ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
- {
+ pub fn resolve_path_in_type_ns_with_prefix_info<'a>(
+ &'a self,
+ db: &'a dyn DefDatabase,
+ path: &'a Path,
+ ) -> Box<
+ dyn Iterator<
+ Item = (
+ ModuleOrTypeNs,
+ Option<usize>,
+ Option<ImportOrExternCrate>,
+ ResolvePathResultPrefixInfo,
+ ),
+ > + 'a,
+ > {
let path = match path {
Path::BarePath(mod_path) => mod_path,
Path::Normal(it) => &it.mod_path,
@@ -192,67 +209,73 @@ impl Resolver {
LangItemTarget::Trait(it) => TypeNs::TraitId(it),
LangItemTarget::Function(_)
| LangItemTarget::ImplDef(_)
- | LangItemTarget::Static(_) => return None,
+ | LangItemTarget::Static(_) => return Box::new(iter::empty()),
};
- return Some((
- type_ns,
+ return Box::new(iter::once((
+ ModuleOrTypeNs::TypeNs(type_ns),
seg.as_ref().map(|_| 1),
None,
ResolvePathResultPrefixInfo::default(),
- ));
+ )));
}
};
- let first_name = path.segments().first()?;
+ let Some(first_name) = path.segments().first() else { return Box::new(iter::empty()) };
let skip_to_mod = path.kind != PathKind::Plain;
if skip_to_mod {
- return self.module_scope.resolve_path_in_type_ns(db, path);
+ return Box::new(self.module_scope.resolve_path_in_type_ns(db, path).into_iter());
}
let remaining_idx = || {
if path.segments().len() == 1 { None } else { Some(1) }
};
- for scope in self.scopes() {
- match scope {
- Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
+ let ns = self
+ .scopes()
+ .filter_map(move |scope| match scope {
+ Scope::ExprScope(_) | Scope::MacroDefScope(_) => None,
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
return Some((
- TypeNs::GenericParam(id),
+ ModuleOrTypeNs::TypeNs(TypeNs::GenericParam(id)),
remaining_idx(),
None,
ResolvePathResultPrefixInfo::default(),
));
}
+ None
}
&Scope::ImplDefScope(impl_) => {
if *first_name == sym::Self_.clone() {
return Some((
- TypeNs::SelfType(impl_),
+ ModuleOrTypeNs::TypeNs(TypeNs::SelfType(impl_)),
remaining_idx(),
None,
ResolvePathResultPrefixInfo::default(),
));
}
+ None
}
&Scope::AdtScope(adt) => {
if *first_name == sym::Self_.clone() {
return Some((
- TypeNs::AdtSelfType(adt),
+ ModuleOrTypeNs::TypeNs(TypeNs::AdtSelfType(adt)),
remaining_idx(),
None,
ResolvePathResultPrefixInfo::default(),
));
}
+ None
}
Scope::BlockScope(m) => {
if let Some(res) = m.resolve_path_in_type_ns(db, path) {
return Some(res);
}
+ None
}
- }
- }
- self.module_scope.resolve_path_in_type_ns(db, path)
+ })
+ .chain(self.module_scope.resolve_path_in_type_ns(db, path));
+
+ Box::new(ns)
}
pub fn resolve_path_in_type_ns_fully(
@@ -260,7 +283,13 @@ impl Resolver {
db: &dyn DefDatabase,
path: &Path,
) -> Option<TypeNs> {
- let (res, unresolved, _) = self.resolve_path_in_type_ns(db, path)?;
+ let (res, unresolved) = self
+ .resolve_path_in_type_ns(db, path)
+ .filter_map(|(res, unresolved, _)| match res {
+ ModuleOrTypeNs::TypeNs(it) => Some((it, unresolved)),
+ ModuleOrTypeNs::ModuleNs(_) => None,
+ })
+ .next()?;
if unresolved.is_some() {
return None;
}
@@ -1158,8 +1187,12 @@ impl ModuleItemMap {
&self,
db: &dyn DefDatabase,
path: &ModPath,
- ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
- {
+ ) -> Option<(
+ ModuleOrTypeNs,
+ Option<usize>,
+ Option<ImportOrExternCrate>,
+ ResolvePathResultPrefixInfo,
+ )> {
let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally(
&self.local_def_map,
db,
@@ -1167,7 +1200,7 @@ impl ModuleItemMap {
path,
BuiltinShadowMode::Other,
);
- let (res, import) = to_type_ns(module_def)?;
+ let (res, import) = to_module_or_type_ns(module_def)?;
Some((res, idx, import, prefix_info))
}
}
@@ -1192,23 +1225,24 @@ fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportOrGlob>)> {
Some((res, import))
}
-fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
+fn to_module_or_type_ns(per_ns: PerNs) -> Option<(ModuleOrTypeNs, Option<ImportOrExternCrate>)> {
let def = per_ns.take_types_full()?;
let res = match def.def {
- ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
- ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
+ ModuleDefId::AdtId(it) => ModuleOrTypeNs::TypeNs(TypeNs::AdtId(it)),
+ ModuleDefId::EnumVariantId(it) => ModuleOrTypeNs::TypeNs(TypeNs::EnumVariantId(it)),
+
+ ModuleDefId::TypeAliasId(it) => ModuleOrTypeNs::TypeNs(TypeNs::TypeAliasId(it)),
+ ModuleDefId::BuiltinType(it) => ModuleOrTypeNs::TypeNs(TypeNs::BuiltinType(it)),
- ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
- ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
+ ModuleDefId::TraitId(it) => ModuleOrTypeNs::TypeNs(TypeNs::TraitId(it)),
+ ModuleDefId::TraitAliasId(it) => ModuleOrTypeNs::TypeNs(TypeNs::TraitAliasId(it)),
- ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
- ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
+ ModuleDefId::ModuleId(it) => ModuleOrTypeNs::ModuleNs(it),
ModuleDefId::FunctionId(_)
| ModuleDefId::ConstId(_)
| ModuleDefId::MacroId(_)
- | ModuleDefId::StaticId(_)
- | ModuleDefId::ModuleId(_) => return None,
+ | ModuleDefId::StaticId(_) => return None,
};
Some((res, def.import))
}
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs
index 0425670308..2064cad597 100644
--- a/crates/hir-ty/src/lower/path.rs
+++ b/crates/hir-ty/src/lower/path.rs
@@ -10,7 +10,7 @@ use hir_def::{
expr_store::HygieneId,
generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments},
- resolver::{ResolveValueResult, TypeNs, ValueNs},
+ resolver::{ModuleOrTypeNs, ResolveValueResult, TypeNs, ValueNs},
type_ref::{TypeBound, TypeRef, TypesMap},
};
use smallvec::SmallVec;
@@ -333,10 +333,15 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
}
pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option<usize>)> {
- let (resolution, remaining_index, _, prefix_info) = self
+ let (resolution, remaining_index, prefix_info) = self
.ctx
.resolver
- .resolve_path_in_type_ns_with_prefix_info(self.ctx.db.upcast(), self.path)?;
+ .resolve_path_in_type_ns_with_prefix_info(self.ctx.db.upcast(), self.path)
+ .filter_map(|(res, remaining_index, _, prefix_info)| match res {
+ ModuleOrTypeNs::TypeNs(type_ns) => Some((type_ns, remaining_index, prefix_info)),
+ ModuleOrTypeNs::ModuleNs(_) => None,
+ })
+ .next()?;
let segments = self.segments;
if segments.is_empty() || matches!(self.path, Path::LangItem(..)) {
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index ba5ceef00a..586e55f42a 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -25,7 +25,7 @@ use hir_def::{
lower::LowerCtx,
nameres::MacroSubNs,
path::{ModPath, Path, PathKind},
- resolver::{Resolver, TypeNs, ValueNs, resolver_for_scope},
+ resolver::{ModuleOrTypeNs, Resolver, TypeNs, ValueNs, resolver_for_scope},
type_ref::{Mutability, TypesMap, TypesSourceMap},
};
use hir_expand::{
@@ -1365,6 +1365,23 @@ pub(crate) fn resolve_hir_path_as_attr_macro(
.map(Into::into)
}
+fn resolve_path_in_module_or_type_ns(
+ db: &dyn HirDatabase,
+ resolver: &Resolver,
+ path: &Path,
+) -> Option<(ModuleOrTypeNs, Option<usize>)> {
+ let mut types = resolver
+ .resolve_path_in_type_ns(db.upcast(), path)
+ .map(|(ty, remaining_idx, _)| (ty, remaining_idx))
+ .peekable();
+ let (ty, _) = types.peek()?;
+ match ty {
+ ModuleOrTypeNs::ModuleNs(_) => types
+ .find_or_first(|(ty, _)| matches!(ty, ModuleOrTypeNs::TypeNs(TypeNs::BuiltinType(_)))),
+ ModuleOrTypeNs::TypeNs(_) => types.next(),
+ }
+}
+
fn resolve_hir_path_(
db: &dyn HirDatabase,
resolver: &Resolver,
@@ -1384,10 +1401,10 @@ fn resolve_hir_path_(
resolver.type_owner(),
)
.lower_ty_ext(type_ref);
- res.map(|ty_ns| (ty_ns, path.segments().first()))
+ res.map(|ty_ns| (ModuleOrTypeNs::TypeNs(ty_ns), path.segments().first()))
}
None => {
- let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
+ let (ty, remaining_idx) = resolve_path_in_module_or_type_ns(db, resolver, path)?;
match remaining_idx {
Some(remaining_idx) => {
if remaining_idx + 1 == path.segments().len() {
@@ -1403,25 +1420,30 @@ fn resolve_hir_path_(
// If we are in a TypeNs for a Trait, and we have an unresolved name, try to resolve it as a type
// within the trait's associated types.
- if let (Some(unresolved), &TypeNs::TraitId(trait_id)) = (&unresolved, &ty) {
+ if let (Some(unresolved), ModuleOrTypeNs::TypeNs(TypeNs::TraitId(trait_id))) =
+ (&unresolved, &ty)
+ {
if let Some(type_alias_id) =
- db.trait_items(trait_id).associated_type_by_name(unresolved.name)
+ db.trait_items(*trait_id).associated_type_by_name(unresolved.name)
{
return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into()));
}
}
let res = match ty {
- TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
- TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
- TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
- PathResolution::Def(Adt::from(it).into())
- }
- TypeNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()),
- TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
- TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
- TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
- TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()),
+ ModuleOrTypeNs::TypeNs(ty) => match ty {
+ TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
+ TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
+ TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
+ PathResolution::Def(Adt::from(it).into())
+ }
+ TypeNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()),
+ TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
+ TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
+ TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
+ TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()),
+ },
+ ModuleOrTypeNs::ModuleNs(it) => PathResolution::Def(ModuleDef::Module(it.into())),
};
match unresolved {
Some(unresolved) => resolver
@@ -1517,10 +1539,10 @@ fn resolve_hir_path_qualifier(
resolver.type_owner(),
)
.lower_ty_ext(type_ref);
- res.map(|ty_ns| (ty_ns, path.segments().first()))
+ res.map(|ty_ns| (ModuleOrTypeNs::TypeNs(ty_ns), path.segments().first()))
}
None => {
- let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
+ let (ty, remaining_idx) = resolve_path_in_module_or_type_ns(db, resolver, path)?;
match remaining_idx {
Some(remaining_idx) => {
if remaining_idx + 1 == path.segments().len() {
@@ -1536,25 +1558,29 @@ fn resolve_hir_path_qualifier(
// If we are in a TypeNs for a Trait, and we have an unresolved name, try to resolve it as a type
// within the trait's associated types.
- if let (Some(unresolved), &TypeNs::TraitId(trait_id)) = (&unresolved, &ty) {
+ if let (Some(unresolved), &ModuleOrTypeNs::TypeNs(TypeNs::TraitId(trait_id))) =
+ (&unresolved, &ty)
+ {
if let Some(type_alias_id) =
db.trait_items(trait_id).associated_type_by_name(unresolved.name)
{
return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into()));
}
}
-
let res = match ty {
- TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
- TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
- TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
- PathResolution::Def(Adt::from(it).into())
- }
- TypeNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()),
- TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
- TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
- TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
- TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()),
+ ModuleOrTypeNs::TypeNs(ty) => match ty {
+ TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
+ TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
+ TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
+ PathResolution::Def(Adt::from(it).into())
+ }
+ TypeNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()),
+ TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
+ TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
+ TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
+ TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()),
+ },
+ ModuleOrTypeNs::ModuleNs(it) => PathResolution::Def(ModuleDef::Module(it.into())),
};
match unresolved {
Some(unresolved) => resolver
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index fe8295ca2d..c39d6ff345 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -3326,4 +3326,148 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn struct_shadow_by_module() {
+ check(
+ r#"
+mod foo {
+ pub mod bar {
+ // ^^^
+ pub type baz = usize;
+ }
+}
+struct bar;
+fn main() {
+ use foo::bar;
+ let x: ba$0r::baz = 5;
+
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn type_alias_shadow_by_module() {
+ check(
+ r#"
+mod foo {
+ pub mod bar {
+ // ^^^
+ pub fn baz() {}
+ }
+}
+
+trait Qux {}
+
+fn item<bar: Qux>() {
+ use foo::bar;
+ ba$0r::baz();
+}
+}
+"#,
+ );
+
+ check(
+ r#"
+mod foo {
+ pub mod bar {
+ // ^^^
+ pub fn baz() {}
+ }
+}
+
+fn item<bar>(x: bar) {
+ use foo::bar;
+ let x: bar$0 = x;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn trait_shadow_by_module() {
+ check(
+ r#"
+pub mod foo {
+ pub mod Bar {}
+ // ^^^
+}
+
+trait Bar {}
+
+fn main() {
+ use foo::Bar;
+ fn f<Qux: B$0ar>() {}
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn const_shadow_by_module() {
+ check(
+ r#"
+pub mod foo {
+ pub struct u8 {}
+ pub mod bar {
+ pub mod u8 {}
+ }
+}
+
+fn main() {
+ use foo::u8;
+ {
+ use foo::bar::u8;
+
+ fn f1<const N: u$08>() {}
+ }
+ fn f2<const N: u8>() {}
+}
+"#,
+ );
+
+ check(
+ r#"
+pub mod foo {
+ pub struct u8 {}
+ // ^^
+ pub mod bar {
+ pub mod u8 {}
+ }
+}
+
+fn main() {
+ use foo::u8;
+ {
+ use foo::bar::u8;
+
+ fn f1<const N: u8>() {}
+ }
+ fn f2<const N: u$08>() {}
+}
+"#,
+ );
+
+ check(
+ r#"
+pub mod foo {
+ pub struct buz {}
+ pub mod bar {
+ pub mod buz {}
+ // ^^^
+ }
+}
+
+fn main() {
+ use foo::buz;
+ {
+ use foo::bar::buz;
+
+ fn f1<const N: buz$0>() {}
+ }
+}
+"#,
+ );
+ }
}