From 31e8419de6569acd526bd0e7026b0341b26f93ea Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 19 Jan 2025 06:31:23 +0200 Subject: [PATCH] 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. --- .../crates/hir-def/src/resolver.rs | 58 +++++++++++++------ .../rust-analyzer/crates/hir-ty/src/lower.rs | 33 +++++++---- .../src/handlers/generic_args_prohibited.rs | 19 ++++++ 3 files changed, 81 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index df48ce6737314..0b9b6da8d5133 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -166,6 +166,17 @@ impl Resolver { db: &dyn DefDatabase, path: &Path, ) -> Option<(TypeNs, Option, Option)> { + 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, Option, 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)> { - 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, Option)> { - let (module_def, idx, _) = + ) -> Option<(TypeNs, Option, Option, 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/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 24f67fd660208..432b8f4d94eac 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/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)> { - 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index 2b59c1a22f6fe..7d62daf716c47 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs @@ -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 { + 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 } "#, );