From b5b68f2094d49cacde6d7f0c49f521a0b25f34bd Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 24 Dec 2018 19:07:48 +0100 Subject: Add basic HIR and types for structs/enums --- crates/ra_analysis/src/db.rs | 2 + crates/ra_hir/src/adt.rs | 114 ++++++++++++++++++++++++ crates/ra_hir/src/db.rs | 11 +++ crates/ra_hir/src/lib.rs | 18 +++- crates/ra_hir/src/mock.rs | 2 + crates/ra_hir/src/query_definitions.rs | 21 ++++- crates/ra_hir/src/ty.rs | 72 +++++++++------ crates/ra_hir/src/ty/tests.rs | 23 +++++ crates/ra_hir/src/ty/tests/data/0004_struct.txt | 10 +++ 9 files changed, 244 insertions(+), 29 deletions(-) create mode 100644 crates/ra_hir/src/adt.rs create mode 100644 crates/ra_hir/src/ty/tests/data/0004_struct.txt (limited to 'crates') diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index 780a84291..7043a0f4d 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs @@ -95,6 +95,8 @@ salsa::database_storage! { 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 index 000000000..a2d228593 --- /dev/null +++ b/crates/ra_hir/src/adt.rs @@ -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 { + 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 { + 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), + Tuple(Vec), + 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 + } + } +} diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index d94f75857..113790ee9 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -15,6 +15,7 @@ use crate::{ module::{ModuleId, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems}}, ty::{InferenceResult, Ty}, + adt::{StructData, EnumData}, }; salsa::query_group! { @@ -31,6 +32,16 @@ pub trait HirDatabase: SyntaxDatabase use fn query_definitions::fn_syntax; } + fn struct_data(def_id: DefId) -> Cancelable> { + type StructDataQuery; + use fn query_definitions::struct_data; + } + + fn enum_data(def_id: DefId) -> Cancelable> { + type EnumDataQuery; + use fn query_definitions::enum_data; + } + fn infer(fn_id: FnId) -> Cancelable> { type InferQuery; use fn query_definitions::infer; diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index a0d99a84d..7e9824de9 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -25,6 +25,7 @@ pub mod source_binder; mod krate; mod module; mod function; +mod adt; mod ty; use std::ops::Index; @@ -42,6 +43,7 @@ pub use self::{ 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 @@ ra_db::impl_numeric_id!(DefId); pub(crate) enum DefKind { Module, Function, + Struct, + Enum, Item, } @@ -73,8 +77,8 @@ impl 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 @@ impl DefLoc { pub enum Def { Module(Module), Function(Function), + Struct(Struct), + Enum(Enum), Item, } @@ -114,6 +120,14 @@ impl DefId { 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) diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index b5a997170..ead2b8414 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -193,6 +193,8 @@ salsa::database_storage! { 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; } } } diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index b654af920..72440d1d6 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs @@ -19,7 +19,8 @@ use crate::{ 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 Cancelable> { + 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> { + 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 { let mut res = SourceFileItems::new(file_id); let source_file = db.source_file(file_id); diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index c759d4c8b..f86b749ec 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -17,7 +17,7 @@ use ra_syntax::{ 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; -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct FnSig { input: Vec, output: Ty, } impl Ty { - pub fn new(_db: &impl HirDatabase, node: ast::TypeRef) -> Cancelable { + pub(crate) fn new( + db: &impl HirDatabase, + module: &Module, + node: ast::TypeRef, + ) -> Cancelable { 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 @@ impl fmt::Display for Ty { } write!(f, ") -> {}", sig.output) } + Ty::Adt { name, .. } => write!(f, "{}", name), Ty::Unknown => write!(f, "[unknown]"), } } @@ -196,6 +207,7 @@ impl fmt::Display for Ty { pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { 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 { 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 { 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 { 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 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; 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 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { 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 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 index 000000000..70ad055ff --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt @@ -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] -- cgit v1.2.3