From 60a607d33f1c50acd0a4218da32abe35b2941e38 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Thu, 24 Jan 2019 17:54:18 +0300
Subject: [PATCH] new struct id

---
 crates/ra_hir/src/adt.rs                      |  26 +--
 crates/ra_hir/src/code_model_api.rs           |  33 ++--
 crates/ra_hir/src/code_model_impl/module.rs   |   2 +-
 crates/ra_hir/src/db.rs                       |   7 +-
 crates/ra_hir/src/generics.rs                 |  15 +-
 crates/ra_hir/src/ids.rs                      |   7 +-
 crates/ra_hir/src/lib.rs                      |   2 +-
 crates/ra_hir/src/nameres/lower.rs            |  11 +-
 crates/ra_hir/src/ty.rs                       | 150 +++++++++++++-----
 crates/ra_hir/src/ty/method_resolution.rs     |   8 +-
 .../src/ty/snapshots/tests__infer_struct.snap |  12 +-
 .../ra_ide_api/src/completion/complete_dot.rs |  33 ++--
 12 files changed, 200 insertions(+), 106 deletions(-)

diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
index 4cca0935171..3caf60ee6d0 100644
--- a/crates/ra_hir/src/adt.rs
+++ b/crates/ra_hir/src/adt.rs
@@ -9,19 +9,27 @@ use ra_syntax::{
 };
 
 use crate::{
-    DefId, DefLoc, Name, AsName, Struct, Enum, EnumVariant,
+    DefId, DefLoc, Name, AsName, Struct, Enum, EnumVariant, Module, HirFileId,
     HirDatabase, DefKind,
     SourceItemId,
     type_ref::TypeRef,
+    ids::{StructLoc},
 };
 
 impl Struct {
-    pub(crate) fn new(def_id: DefId) -> Self {
-        Struct { def_id }
+    pub(crate) fn from_ast(
+        db: &impl HirDatabase,
+        module: Module,
+        file_id: HirFileId,
+        ast: &ast::StructDef,
+    ) -> Struct {
+        let loc: StructLoc = StructLoc::from_ast(db, module, file_id, ast);
+        let id = loc.id(db);
+        Struct { id }
     }
 
     pub(crate) fn variant_data(&self, db: &impl HirDatabase) -> Arc<VariantData> {
-        db.struct_data(self.def_id).variant_data.clone()
+        db.struct_data((*self).into()).variant_data.clone()
     }
 }
 
@@ -39,13 +47,9 @@ impl StructData {
         StructData { name, variant_data }
     }
 
-    pub(crate) fn struct_data_query(db: &impl HirDatabase, def_id: DefId) -> Arc<StructData> {
-        let def_loc = def_id.loc(db);
-        assert!(def_loc.kind == DefKind::Struct);
-        let syntax = db.file_item(def_loc.source_item_id);
-        let struct_def =
-            ast::StructDef::cast(&syntax).expect("struct def should point to StructDef node");
-        Arc::new(StructData::new(struct_def))
+    pub(crate) fn struct_data_query(db: &impl HirDatabase, struct_: Struct) -> Arc<StructData> {
+        let (_, struct_def) = struct_.source(db);
+        Arc::new(StructData::new(&*struct_def))
     }
 }
 
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs
index 9056151277e..948718aa62b 100644
--- a/crates/ra_hir/src/code_model_api.rs
+++ b/crates/ra_hir/src/code_model_api.rs
@@ -10,13 +10,13 @@ use crate::{
     nameres::{ModuleScope, lower::ImportId},
     db::HirDatabase,
     expr::BodySyntaxMapping,
-    ty::InferenceResult,
+    ty::{InferenceResult, VariantDef},
     adt::VariantData,
     generics::GenericParams,
     code_model_impl::def_id_to_ast,
     docs::{Documentation, Docs, docs_from_ast},
     module_tree::ModuleId,
-    ids::FunctionId,
+    ids::{FunctionId, StructId},
 };
 
 /// hir::Crate describes a single crate. It's the main interface with which
@@ -68,6 +68,7 @@ pub struct Module {
 pub enum ModuleDef {
     Module(Module),
     Function(Function),
+    Struct(Struct),
     Def(DefId),
 }
 
@@ -83,6 +84,12 @@ impl Into<ModuleDef> for Function {
     }
 }
 
+impl Into<ModuleDef> for Struct {
+    fn into(self) -> ModuleDef {
+        ModuleDef::Struct(self)
+    }
+}
+
 impl Into<ModuleDef> for DefId {
     fn into(self) -> ModuleDef {
         ModuleDef::Def(self)
@@ -187,7 +194,7 @@ impl Module {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct StructField {
-    parent: DefId,
+    parent: VariantDef,
     name: Name,
 }
 
@@ -201,38 +208,38 @@ impl StructField {
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Struct {
-    pub(crate) def_id: DefId,
+    pub(crate) id: StructId,
 }
 
 impl Struct {
-    pub fn def_id(&self) -> DefId {
-        self.def_id
+    pub fn module(&self, db: &impl HirDatabase) -> Module {
+        self.id.loc(db).module
     }
 
     pub fn name(&self, db: &impl HirDatabase) -> Option<Name> {
-        db.struct_data(self.def_id).name.clone()
+        db.struct_data(*self).name.clone()
     }
 
     pub fn fields(&self, db: &impl HirDatabase) -> Vec<StructField> {
-        db.struct_data(self.def_id)
+        db.struct_data(*self)
             .variant_data
             .fields()
             .iter()
             .map(|it| StructField {
-                parent: self.def_id,
+                parent: (*self).into(),
                 name: it.name.clone(),
             })
             .collect()
     }
 
     pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::StructDef>) {
-        def_id_to_ast(db, self.def_id)
+        self.id.loc(db).source(db)
     }
 
     pub fn generic_params(&self, db: &impl HirDatabase) -> Arc<GenericParams> {
-        db.generic_params(self.def_id.into())
+        db.generic_params((*self).into())
     }
 }
 
@@ -310,7 +317,7 @@ impl EnumVariant {
             .fields()
             .iter()
             .map(|it| StructField {
-                parent: self.def_id,
+                parent: self.def_id.into(),
                 name: it.name.clone(),
             })
             .collect()
diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs
index b2828c7be0a..42f10e9415a 100644
--- a/crates/ra_hir/src/code_model_impl/module.rs
+++ b/crates/ra_hir/src/code_model_impl/module.rs
@@ -135,7 +135,7 @@ impl Module {
                         None => PerNs::none(),
                     }
                 }
-                ModuleDef::Function(_) => PerNs::none(),
+                ModuleDef::Function(_) | ModuleDef::Struct(_) => PerNs::none(),
                 ModuleDef::Def(def) => {
                     match def.resolve(db) {
                         Def::Enum(e) => {
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 97de7da3177..75935c30f50 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -8,10 +8,11 @@ use crate::{
     SourceFileItems, SourceItemId, Crate, Module, HirInterner,
     query_definitions,
     Function, FnSignature, FnScopes,
+    Struct,
     macros::MacroExpansion,
     module_tree::ModuleTree,
     nameres::{ItemMap, lower::{LoweredModule, ImportSourceMap}},
-    ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef},
+    ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, VariantDef},
     adt::{StructData, EnumData, EnumVariantData},
     impl_block::ModuleImplBlocks,
     generics::{GenericParams, GenericDef},
@@ -29,7 +30,7 @@ pub trait HirDatabase: SyntaxDatabase + AsRef<HirInterner> {
     fn fn_scopes(&self, func: Function) -> Arc<FnScopes>;
 
     #[salsa::invoke(crate::adt::StructData::struct_data_query)]
-    fn struct_data(&self, def_id: DefId) -> Arc<StructData>;
+    fn struct_data(&self, struct_: Struct) -> Arc<StructData>;
 
     #[salsa::invoke(crate::adt::EnumData::enum_data_query)]
     fn enum_data(&self, def_id: DefId) -> Arc<EnumData>;
@@ -44,7 +45,7 @@ pub trait HirDatabase: SyntaxDatabase + AsRef<HirInterner> {
     fn type_for_def(&self, def: TypableDef) -> Ty;
 
     #[salsa::invoke(crate::ty::type_for_field)]
-    fn type_for_field(&self, def_id: DefId, field: Name) -> Option<Ty>;
+    fn type_for_field(&self, def: VariantDef, field: Name) -> Option<Ty>;
 
     #[salsa::invoke(query_definitions::file_items)]
     fn file_items(&self, file_id: HirFileId) -> Arc<SourceFileItems>;
diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs
index 88c53705fa4..e10b85ec90b 100644
--- a/crates/ra_hir/src/generics.rs
+++ b/crates/ra_hir/src/generics.rs
@@ -7,7 +7,7 @@ use std::sync::Arc;
 
 use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
 
-use crate::{db::HirDatabase, DefId, Name, AsName, Function};
+use crate::{db::HirDatabase, DefId, Name, AsName, Function, Struct};
 
 /// Data about a generic parameter (to a function, struct, impl, ...).
 #[derive(Clone, PartialEq, Eq, Debug)]
@@ -25,6 +25,7 @@ pub struct GenericParams {
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 pub enum GenericDef {
     Function(Function),
+    Struct(Struct),
     Def(DefId),
 }
 
@@ -34,6 +35,12 @@ impl From<Function> for GenericDef {
     }
 }
 
+impl From<Struct> for GenericDef {
+    fn from(func: Struct) -> GenericDef {
+        GenericDef::Struct(func)
+    }
+}
+
 impl From<DefId> for GenericDef {
     fn from(def_id: DefId) -> GenericDef {
         GenericDef::Def(def_id)
@@ -53,6 +60,12 @@ impl GenericParams {
                     generics.fill(type_param_list)
                 }
             }
+            GenericDef::Struct(s) => {
+                let (_, struct_def) = s.source(db);
+                if let Some(type_param_list) = struct_def.type_param_list() {
+                    generics.fill(type_param_list)
+                }
+            }
             GenericDef::Def(def_id) => {
                 let (_file_id, node) = def_id.source(db);
                 if let Some(type_param_list) = node.children().find_map(ast::TypeParamList::cast) {
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index 51e3cfb8133..2791149dd90 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -5,7 +5,7 @@ use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, ast};
 use ra_arena::{Arena, RawId, impl_arena_id};
 
 use crate::{
-    HirDatabase, Def, Struct, Enum, EnumVariant, Crate,
+    HirDatabase, Def, Enum, EnumVariant, Crate,
     Module, Trait, Type, Static, Const,
 };
 
@@ -257,10 +257,7 @@ impl DefId {
     pub fn resolve(self, db: &impl HirDatabase) -> Def {
         let loc = self.loc(db);
         match loc.kind {
-            DefKind::Struct => {
-                let struct_def = Struct::new(self);
-                Def::Struct(struct_def)
-            }
+            DefKind::Struct => unreachable!(),
             DefKind::Enum => Def::Enum(Enum::new(self)),
             DefKind::EnumVariant => Def::EnumVariant(EnumVariant::new(self)),
             DefKind::Const => {
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index a6246a5e940..5d63718924e 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -43,7 +43,7 @@ pub use self::{
     ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc, HirInterner},
     macros::{MacroDef, MacroInput, MacroExpansion},
     nameres::{ItemMap, PerNs, Namespace, Resolution},
-    ty::Ty,
+    ty::{Ty, AdtDef},
     impl_block::{ImplBlock, ImplItem},
     code_model_impl::function::{FnScopes, ScopesWithSyntaxMapping},
     docs::{Docs, Documentation}
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs
index 1f8adc7eb18..b0c4aa819d2 100644
--- a/crates/ra_hir/src/nameres/lower.rs
+++ b/crates/ra_hir/src/nameres/lower.rs
@@ -10,7 +10,7 @@ use rustc_hash::FxHashMap;
 use crate::{
     SourceItemId, Path, ModuleSource, HirDatabase, Name, SourceFileItems,
     HirFileId, MacroCallLoc, AsName, PerNs, DefKind, DefLoc, Function,
-    ModuleDef, Module,
+    ModuleDef, Module, Struct,
 };
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -147,7 +147,14 @@ impl LoweredModule {
         item: &ast::ModuleItem,
     ) {
         let name = match item.kind() {
-            ast::ModuleItemKind::StructDef(it) => it.name(),
+            ast::ModuleItemKind::StructDef(it) => {
+                if let Some(name) = it.name() {
+                    let s = Struct::from_ast(db, module, file_id, it);
+                    let s: ModuleDef = s.into();
+                    self.declarations.insert(name.as_name(), PerNs::both(s, s));
+                }
+                return;
+            }
             ast::ModuleItemKind::EnumDef(it) => it.name(),
             ast::ModuleItemKind::FnDef(it) => {
                 if let Some(name) = it.name() {
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index f9cdbcab333..fc699a2ae2e 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -32,7 +32,7 @@ use rustc_hash::FxHashMap;
 
 use crate::{
     Def, DefId, Module, Function, Struct, StructField, Enum, EnumVariant, Path, Name, ImplBlock,
-    FnSignature, FnScopes, ModuleDef,
+    FnSignature, FnScopes, ModuleDef, Crate,
     db::HirDatabase,
     type_ref::{TypeRef, Mutability},
     name::KnownName,
@@ -163,6 +163,33 @@ impl Substs {
     }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum AdtDef {
+    Struct(Struct),
+    Def(DefId), // Enum
+}
+
+impl From<Struct> for AdtDef {
+    fn from(struct_: Struct) -> AdtDef {
+        AdtDef::Struct(struct_)
+    }
+}
+
+impl From<DefId> for AdtDef {
+    fn from(def_id: DefId) -> AdtDef {
+        AdtDef::Def(def_id)
+    }
+}
+
+impl AdtDef {
+    fn krate(self, db: &impl HirDatabase) -> Option<Crate> {
+        match self {
+            AdtDef::Struct(s) => s.module(db).krate(db),
+            AdtDef::Def(def_id) => def_id.krate(db),
+        }
+    }
+}
+
 /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs).
 ///
 /// This should be cheap to clone.
@@ -184,7 +211,7 @@ pub enum Ty {
     /// Structures, enumerations and unions.
     Adt {
         /// The DefId of the struct/enum.
-        def_id: DefId,
+        def_id: AdtDef,
         /// The name, for displaying.
         name: Name,
         /// Substitutions for the generic parameters of the type.
@@ -384,6 +411,7 @@ impl Ty {
         let resolved = match module.resolve_path(db, path).take_types() {
             Some(ModuleDef::Def(r)) => r.into(),
             Some(ModuleDef::Function(f)) => f.into(),
+            Some(ModuleDef::Struct(s)) => s.into(),
             None | Some(ModuleDef::Module(_)) => return Ty::Unknown,
         };
         let ty = db.type_for_def(resolved);
@@ -409,6 +437,7 @@ impl Ty {
             .expect("path should have at least one segment");
         let (def_generics, segment) = match resolved {
             TypableDef::Function(func) => (func.generic_params(db), last),
+            TypableDef::Struct(s) => (s.generic_params(db), last),
             TypableDef::Def(def_id) => match def_id.resolve(db) {
                 Def::Struct(s) => (s.generic_params(db), last),
                 Def::Enum(e) => (e.generic_params(db), last),
@@ -642,7 +671,7 @@ fn make_substs(generics: &GenericParams) -> Substs {
 fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty {
     let generics = s.generic_params(db);
     Ty::Adt {
-        def_id: s.def_id(),
+        def_id: s.into(),
         name: s.name(db).unwrap_or_else(Name::missing),
         substs: make_substs(&generics),
     }
@@ -651,7 +680,7 @@ fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty {
 pub(crate) fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty {
     let generics = s.generic_params(db);
     Ty::Adt {
-        def_id: s.def_id(),
+        def_id: s.def_id().into(),
         name: s.name(db).unwrap_or_else(Name::missing),
         substs: make_substs(&generics),
     }
@@ -666,6 +695,7 @@ pub(crate) fn type_for_enum_variant(db: &impl HirDatabase, ev: EnumVariant) -> T
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum TypableDef {
     Function(Function),
+    Struct(Struct),
     Def(DefId),
 }
 
@@ -675,6 +705,12 @@ impl From<Function> for TypableDef {
     }
 }
 
+impl From<Struct> for TypableDef {
+    fn from(struct_: Struct) -> TypableDef {
+        TypableDef::Struct(struct_)
+    }
+}
+
 impl From<DefId> for TypableDef {
     fn from(func: DefId) -> TypableDef {
         TypableDef::Def(func)
@@ -684,8 +720,8 @@ impl From<DefId> for TypableDef {
 pub(super) fn type_for_def(db: &impl HirDatabase, def: TypableDef) -> Ty {
     match def {
         TypableDef::Function(f) => type_for_fn(db, f),
+        TypableDef::Struct(s) => type_for_struct(db, s),
         TypableDef::Def(def_id) => match def_id.resolve(db) {
-            Def::Struct(s) => type_for_struct(db, s),
             Def::Enum(e) => type_for_enum(db, e),
             Def::EnumVariant(ev) => type_for_enum_variant(db, ev),
             _ => {
@@ -700,22 +736,44 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def: TypableDef) -> Ty {
     }
 }
 
-pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Option<Ty> {
-    let def = def_id.resolve(db);
-    let (variant_data, generics) = match def {
-        Def::Struct(s) => (s.variant_data(db), s.generic_params(db)),
-        Def::EnumVariant(ev) => (ev.variant_data(db), ev.parent_enum(db).generic_params(db)),
-        // TODO: unions
-        Def::Enum(_) => {
-            // this can happen in (invalid) code, but enums don't have fields themselves
-            return None;
-        }
-        _ => panic!(
-            "trying to get type for field {:?} in non-struct/variant {:?}",
-            field, def_id
-        ),
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum VariantDef {
+    Struct(Struct),
+    Def(DefId), // EnumVariant
+}
+
+impl From<Struct> for VariantDef {
+    fn from(struct_: Struct) -> VariantDef {
+        VariantDef::Struct(struct_)
+    }
+}
+
+impl From<DefId> for VariantDef {
+    fn from(def_id: DefId) -> VariantDef {
+        VariantDef::Def(def_id)
+    }
+}
+
+pub(super) fn type_for_field(db: &impl HirDatabase, def: VariantDef, field: Name) -> Option<Ty> {
+    let (variant_data, generics, module) = match def {
+        VariantDef::Struct(s) => (s.variant_data(db), s.generic_params(db), s.module(db)),
+        VariantDef::Def(def_id) => match def_id.resolve(db) {
+            Def::EnumVariant(ev) => (
+                ev.variant_data(db),
+                ev.parent_enum(db).generic_params(db),
+                def_id.module(db),
+            ),
+            // TODO: unions
+            Def::Enum(_) => {
+                // this can happen in (invalid) code, but enums don't have fields themselves
+                return None;
+            }
+            _ => panic!(
+                "trying to get type for field {:?} in non-struct/variant {:?}",
+                field, def_id
+            ),
+        },
     };
-    let module = def_id.module(db);
     // We can't have an impl block ere, right?
     // let impl_block = def_id.impl_block(db);
     let type_ref = variant_data.get_field_type_ref(&field)?;
@@ -1076,17 +1134,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         };
 
         // resolve in module
-        let resolved = match self.module.resolve_path(self.db, &path).take_values()? {
+        let typable = match self.module.resolve_path(self.db, &path).take_values()? {
             ModuleDef::Def(it) => it.into(),
             ModuleDef::Function(func) => func.into(),
+            ModuleDef::Struct(s) => s.into(),
             ModuleDef::Module(_) => return None,
         };
-        let ty = self.db.type_for_def(resolved);
+        let ty = self.db.type_for_def(typable);
         let ty = self.insert_type_vars(ty);
         Some(ty)
     }
 
-    fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<DefId>) {
+    fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantDef>) {
         let path = match path {
             Some(path) => path,
             None => return (Ty::Unknown, None),
@@ -1094,7 +1153,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         let def = match self.module.resolve_path(self.db, &path).take_types() {
             Some(ModuleDef::Def(def_id)) => def_id.into(),
             Some(ModuleDef::Function(func)) => func.into(),
-            _ => return (Ty::Unknown, None),
+            Some(ModuleDef::Struct(s)) => s.into(),
+            None | Some(ModuleDef::Module(_)) => return (Ty::Unknown, None),
         };
         // TODO remove the duplication between here and `Ty::from_path`?
         // TODO provide generics of function
@@ -1109,37 +1169,36 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         );
         match def {
             TypableDef::Def(def_id) => match def_id.resolve(self.db) {
-                Def::Struct(s) => {
-                    let ty = type_for_struct(self.db, s);
-                    let ty = self.insert_type_vars(ty.apply_substs(substs));
-                    (ty, Some(def_id))
-                }
                 Def::EnumVariant(ev) => {
                     let ty = type_for_enum_variant(self.db, ev);
                     let ty = self.insert_type_vars(ty.apply_substs(substs));
-                    (ty, Some(def_id))
+                    (ty, Some(def_id.into()))
                 }
                 _ => (Ty::Unknown, None),
             },
             TypableDef::Function(_) => (Ty::Unknown, None),
+            TypableDef::Struct(s) => {
+                let ty = type_for_struct(self.db, s);
+                let ty = self.insert_type_vars(ty.apply_substs(substs));
+                (ty, Some(s.into()))
+            }
         }
     }
 
     fn resolve_fields(&mut self, path: Option<&Path>) -> Option<(Ty, Vec<StructField>)> {
-        let (ty, def_id) = self.resolve_variant(path);
-        let def_id = def_id?;
-        let def = def_id.resolve(self.db);
-
-        match def {
-            Def::Struct(s) => {
+        let (ty, def) = self.resolve_variant(path);
+        match def? {
+            VariantDef::Struct(s) => {
                 let fields = s.fields(self.db);
                 Some((ty, fields))
             }
-            Def::EnumVariant(ev) => {
-                let fields = ev.fields(self.db);
-                Some((ty, fields))
-            }
-            _ => None,
+            VariantDef::Def(def_id) => match def_id.resolve(self.db) {
+                Def::EnumVariant(ev) => {
+                    let fields = ev.fields(self.db);
+                    Some((ty, fields))
+                }
+                _ => None,
+            },
         }
     }
 
@@ -1240,6 +1299,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 .and_then(|module_def| match module_def {
                     ModuleDef::Def(it) => Some(it.into()),
                     ModuleDef::Function(func) => Some(func.into()),
+                    ModuleDef::Struct(s) => Some(s.into()),
                     ModuleDef::Module(_) => None,
                 })
                 .map_or(Ty::Unknown, |resolved| self.db.type_for_def(resolved)),
@@ -1433,7 +1493,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 for field in fields {
                     let field_ty = if let Some(def_id) = def_id {
                         self.db
-                            .type_for_field(def_id, field.name.clone())
+                            .type_for_field(def_id.into(), field.name.clone())
                             .unwrap_or(Ty::Unknown)
                             .subst(&substs)
                     } else {
@@ -1457,10 +1517,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                             i.and_then(|i| fields.get(i).cloned())
                         }
                         Ty::Adt {
-                            def_id, ref substs, ..
+                            def_id: AdtDef::Struct(s),
+                            ref substs,
+                            ..
                         } => self
                             .db
-                            .type_for_field(def_id, name.clone())
+                            .type_for_field(s.into(), name.clone())
                             .map(|ty| ty.subst(substs)),
                         _ => None,
                     })
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs
index 0084b24dc74..9a571c2aaf5 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -7,16 +7,16 @@ use std::sync::Arc;
 use rustc_hash::FxHashMap;
 
 use crate::{
-    HirDatabase, DefId, module_tree::ModuleId, Module, Crate, Name, Function,
+    HirDatabase, module_tree::ModuleId, Module, Crate, Name, Function,
     impl_block::{ImplId, ImplBlock, ImplItem},
-    generics::GenericParams
+    generics::GenericParams,
+    ty::{AdtDef, Ty}
 };
-use super::Ty;
 
 /// This is used as a key for indexing impls.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum TyFingerprint {
-    Adt(DefId),
+    Adt(AdtDef),
     // we'll also want to index impls for primitive types etc.
 }
 
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap
index c4a2d5cf8bd..8747fae1814 100644
--- a/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap
+++ b/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap
@@ -1,19 +1,19 @@
 ---
-created: "2019-01-22T14:45:00.058678600+00:00"
-creator: insta@0.4.0
+created: "2019-01-24T14:51:32.808861856+00:00"
+creator: insta@0.5.2
 expression: "&result"
-source: "crates\\ra_hir\\src\\ty\\tests.rs"
+source: crates/ra_hir/src/ty/tests.rs
 ---
 [72; 154) '{     ...a.c; }': ()
 [82; 83) 'c': [unknown]
-[86; 87) 'C': [unknown]
+[86; 87) 'C': C
 [86; 90) 'C(1)': [unknown]
 [88; 89) '1': i32
-[96; 97) 'B': [unknown]
+[96; 97) 'B': B
 [107; 108) 'a': A
 [114; 133) 'A { b:...C(1) }': A
 [121; 122) 'B': B
-[127; 128) 'C': [unknown]
+[127; 128) 'C': C
 [127; 131) 'C(1)': C
 [129; 130) '1': i32
 [139; 140) 'a': A
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs
index 32fd497be9b..1a2b0b2f68a 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -1,4 +1,4 @@
-use hir::{Ty, Def};
+use hir::{Ty, Def, AdtDef};
 
 use crate::completion::{CompletionContext, Completions, CompletionItem, CompletionItemKind};
 use crate::completion::completion_item::CompletionKind;
@@ -28,21 +28,24 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
             Ty::Adt {
                 def_id, ref substs, ..
             } => {
-                match def_id.resolve(ctx.db) {
-                    Def::Struct(s) => {
-                        for field in s.fields(ctx.db) {
-                            CompletionItem::new(
-                                CompletionKind::Reference,
-                                ctx.source_range(),
-                                field.name().to_string(),
-                            )
-                            .kind(CompletionItemKind::Field)
-                            .set_detail(field.ty(ctx.db).map(|ty| ty.subst(substs).to_string()))
-                            .add_to(acc);
+                match def_id {
+                    AdtDef::Struct() => {}
+                    AdtDef::Def(def_id) => match def_id.resolve(ctx.db) {
+                        Def::Struct(s) => {
+                            for field in s.fields(ctx.db) {
+                                CompletionItem::new(
+                                    CompletionKind::Reference,
+                                    ctx.source_range(),
+                                    field.name().to_string(),
+                                )
+                                .kind(CompletionItemKind::Field)
+                                .set_detail(field.ty(ctx.db).map(|ty| ty.subst(substs).to_string()))
+                                .add_to(acc);
+                            }
                         }
-                    }
-                    // TODO unions
-                    _ => {}
+                        // TODO unions
+                        _ => {}
+                    },
                 }
             }
             Ty::Tuple(fields) => {