1
Fork 0

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.
This commit is contained in:
Chayim Refael Friedman 2025-01-19 06:31:23 +02:00
parent 61af2cc09a
commit 31e8419de6
3 changed files with 81 additions and 29 deletions

View file

@ -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))
}
}

View file

@ -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,
);

View file

@ -600,6 +600,25 @@ pub mod __private {
//- /bar.rs crate:bar deps:foo edition:2018
fn bar() {
_ = foo::__private::Result::<(), ()>::Ok;
}
"#,
);
}
#[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
}
"#,
);