From 3f42b2e837c4672a0fbe953e14ae2fd3fe6fc3b6 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 15 May 2020 17:15:40 +0200 Subject: Handle `Self` in values and patterns I.e. - `Self(x)` or `Self` in tuple/unit struct impls - `Self::Variant(x)` or `Self::Variant` in enum impls - the same in patterns Fixes #4454. --- crates/ra_hir/src/source_analyzer.rs | 1 + crates/ra_hir_def/src/resolver.rs | 18 +++++--- crates/ra_hir_ty/src/infer.rs | 79 ++++++++++++++++++++++++++++------ crates/ra_hir_ty/src/infer/path.rs | 35 ++++++++++++++- crates/ra_hir_ty/src/tests/patterns.rs | 39 +++++++++++++++++ crates/ra_hir_ty/src/tests/simple.rs | 44 +++++++++++++++++++ 6 files changed, 197 insertions(+), 19 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index c63d1b847..c862a4f48 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -417,6 +417,7 @@ pub(crate) fn resolve_hir_path( ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), + ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), }; Some(res) }); diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 717506358..15fdd9019 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs @@ -86,6 +86,7 @@ pub enum ResolveValueResult { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ValueNs { + ImplSelf(ImplId), LocalBinding(PatId), FunctionId(FunctionId), ConstId(ConstId), @@ -291,19 +292,26 @@ impl Resolver { } Scope::GenericParams { .. } => continue, - Scope::ImplDefScope(impl_) if n_segments > 1 => { + Scope::ImplDefScope(impl_) => { if first_name == &name![Self] { - let ty = TypeNs::SelfType(*impl_); - return Some(ResolveValueResult::Partial(ty, 1)); + if n_segments > 1 { + let ty = TypeNs::SelfType(*impl_); + return Some(ResolveValueResult::Partial(ty, 1)); + } else { + return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(*impl_))); + } } } - Scope::AdtScope(adt) if n_segments > 1 => { + Scope::AdtScope(adt) => { + if n_segments == 1 { + // bare `Self` doesn't work in the value namespace in a struct/enum definition + continue; + } if first_name == &name![Self] { let ty = TypeNs::AdtSelfType(*adt); return Some(ResolveValueResult::Partial(ty, 1)); } } - Scope::ImplDefScope(_) | Scope::AdtScope(_) => continue, Scope::ModuleScope(m) => { let (module_def, idx) = m.crate_def_map.resolve_path( diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index fb7c6cd8c..2876cb141 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -28,7 +28,8 @@ use hir_def::{ path::{path, Path}, resolver::{HasResolver, Resolver, TypeNs}, type_ref::{Mutability, TypeRef}, - AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, TraitId, TypeAliasId, VariantId, + AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, TraitId, TypeAliasId, + VariantId, }; use hir_expand::{diagnostics::DiagnosticSink, name::name}; use ra_arena::map::ArenaMap; @@ -438,43 +439,95 @@ impl<'a> InferenceContext<'a> { let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); // FIXME: this should resolve assoc items as well, see this example: // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 - return match resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path()) { - Some(TypeNs::AdtId(AdtId::StructId(strukt))) => { + let (resolution, unresolved) = + match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { + Some(it) => it, + None => return (Ty::Unknown, None), + }; + return match resolution { + TypeNs::AdtId(AdtId::StructId(strukt)) => { let substs = Ty::substs_from_path(&ctx, path, strukt.into()); let ty = self.db.ty(strukt.into()); let ty = self.insert_type_vars(ty.subst(&substs)); - (ty, Some(strukt.into())) + forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) } - Some(TypeNs::EnumVariantId(var)) => { + TypeNs::EnumVariantId(var) => { let substs = Ty::substs_from_path(&ctx, path, var.into()); let ty = self.db.ty(var.parent.into()); let ty = self.insert_type_vars(ty.subst(&substs)); - (ty, Some(var.into())) + forbid_unresolved_segments((ty, Some(var.into())), unresolved) } - Some(TypeNs::SelfType(impl_id)) => { + TypeNs::SelfType(impl_id) => { let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); let substs = Substs::type_params_for_generics(&generics); let ty = self.db.impl_self_ty(impl_id).subst(&substs); - let variant = ty_variant(&ty); - (ty, variant) + match unresolved { + None => { + let variant = ty_variant(&ty); + (ty, variant) + } + Some(1) => { + let segment = path.mod_path().segments.last().unwrap(); + // this could be an enum variant or associated type + if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { + let enum_data = self.db.enum_data(enum_id); + if let Some(local_id) = enum_data.variant(segment) { + let variant = EnumVariantId { parent: enum_id, local_id }; + return (ty, Some(variant.into())); + } + } + // FIXME potentially resolve assoc type + (Ty::Unknown, None) + } + Some(_) => { + // FIXME diagnostic + (Ty::Unknown, None) + } + } } - Some(TypeNs::TypeAliasId(it)) => { + TypeNs::TypeAliasId(it) => { let substs = Substs::build_for_def(self.db, it) .fill(std::iter::repeat_with(|| self.table.new_type_var())) .build(); let ty = self.db.ty(it.into()).subst(&substs); let variant = ty_variant(&ty); - (ty, variant) + forbid_unresolved_segments((ty, variant), unresolved) + } + TypeNs::AdtSelfType(_) => { + // FIXME this could happen in array size expressions, once we're checking them + (Ty::Unknown, None) + } + TypeNs::GenericParam(_) => { + // FIXME potentially resolve assoc type + (Ty::Unknown, None) + } + TypeNs::AdtId(AdtId::EnumId(_)) + | TypeNs::AdtId(AdtId::UnionId(_)) + | TypeNs::BuiltinType(_) + | TypeNs::TraitId(_) => { + // FIXME diagnostic + (Ty::Unknown, None) } - Some(_) | None => (Ty::Unknown, None), }; + fn forbid_unresolved_segments( + result: (Ty, Option), + unresolved: Option, + ) -> (Ty, Option) { + if unresolved.is_none() { + result + } else { + // FIXME diagnostic + (Ty::Unknown, None) + } + } + fn ty_variant(ty: &Ty) -> Option { ty.as_adt().and_then(|(adt_id, _)| match adt_id { AdtId::StructId(s) => Some(VariantId::StructId(s)), AdtId::UnionId(u) => Some(VariantId::UnionId(u)), AdtId::EnumId(_) => { - // Error E0071, expected struct, variant or union type, found enum `Foo` + // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` None } }) diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs index 2b6bc0f79..1c2e56fb0 100644 --- a/crates/ra_hir_ty/src/infer/path.rs +++ b/crates/ra_hir_ty/src/infer/path.rs @@ -5,7 +5,7 @@ use std::iter; use hir_def::{ path::{Path, PathSegment}, resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs}, - AssocContainerId, AssocItemId, Lookup, + AdtId, AssocContainerId, AssocItemId, EnumVariantId, Lookup, }; use hir_expand::name::Name; @@ -77,6 +77,18 @@ impl<'a> InferenceContext<'a> { it.into() } + ValueNs::ImplSelf(impl_id) => { + let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); + let substs = Substs::type_params_for_generics(&generics); + let ty = self.db.impl_self_ty(impl_id).subst(&substs); + if let Some((AdtId::StructId(struct_id), _)) = ty.as_adt() { + let ty = self.db.value_ty(struct_id.into()).subst(&substs); + return Some(ty); + } else { + // FIXME: diagnostic, invalid Self reference + return None; + } + } }; let ty = self.db.value_ty(typable); @@ -199,6 +211,10 @@ impl<'a> InferenceContext<'a> { return None; } + if let Some(result) = self.resolve_enum_variant_on_ty(&ty, name, id) { + return Some(result); + } + let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); let krate = self.resolver.krate()?; let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); @@ -250,4 +266,21 @@ impl<'a> InferenceContext<'a> { }, ) } + + fn resolve_enum_variant_on_ty( + &mut self, + ty: &Ty, + name: &Name, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option)> { + let (enum_id, subst) = match ty.as_adt() { + Some((AdtId::EnumId(e), subst)) => (e, subst), + _ => return None, + }; + let enum_data = self.db.enum_data(enum_id); + let local_id = enum_data.variant(name)?; + let variant = EnumVariantId { parent: enum_id, local_id }; + self.write_variant_resolution(id, variant.into()); + Some((ValueNs::EnumVariantId(variant), Some(subst.clone()))) + } } diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index af291092d..d83ff5e0e 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs @@ -368,6 +368,45 @@ fn test() { ); } +#[test] +fn enum_variant_through_self_in_pattern() { + assert_snapshot!( + infer(r#" +enum E { + A { x: usize }, + B(usize), + C +} + +impl E { + fn test() { + match (loop {}) { + Self::A { x } => { x; }, + Self::B(x) => { x; }, + Self::C => {}, + }; + } +} +"#), + @r###" + 76..218 '{ ... }': () + 86..211 'match ... }': () + 93..100 'loop {}': ! + 98..100 '{}': () + 116..129 'Self::A { x }': E + 126..127 'x': usize + 133..139 '{ x; }': () + 135..136 'x': usize + 153..163 'Self::B(x)': E + 161..162 'x': usize + 167..173 '{ x; }': () + 169..170 'x': usize + 187..194 'Self::C': E + 198..200 '{}': () + "### + ); +} + #[test] fn infer_generics_in_patterns() { assert_snapshot!( diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 322838f02..72122c070 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -575,6 +575,50 @@ impl S { ); } +#[test] +fn infer_self_as_path() { + assert_snapshot!( + infer(r#" +struct S1; +struct S2(isize); +enum E { + V1, + V2(u32), +} + +impl S1 { + fn test() { + Self; + } +} +impl S2 { + fn test() { + Self(1); + } +} +impl E { + fn test() { + Self::V1; + Self::V2(1); + } +} +"#), + @r###" + 87..108 '{ ... }': () + 97..101 'Self': S1 + 135..159 '{ ... }': () + 145..149 'Self': S2(isize) -> S2 + 145..152 'Self(1)': S2 + 150..151 '1': isize + 185..231 '{ ... }': () + 195..203 'Self::V1': E + 213..221 'Self::V2': V2(u32) -> E + 213..224 'Self::V2(1)': E + 222..223 '1': u32 + "### + ); +} + #[test] fn infer_binary_op() { assert_snapshot!( -- cgit v1.2.3