From 55c941cd9fb90c9340f01981e113aabd058b185b Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
Date: Tue, 25 Dec 2018 13:54:38 +0100
Subject: Type field accesses

---
 crates/ra_hir/src/adt.rs                        |  9 +++++++
 crates/ra_hir/src/ty.rs                         | 32 ++++++++++++++++++++++++-
 crates/ra_hir/src/ty/tests/data/0004_struct.txt |  6 +++--
 crates/ra_syntax/src/ast/generated.rs           | 10 +++++++-
 crates/ra_syntax/src/grammar.ron                |  2 +-
 5 files changed, 54 insertions(+), 5 deletions(-)

(limited to 'crates')

diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
index b44f59f0b..03770ed7d 100644
--- a/crates/ra_hir/src/adt.rs
+++ b/crates/ra_hir/src/adt.rs
@@ -22,6 +22,10 @@ impl Struct {
         self.def_id
     }
 
+    pub fn variant_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<VariantData>> {
+        Ok(db.struct_data(self.def_id)?.variant_data.clone())
+    }
+
     pub fn struct_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<StructData>> {
         Ok(db.struct_data(self.def_id)?)
     }
@@ -162,6 +166,11 @@ impl VariantData {
             StructFlavor::Unit => VariantData::Unit,
         })
     }
+
+    pub(crate) fn get_field_ty(&self, field_name: &str) -> Option<Ty> {
+        self.fields().iter().find(|f| f.name == field_name).map(|f| f.ty.clone())
+    }
+
     pub fn fields(&self) -> &[StructField] {
         match *self {
             VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields,
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index e2428a37f..f931f3c87 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -384,6 +384,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         })
     }
 
+    fn infer_expr_opt(&mut self, expr: Option<ast::Expr>) -> Cancelable<Ty> {
+        if let Some(e) = expr {
+            self.infer_expr(e)
+        } else {
+            Ok(Ty::Unknown)
+        }
+    }
+
     fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> {
         let ty = match expr {
             ast::Expr::IfExpr(e) => {
@@ -559,7 +567,29 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 Ty::Unknown
             }
             ast::Expr::IndexExpr(_e) => Ty::Unknown,
-            ast::Expr::FieldExpr(_e) => Ty::Unknown,
+            ast::Expr::FieldExpr(e) => {
+                let receiver_ty = self.infer_expr_opt(e.expr())?;
+                if let Some(nr) = e.name_ref() {
+                    let text = nr.text();
+                    match receiver_ty {
+                        Ty::Tuple(fields) => {
+                            let i = text.parse::<usize>().ok();
+                            i.and_then(|i| fields.get(i).cloned()).unwrap_or(Ty::Unknown)
+                        }
+                        Ty::Adt { def_id, .. } => {
+                            let field_ty = match def_id.resolve(self.db)? {
+                                Def::Struct(s) => s.variant_data(self.db)?.get_field_ty(&text),
+                                // TODO unions
+                                _ => None,
+                            };
+                            field_ty.unwrap_or(Ty::Unknown)
+                        }
+                        _ => Ty::Unknown,
+                    }
+                } else {
+                    Ty::Unknown
+                }
+            },
             ast::Expr::TryExpr(e) => {
                 let _inner_ty = if let Some(e) = e.expr() {
                     self.infer_expr(e)?
diff --git a/crates/ra_hir/src/ty/tests/data/0004_struct.txt b/crates/ra_hir/src/ty/tests/data/0004_struct.txt
index 41357749f..cc8f3665b 100644
--- a/crates/ra_hir/src/ty/tests/data/0004_struct.txt
+++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt
@@ -4,9 +4,11 @@
 [129; 130) '1': [unknown]
 [107; 108) 'a': A
 [127; 128) 'C': [unknown]
-[139; 142) 'a.b': [unknown]
+[139; 142) 'a.b': B
 [114; 133) 'A { b:...C(1) }': A
-[148; 151) 'a.c': [unknown]
+[148; 151) 'a.c': C
+[148; 149) 'a': A
+[139; 140) 'a': A
 [72; 154) '{     ...a.c; }': ()
 [96; 97) 'B': [unknown]
 [88; 89) '1': [unknown]
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 35a9770a6..4e0550487 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -1123,7 +1123,15 @@ impl<R: TreeRoot<RaTypes>> FieldExprNode<R> {
 }
 
 
-impl<'a> FieldExpr<'a> {}
+impl<'a> FieldExpr<'a> {
+    pub fn expr(self) -> Option<Expr<'a>> {
+        super::child_opt(self)
+    }
+
+    pub fn name_ref(self) -> Option<NameRef<'a>> {
+        super::child_opt(self)
+    }
+}
 
 // FieldPatList
 #[derive(Debug, Clone, Copy,)]
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index e4e41d077..923da0324 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -406,7 +406,7 @@ Grammar(
             options: [ "Expr" ],
         ),
         "IndexExpr": (),
-        "FieldExpr": (),
+        "FieldExpr": (options: ["Expr", "NameRef"]),
         "TryExpr": (options: ["Expr"]),
         "CastExpr": (options: ["Expr", "TypeRef"]),
         "RefExpr": (options: ["Expr"]),
-- 
cgit v1.2.3