Unnamed repository; edit this file 'description' to name the repository.
Fix a bug where enum variants were not considered properly in type ns resolution
They should be considered just as well as in value ns, for example for struct literals.
Chayim Refael Friedman 2025-01-19
parent 248bd51 · commit 044c831
-rw-r--r--crates/hir-def/src/resolver.rs58
-rw-r--r--crates/hir-ty/src/lower.rs33
-rw-r--r--crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs19
3 files changed, 81 insertions, 29 deletions
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index df48ce6737..0b9b6da8d5 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -166,6 +166,17 @@ impl Resolver {
db: &dyn DefDatabase,
path: &Path,
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
+ self.resolve_path_in_type_ns_with_prefix_info(db, path).map(
+ |(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)>
+ {
let path = match path {
Path::BarePath(mod_path) => mod_path,
Path::Normal(it) => it.mod_path(),
@@ -181,7 +192,12 @@ impl Resolver {
| LangItemTarget::ImplDef(_)
| LangItemTarget::Static(_) => return None,
};
- return Some((type_ns, seg.as_ref().map(|_| 1), None));
+ return Some((
+ type_ns,
+ seg.as_ref().map(|_| 1),
+ None,
+ ResolvePathResultPrefixInfo::default(),
+ ));
}
};
let first_name = path.segments().first()?;
@@ -197,17 +213,32 @@ impl Resolver {
Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
- return Some((TypeNs::GenericParam(id), remaining_idx(), None));
+ return Some((
+ TypeNs::GenericParam(id),
+ remaining_idx(),
+ None,
+ ResolvePathResultPrefixInfo::default(),
+ ));
}
}
&Scope::ImplDefScope(impl_) => {
if *first_name == sym::Self_.clone() {
- return Some((TypeNs::SelfType(impl_), remaining_idx(), None));
+ return Some((
+ TypeNs::SelfType(impl_),
+ remaining_idx(),
+ None,
+ ResolvePathResultPrefixInfo::default(),
+ ));
}
}
&Scope::AdtScope(adt) => {
if *first_name == sym::Self_.clone() {
- return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None));
+ return Some((
+ TypeNs::AdtSelfType(adt),
+ remaining_idx(),
+ None,
+ ResolvePathResultPrefixInfo::default(),
+ ));
}
}
Scope::BlockScope(m) => {
@@ -220,18 +251,6 @@ impl Resolver {
self.module_scope.resolve_path_in_type_ns(db, path)
}
- pub fn resolve_path_in_type_ns_fully_with_imports(
- &self,
- db: &dyn DefDatabase,
- path: &Path,
- ) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
- let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?;
- if unresolved.is_some() {
- return None;
- }
- Some((res, imp))
- }
-
pub fn resolve_path_in_type_ns_fully(
&self,
db: &dyn DefDatabase,
@@ -986,11 +1005,12 @@ impl ModuleItemMap {
&self,
db: &dyn DefDatabase,
path: &ModPath,
- ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
- let (module_def, idx, _) =
+ ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
+ {
+ let (module_def, idx, prefix_info) =
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
let (res, import) = to_type_ns(module_def)?;
- Some((res, idx, import))
+ Some((res, idx, import, prefix_info))
}
}
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 24f67fd660..432b8f4d94 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -761,8 +761,8 @@ impl<'a> TyLoweringContext<'a> {
path: &Path,
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
) -> Option<(TypeNs, Option<usize>)> {
- let (resolution, remaining_index, _) =
- self.resolver.resolve_path_in_type_ns(self.db.upcast(), path)?;
+ let (resolution, remaining_index, _, prefix_info) =
+ self.resolver.resolve_path_in_type_ns_with_prefix_info(self.db.upcast(), path)?;
let segments = path.segments();
match path {
@@ -771,13 +771,12 @@ impl<'a> TyLoweringContext<'a> {
_ => return Some((resolution, remaining_index)),
};
- let (module_segments, resolved_segment_idx, resolved_segment) = match remaining_index {
- None => (
- segments.strip_last(),
- segments.len() - 1,
- segments.last().expect("resolved path has at least one element"),
- ),
- Some(i) => (segments.take(i - 1), i - 1, segments.get(i - 1).unwrap()),
+ let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index {
+ None if prefix_info.enum_variant => {
+ (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2))
+ }
+ None => (segments.strip_last(), segments.len() - 1, None),
+ Some(i) => (segments.take(i - 1), i - 1, None),
};
for (i, mod_segment) in module_segments.iter().enumerate() {
@@ -792,9 +791,23 @@ impl<'a> TyLoweringContext<'a> {
}
}
+ if let Some(enum_segment) = enum_segment {
+ if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
+ && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
+ {
+ on_diagnostic(
+ self,
+ PathLoweringDiagnostic::GenericArgsProhibited {
+ segment: (enum_segment + 1) as u32,
+ reason: GenericArgsProhibitedReason::EnumVariant,
+ },
+ );
+ }
+ }
+
self.handle_type_ns_resolution(
&resolution,
- resolved_segment,
+ segments.get(resolved_segment_idx).expect("should have resolved segment"),
resolved_segment_idx,
on_diagnostic,
);
diff --git a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
index 2b59c1a22f..7d62daf716 100644
--- a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
+++ b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
@@ -604,4 +604,23 @@ fn bar() {
"#,
);
}
+
+ #[test]
+ fn enum_variant_type_ns() {
+ check_diagnostics(
+ r#"
+enum KvnDeserializerErr<I> {
+ UnexpectedKeyword { found: I, expected: I },
+}
+
+fn foo() {
+ let _x: KvnDeserializerErr<()> =
+ KvnDeserializerErr::<()>::UnexpectedKeyword { found: (), expected: () };
+ let _x: KvnDeserializerErr<()> =
+ KvnDeserializerErr::<()>::UnexpectedKeyword::<()> { found: (), expected: () };
+ // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both
+}
+ "#,
+ );
+ }
}