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_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 +++ 8 files changed, 242 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/ra_hir') 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 From 4ff161852016c6c15954d6f30bd637834a2b2b68 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 24 Dec 2018 20:32:39 +0100 Subject: Do name resolution by namespace (types/values) --- crates/ra_hir/src/lib.rs | 25 ++-- crates/ra_hir/src/module.rs | 42 +++++-- crates/ra_hir/src/module/nameres.rs | 154 ++++++++++++++++++------ crates/ra_hir/src/module/nameres/tests.rs | 6 +- crates/ra_hir/src/ty.rs | 22 ++-- crates/ra_hir/src/ty/tests/data/0004_struct.txt | 4 +- 6 files changed, 182 insertions(+), 71 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 7e9824de9..81526fe9c 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -41,7 +41,7 @@ use crate::{ pub use self::{ path::{Path, PathKind}, krate::Crate, - module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution}, + module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, function::{Function, FnScopes}, adt::{Struct, Enum}, }; @@ -61,6 +61,8 @@ pub(crate) enum DefKind { Struct, Enum, Item, + + StructCtor, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -72,18 +74,18 @@ pub struct DefLoc { } impl DefKind { - pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> Option { + pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs { match kind { - SyntaxKind::FN_DEF => Some(DefKind::Function), - SyntaxKind::MODULE => Some(DefKind::Module), + SyntaxKind::FN_DEF => PerNs::values(DefKind::Function), + SyntaxKind::MODULE => PerNs::types(DefKind::Module), + SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor), + SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum), // These define items, but don't have their own DefKinds yet: - 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), - SyntaxKind::STATIC_DEF => Some(DefKind::Item), - _ => None, + SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item), + SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item), + SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item), + SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item), + _ => PerNs::none(), } } } @@ -128,6 +130,7 @@ impl DefId { let enum_def = Enum::new(self); Def::Enum(enum_def) } + DefKind::StructCtor => Def::Item, DefKind::Item => Def::Item, }; Ok(res) diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index 891119953..e1a0e4b59 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs @@ -17,7 +17,7 @@ use crate::{ arena::{Arena, Id}, }; -pub use self::nameres::{ModuleScope, Resolution}; +pub use self::nameres::{ModuleScope, Resolution, Namespace, PerNs}; /// `Module` is API entry point to get all the information /// about a particular module. @@ -115,16 +115,29 @@ impl Module { Ok(res) } - pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable> { - let mut curr = match path.kind { - PathKind::Crate => self.crate_root(), - PathKind::Self_ | PathKind::Plain => self.clone(), - PathKind::Super => ctry!(self.parent()), - } - .def_id(db); + pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable> { + let mut curr_per_ns = PerNs::types( + match path.kind { + PathKind::Crate => self.crate_root(), + PathKind::Self_ | PathKind::Plain => self.clone(), + PathKind::Super => { + if let Some(p) = self.parent() { + p + } else { + return Ok(PerNs::none()); + } + } + } + .def_id(db), + ); let segments = path.segments; for name in segments.iter() { + let curr = if let Some(r) = curr_per_ns.as_ref().take(Namespace::Types) { + r + } else { + return Ok(PerNs::none()); + }; let module = match curr.loc(db) { DefLoc { kind: DefKind::Module, @@ -132,12 +145,17 @@ impl Module { module_id, .. } => Module::new(db, source_root_id, module_id)?, - _ => return Ok(None), + // TODO here would be the place to handle enum variants... + _ => return Ok(PerNs::none()), }; let scope = module.scope(db)?; - curr = ctry!(ctry!(scope.get(&name)).def_id); + curr_per_ns = if let Some(r) = scope.get(&name) { + r.def_id + } else { + return Ok(PerNs::none()); + }; } - Ok(Some(curr)) + Ok(curr_per_ns) } pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { @@ -145,7 +163,7 @@ impl Module { } } -/// Phisically, rust source is organized as a set of files, but logically it is +/// Physically, rust source is organized as a set of files, but logically it is /// organized as a tree of modules. Usually, a single file corresponds to a /// single module, but it is not nessary the case. /// diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 0b152a406..33c9d93c2 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs @@ -118,22 +118,96 @@ enum ImportKind { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Resolution { /// None for unresolved - pub def_id: Option, + pub def_id: PerNs, /// ident by whitch this is imported into local scope. pub import: Option, } -// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -// enum Namespace { -// Types, -// Values, -// } +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Namespace { + Types, + Values, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct PerNs { + pub types: Option, + pub values: Option, +} + +impl PerNs { + pub fn none() -> PerNs { + PerNs { + types: None, + values: None, + } + } + + pub fn values(t: T) -> PerNs { + PerNs { + types: None, + values: Some(t), + } + } + + pub fn types(t: T) -> PerNs { + PerNs { + types: Some(t), + values: None, + } + } + + pub fn both(types: T, values: T) -> PerNs { + PerNs { + types: Some(types), + values: Some(values), + } + } + + pub fn is_none(&self) -> bool { + self.types.is_none() && self.values.is_none() + } + + pub fn take(self, namespace: Namespace) -> Option { + match namespace { + Namespace::Types => self.types, + Namespace::Values => self.values, + } + } + + pub fn take_types(self) -> Option { + self.types + } + + pub fn take_values(self) -> Option { + self.values + } -// #[derive(Debug)] -// struct PerNs { -// types: Option, -// values: Option, -// } + pub fn get(&self, namespace: Namespace) -> Option<&T> { + self.as_ref().take(namespace) + } + + pub fn as_ref(&self) -> PerNs<&T> { + PerNs { + types: self.types.as_ref(), + values: self.values.as_ref(), + } + } + + pub fn and_then(self, f: impl Fn(T) -> Option) -> PerNs { + PerNs { + types: self.types.and_then(&f), + values: self.values.and_then(&f), + } + } + + pub fn map(self, f: impl Fn(T) -> U) -> PerNs { + PerNs { + types: self.types.map(&f), + values: self.values.map(&f), + } + } +} impl InputModuleItems { pub(crate) fn new<'a>( @@ -254,7 +328,7 @@ where for dep in krate.dependencies(self.db) { if let Some(module) = dep.krate.root_module(self.db)? { let def_id = module.def_id(self.db); - self.add_module_item(&mut module_items, dep.name, def_id); + self.add_module_item(&mut module_items, dep.name, PerNs::types(def_id)); } } }; @@ -265,7 +339,7 @@ where module_items.items.insert( name.clone(), Resolution { - def_id: None, + def_id: PerNs::none(), import: Some(import), }, ); @@ -277,18 +351,23 @@ where if item.kind == MODULE { continue; } - let def_loc = DefLoc { - kind: DefKind::for_syntax_kind(item.kind).unwrap_or(DefKind::Item), - source_root_id: self.source_root, - module_id, - source_item_id: SourceItemId { - file_id, - item_id: Some(item.id), - }, - }; - let def_id = def_loc.id(self.db); + // depending on the item kind, the location can define something in + // the values namespace, the types namespace, or both + let kind = DefKind::for_syntax_kind(item.kind); + let def_id = kind.map(|k| { + let def_loc = DefLoc { + kind: k, + source_root_id: self.source_root, + module_id, + source_item_id: SourceItemId { + file_id, + item_id: Some(item.id), + }, + }; + def_loc.id(self.db) + }); let resolution = Resolution { - def_id: Some(def_id), + def_id, import: None, }; module_items.items.insert(item.name.clone(), resolution); @@ -303,16 +382,16 @@ where source_item_id: module_id.source(&self.module_tree).0, }; let def_id = def_loc.id(self.db); - self.add_module_item(&mut module_items, name, def_id); + self.add_module_item(&mut module_items, name, PerNs::types(def_id)); } self.result.per_module.insert(module_id, module_items); Ok(()) } - fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, def_id: DefId) { + fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, def_id: PerNs) { let resolution = Resolution { - def_id: Some(def_id), + def_id, import: None, }; module_items.items.insert(name, resolution); @@ -347,15 +426,17 @@ where let is_last = i == import.path.segments.len() - 1; let def_id = match self.result.per_module[&curr].items.get(name) { - None => return Ok(()), - Some(res) => match res.def_id { - Some(it) => it, - None => return Ok(()), - }, + Some(res) if !res.def_id.is_none() => res.def_id, + _ => return Ok(()), }; if !is_last { - curr = match def_id.loc(self.db) { + let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { + d + } else { + return Ok(()); + }; + curr = match type_def_id.loc(self.db) { DefLoc { kind: DefKind::Module, module_id: target_module_id, @@ -370,10 +451,11 @@ where segments: import.path.segments[i + 1..].iter().cloned().collect(), kind: PathKind::Crate, }; - if let Some(def_id) = module.resolve_path(self.db, path)? { + let def_id = module.resolve_path(self.db, path)?; + if !def_id.is_none() { self.update(module_id, |items| { let res = Resolution { - def_id: Some(def_id), + def_id: def_id, import: Some(ptr), }; items.items.insert(name.clone(), res); @@ -387,7 +469,7 @@ where } else { self.update(module_id, |items| { let res = Resolution { - def_id: Some(def_id), + def_id: def_id, import: Some(ptr), }; items.items.insert(name.clone(), res); diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs index 3e29c3954..03ea5c1d6 100644 --- a/crates/ra_hir/src/module/nameres/tests.rs +++ b/crates/ra_hir/src/module/nameres/tests.rs @@ -40,7 +40,7 @@ fn item_map_smoke_test() { ); let name = SmolStr::from("Baz"); let resolution = &item_map.per_module[&module_id].items[&name]; - assert!(resolution.def_id.is_some()); + assert!(resolution.def_id.take_types().is_some()); } #[test] @@ -59,7 +59,7 @@ fn test_self() { ); let name = SmolStr::from("Baz"); let resolution = &item_map.per_module[&module_id].items[&name]; - assert!(resolution.def_id.is_some()); + assert!(resolution.def_id.take_types().is_some()); } #[test] @@ -92,7 +92,7 @@ fn item_map_across_crates() { let name = SmolStr::from("Baz"); let resolution = &item_map.per_module[&module_id].items[&name]; - assert!(resolution.def_id.is_some()); + assert!(resolution.def_id.take_types().is_some()); } #[test] diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index f86b749ec..429292cfc 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -15,7 +15,11 @@ use ra_syntax::{ SyntaxNodeRef }; -use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase}; +use crate::{ + Def, DefId, FnScopes, Module, Function, + Path, db::HirDatabase, + module::nameres::Namespace +}; #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum Ty { @@ -149,11 +153,12 @@ impl Ty { } // Resolve in module (in type namespace) - let resolved = if let Some(r) = module.resolve_path(db, path)? { - r - } else { - return Ok(Ty::Unknown); - }; + let resolved = + if let Some(r) = module.resolve_path(db, path)?.take(Namespace::Types) { + r + } else { + return Ok(Ty::Unknown); + }; let ty = db.type_for_def(resolved)?; ty } @@ -325,7 +330,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; // resolve in module - let resolved = ctry!(self.module.resolve_path(self.db, path)?); + let resolved = ctry!(self + .module + .resolve_path(self.db, path)? + .take(Namespace::Values)); let ty = self.db.type_for_def(resolved)?; // TODO we will need to add type variables for type parameters etc. here Ok(Some(ty)) 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 70ad055ff..a4371c5a5 100644 --- a/crates/ra_hir/src/ty/tests/data/0004_struct.txt +++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt @@ -1,10 +1,10 @@ [86; 90) 'C(1)': [unknown] [72; 153) '{ ...a.c; }': () -[86; 87) 'C': C +[86; 87) 'C': [unknown] [107; 108) 'a': A [114; 132) 'A { b:... C() }': [unknown] [138; 141) 'a.b': [unknown] [147; 150) 'a.c': [unknown] -[96; 97) 'B': B +[96; 97) 'B': [unknown] [88; 89) '1': [unknown] [82; 83) 'c': [unknown] -- cgit v1.2.3 From 6fcd38cc81bdcc9921da767872dfce65ee7d2d27 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 24 Dec 2018 21:00:14 +0100 Subject: Infer result of struct literals, and recurse into their child expressions --- crates/ra_hir/src/adt.rs | 27 +++++- crates/ra_hir/src/ty.rs | 119 +++++++++++++++++------- crates/ra_hir/src/ty/tests.rs | 2 +- crates/ra_hir/src/ty/tests/data/0004_struct.txt | 12 ++- 4 files changed, 117 insertions(+), 43 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index a2d228593..ee270ac45 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use ra_syntax::{SmolStr, ast::{self, NameOwner}}; use crate::{ @@ -15,6 +17,14 @@ impl Struct { Struct { def_id } } + pub fn def_id(&self) -> DefId { + self.def_id + } + + pub fn struct_data(&self, db: &impl HirDatabase) -> Cancelable> { + Ok(db.struct_data(self.def_id)?) + } + pub fn name(&self, db: &impl HirDatabase) -> Cancelable { Ok(db.struct_data(self.def_id)?.name.clone()) } @@ -23,7 +33,7 @@ impl Struct { #[derive(Debug, Clone, PartialEq, Eq)] pub struct StructData { name: SmolStr, - variant_data: VariantData, + variant_data: Arc, } impl StructData { @@ -33,8 +43,17 @@ impl StructData { .map(|n| n.text()) .unwrap_or(SmolStr::new("[error]")); let variant_data = VariantData::Unit; // TODO implement this + let variant_data = Arc::new(variant_data); StructData { name, variant_data } } + + pub fn name(&self) -> &SmolStr { + &self.name + } + + pub fn variant_data(&self) -> &Arc { + &self.variant_data + } } pub struct Enum { @@ -46,6 +65,10 @@ impl Enum { Enum { def_id } } + pub fn def_id(&self) -> DefId { + self.def_id + } + pub fn name(&self, db: &impl HirDatabase) -> Cancelable { Ok(db.enum_data(self.def_id)?.name.clone()) } @@ -54,7 +77,7 @@ impl Enum { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumData { name: SmolStr, - variants: Vec<(SmolStr, VariantData)>, + variants: Vec<(SmolStr, Arc)>, } impl EnumData { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 429292cfc..386af8120 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -16,9 +16,9 @@ use ra_syntax::{ }; use crate::{ - Def, DefId, FnScopes, Module, Function, - Path, db::HirDatabase, - module::nameres::Namespace + Def, DefId, FnScopes, Module, Function, Struct, Path, + db::HirDatabase, + adt::VariantData, }; #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -125,6 +125,37 @@ pub struct FnSig { } impl Ty { + pub(crate) fn new_from_ast_path( + db: &impl HirDatabase, + module: &Module, + path: ast::Path, + ) -> Cancelable { + let path = if let Some(p) = Path::from_ast(path) { + p + } else { + return Ok(Ty::Unknown); + }; + if path.is_ident() { + let name = &path.segments[0]; + if let Some(int_ty) = primitive::IntTy::from_string(&name) { + return Ok(Ty::Int(int_ty)); + } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { + return Ok(Ty::Uint(uint_ty)); + } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { + return Ok(Ty::Float(float_ty)); + } + } + + // Resolve in module (in type namespace) + let resolved = if let Some(r) = module.resolve_path(db, path)?.take_types() { + r + } else { + return Ok(Ty::Unknown); + }; + let ty = db.type_for_def(resolved)?; + Ok(ty) + } + pub(crate) fn new( db: &impl HirDatabase, module: &Module, @@ -136,31 +167,11 @@ impl Ty { TupleType(_inner) => Ty::Unknown, // TODO NeverType(..) => Ty::Never, PathType(inner) => { - let path = if let Some(p) = inner.path().and_then(Path::from_ast) { - p + if let Some(path) = inner.path() { + Ty::new_from_ast_path(db, module, path)? } else { - return Ok(Ty::Unknown); - }; - if path.is_ident() { - let name = &path.segments[0]; - if let Some(int_ty) = primitive::IntTy::from_string(&name) { - return Ok(Ty::Int(int_ty)); - } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { - return Ok(Ty::Uint(uint_ty)); - } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { - return Ok(Ty::Float(float_ty)); - } + Ty::Unknown } - - // Resolve in module (in type namespace) - let resolved = - if let Some(r) = module.resolve_path(db, path)?.take(Namespace::Types) { - r - } else { - return Ok(Ty::Unknown); - }; - let ty = db.type_for_def(resolved)?; - ty } PointerType(_inner) => Ty::Unknown, // TODO ArrayType(_inner) => Ty::Unknown, // TODO @@ -236,6 +247,13 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { Ok(Ty::FnPtr(Arc::new(sig))) } +pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable { + Ok(Ty::Adt { + def_id: s.def_id(), + name: s.name(db)?, + }) +} + // TODO this should probably be per namespace (i.e. types vs. values), since for // a tuple struct `struct Foo(Bar)`, Foo has function type as a value, but // defines the struct type Foo when used in the type namespace. rustc has a @@ -249,10 +267,7 @@ 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::Struct(s) => type_for_struct(db, s), Def::Enum(e) => Ok(Ty::Adt { def_id, name: e.name(db)?, @@ -330,15 +345,36 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; // resolve in module - let resolved = ctry!(self - .module - .resolve_path(self.db, path)? - .take(Namespace::Values)); + let resolved = ctry!(self.module.resolve_path(self.db, path)?.take_values()); let ty = self.db.type_for_def(resolved)?; // TODO we will need to add type variables for type parameters etc. here Ok(Some(ty)) } + fn resolve_variant( + &self, + path: Option, + ) -> Cancelable<(Ty, Option>)> { + let path = if let Some(path) = path.and_then(Path::from_ast) { + path + } else { + return Ok((Ty::Unknown, None)); + }; + let def_id = if let Some(def_id) = self.module.resolve_path(self.db, path)?.take_types() { + def_id + } else { + return Ok((Ty::Unknown, None)); + }; + Ok(match def_id.resolve(self.db)? { + Def::Struct(s) => { + let struct_data = self.db.struct_data(def_id)?; + let ty = type_for_struct(self.db, s)?; + (ty, Some(struct_data.variant_data().clone())) + } + _ => (Ty::Unknown, None), + }) + } + fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable { let ty = match expr { ast::Expr::IfExpr(e) => { @@ -488,7 +524,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ast::Expr::Label(_e) => Ty::Unknown, ast::Expr::ReturnExpr(e) => { if let Some(e) = e.expr() { - // TODO unify with return type + // TODO unify with / expect return type self.infer_expr(e)?; }; Ty::Never @@ -497,7 +533,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // Can this even occur outside of a match expression? Ty::Unknown } - ast::Expr::StructLit(_e) => Ty::Unknown, + ast::Expr::StructLit(e) => { + let (ty, variant_data) = self.resolve_variant(e.path())?; + if let Some(nfl) = e.named_field_list() { + for field in nfl.fields() { + if let Some(e) = field.expr() { + // TODO unify with / expect field type + self.infer_expr(e)?; + } + } + } + ty + } ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { // Can this even occur outside of a struct literal? Ty::Unknown diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 170eef147..9bb58ec85 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -82,7 +82,7 @@ struct C(usize); fn test() { let c = C(1); B; - let a: A = A { b: B, c: C() }; + let a: A = A { b: B, c: C(1) }; a.b; a.c; } 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 a4371c5a5..41357749f 100644 --- a/crates/ra_hir/src/ty/tests/data/0004_struct.txt +++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt @@ -1,10 +1,14 @@ [86; 90) 'C(1)': [unknown] -[72; 153) '{ ...a.c; }': () +[121; 122) 'B': [unknown] [86; 87) 'C': [unknown] +[129; 130) '1': [unknown] [107; 108) 'a': A -[114; 132) 'A { b:... C() }': [unknown] -[138; 141) 'a.b': [unknown] -[147; 150) 'a.c': [unknown] +[127; 128) 'C': [unknown] +[139; 142) 'a.b': [unknown] +[114; 133) 'A { b:...C(1) }': A +[148; 151) 'a.c': [unknown] +[72; 154) '{ ...a.c; }': () [96; 97) 'B': [unknown] [88; 89) '1': [unknown] [82; 83) 'c': [unknown] +[127; 131) 'C(1)': [unknown] -- cgit v1.2.3 From 07a72859650254d8ba326e2c9683ae52164cb3b5 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 13:31:30 +0100 Subject: Collect field data for structs/enum variants --- crates/ra_hir/src/adt.rs | 69 ++++++++++++++++++++++++++++++---- crates/ra_hir/src/function.rs | 3 +- crates/ra_hir/src/lib.rs | 6 +++ crates/ra_hir/src/query_definitions.rs | 10 ++++- crates/ra_hir/src/ty.rs | 11 +++++- 5 files changed, 87 insertions(+), 12 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index ee270ac45..b44f59f0b 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -1,10 +1,11 @@ use std::sync::Arc; -use ra_syntax::{SmolStr, ast::{self, NameOwner}}; +use ra_syntax::{SmolStr, ast::{self, NameOwner, StructFlavor}}; use crate::{ DefId, Cancelable, db::{HirDatabase}, + module::Module, ty::{Ty}, }; @@ -37,14 +38,18 @@ pub struct StructData { } impl StructData { - pub(crate) fn new(struct_def: ast::StructDef) -> StructData { + pub(crate) fn new( + db: &impl HirDatabase, + module: &Module, + struct_def: ast::StructDef, + ) -> Cancelable { let name = struct_def .name() .map(|n| n.text()) .unwrap_or(SmolStr::new("[error]")); - let variant_data = VariantData::Unit; // TODO implement this + let variant_data = VariantData::new(db, module, struct_def.flavor())?; let variant_data = Arc::new(variant_data); - StructData { name, variant_data } + Ok(StructData { name, variant_data }) } pub fn name(&self) -> &SmolStr { @@ -81,13 +86,30 @@ pub struct EnumData { } impl EnumData { - pub(crate) fn new(enum_def: ast::EnumDef) -> Self { + pub(crate) fn new( + db: &impl HirDatabase, + module: &Module, + enum_def: ast::EnumDef, + ) -> Cancelable { let name = enum_def .name() .map(|n| n.text()) .unwrap_or(SmolStr::new("[error]")); - let variants = Vec::new(); // TODO implement this - EnumData { name, variants } + let variants = if let Some(evl) = enum_def.variant_list() { + evl.variants() + .map(|v| { + Ok(( + v.name() + .map(|n| n.text()) + .unwrap_or_else(|| SmolStr::new("[error]")), + Arc::new(VariantData::new(db, module, v.flavor())?), + )) + }) + .collect::>()? + } else { + Vec::new() + }; + Ok(EnumData { name, variants }) } } @@ -107,6 +129,39 @@ pub enum VariantData { } impl VariantData { + pub fn new(db: &impl HirDatabase, module: &Module, flavor: StructFlavor) -> Cancelable { + Ok(match flavor { + StructFlavor::Tuple(fl) => { + let fields = fl + .fields() + .enumerate() + .map(|(i, fd)| { + Ok(StructField { + name: SmolStr::new(i.to_string()), + ty: Ty::new_opt(db, &module, fd.type_ref())?, + }) + }) + .collect::>()?; + VariantData::Tuple(fields) + } + StructFlavor::Named(fl) => { + let fields = fl + .fields() + .map(|fd| { + Ok(StructField { + name: fd + .name() + .map(|n| n.text()) + .unwrap_or_else(|| SmolStr::new("[error]")), + ty: Ty::new_opt(db, &module, fd.type_ref())?, + }) + }) + .collect::>()?; + VariantData::Struct(fields) + } + StructFlavor::Unit => VariantData::Unit, + }) + } pub fn fields(&self) -> &[StructField] { match *self { VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields, diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs index d36477b48..01f0f3a66 100644 --- a/crates/ra_hir/src/function.rs +++ b/crates/ra_hir/src/function.rs @@ -46,8 +46,7 @@ impl Function { } pub fn module(&self, db: &impl HirDatabase) -> Cancelable { - let loc = self.fn_id.0.loc(db); - Module::new(db, loc.source_root_id, loc.module_id) + self.fn_id.0.module(db) } } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 81526fe9c..796970d8a 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -135,6 +135,12 @@ impl DefId { }; Ok(res) } + + /// For a module, returns that module; for any other def, returns the containing module. + pub fn module(self, db: &impl HirDatabase) -> Cancelable { + let loc = self.loc(db); + Module::new(db, loc.source_root_id, loc.module_id) + } } /// Identifier of item within a specific file. This is stable over reparses, so diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index 72440d1d6..405e359f1 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs @@ -52,7 +52,12 @@ pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable Cancelable> { @@ -61,7 +66,8 @@ pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable Arc { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 386af8120..e2428a37f 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -156,6 +156,15 @@ impl Ty { Ok(ty) } + pub(crate) fn new_opt( + db: &impl HirDatabase, + module: &Module, + node: Option, + ) -> Cancelable { + node.map(|n| Ty::new(db, module, n)) + .unwrap_or(Ok(Ty::Unknown)) + } + pub(crate) fn new( db: &impl HirDatabase, module: &Module, @@ -534,7 +543,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::Unknown } ast::Expr::StructLit(e) => { - let (ty, variant_data) = self.resolve_variant(e.path())?; + let (ty, _variant_data) = self.resolve_variant(e.path())?; if let Some(nfl) = e.named_field_list() { for field in nfl.fields() { if let Some(e) = field.expr() { -- cgit v1.2.3 From 55c941cd9fb90c9340f01981e113aabd058b185b Mon Sep 17 00:00:00 2001 From: Florian Diebold 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 +++-- 3 files changed, 44 insertions(+), 3 deletions(-) (limited to 'crates/ra_hir') 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> { + Ok(db.struct_data(self.def_id)?.variant_data.clone()) + } + pub fn struct_data(&self, db: &impl HirDatabase) -> Cancelable> { 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 { + 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) -> Cancelable { + if let Some(e) = expr { + self.infer_expr(e) + } else { + Ok(Ty::Unknown) + } + } + fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable { 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::().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] -- cgit v1.2.3 From ab0b63992be0cec4999810096a53b40f63f90349 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 15:15:40 +0100 Subject: Implement basic completion for fields --- crates/ra_hir/src/adt.rs | 14 +++++++++++++- crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/ty.rs | 5 +++-- 3 files changed, 17 insertions(+), 3 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index 03770ed7d..e65f8deb8 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -124,6 +124,15 @@ pub struct StructField { ty: Ty, } +impl StructField { + pub fn name(&self) -> SmolStr { + self.name.clone() + } + pub fn ty(&self) -> Ty { + self.ty.clone() + } +} + /// Fields of an enum variant or struct #[derive(Debug, Clone, PartialEq, Eq)] pub enum VariantData { @@ -168,7 +177,10 @@ impl VariantData { } pub(crate) fn get_field_ty(&self, field_name: &str) -> Option { - self.fields().iter().find(|f| f.name == field_name).map(|f| f.ty.clone()) + self.fields() + .iter() + .find(|f| f.name == field_name) + .map(|f| f.ty.clone()) } pub fn fields(&self) -> &[StructField] { diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 796970d8a..68fdbb7ea 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -44,6 +44,7 @@ pub use self::{ module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, function::{Function, FnScopes}, adt::{Struct, Enum}, + ty::Ty, }; pub use self::function::FnSignatureInfo; diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index f931f3c87..83da13f1a 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -574,7 +574,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match receiver_ty { Ty::Tuple(fields) => { let i = text.parse::().ok(); - i.and_then(|i| fields.get(i).cloned()).unwrap_or(Ty::Unknown) + 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)? { @@ -589,7 +590,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } else { Ty::Unknown } - }, + } ast::Expr::TryExpr(e) => { let _inner_ty = if let Some(e) = e.expr() { self.infer_expr(e)? -- cgit v1.2.3 From 3befd1a9e82809fef5bc68950d3265dbcbbd5527 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 15:44:10 +0100 Subject: Cleanup --- crates/ra_hir/src/ty.rs | 162 +++++++++++------------------------------------- 1 file changed, 37 insertions(+), 125 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 83da13f1a..7b8dbe6b7 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -239,19 +239,11 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { .param_list() .map(|pl| { pl.params() - .map(|p| { - p.type_ref() - .map(|t| Ty::new(db, &module, t)) - .unwrap_or(Ok(Ty::Unknown)) - }) + .map(|p| Ty::new_opt(db, &module, p.type_ref())) .collect() }) .unwrap_or_else(|| Ok(Vec::new()))?; - let output = node - .ret_type() - .and_then(|rt| rt.type_ref()) - .map(|t| Ty::new(db, &module, t)) - .unwrap_or(Ok(Ty::Unknown))?; + let output = Ty::new_opt(db, &module, node.ret_type().and_then(|rt| rt.type_ref()))?; let sig = FnSig { input, output }; Ok(Ty::FnPtr(Arc::new(sig))) } @@ -263,11 +255,6 @@ pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable { }) } -// TODO this should probably be per namespace (i.e. types vs. values), since for -// a tuple struct `struct Foo(Bar)`, Foo has function type as a value, but -// defines the struct type Foo when used in the type namespace. rustc has a -// separate DefId for the constructor, but with the current DefId approach, that -// seems complicated. pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable { let def = def_id.resolve(db)?; match def { @@ -396,22 +383,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty = match expr { ast::Expr::IfExpr(e) => { if let Some(condition) = e.condition() { - if let Some(e) = condition.expr() { - // TODO if no pat, this should be bool - self.infer_expr(e)?; - } + // TODO if no pat, this should be bool + self.infer_expr_opt(condition.expr())?; // TODO write type for pat }; - let if_ty = if let Some(block) = e.then_branch() { - self.infer_block(block)? - } else { - Ty::Unknown - }; - let else_ty = if let Some(block) = e.else_branch() { - self.infer_block(block)? - } else { - Ty::Unknown - }; + let if_ty = self.infer_block_opt(e.then_branch())?; + let else_ty = self.infer_block_opt(e.else_branch())?; if let Some(ty) = self.unify(&if_ty, &else_ty) { ty } else { @@ -419,62 +396,37 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::Unknown } } - ast::Expr::BlockExpr(e) => { - if let Some(block) = e.block() { - self.infer_block(block)? - } else { - Ty::Unknown - } - } + ast::Expr::BlockExpr(e) => self.infer_block_opt(e.block())?, ast::Expr::LoopExpr(e) => { - if let Some(block) = e.loop_body() { - self.infer_block(block)?; - }; + self.infer_block_opt(e.loop_body())?; // TODO never, or the type of the break param Ty::Unknown } ast::Expr::WhileExpr(e) => { if let Some(condition) = e.condition() { - if let Some(e) = condition.expr() { - // TODO if no pat, this should be bool - self.infer_expr(e)?; - } + // TODO if no pat, this should be bool + self.infer_expr_opt(condition.expr())?; // TODO write type for pat }; - if let Some(block) = e.loop_body() { - // TODO - self.infer_block(block)?; - }; + self.infer_block_opt(e.loop_body())?; // TODO always unit? Ty::Unknown } ast::Expr::ForExpr(e) => { - if let Some(expr) = e.iterable() { - self.infer_expr(expr)?; - } + let _iterable_ty = self.infer_expr_opt(e.iterable()); if let Some(_pat) = e.pat() { // TODO write type for pat } - if let Some(block) = e.loop_body() { - self.infer_block(block)?; - } + self.infer_block_opt(e.loop_body())?; // TODO always unit? Ty::Unknown } ast::Expr::LambdaExpr(e) => { - let _body_ty = if let Some(body) = e.body() { - self.infer_expr(body)? - } else { - Ty::Unknown - }; + let _body_ty = self.infer_expr_opt(e.body())?; Ty::Unknown } ast::Expr::CallExpr(e) => { - let callee_ty = if let Some(e) = e.expr() { - self.infer_expr(e)? - } else { - Ty::Unknown - }; + let callee_ty = self.infer_expr_opt(e.expr())?; if let Some(arg_list) = e.arg_list() { for arg in arg_list.args() { // TODO unify / expect argument type @@ -491,11 +443,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } ast::Expr::MethodCallExpr(e) => { - let _receiver_ty = if let Some(e) = e.expr() { - self.infer_expr(e)? - } else { - Ty::Unknown - }; + let _receiver_ty = self.infer_expr_opt(e.expr())?; if let Some(arg_list) = e.arg_list() { for arg in arg_list.args() { // TODO unify / expect argument type @@ -505,20 +453,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::Unknown } ast::Expr::MatchExpr(e) => { - let _ty = if let Some(match_expr) = e.expr() { - self.infer_expr(match_expr)? - } else { - Ty::Unknown - }; + let _ty = self.infer_expr_opt(e.expr())?; if let Some(match_arm_list) = e.match_arm_list() { for arm in match_arm_list.arms() { // TODO type the bindings in pat // TODO type the guard - let _ty = if let Some(e) = arm.expr() { - self.infer_expr(e)? - } else { - Ty::Unknown - }; + let _ty = self.infer_expr_opt(arm.expr())?; } // TODO unify all the match arm types Ty::Unknown @@ -531,19 +471,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown), ast::Expr::ContinueExpr(_e) => Ty::Never, ast::Expr::BreakExpr(_e) => Ty::Never, - ast::Expr::ParenExpr(e) => { - if let Some(e) = e.expr() { - self.infer_expr(e)? - } else { - Ty::Unknown - } - } + ast::Expr::ParenExpr(e) => self.infer_expr_opt(e.expr())?, ast::Expr::Label(_e) => Ty::Unknown, ast::Expr::ReturnExpr(e) => { - if let Some(e) = e.expr() { - // TODO unify with / expect return type - self.infer_expr(e)?; - }; + self.infer_expr_opt(e.expr())?; Ty::Never } ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { @@ -554,10 +485,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let (ty, _variant_data) = self.resolve_variant(e.path())?; if let Some(nfl) = e.named_field_list() { for field in nfl.fields() { - if let Some(e) = field.expr() { - // TODO unify with / expect field type - self.infer_expr(e)?; - } + // TODO unify with / expect field type + self.infer_expr_opt(field.expr())?; } } ty @@ -592,40 +521,21 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } ast::Expr::TryExpr(e) => { - let _inner_ty = if let Some(e) = e.expr() { - self.infer_expr(e)? - } else { - Ty::Unknown - }; + let _inner_ty = self.infer_expr_opt(e.expr())?; Ty::Unknown } ast::Expr::CastExpr(e) => { - let _inner_ty = if let Some(e) = e.expr() { - self.infer_expr(e)? - } else { - Ty::Unknown - }; - let cast_ty = e - .type_ref() - .map(|t| Ty::new(self.db, &self.module, t)) - .unwrap_or(Ok(Ty::Unknown))?; + let _inner_ty = self.infer_expr_opt(e.expr())?; + let cast_ty = Ty::new_opt(self.db, &self.module, e.type_ref())?; // TODO do the coercion... cast_ty } ast::Expr::RefExpr(e) => { - let _inner_ty = if let Some(e) = e.expr() { - self.infer_expr(e)? - } else { - Ty::Unknown - }; + let _inner_ty = self.infer_expr_opt(e.expr())?; Ty::Unknown } ast::Expr::PrefixExpr(e) => { - let _inner_ty = if let Some(e) = e.expr() { - self.infer_expr(e)? - } else { - Ty::Unknown - }; + let _inner_ty = self.infer_expr_opt(e.expr())?; Ty::Unknown } ast::Expr::RangeExpr(_e) => Ty::Unknown, @@ -636,15 +546,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ok(ty) } + fn infer_block_opt(&mut self, node: Option) -> Cancelable { + if let Some(b) = node { + self.infer_block(b) + } else { + Ok(Ty::Unknown) + } + } + fn infer_block(&mut self, node: ast::Block) -> Cancelable { for stmt in node.statements() { match stmt { ast::Stmt::LetStmt(stmt) => { - let decl_ty = if let Some(type_ref) = stmt.type_ref() { - Ty::new(self.db, &self.module, type_ref)? - } else { - Ty::Unknown - }; + let decl_ty = Ty::new_opt(self.db, &self.module, stmt.type_ref())?; let ty = if let Some(expr) = stmt.initializer() { // TODO pass expectation let expr_ty = self.infer_expr(expr)?; @@ -659,9 +573,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; } ast::Stmt::ExprStmt(expr_stmt) => { - if let Some(expr) = expr_stmt.expr() { - self.infer_expr(expr)?; - } + self.infer_expr_opt(expr_stmt.expr())?; } } } -- cgit v1.2.3 From b96d3612390e070936a176571c946ad0cafa69a9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 17:55:50 +0100 Subject: Handle structs/enums with missing names a bit better --- crates/ra_hir/src/adt.rs | 22 ++++++++-------------- crates/ra_hir/src/ty.rs | 20 ++++++++++++++------ 2 files changed, 22 insertions(+), 20 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index e65f8deb8..40a45b831 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -30,14 +30,14 @@ impl Struct { Ok(db.struct_data(self.def_id)?) } - pub fn name(&self, db: &impl HirDatabase) -> Cancelable { + 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, + name: Option, variant_data: Arc, } @@ -47,17 +47,14 @@ impl StructData { module: &Module, struct_def: ast::StructDef, ) -> Cancelable { - let name = struct_def - .name() - .map(|n| n.text()) - .unwrap_or(SmolStr::new("[error]")); + let name = struct_def.name().map(|n| n.text()); let variant_data = VariantData::new(db, module, struct_def.flavor())?; let variant_data = Arc::new(variant_data); Ok(StructData { name, variant_data }) } - pub fn name(&self) -> &SmolStr { - &self.name + pub fn name(&self) -> Option<&SmolStr> { + self.name.as_ref() } pub fn variant_data(&self) -> &Arc { @@ -78,14 +75,14 @@ impl Enum { self.def_id } - pub fn name(&self, db: &impl HirDatabase) -> Cancelable { + 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, + name: Option, variants: Vec<(SmolStr, Arc)>, } @@ -95,10 +92,7 @@ impl EnumData { module: &Module, enum_def: ast::EnumDef, ) -> Cancelable { - let name = enum_def - .name() - .map(|n| n.text()) - .unwrap_or(SmolStr::new("[error]")); + let name = enum_def.name().map(|n| n.text()); let variants = if let Some(evl) = enum_def.variant_list() { evl.variants() .map(|v| { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 7b8dbe6b7..3674688ef 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -16,7 +16,7 @@ use ra_syntax::{ }; use crate::{ - Def, DefId, FnScopes, Module, Function, Struct, Path, + Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, db::HirDatabase, adt::VariantData, }; @@ -251,7 +251,18 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable { Ok(Ty::Adt { def_id: s.def_id(), - name: s.name(db)?, + name: s + .name(db)? + .unwrap_or_else(|| SmolStr::new("[unnamed struct]")), + }) +} + +pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable { + Ok(Ty::Adt { + def_id: s.def_id(), + name: s + .name(db)? + .unwrap_or_else(|| SmolStr::new("[unnamed enum]")), }) } @@ -264,10 +275,7 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable { } Def::Function(f) => type_for_fn(db, f), Def::Struct(s) => type_for_struct(db, s), - Def::Enum(e) => Ok(Ty::Adt { - def_id, - name: e.name(db)?, - }), + Def::Enum(e) => type_for_enum(db, e), Def::Item => { log::debug!("trying to get type for item of unknown type {:?}", def_id); Ok(Ty::Unknown) -- cgit v1.2.3 From 2870effd5c69941bbf32a44c0ee6d9d42e0b038d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 17:17:39 +0100 Subject: Implement reference / pointer types - parse them - infer types of & and * expressions --- crates/ra_hir/src/ty.rs | 84 ++++++++++++++++++++----- crates/ra_hir/src/ty/tests.rs | 22 +++++++ crates/ra_hir/src/ty/tests/data/0001_basics.txt | 4 +- crates/ra_hir/src/ty/tests/data/0005_refs.txt | 23 +++++++ 4 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 crates/ra_hir/src/ty/tests/data/0005_refs.txt (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 3674688ef..54aa6715c 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -11,7 +11,7 @@ use rustc_hash::{FxHashMap}; use ra_db::{LocalSyntaxPtr, Cancelable}; use ra_syntax::{ SmolStr, - ast::{self, AstNode, LoopBodyOwner, ArgListOwner}, + ast::{self, AstNode, LoopBodyOwner, ArgListOwner, PrefixOp}, SyntaxNodeRef }; @@ -21,6 +21,36 @@ use crate::{ adt::VariantData, }; +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum Mutability { + Shared, + Mut, +} + +impl Mutability { + pub fn from_mutable(mutable: bool) -> Mutability { + if mutable { + Mutability::Mut + } else { + Mutability::Shared + } + } + + pub fn as_keyword_for_ref(self) -> &'static str { + match self { + Mutability::Shared => "", + Mutability::Mut => "mut ", + } + } + + pub fn as_keyword_for_ptr(self) -> &'static str { + match self { + Mutability::Shared => "const ", + Mutability::Mut => "mut ", + } + } +} + #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum Ty { /// The primitive boolean type. Written as `bool`. @@ -56,12 +86,13 @@ pub enum Ty { /// The pointee of an array slice. Written as `[T]`. Slice(TyRef), - // A raw pointer. Written as `*mut T` or `*const T` - // RawPtr(TypeAndMut<'tcx>), + /// A raw pointer. Written as `*mut T` or `*const T` + RawPtr(TyRef, Mutability), + + /// A reference; a pointer with an associated lifetime. Written as + /// `&'a mut T` or `&'a T`. + Ref(TyRef, Mutability), - // A reference; a pointer with an associated lifetime. Written as - // `&'a mut T` or `&'a T`. - // Ref(Ty<'tcx>, hir::Mutability), /// A pointer to a function. Written as `fn() -> i32`. /// /// For example the type of `bar` here: @@ -172,7 +203,7 @@ impl Ty { ) -> Cancelable { use ra_syntax::ast::TypeRef::*; Ok(match node { - ParenType(_inner) => Ty::Unknown, // TODO + ParenType(inner) => Ty::new_opt(db, module, inner.type_ref())?, TupleType(_inner) => Ty::Unknown, // TODO NeverType(..) => Ty::Never, PathType(inner) => { @@ -182,10 +213,18 @@ impl Ty { Ty::Unknown } } - PointerType(_inner) => Ty::Unknown, // TODO - ArrayType(_inner) => Ty::Unknown, // TODO - SliceType(_inner) => Ty::Unknown, // TODO - ReferenceType(_inner) => Ty::Unknown, // TODO + PointerType(inner) => { + let inner_ty = Ty::new_opt(db, module, inner.type_ref())?; + let mutability = Mutability::from_mutable(inner.is_mut()); + Ty::RawPtr(Arc::new(inner_ty), mutability) + } + ArrayType(_inner) => Ty::Unknown, // TODO + SliceType(_inner) => Ty::Unknown, // TODO + ReferenceType(inner) => { + let inner_ty = Ty::new_opt(db, module, inner.type_ref())?; + let mutability = Mutability::from_mutable(inner.is_mut()); + Ty::Ref(Arc::new(inner_ty), mutability) + } PlaceholderType(_inner) => Ty::Unknown, // TODO FnPointerType(_inner) => Ty::Unknown, // TODO ForType(_inner) => Ty::Unknown, // TODO @@ -209,6 +248,8 @@ impl fmt::Display for Ty { Ty::Float(t) => write!(f, "{}", t.ty_to_string()), Ty::Str => write!(f, "str"), Ty::Slice(t) => write!(f, "[{}]", t), + Ty::RawPtr(t, m) => write!(f, "*{}{}", m.as_keyword_for_ptr(), t), + Ty::Ref(t, m) => write!(f, "&{}{}", m.as_keyword_for_ref(), t), Ty::Never => write!(f, "!"), Ty::Tuple(ts) => { write!(f, "(")?; @@ -539,12 +580,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { cast_ty } ast::Expr::RefExpr(e) => { - let _inner_ty = self.infer_expr_opt(e.expr())?; - Ty::Unknown + let inner_ty = self.infer_expr_opt(e.expr())?; + let m = Mutability::from_mutable(e.is_mut()); + // TODO reference coercions etc. + Ty::Ref(Arc::new(inner_ty), m) } ast::Expr::PrefixExpr(e) => { - let _inner_ty = self.infer_expr_opt(e.expr())?; - Ty::Unknown + let inner_ty = self.infer_expr_opt(e.expr())?; + match e.op() { + Some(PrefixOp::Deref) => { + match inner_ty { + // builtin deref: + Ty::Ref(ref_inner, _) => (*ref_inner).clone(), + Ty::RawPtr(ptr_inner, _) => (*ptr_inner).clone(), + // TODO Deref::deref + _ => Ty::Unknown, + } + } + _ => Ty::Unknown, + } } ast::Expr::RangeExpr(_e) => Ty::Unknown, ast::Expr::BinExpr(_e) => Ty::Unknown, diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 9bb58ec85..a76925b58 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -91,6 +91,28 @@ fn test() { ); } +#[test] +fn infer_refs_and_ptrs() { + check_inference( + r#" +fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { + a; + *a; + &a; + &mut a; + b; + *b; + &b; + c; + *c; + d; + *d; +} +"#, + "0005_refs.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/0001_basics.txt b/crates/ra_hir/src/ty/tests/data/0001_basics.txt index 0c46f243a..212e92e00 100644 --- a/crates/ra_hir/src/ty/tests/data/0001_basics.txt +++ b/crates/ra_hir/src/ty/tests/data/0001_basics.txt @@ -1,4 +1,4 @@ -[33; 34) 'd': [unknown] +[33; 34) 'd': &[unknown] [88; 94) '1isize': [unknown] [48; 49) 'a': u32 [55; 56) 'b': isize @@ -10,4 +10,4 @@ [17; 18) 'b': isize [100; 106) '"test"': [unknown] [42; 121) '{ ...f32; }': () -[69; 70) 'd': [unknown] +[69; 70) 'd': &[unknown] diff --git a/crates/ra_hir/src/ty/tests/data/0005_refs.txt b/crates/ra_hir/src/ty/tests/data/0005_refs.txt new file mode 100644 index 000000000..296e955c1 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0005_refs.txt @@ -0,0 +1,23 @@ +[115; 117) '&b': &&mut u32 +[88; 94) '&mut a': &mut &u32 +[146; 147) 'd': *mut u32 +[145; 147) '*d': u32 +[65; 66) 'a': &u32 +[46; 47) 'd': *mut u32 +[59; 150) '{ ... *d; }': () +[116; 117) 'b': &mut u32 +[131; 132) 'c': *const u32 +[130; 132) '*c': u32 +[72; 74) '*a': u32 +[107; 109) '*b': u32 +[108; 109) 'b': &mut u32 +[9; 10) 'a': &u32 +[18; 19) 'b': &mut u32 +[93; 94) 'a': &u32 +[100; 101) 'b': &mut u32 +[81; 82) 'a': &u32 +[80; 82) '&a': &&u32 +[73; 74) 'a': &u32 +[123; 124) 'c': *const u32 +[31; 32) 'c': *const u32 +[138; 139) 'd': *mut u32 -- cgit v1.2.3 From cdca39706121b2d1734a94938a2372da881e10c6 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 21:14:13 +0100 Subject: Add a hir::TypeRef as an intermediate between ast::TypeRef and ty::Ty --- crates/ra_hir/src/adt.rs | 4 +- crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/module.rs | 4 +- crates/ra_hir/src/module/nameres.rs | 2 +- crates/ra_hir/src/path.rs | 4 +- crates/ra_hir/src/ty.rs | 139 ++++++++++++++++-------------------- crates/ra_hir/src/type_ref.rs | 110 ++++++++++++++++++++++++++++ 7 files changed, 180 insertions(+), 84 deletions(-) create mode 100644 crates/ra_hir/src/type_ref.rs (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index 40a45b831..dae04d258 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -145,7 +145,7 @@ impl VariantData { .map(|(i, fd)| { Ok(StructField { name: SmolStr::new(i.to_string()), - ty: Ty::new_opt(db, &module, fd.type_ref())?, + ty: Ty::from_ast_opt(db, &module, fd.type_ref())?, }) }) .collect::>()?; @@ -160,7 +160,7 @@ impl VariantData { .name() .map(|n| n.text()) .unwrap_or_else(|| SmolStr::new("[error]")), - ty: Ty::new_opt(db, &module, fd.type_ref())?, + ty: Ty::from_ast_opt(db, &module, fd.type_ref())?, }) }) .collect::>()?; diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 68fdbb7ea..f1cc0ccd0 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -26,6 +26,7 @@ mod krate; mod module; mod function; mod adt; +mod type_ref; mod ty; use std::ops::Index; diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index e1a0e4b59..b9d36f01f 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs @@ -115,7 +115,7 @@ impl Module { Ok(res) } - pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable> { + pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> Cancelable> { let mut curr_per_ns = PerNs::types( match path.kind { PathKind::Crate => self.crate_root(), @@ -131,7 +131,7 @@ impl Module { .def_id(db), ); - let segments = path.segments; + let segments = &path.segments; for name in segments.iter() { let curr = if let Some(r) = curr_per_ns.as_ref().take(Namespace::Types) { r diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 33c9d93c2..98cd225dd 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs @@ -451,7 +451,7 @@ where segments: import.path.segments[i + 1..].iter().cloned().collect(), kind: PathKind::Crate, }; - let def_id = module.resolve_path(self.db, path)?; + let def_id = module.resolve_path(self.db, &path)?; if !def_id.is_none() { self.update(module_id, |items| { let res = Resolution { diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index e04d00900..0b260072c 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -1,12 +1,12 @@ use ra_syntax::{SmolStr, ast, AstNode, TextRange}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Path { pub kind: PathKind, pub segments: Vec, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum PathKind { Plain, Self_, diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 54aa6715c..11b4caa23 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -19,38 +19,9 @@ use crate::{ Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, db::HirDatabase, adt::VariantData, + type_ref::{TypeRef, Mutability}, }; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum Mutability { - Shared, - Mut, -} - -impl Mutability { - pub fn from_mutable(mutable: bool) -> Mutability { - if mutable { - Mutability::Mut - } else { - Mutability::Shared - } - } - - pub fn as_keyword_for_ref(self) -> &'static str { - match self { - Mutability::Shared => "", - Mutability::Mut => "mut ", - } - } - - pub fn as_keyword_for_ptr(self) -> &'static str { - match self { - Mutability::Shared => "const ", - Mutability::Mut => "mut ", - } - } -} - #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum Ty { /// The primitive boolean type. Written as `bool`. @@ -156,16 +127,58 @@ pub struct FnSig { } impl Ty { - pub(crate) fn new_from_ast_path( + pub(crate) fn from_hir( db: &impl HirDatabase, module: &Module, - path: ast::Path, + type_ref: &TypeRef, + ) -> Cancelable { + Ok(match type_ref { + TypeRef::Never => Ty::Never, + TypeRef::Tuple(inner) => { + let inner_tys = inner + .iter() + .map(|tr| Ty::from_hir(db, module, tr)) + .collect::>()?; + Ty::Tuple(inner_tys) + } + TypeRef::Path(path) => Ty::from_hir_path(db, module, path)?, + TypeRef::RawPtr(inner, mutability) => { + let inner_ty = Ty::from_hir(db, module, inner)?; + Ty::RawPtr(Arc::new(inner_ty), *mutability) + } + TypeRef::Array(_inner) => Ty::Unknown, // TODO + TypeRef::Slice(inner) => { + let inner_ty = Ty::from_hir(db, module, inner)?; + Ty::Slice(Arc::new(inner_ty)) + } + TypeRef::Reference(inner, mutability) => { + let inner_ty = Ty::from_hir(db, module, inner)?; + Ty::Ref(Arc::new(inner_ty), *mutability) + } + TypeRef::Placeholder => Ty::Unknown, // TODO + TypeRef::Fn(params) => { + let mut inner_tys = params + .iter() + .map(|tr| Ty::from_hir(db, module, tr)) + .collect::>>()?; + let return_ty = inner_tys + .pop() + .expect("TypeRef::Fn should always have at least return type"); + let sig = FnSig { + input: inner_tys, + output: return_ty, + }; + Ty::FnPtr(Arc::new(sig)) + } + TypeRef::Error => Ty::Unknown, + }) + } + + pub(crate) fn from_hir_path( + db: &impl HirDatabase, + module: &Module, + path: &Path, ) -> Cancelable { - let path = if let Some(p) = Path::from_ast(path) { - p - } else { - return Ok(Ty::Unknown); - }; if path.is_ident() { let name = &path.segments[0]; if let Some(int_ty) = primitive::IntTy::from_string(&name) { @@ -187,50 +200,22 @@ impl Ty { Ok(ty) } - pub(crate) fn new_opt( + // TODO: These should not be necessary long-term, since everything will work on HIR + pub(crate) fn from_ast_opt( db: &impl HirDatabase, module: &Module, node: Option, ) -> Cancelable { - node.map(|n| Ty::new(db, module, n)) + node.map(|n| Ty::from_ast(db, module, n)) .unwrap_or(Ok(Ty::Unknown)) } - pub(crate) fn new( + pub(crate) fn from_ast( db: &impl HirDatabase, module: &Module, node: ast::TypeRef, ) -> Cancelable { - use ra_syntax::ast::TypeRef::*; - Ok(match node { - ParenType(inner) => Ty::new_opt(db, module, inner.type_ref())?, - TupleType(_inner) => Ty::Unknown, // TODO - NeverType(..) => Ty::Never, - PathType(inner) => { - if let Some(path) = inner.path() { - Ty::new_from_ast_path(db, module, path)? - } else { - Ty::Unknown - } - } - PointerType(inner) => { - let inner_ty = Ty::new_opt(db, module, inner.type_ref())?; - let mutability = Mutability::from_mutable(inner.is_mut()); - Ty::RawPtr(Arc::new(inner_ty), mutability) - } - ArrayType(_inner) => Ty::Unknown, // TODO - SliceType(_inner) => Ty::Unknown, // TODO - ReferenceType(inner) => { - let inner_ty = Ty::new_opt(db, module, inner.type_ref())?; - let mutability = Mutability::from_mutable(inner.is_mut()); - Ty::Ref(Arc::new(inner_ty), mutability) - } - PlaceholderType(_inner) => Ty::Unknown, // TODO - FnPointerType(_inner) => Ty::Unknown, // TODO - ForType(_inner) => Ty::Unknown, // TODO - ImplTraitType(_inner) => Ty::Unknown, // TODO - DynTraitType(_inner) => Ty::Unknown, // TODO - }) + Ty::from_hir(db, module, &TypeRef::from_ast(node)) } pub fn unit() -> Self { @@ -280,11 +265,11 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { .param_list() .map(|pl| { pl.params() - .map(|p| Ty::new_opt(db, &module, p.type_ref())) + .map(|p| Ty::from_ast_opt(db, &module, p.type_ref())) .collect() }) .unwrap_or_else(|| Ok(Vec::new()))?; - let output = Ty::new_opt(db, &module, node.ret_type().and_then(|rt| rt.type_ref()))?; + let output = Ty::from_ast_opt(db, &module, node.ret_type().and_then(|rt| rt.type_ref()))?; let sig = FnSig { input, output }; Ok(Ty::FnPtr(Arc::new(sig))) } @@ -390,7 +375,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; // resolve in module - let resolved = ctry!(self.module.resolve_path(self.db, path)?.take_values()); + let resolved = ctry!(self.module.resolve_path(self.db, &path)?.take_values()); let ty = self.db.type_for_def(resolved)?; // TODO we will need to add type variables for type parameters etc. here Ok(Some(ty)) @@ -405,7 +390,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } else { return Ok((Ty::Unknown, None)); }; - let def_id = if let Some(def_id) = self.module.resolve_path(self.db, path)?.take_types() { + let def_id = if let Some(def_id) = self.module.resolve_path(self.db, &path)?.take_types() { def_id } else { return Ok((Ty::Unknown, None)); @@ -575,7 +560,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } ast::Expr::CastExpr(e) => { let _inner_ty = self.infer_expr_opt(e.expr())?; - let cast_ty = Ty::new_opt(self.db, &self.module, e.type_ref())?; + let cast_ty = Ty::from_ast_opt(self.db, &self.module, e.type_ref())?; // TODO do the coercion... cast_ty } @@ -620,7 +605,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { for stmt in node.statements() { match stmt { ast::Stmt::LetStmt(stmt) => { - let decl_ty = Ty::new_opt(self.db, &self.module, stmt.type_ref())?; + let decl_ty = Ty::from_ast_opt(self.db, &self.module, stmt.type_ref())?; let ty = if let Some(expr) = stmt.initializer() { // TODO pass expectation let expr_ty = self.infer_expr(expr)?; @@ -665,7 +650,7 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable Mutability { + if mutable { + Mutability::Mut + } else { + Mutability::Shared + } + } + + pub fn as_keyword_for_ref(self) -> &'static str { + match self { + Mutability::Shared => "", + Mutability::Mut => "mut ", + } + } + + pub fn as_keyword_for_ptr(self) -> &'static str { + match self { + Mutability::Shared => "const ", + Mutability::Mut => "mut ", + } + } +} + +/// Compare ty::Ty +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum TypeRef { + Never, + Placeholder, + Tuple(Vec), + Path(Path), + RawPtr(Box, Mutability), + Reference(Box, Mutability), + Array(Box /*, Expr*/), + Slice(Box), + /// A fn pointer. Last element of the vector is the return type. + Fn(Vec), + // For + // ImplTrait, + // DynTrait, + Error, +} + +impl TypeRef { + /// Converts an `ast::TypeRef` to a `hir::TypeRef`. + pub(crate) fn from_ast(node: ast::TypeRef) -> Self { + use ra_syntax::ast::TypeRef::*; + match node { + ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()), + TupleType(inner) => TypeRef::Tuple(inner.fields().map(TypeRef::from_ast).collect()), + NeverType(..) => TypeRef::Never, + PathType(inner) => inner + .path() + .and_then(Path::from_ast) + .map(TypeRef::Path) + .unwrap_or(TypeRef::Error), + PointerType(inner) => { + let inner_ty = TypeRef::from_ast_opt(inner.type_ref()); + let mutability = Mutability::from_mutable(inner.is_mut()); + TypeRef::RawPtr(Box::new(inner_ty), mutability) + } + ArrayType(inner) => TypeRef::Array(Box::new(TypeRef::from_ast_opt(inner.type_ref()))), + SliceType(inner) => TypeRef::Slice(Box::new(TypeRef::from_ast_opt(inner.type_ref()))), + ReferenceType(inner) => { + let inner_ty = TypeRef::from_ast_opt(inner.type_ref()); + let mutability = Mutability::from_mutable(inner.is_mut()); + TypeRef::Reference(Box::new(inner_ty), mutability) + } + PlaceholderType(_inner) => TypeRef::Placeholder, + FnPointerType(inner) => { + let ret_ty = TypeRef::from_ast_opt(inner.ret_type().and_then(|rt| rt.type_ref())); + let mut params = if let Some(pl) = inner.param_list() { + pl.params() + .map(|p| p.type_ref()) + .map(TypeRef::from_ast_opt) + .collect() + } else { + Vec::new() + }; + params.push(ret_ty); + TypeRef::Fn(params) + } + // for types are close enough for our purposes to the inner type for now... + ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()), + ImplTraitType(_inner) => TypeRef::Error, + DynTraitType(_inner) => TypeRef::Error, + } + } + + fn from_ast_opt(node: Option) -> Self { + if let Some(node) = node { + TypeRef::from_ast(node) + } else { + TypeRef::Error + } + } +} -- cgit v1.2.3 From bc745a139674f289386f3081458793f756cab5b9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 21:40:33 +0100 Subject: Resolve field types lazily I.e. not already when getting the HIR for the struct. --- crates/ra_hir/src/adt.rs | 71 ++++++++++++++-------------------- crates/ra_hir/src/db.rs | 6 +++ crates/ra_hir/src/mock.rs | 1 + crates/ra_hir/src/query_definitions.rs | 18 +++++---- crates/ra_hir/src/ty.rs | 36 +++++++++++++---- crates/ra_hir/src/type_ref.rs | 2 +- 6 files changed, 75 insertions(+), 59 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index dae04d258..65c461148 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -5,8 +5,7 @@ use ra_syntax::{SmolStr, ast::{self, NameOwner, StructFlavor}}; use crate::{ DefId, Cancelable, db::{HirDatabase}, - module::Module, - ty::{Ty}, + type_ref::TypeRef, }; pub struct Struct { @@ -42,15 +41,11 @@ pub struct StructData { } impl StructData { - pub(crate) fn new( - db: &impl HirDatabase, - module: &Module, - struct_def: ast::StructDef, - ) -> Cancelable { + pub(crate) fn new(struct_def: ast::StructDef) -> StructData { let name = struct_def.name().map(|n| n.text()); - let variant_data = VariantData::new(db, module, struct_def.flavor())?; + let variant_data = VariantData::new(struct_def.flavor()); let variant_data = Arc::new(variant_data); - Ok(StructData { name, variant_data }) + StructData { name, variant_data } } pub fn name(&self) -> Option<&SmolStr> { @@ -87,27 +82,23 @@ pub struct EnumData { } impl EnumData { - pub(crate) fn new( - db: &impl HirDatabase, - module: &Module, - enum_def: ast::EnumDef, - ) -> Cancelable { + pub(crate) fn new(enum_def: ast::EnumDef) -> Self { let name = enum_def.name().map(|n| n.text()); let variants = if let Some(evl) = enum_def.variant_list() { evl.variants() .map(|v| { - Ok(( + ( v.name() .map(|n| n.text()) .unwrap_or_else(|| SmolStr::new("[error]")), - Arc::new(VariantData::new(db, module, v.flavor())?), - )) + Arc::new(VariantData::new(v.flavor())), + ) }) - .collect::>()? + .collect() } else { Vec::new() }; - Ok(EnumData { name, variants }) + EnumData { name, variants } } } @@ -115,15 +106,15 @@ impl EnumData { #[derive(Debug, Clone, PartialEq, Eq)] pub struct StructField { name: SmolStr, - ty: Ty, + type_ref: TypeRef, } impl StructField { pub fn name(&self) -> SmolStr { self.name.clone() } - pub fn ty(&self) -> Ty { - self.ty.clone() + pub fn type_ref(&self) -> &TypeRef { + &self.type_ref } } @@ -136,45 +127,41 @@ pub enum VariantData { } impl VariantData { - pub fn new(db: &impl HirDatabase, module: &Module, flavor: StructFlavor) -> Cancelable { - Ok(match flavor { + pub fn new(flavor: StructFlavor) -> Self { + match flavor { StructFlavor::Tuple(fl) => { let fields = fl .fields() .enumerate() - .map(|(i, fd)| { - Ok(StructField { - name: SmolStr::new(i.to_string()), - ty: Ty::from_ast_opt(db, &module, fd.type_ref())?, - }) + .map(|(i, fd)| StructField { + name: SmolStr::new(i.to_string()), + type_ref: TypeRef::from_ast_opt(fd.type_ref()), }) - .collect::>()?; + .collect(); VariantData::Tuple(fields) } StructFlavor::Named(fl) => { let fields = fl .fields() - .map(|fd| { - Ok(StructField { - name: fd - .name() - .map(|n| n.text()) - .unwrap_or_else(|| SmolStr::new("[error]")), - ty: Ty::from_ast_opt(db, &module, fd.type_ref())?, - }) + .map(|fd| StructField { + name: fd + .name() + .map(|n| n.text()) + .unwrap_or_else(|| SmolStr::new("[error]")), + type_ref: TypeRef::from_ast_opt(fd.type_ref()), }) - .collect::>()?; + .collect(); VariantData::Struct(fields) } StructFlavor::Unit => VariantData::Unit, - }) + } } - pub(crate) fn get_field_ty(&self, field_name: &str) -> Option { + pub(crate) fn get_field_type_ref(&self, field_name: &str) -> Option<&TypeRef> { self.fields() .iter() .find(|f| f.name == field_name) - .map(|f| f.ty.clone()) + .map(|f| &f.type_ref) } pub fn fields(&self) -> &[StructField] { diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 113790ee9..e7f9afa77 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use ra_syntax::{ + SmolStr, SyntaxNode, ast::FnDefNode, }; @@ -52,6 +53,11 @@ pub trait HirDatabase: SyntaxDatabase use fn query_definitions::type_for_def; } + fn type_for_field(def_id: DefId, field: SmolStr) -> Cancelable { + type TypeForFieldQuery; + use fn query_definitions::type_for_field; + } + fn file_items(file_id: FileId) -> Arc { type SourceFileItemsQuery; use fn query_definitions::file_items; diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index ead2b8414..f6882cb77 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -193,6 +193,7 @@ salsa::database_storage! { fn submodules() for db::SubmodulesQuery; fn infer() for db::InferQuery; fn type_for_def() for db::TypeForDefQuery; + fn type_for_field() for db::TypeForFieldQuery; 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 405e359f1..4a7958a12 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs @@ -46,18 +46,21 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable Cancelable { + ty::type_for_field(db, def_id, field) +} + pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> 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"); - let module = def_id.module(db)?; - Ok(Arc::new(StructData::new( - db, - &module, - struct_def.borrowed(), - )?)) + Ok(Arc::new(StructData::new(struct_def.borrowed()))) } pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable> { @@ -66,8 +69,7 @@ pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable Arc { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 11b4caa23..67b523c2c 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -309,6 +309,33 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable { } } +pub(super) fn type_for_field( + db: &impl HirDatabase, + def_id: DefId, + field: SmolStr, +) -> Cancelable { + let def = def_id.resolve(db)?; + let variant_data = match def { + Def::Struct(s) => { + let variant_data = s.variant_data(db)?; + variant_data + } + // TODO: unions + // TODO: enum variants + _ => panic!( + "trying to get type for field in non-struct/variant {:?}", + def_id + ), + }; + let module = def_id.module(db)?; + let type_ref = if let Some(tr) = variant_data.get_field_type_ref(&field) { + tr + } else { + return Ok(Ty::Unknown); + }; + Ty::from_hir(db, &module, &type_ref) +} + #[derive(Clone, PartialEq, Eq, Debug)] pub struct InferenceResult { type_of: FxHashMap, @@ -540,14 +567,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { 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::Adt { def_id, .. } => self.db.type_for_field(def_id, text)?, _ => Ty::Unknown, } } else { diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs index ae163313f..b36bb35d8 100644 --- a/crates/ra_hir/src/type_ref.rs +++ b/crates/ra_hir/src/type_ref.rs @@ -100,7 +100,7 @@ impl TypeRef { } } - fn from_ast_opt(node: Option) -> Self { + pub(crate) fn from_ast_opt(node: Option) -> Self { if let Some(node) = node { TypeRef::from_ast(node) } else { -- cgit v1.2.3