]> git.lizzy.rs Git - rust.git/commitdiff
Add basic HIR and types for structs/enums
authorFlorian Diebold <flodiebold@gmail.com>
Mon, 24 Dec 2018 18:07:48 +0000 (19:07 +0100)
committerFlorian Diebold <flodiebold@gmail.com>
Tue, 25 Dec 2018 14:16:42 +0000 (15:16 +0100)
crates/ra_analysis/src/db.rs
crates/ra_hir/src/adt.rs [new file with mode: 0644]
crates/ra_hir/src/db.rs
crates/ra_hir/src/lib.rs
crates/ra_hir/src/mock.rs
crates/ra_hir/src/query_definitions.rs
crates/ra_hir/src/ty.rs
crates/ra_hir/src/ty/tests.rs
crates/ra_hir/src/ty/tests/data/0004_struct.txt [new file with mode: 0644]

index 780a84291ac106e4c1850cadc93713b48d40e4de..7043a0f4d099b54cdf13374785432c4bbdf08721 100644 (file)
@@ -95,6 +95,8 @@ impl hir::db::HirDatabase {
             fn submodules() for hir::db::SubmodulesQuery;
             fn infer() for hir::db::InferQuery;
             fn type_for_def() for hir::db::TypeForDefQuery;
+            fn struct_data() for db::StructDataQuery;
+            fn enum_data() for db::EnumDataQuery;
         }
     }
 }
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
new file mode 100644 (file)
index 0000000..a2d2285
--- /dev/null
@@ -0,0 +1,114 @@
+use ra_syntax::{SmolStr, ast::{self, NameOwner}};
+
+use crate::{
+    DefId, Cancelable,
+    db::{HirDatabase},
+    ty::{Ty},
+};
+
+pub struct Struct {
+    def_id: DefId,
+}
+
+impl Struct {
+    pub(crate) fn new(def_id: DefId) -> Self {
+        Struct { def_id }
+    }
+
+    pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> {
+        Ok(db.struct_data(self.def_id)?.name.clone())
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct StructData {
+    name: SmolStr,
+    variant_data: VariantData,
+}
+
+impl StructData {
+    pub(crate) fn new(struct_def: ast::StructDef) -> StructData {
+        let name = struct_def
+            .name()
+            .map(|n| n.text())
+            .unwrap_or(SmolStr::new("[error]"));
+        let variant_data = VariantData::Unit; // TODO implement this
+        StructData { name, variant_data }
+    }
+}
+
+pub struct Enum {
+    def_id: DefId,
+}
+
+impl Enum {
+    pub(crate) fn new(def_id: DefId) -> Self {
+        Enum { def_id }
+    }
+
+    pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> {
+        Ok(db.enum_data(self.def_id)?.name.clone())
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct EnumData {
+    name: SmolStr,
+    variants: Vec<(SmolStr, VariantData)>,
+}
+
+impl EnumData {
+    pub(crate) fn new(enum_def: ast::EnumDef) -> Self {
+        let name = enum_def
+            .name()
+            .map(|n| n.text())
+            .unwrap_or(SmolStr::new("[error]"));
+        let variants = Vec::new(); // TODO implement this
+        EnumData { name, variants }
+    }
+}
+
+/// A single field of an enum variant or struct
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct StructField {
+    name: SmolStr,
+    ty: Ty,
+}
+
+/// Fields of an enum variant or struct
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum VariantData {
+    Struct(Vec<StructField>),
+    Tuple(Vec<StructField>),
+    Unit,
+}
+
+impl VariantData {
+    pub fn fields(&self) -> &[StructField] {
+        match *self {
+            VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields,
+            _ => &[],
+        }
+    }
+    pub fn is_struct(&self) -> bool {
+        if let VariantData::Struct(..) = *self {
+            true
+        } else {
+            false
+        }
+    }
+    pub fn is_tuple(&self) -> bool {
+        if let VariantData::Tuple(..) = *self {
+            true
+        } else {
+            false
+        }
+    }
+    pub fn is_unit(&self) -> bool {
+        if let VariantData::Unit = *self {
+            true
+        } else {
+            false
+        }
+    }
+}
index d94f75857fca5b64b62b4cf89640716ee93e2f8e..113790ee9b7e65c51dc2132e20e2825e6391a2b3 100644 (file)
@@ -15,6 +15,7 @@
     module::{ModuleId, ModuleTree, ModuleSource,
     nameres::{ItemMap, InputModuleItems}},
     ty::{InferenceResult, Ty},
+    adt::{StructData, EnumData},
 };
 
 salsa::query_group! {
@@ -31,6 +32,16 @@ fn fn_syntax(fn_id: FnId) -> FnDefNode {
         use fn query_definitions::fn_syntax;
     }
 
+    fn struct_data(def_id: DefId) -> Cancelable<Arc<StructData>> {
+        type StructDataQuery;
+        use fn query_definitions::struct_data;
+    }
+
+    fn enum_data(def_id: DefId) -> Cancelable<Arc<EnumData>> {
+        type EnumDataQuery;
+        use fn query_definitions::enum_data;
+    }
+
     fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
         type InferQuery;
         use fn query_definitions::infer;
index a0d99a84dfa556d69b6b52b2554de28a302a64ca..7e9824de9ca96b024d81440950efcf00e2f44373 100644 (file)
@@ -25,6 +25,7 @@ macro_rules! ctry {
 mod krate;
 mod module;
 mod function;
+mod adt;
 mod ty;
 
 use std::ops::Index;
@@ -42,6 +43,7 @@ macro_rules! ctry {
     krate::Crate,
     module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution},
     function::{Function, FnScopes},
+    adt::{Struct, Enum},
 };
 
 pub use self::function::FnSignatureInfo;
@@ -56,6 +58,8 @@ macro_rules! ctry {
 pub(crate) enum DefKind {
     Module,
     Function,
+    Struct,
+    Enum,
     Item,
 }
 
@@ -73,8 +77,8 @@ pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> Option<DefKind> {
             SyntaxKind::FN_DEF => Some(DefKind::Function),
             SyntaxKind::MODULE => Some(DefKind::Module),
             // These define items, but don't have their own DefKinds yet:
-            SyntaxKind::STRUCT_DEF => Some(DefKind::Item),
-            SyntaxKind::ENUM_DEF => Some(DefKind::Item),
+            SyntaxKind::STRUCT_DEF => Some(DefKind::Struct),
+            SyntaxKind::ENUM_DEF => Some(DefKind::Enum),
             SyntaxKind::TRAIT_DEF => Some(DefKind::Item),
             SyntaxKind::TYPE_DEF => Some(DefKind::Item),
             SyntaxKind::CONST_DEF => Some(DefKind::Item),
@@ -99,6 +103,8 @@ pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefI
 pub enum Def {
     Module(Module),
     Function(Function),
+    Struct(Struct),
+    Enum(Enum),
     Item,
 }
 
@@ -114,6 +120,14 @@ pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
                 let function = Function::new(self);
                 Def::Function(function)
             }
+            DefKind::Struct => {
+                let struct_def = Struct::new(self);
+                Def::Struct(struct_def)
+            }
+            DefKind::Enum => {
+                let enum_def = Enum::new(self);
+                Def::Enum(enum_def)
+            }
             DefKind::Item => Def::Item,
         };
         Ok(res)
index b5a99717072ac8835dc10c025287e34a98fc7959..ead2b8414b3ed084377f252c77282b967ade5d16 100644 (file)
@@ -193,6 +193,8 @@ impl db::HirDatabase {
             fn submodules() for db::SubmodulesQuery;
             fn infer() for db::InferQuery;
             fn type_for_def() for db::TypeForDefQuery;
+            fn struct_data() for db::StructDataQuery;
+            fn enum_data() for db::EnumDataQuery;
         }
     }
 }
index b654af9204f35fb8b60801d7b23748cc77a8f3bd..72440d1d6f3896cc6f632977f72b3c18fc7b9eaa 100644 (file)
@@ -19,7 +19,8 @@
         imp::Submodule,
         nameres::{InputModuleItems, ItemMap, Resolver},
     },
-    ty::{self, InferenceResult, Ty}
+    ty::{self, InferenceResult, Ty},
+    adt::{StructData, EnumData},
 };
 
 /// Resolve `FnId` to the corresponding `SyntaxNode`
@@ -45,6 +46,24 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<T
     ty::type_for_def(db, def_id)
 }
 
+pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<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.borrowed()).expect("struct def should point to StructDef node");
+    Ok(Arc::new(StructData::new(struct_def.borrowed())))
+}
+
+pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<EnumData>> {
+    let def_loc = def_id.loc(db);
+    assert!(def_loc.kind == DefKind::Enum);
+    let syntax = db.file_item(def_loc.source_item_id);
+    let enum_def =
+        ast::EnumDef::cast(syntax.borrowed()).expect("enum def should point to EnumDef node");
+    Ok(Arc::new(EnumData::new(enum_def.borrowed())))
+}
+
 pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
     let mut res = SourceFileItems::new(file_id);
     let source_file = db.source_file(file_id);
index c759d4c8b1d7a983146438cd00c3cf268540e58f..f86b749ec19d2e66874fad6ce26f2c6a83bc3b34 100644 (file)
@@ -17,7 +17,7 @@
 
 use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase};
 
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum Ty {
     /// The primitive boolean type. Written as `bool`.
     Bool,
@@ -35,8 +35,15 @@ pub enum Ty {
     /// A primitive floating-point type. For example, `f64`.
     Float(primitive::FloatTy),
 
-    // Structures, enumerations and unions.
-    // Adt(AdtDef, Substs),
+    /// Structures, enumerations and unions.
+    Adt {
+        /// The DefId of the struct/enum.
+        def_id: DefId,
+        /// The name, for displaying.
+        name: SmolStr,
+        // later we'll need generic substitutions here
+    },
+
     /// The pointee of a string slice. Written as `str`.
     Str,
 
@@ -107,45 +114,48 @@ pub enum Ty {
 
 type TyRef = Arc<Ty>;
 
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub struct FnSig {
     input: Vec<Ty>,
     output: Ty,
 }
 
 impl Ty {
-    pub fn new(_db: &impl HirDatabase, node: ast::TypeRef) -> Cancelable<Self> {
+    pub(crate) fn new(
+        db: &impl HirDatabase,
+        module: &Module,
+        node: ast::TypeRef,
+    ) -> Cancelable<Self> {
         use ra_syntax::ast::TypeRef::*;
         Ok(match node {
             ParenType(_inner) => Ty::Unknown, // TODO
             TupleType(_inner) => Ty::Unknown, // TODO
             NeverType(..) => Ty::Never,
             PathType(inner) => {
-                let path = if let Some(p) = inner.path() {
+                let path = if let Some(p) = inner.path().and_then(Path::from_ast) {
                     p
                 } else {
                     return Ok(Ty::Unknown);
                 };
-                if path.qualifier().is_none() {
-                    let name = path
-                        .segment()
-                        .and_then(|s| s.name_ref())
-                        .map(|n| n.text())
-                        .unwrap_or(SmolStr::new(""));
+                if path.is_ident() {
+                    let name = &path.segments[0];
                     if let Some(int_ty) = primitive::IntTy::from_string(&name) {
-                        Ty::Int(int_ty)
+                        return Ok(Ty::Int(int_ty));
                     } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) {
-                        Ty::Uint(uint_ty)
+                        return Ok(Ty::Uint(uint_ty));
                     } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) {
-                        Ty::Float(float_ty)
-                    } else {
-                        // TODO
-                        Ty::Unknown
+                        return Ok(Ty::Float(float_ty));
                     }
-                } else {
-                    // TODO
-                    Ty::Unknown
                 }
+
+                // Resolve in module (in type namespace)
+                let resolved = if let Some(r) = module.resolve_path(db, path)? {
+                    r
+                } else {
+                    return Ok(Ty::Unknown);
+                };
+                let ty = db.type_for_def(resolved)?;
+                ty
             }
             PointerType(_inner) => Ty::Unknown,     // TODO
             ArrayType(_inner) => Ty::Unknown,       // TODO
@@ -189,6 +199,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                 }
                 write!(f, ") -> {}", sig.output)
             }
+            Ty::Adt { name, .. } => write!(f, "{}", name),
             Ty::Unknown => write!(f, "[unknown]"),
         }
     }
@@ -196,6 +207,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 
 pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
     let syntax = f.syntax(db);
+    let module = f.module(db)?;
     let node = syntax.borrowed();
     // TODO we ignore type parameters for now
     let input = node
@@ -204,7 +216,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
             pl.params()
                 .map(|p| {
                     p.type_ref()
-                        .map(|t| Ty::new(db, t))
+                        .map(|t| Ty::new(db, &module, t))
                         .unwrap_or(Ok(Ty::Unknown))
                 })
                 .collect()
@@ -213,7 +225,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
     let output = node
         .ret_type()
         .and_then(|rt| rt.type_ref())
-        .map(|t| Ty::new(db, t))
+        .map(|t| Ty::new(db, &module, t))
         .unwrap_or(Ok(Ty::Unknown))?;
     let sig = FnSig { input, output };
     Ok(Ty::FnPtr(Arc::new(sig)))
@@ -232,6 +244,14 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
             Ok(Ty::Unknown)
         }
         Def::Function(f) => type_for_fn(db, f),
+        Def::Struct(s) => Ok(Ty::Adt {
+            def_id,
+            name: s.name(db)?,
+        }),
+        Def::Enum(e) => Ok(Ty::Adt {
+            def_id,
+            name: e.name(db)?,
+        }),
         Def::Item => {
             log::debug!("trying to get type for item of unknown type {:?}", def_id);
             Ok(Ty::Unknown)
@@ -492,7 +512,7 @@ fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> {
                 };
                 let cast_ty = e
                     .type_ref()
-                    .map(|t| Ty::new(self.db, t))
+                    .map(|t| Ty::new(self.db, &self.module, t))
                     .unwrap_or(Ok(Ty::Unknown))?;
                 // TODO do the coercion...
                 cast_ty
@@ -526,7 +546,7 @@ fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> {
             match stmt {
                 ast::Stmt::LetStmt(stmt) => {
                     let decl_ty = if let Some(type_ref) = stmt.type_ref() {
-                        Ty::new(self.db, type_ref)?
+                        Ty::new(self.db, &self.module, type_ref)?
                     } else {
                         Ty::Unknown
                     };
@@ -576,7 +596,7 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceR
                 continue;
             };
             if let Some(type_ref) = param.type_ref() {
-                let ty = Ty::new(db, type_ref)?;
+                let ty = Ty::new(db, &ctx.module, type_ref)?;
                 ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty);
             } else {
                 // TODO self param
index b6c02cd80c2bbed09c6f33578e92fc65fdf18cde..170eef1471b4a9256a297d105235d85ea6d81d1c 100644 (file)
@@ -68,6 +68,29 @@ fn test() {
     );
 }
 
+#[test]
+fn infer_struct() {
+    check_inference(
+        r#"
+struct A {
+    b: B,
+    c: C,
+}
+struct B;
+struct C(usize);
+
+fn test() {
+    let c = C(1);
+    B;
+    let a: A = A { b: B, c: C() };
+    a.b;
+    a.c;
+}
+"#,
+        "0004_struct.txt",
+    );
+}
+
 fn infer(content: &str) -> String {
     let (db, _, file_id) = MockDatabase::with_single_file(content);
     let source_file = db.source_file(file_id);
diff --git a/crates/ra_hir/src/ty/tests/data/0004_struct.txt b/crates/ra_hir/src/ty/tests/data/0004_struct.txt
new file mode 100644 (file)
index 0000000..70ad055
--- /dev/null
@@ -0,0 +1,10 @@
+[86; 90) 'C(1)': [unknown]
+[72; 153) '{     ...a.c; }': ()
+[86; 87) 'C': C
+[107; 108) 'a': A
+[114; 132) 'A { b:... C() }': [unknown]
+[138; 141) 'a.b': [unknown]
+[147; 150) 'a.c': [unknown]
+[96; 97) 'B': B
+[88; 89) '1': [unknown]
+[82; 83) 'c': [unknown]