about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Diebold <florian.diebold@freiheit.com>2020-05-15 17:15:40 +0200
committerFlorian Diebold <florian.diebold@freiheit.com>2020-05-15 17:25:28 +0200
commit3f42b2e837c4672a0fbe953e14ae2fd3fe6fc3b6 (patch)
tree481c93f91eec65f98a82fa28a123140d5a227eea
parentd51c1f62178c383363a2d95e865131d9a7b969d0 (diff)
downloadrust-3f42b2e837c4672a0fbe953e14ae2fd3fe6fc3b6.tar.gz
rust-3f42b2e837c4672a0fbe953e14ae2fd3fe6fc3b6.zip
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.
-rw-r--r--crates/ra_hir/src/source_analyzer.rs1
-rw-r--r--crates/ra_hir_def/src/resolver.rs18
-rw-r--r--crates/ra_hir_ty/src/infer.rs79
-rw-r--r--crates/ra_hir_ty/src/infer/path.rs35
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs39
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs44
6 files changed, 197 insertions, 19 deletions
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index c63d1b8473e..c862a4f483f 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 717506358f9..15fdd9019b7 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 fb7c6cd8c14..2876cb141aa 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<VariantId>),
+            unresolved: Option<usize>,
+        ) -> (Ty, Option<VariantId>) {
+            if unresolved.is_none() {
+                result
+            } else {
+                // FIXME diagnostic
+                (Ty::Unknown, None)
+            }
+        }
+
         fn ty_variant(ty: &Ty) -> Option<VariantId> {
             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 2b6bc0f798f..1c2e56fb0d8 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<Substs>)> {
+        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 af291092d30..d83ff5e0ea7 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -369,6 +369,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!(
         infer(r#"
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 322838f020a..72122c070ad 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -576,6 +576,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!(
         infer(r#"