From b5b68f2094d49cacde6d7f0c49f521a0b25f34bd Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 24 Dec 2018 19:07:48 +0100 Subject: [PATCH 01/15] 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 ++++ .../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 diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index 780a84291ac1..7043a0f4d099 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 000000000000..a2d228593af7 --- /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 d94f75857fca..113790ee9b7e 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 a0d99a84dfa5..7e9824de9ca9 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 b5a99717072a..ead2b8414b3e 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 b654af9204f3..72440d1d6f38 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 c759d4c8b1d7..f86b749ec19d 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 000000000000..70ad055ff308 --- /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] From 4ff161852016c6c15954d6f30bd637834a2b2b68 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 24 Dec 2018 20:32:39 +0100 Subject: [PATCH 02/15] Do name resolution by namespace (types/values) --- .../src/completion/complete_path.rs | 2 +- .../src/completion/completion_item.rs | 35 ++-- crates/ra_analysis/src/db.rs | 4 +- 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 ++- .../ra_hir/src/ty/tests/data/0004_struct.txt | 4 +- crates/ra_lsp_server/src/conv.rs | 2 + 10 files changed, 212 insertions(+), 84 deletions(-) diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs index ad4d68a3326b..8c00be499eda 100644 --- a/crates/ra_analysis/src/completion/complete_path.rs +++ b/crates/ra_analysis/src/completion/complete_path.rs @@ -8,7 +8,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C (Some(path), Some(module)) => (path.clone(), module), _ => return Ok(()), }; - let def_id = match module.resolve_path(ctx.db, path)? { + let def_id = match module.resolve_path(ctx.db, path)?.take_types() { Some(it) => it, None => return Ok(()), }; diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs index 911f08468ea4..6d466c8bdd40 100644 --- a/crates/ra_analysis/src/completion/completion_item.rs +++ b/crates/ra_analysis/src/completion/completion_item.rs @@ -1,5 +1,7 @@ use crate::db; +use hir::PerNs; + /// `CompletionItem` describes a single completion variant in the editor pop-up. /// It is basically a POD with various properties. To construct a /// `CompletionItem`, use `new` method and the `Builder` struct. @@ -25,6 +27,8 @@ pub enum CompletionItemKind { Keyword, Module, Function, + Struct, + Enum, Binding, } @@ -117,16 +121,27 @@ impl Builder { db: &db::RootDatabase, resolution: &hir::Resolution, ) -> Builder { - if let Some(def_id) = resolution.def_id { - if let Ok(def) = def_id.resolve(db) { - let kind = match def { - hir::Def::Module(..) => CompletionItemKind::Module, - hir::Def::Function(..) => CompletionItemKind::Function, - _ => return self, - }; - self.kind = Some(kind); - } - } + let resolved = resolution.def_id.and_then(|d| d.resolve(db).ok()); + let kind = match resolved { + PerNs { + types: Some(hir::Def::Module(..)), + .. + } => CompletionItemKind::Module, + PerNs { + types: Some(hir::Def::Struct(..)), + .. + } => CompletionItemKind::Struct, + PerNs { + types: Some(hir::Def::Enum(..)), + .. + } => CompletionItemKind::Enum, + PerNs { + values: Some(hir::Def::Function(..)), + .. + } => CompletionItemKind::Function, + _ => return self, + }; + self.kind = Some(kind); self } } diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index 7043a0f4d099..677745d57db7 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs @@ -95,8 +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; + fn struct_data() for hir::db::StructDataQuery; + fn enum_data() for hir::db::EnumDataQuery; } } } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 7e9824de9ca9..81526fe9cab9 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 89111995306d..e1a0e4b59536 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 0b152a406230..33c9d93c2886 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)] -// struct PerNs { -// types: Option, -// values: Option, -// } +#[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 + } + + 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 3e29c39541ab..03ea5c1d6c0e 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 f86b749ec19d..429292cfc946 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 70ad055ff308..a4371c5a531a 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] diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 051f1f995b3c..af52893114c5 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -55,6 +55,8 @@ impl Conv for CompletionItemKind { CompletionItemKind::Snippet => Snippet, CompletionItemKind::Module => Module, CompletionItemKind::Function => Function, + CompletionItemKind::Struct => Struct, + CompletionItemKind::Enum => Enum, CompletionItemKind::Binding => Variable, } } From 6fcd38cc81bdcc9921da767872dfce65ee7d2d27 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 24 Dec 2018 21:00:14 +0100 Subject: [PATCH 03/15] 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 +- .../ra_hir/src/ty/tests/data/0004_struct.txt | 12 +- crates/ra_syntax/src/ast/generated.rs | 26 +++- crates/ra_syntax/src/grammar.ron | 6 +- 6 files changed, 143 insertions(+), 49 deletions(-) diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index a2d228593af7..ee270ac459d7 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 429292cfc946..386af8120abf 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 170eef1471b4..9bb58ec850a5 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 a4371c5a531a..41357749f27f 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] diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index c735338619aa..334da67ef038 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -2142,7 +2142,15 @@ impl> NamedFieldNode { } -impl<'a> NamedField<'a> {} +impl<'a> NamedField<'a> { + pub fn name_ref(self) -> Option> { + super::child_opt(self) + } + + pub fn expr(self) -> Option> { + super::child_opt(self) + } +} // NamedFieldDef #[derive(Debug, Clone, Copy,)] @@ -2218,7 +2226,11 @@ impl> NamedFieldListNode { } -impl<'a> NamedFieldList<'a> {} +impl<'a> NamedFieldList<'a> { + pub fn fields(self) -> impl Iterator> + 'a { + super::children(self) + } +} // NeverType #[derive(Debug, Clone, Copy,)] @@ -3467,7 +3479,15 @@ impl> StructLitNode { } -impl<'a> StructLit<'a> {} +impl<'a> StructLit<'a> { + pub fn path(self) -> Option> { + super::child_opt(self) + } + + pub fn named_field_list(self) -> Option> { + super::child_opt(self) + } +} // StructPat #[derive(Debug, Clone, Copy,)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index e3b9032a0c6e..0da8b8183d98 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -392,9 +392,9 @@ Grammar( collections: [ [ "pats", "Pat" ] ] ), "MatchGuard": (), - "StructLit": (), - "NamedFieldList": (), - "NamedField": (), + "StructLit": (options: ["Path", "NamedFieldList"]), + "NamedFieldList": (collections: [ ["fields", "NamedField"] ]), + "NamedField": (options: ["NameRef", "Expr"]), "CallExpr": ( traits: ["ArgListOwner"], options: [ "Expr" ], From 4cb7b0f2af9787abe1c73fc3922e9b426a96e0ef Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 13:01:47 +0100 Subject: [PATCH 04/15] Add AST definitions for struct/variant fields etc. Fixes #117 --- crates/ra_syntax/src/ast.rs | 31 ++++ crates/ra_syntax/src/ast/generated.rs | 225 +++++++++++++++++++++++++- crates/ra_syntax/src/grammar.ron | 12 +- 3 files changed, 256 insertions(+), 12 deletions(-) diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index f12479fb46e1..5dbf9b221aad 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -363,3 +363,34 @@ impl<'a, N: AstNode<'a>> Iterator for AstChildren<'a, N> { } } } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum StructFlavor<'a> { + Tuple(PosFieldList<'a>), + Named(NamedFieldDefList<'a>), + Unit, +} + +impl<'a> StructFlavor<'a> { + fn from_node>(node: N) -> StructFlavor<'a> { + if let Some(nfdl) = child_opt::<_, NamedFieldDefList>(node) { + StructFlavor::Named(nfdl) + } else if let Some(pfl) = child_opt::<_, PosFieldList>(node) { + StructFlavor::Tuple(pfl) + } else { + StructFlavor::Unit + } + } +} + +impl<'a> StructDef<'a> { + pub fn flavor(self) -> StructFlavor<'a> { + StructFlavor::from_node(self) + } +} + +impl<'a> EnumVariant<'a> { + pub fn flavor(self) -> StructFlavor<'a> { + StructFlavor::from_node(self) + } +} diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 334da67ef038..35a9770a6f94 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -806,7 +806,94 @@ impl<'a> ast::NameOwner<'a> for EnumDef<'a> {} impl<'a> ast::TypeParamsOwner<'a> for EnumDef<'a> {} impl<'a> ast::AttrsOwner<'a> for EnumDef<'a> {} impl<'a> ast::DocCommentsOwner<'a> for EnumDef<'a> {} -impl<'a> EnumDef<'a> {} +impl<'a> EnumDef<'a> { + pub fn variant_list(self) -> Option> { + super::child_opt(self) + } +} + +// EnumVariant +#[derive(Debug, Clone, Copy,)] +pub struct EnumVariantNode = OwnedRoot> { + pub(crate) syntax: SyntaxNode, +} +pub type EnumVariant<'a> = EnumVariantNode>; + +impl, R2: TreeRoot> PartialEq> for EnumVariantNode { + fn eq(&self, other: &EnumVariantNode) -> bool { self.syntax == other.syntax } +} +impl> Eq for EnumVariantNode {} +impl> Hash for EnumVariantNode { + fn hash(&self, state: &mut H) { self.syntax.hash(state) } +} + +impl<'a> AstNode<'a> for EnumVariant<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + ENUM_VARIANT => Some(EnumVariant { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl> EnumVariantNode { + pub fn borrowed(&self) -> EnumVariant { + EnumVariantNode { syntax: self.syntax.borrowed() } + } + pub fn owned(&self) -> EnumVariantNode { + EnumVariantNode { syntax: self.syntax.owned() } + } +} + + +impl<'a> ast::NameOwner<'a> for EnumVariant<'a> {} +impl<'a> EnumVariant<'a> { + pub fn expr(self) -> Option> { + super::child_opt(self) + } +} + +// EnumVariantList +#[derive(Debug, Clone, Copy,)] +pub struct EnumVariantListNode = OwnedRoot> { + pub(crate) syntax: SyntaxNode, +} +pub type EnumVariantList<'a> = EnumVariantListNode>; + +impl, R2: TreeRoot> PartialEq> for EnumVariantListNode { + fn eq(&self, other: &EnumVariantListNode) -> bool { self.syntax == other.syntax } +} +impl> Eq for EnumVariantListNode {} +impl> Hash for EnumVariantListNode { + fn hash(&self, state: &mut H) { self.syntax.hash(state) } +} + +impl<'a> AstNode<'a> for EnumVariantList<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + ENUM_VARIANT_LIST => Some(EnumVariantList { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl> EnumVariantListNode { + pub fn borrowed(&self) -> EnumVariantList { + EnumVariantListNode { syntax: self.syntax.borrowed() } + } + pub fn owned(&self) -> EnumVariantListNode { + EnumVariantListNode { syntax: self.syntax.owned() } + } +} + + +impl<'a> EnumVariantList<'a> { + pub fn variants(self) -> impl Iterator> + 'a { + super::children(self) + } +} // Expr #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -2189,7 +2276,52 @@ impl> NamedFieldDefNode { impl<'a> ast::NameOwner<'a> for NamedFieldDef<'a> {} impl<'a> ast::AttrsOwner<'a> for NamedFieldDef<'a> {} -impl<'a> NamedFieldDef<'a> {} +impl<'a> NamedFieldDef<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } +} + +// NamedFieldDefList +#[derive(Debug, Clone, Copy,)] +pub struct NamedFieldDefListNode = OwnedRoot> { + pub(crate) syntax: SyntaxNode, +} +pub type NamedFieldDefList<'a> = NamedFieldDefListNode>; + +impl, R2: TreeRoot> PartialEq> for NamedFieldDefListNode { + fn eq(&self, other: &NamedFieldDefListNode) -> bool { self.syntax == other.syntax } +} +impl> Eq for NamedFieldDefListNode {} +impl> Hash for NamedFieldDefListNode { + fn hash(&self, state: &mut H) { self.syntax.hash(state) } +} + +impl<'a> AstNode<'a> for NamedFieldDefList<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + NAMED_FIELD_DEF_LIST => Some(NamedFieldDefList { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl> NamedFieldDefListNode { + pub fn borrowed(&self) -> NamedFieldDefList { + NamedFieldDefListNode { syntax: self.syntax.borrowed() } + } + pub fn owned(&self) -> NamedFieldDefListNode { + NamedFieldDefListNode { syntax: self.syntax.owned() } + } +} + + +impl<'a> NamedFieldDefList<'a> { + pub fn fields(self) -> impl Iterator> + 'a { + super::children(self) + } +} // NamedFieldList #[derive(Debug, Clone, Copy,)] @@ -2830,6 +2962,89 @@ impl> PointerTypeNode { impl<'a> PointerType<'a> {} +// PosField +#[derive(Debug, Clone, Copy,)] +pub struct PosFieldNode = OwnedRoot> { + pub(crate) syntax: SyntaxNode, +} +pub type PosField<'a> = PosFieldNode>; + +impl, R2: TreeRoot> PartialEq> for PosFieldNode { + fn eq(&self, other: &PosFieldNode) -> bool { self.syntax == other.syntax } +} +impl> Eq for PosFieldNode {} +impl> Hash for PosFieldNode { + fn hash(&self, state: &mut H) { self.syntax.hash(state) } +} + +impl<'a> AstNode<'a> for PosField<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + POS_FIELD => Some(PosField { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl> PosFieldNode { + pub fn borrowed(&self) -> PosField { + PosFieldNode { syntax: self.syntax.borrowed() } + } + pub fn owned(&self) -> PosFieldNode { + PosFieldNode { syntax: self.syntax.owned() } + } +} + + +impl<'a> ast::AttrsOwner<'a> for PosField<'a> {} +impl<'a> PosField<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } +} + +// PosFieldList +#[derive(Debug, Clone, Copy,)] +pub struct PosFieldListNode = OwnedRoot> { + pub(crate) syntax: SyntaxNode, +} +pub type PosFieldList<'a> = PosFieldListNode>; + +impl, R2: TreeRoot> PartialEq> for PosFieldListNode { + fn eq(&self, other: &PosFieldListNode) -> bool { self.syntax == other.syntax } +} +impl> Eq for PosFieldListNode {} +impl> Hash for PosFieldListNode { + fn hash(&self, state: &mut H) { self.syntax.hash(state) } +} + +impl<'a> AstNode<'a> for PosFieldList<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + POS_FIELD_LIST => Some(PosFieldList { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl> PosFieldListNode { + pub fn borrowed(&self) -> PosFieldList { + PosFieldListNode { syntax: self.syntax.borrowed() } + } + pub fn owned(&self) -> PosFieldListNode { + PosFieldListNode { syntax: self.syntax.owned() } + } +} + + +impl<'a> PosFieldList<'a> { + pub fn fields(self) -> impl Iterator> + 'a { + super::children(self) + } +} + // PrefixExpr #[derive(Debug, Clone, Copy,)] pub struct PrefixExprNode = OwnedRoot> { @@ -3438,11 +3653,7 @@ impl<'a> ast::NameOwner<'a> for StructDef<'a> {} impl<'a> ast::TypeParamsOwner<'a> for StructDef<'a> {} impl<'a> ast::AttrsOwner<'a> for StructDef<'a> {} impl<'a> ast::DocCommentsOwner<'a> for StructDef<'a> {} -impl<'a> StructDef<'a> { - pub fn fields(self) -> impl Iterator> + 'a { - super::children(self) - } -} +impl<'a> StructDef<'a> {} // StructLit #[derive(Debug, Clone, Copy,)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 0da8b8183d98..e4e41d0776e2 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -261,18 +261,20 @@ Grammar( "TypeParamsOwner", "AttrsOwner", "DocCommentsOwner" - ], - collections: [ - ["fields", "NamedFieldDef"] ] ), - "NamedFieldDef": ( traits: ["NameOwner", "AttrsOwner"] ), + "NamedFieldDefList": (collections: [["fields", "NamedFieldDef"]]), + "NamedFieldDef": ( traits: ["NameOwner", "AttrsOwner"], options: ["TypeRef"] ), + "PosFieldList": (collections: [["fields", "PosField"]]), + "PosField": ( traits: ["AttrsOwner"], options: ["TypeRef"]), "EnumDef": ( traits: [ "NameOwner", "TypeParamsOwner", "AttrsOwner", "DocCommentsOwner" - ] ), + ], options: [["variant_list", "EnumVariantList"]] ), + "EnumVariantList": ( collections: [["variants", "EnumVariant"]] ), + "EnumVariant": ( traits: ["NameOwner"], options: ["Expr"] ), "TraitDef": ( traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner"] ), "Module": ( traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner" ], From 07a72859650254d8ba326e2c9683ae52164cb3b5 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 13:31:30 +0100 Subject: [PATCH 05/15] 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(-) diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index ee270ac459d7..b44f59f0b899 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 d36477b48d1d..01f0f3a66254 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 81526fe9cab9..796970d8a2de 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 72440d1d6f38..405e359f1ce0 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 386af8120abf..e2428a37f63e 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() { From 55c941cd9fb90c9340f01981e113aabd058b185b Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 13:54:38 +0100 Subject: [PATCH 06/15] Type field accesses --- crates/ra_hir/src/adt.rs | 9 ++++++ crates/ra_hir/src/ty.rs | 32 ++++++++++++++++++- .../ra_hir/src/ty/tests/data/0004_struct.txt | 6 ++-- crates/ra_syntax/src/ast/generated.rs | 10 +++++- crates/ra_syntax/src/grammar.ron | 2 +- 5 files changed, 54 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index b44f59f0b899..03770ed7db4a 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 e2428a37f63e..f931f3c87217 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 41357749f27f..cc8f3665bf63 100644 --- a/crates/ra_hir/src/ty/tests/data/0004_struct.txt +++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt @@ -4,9 +4,11 @@ [129; 130) '1': [unknown] [107; 108) 'a': A [127; 128) 'C': [unknown] -[139; 142) 'a.b': [unknown] +[139; 142) 'a.b': B [114; 133) 'A { b:...C(1) }': A -[148; 151) 'a.c': [unknown] +[148; 151) 'a.c': C +[148; 149) 'a': A +[139; 140) 'a': A [72; 154) '{ ...a.c; }': () [96; 97) 'B': [unknown] [88; 89) '1': [unknown] diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 35a9770a6f94..4e0550487f79 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -1123,7 +1123,15 @@ impl> FieldExprNode { } -impl<'a> FieldExpr<'a> {} +impl<'a> FieldExpr<'a> { + pub fn expr(self) -> Option> { + super::child_opt(self) + } + + pub fn name_ref(self) -> Option> { + super::child_opt(self) + } +} // FieldPatList #[derive(Debug, Clone, Copy,)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index e4e41d0776e2..923da0324000 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -406,7 +406,7 @@ Grammar( options: [ "Expr" ], ), "IndexExpr": (), - "FieldExpr": (), + "FieldExpr": (options: ["Expr", "NameRef"]), "TryExpr": (options: ["Expr"]), "CastExpr": (options: ["Expr", "TypeRef"]), "RefExpr": (options: ["Expr"]), From 0d724ea572a5dd26acbbf2eb4538eabe454fb894 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 14:48:54 +0100 Subject: [PATCH 07/15] Improve parsing of incomplete field accesses in preparation for field completion We need to be able to get the receiver even if there is no field name yet, and currently "a." wouldn't get parsed as a field name at all. This seems to help. --- crates/ra_syntax/src/grammar/expressions.rs | 16 ++++----- .../data/parser/err/0029_field_completion.rs | 3 ++ .../data/parser/err/0029_field_completion.txt | 35 +++++++++++++++++++ 3 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 crates/ra_syntax/tests/data/parser/err/0029_field_completion.rs create mode 100644 crates/ra_syntax/tests/data/parser/err/0029_field_completion.txt diff --git a/crates/ra_syntax/src/grammar/expressions.rs b/crates/ra_syntax/src/grammar/expressions.rs index da78d85a2f9f..2d1f174912c6 100644 --- a/crates/ra_syntax/src/grammar/expressions.rs +++ b/crates/ra_syntax/src/grammar/expressions.rs @@ -283,14 +283,10 @@ fn postfix_expr( // } L_PAREN if allow_calls => call_expr(p, lhs), L_BRACK if allow_calls => index_expr(p, lhs), - DOT if p.nth(1) == IDENT => { - if p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON { - method_call_expr(p, lhs) - } else { - field_expr(p, lhs) - } + DOT if p.nth(1) == IDENT && (p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON) => { + method_call_expr(p, lhs) } - DOT if p.nth(1) == INT_NUMBER => field_expr(p, lhs), + DOT => field_expr(p, lhs), // test postfix_range // fn foo() { let x = 1..; } DOTDOT | DOTDOTEQ if !EXPR_FIRST.contains(p.nth(1)) => { @@ -355,13 +351,15 @@ fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { // x.0.bar; // } fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { - assert!(p.at(DOT) && (p.nth(1) == IDENT || p.nth(1) == INT_NUMBER)); + assert!(p.at(DOT)); let m = lhs.precede(p); p.bump(); if p.at(IDENT) { name_ref(p) - } else { + } else if p.at(INT_NUMBER) { p.bump() + } else { + p.error("expected field name or number") } m.complete(p, FIELD_EXPR) } diff --git a/crates/ra_syntax/tests/data/parser/err/0029_field_completion.rs b/crates/ra_syntax/tests/data/parser/err/0029_field_completion.rs new file mode 100644 index 000000000000..a7cdc17bb112 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0029_field_completion.rs @@ -0,0 +1,3 @@ +fn foo(a: A) { + a. +} diff --git a/crates/ra_syntax/tests/data/parser/err/0029_field_completion.txt b/crates/ra_syntax/tests/data/parser/err/0029_field_completion.txt new file mode 100644 index 000000000000..fd2a3f37b74f --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0029_field_completion.txt @@ -0,0 +1,35 @@ +SOURCE_FILE@[0; 24) + FN_DEF@[0; 23) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 12) + L_PAREN@[6; 7) + PARAM@[7; 11) + BIND_PAT@[7; 8) + NAME@[7; 8) + IDENT@[7; 8) "a" + COLON@[8; 9) + WHITESPACE@[9; 10) + PATH_TYPE@[10; 11) + PATH@[10; 11) + PATH_SEGMENT@[10; 11) + NAME_REF@[10; 11) + IDENT@[10; 11) "A" + R_PAREN@[11; 12) + WHITESPACE@[12; 13) + BLOCK@[13; 23) + L_CURLY@[13; 14) + WHITESPACE@[14; 19) + FIELD_EXPR@[19; 21) + PATH_EXPR@[19; 20) + PATH@[19; 20) + PATH_SEGMENT@[19; 20) + NAME_REF@[19; 20) + IDENT@[19; 20) "a" + DOT@[20; 21) + err: `expected field name or number` + WHITESPACE@[21; 22) + R_CURLY@[22; 23) + WHITESPACE@[23; 24) From ab0b63992be0cec4999810096a53b40f63f90349 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 15:15:40 +0100 Subject: [PATCH 08/15] Implement basic completion for fields --- crates/ra_analysis/src/completion.rs | 8 +- .../src/completion/complete_dot.rs | 98 +++++++++++++++++++ .../src/completion/completion_context.rs | 34 ++++++- .../src/completion/completion_item.rs | 1 + crates/ra_hir/src/adt.rs | 14 ++- crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/ty.rs | 5 +- crates/ra_lsp_server/src/conv.rs | 1 + crates/ra_syntax/src/ast/generated.rs | 4 + crates/ra_syntax/src/grammar.ron | 2 +- 10 files changed, 156 insertions(+), 12 deletions(-) create mode 100644 crates/ra_analysis/src/completion/complete_dot.rs diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index d742d62955a0..fe580700ff94 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs @@ -1,6 +1,7 @@ mod completion_item; mod completion_context; +mod complete_dot; mod complete_fn_param; mod complete_keyword; mod complete_snippet; @@ -20,13 +21,13 @@ use crate::{ pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind}; -/// Main entry point for copmletion. We run comletion as a two-phase process. +/// Main entry point for completion. We run completion as a two-phase process. /// /// First, we look at the position and collect a so-called `CompletionContext. /// This is a somewhat messy process, because, during completion, syntax tree is -/// incomplete and can look readlly weired. +/// incomplete and can look really weird. /// -/// Once the context is collected, we run a series of completion routines whihc +/// Once the context is collected, we run a series of completion routines which /// look at the context and produce completion items. pub(crate) fn completions( db: &db::RootDatabase, @@ -43,6 +44,7 @@ pub(crate) fn completions( complete_snippet::complete_item_snippet(&mut acc, &ctx); complete_path::complete_path(&mut acc, &ctx)?; complete_scope::complete_scope(&mut acc, &ctx)?; + complete_dot::complete_dot(&mut acc, &ctx)?; Ok(Some(acc)) } diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs new file mode 100644 index 000000000000..fa62da210523 --- /dev/null +++ b/crates/ra_analysis/src/completion/complete_dot.rs @@ -0,0 +1,98 @@ +use ra_syntax::ast::AstNode; +use hir::{Ty, Def}; + +use crate::Cancelable; +use crate::completion::{CompletionContext, Completions, CompletionKind, CompletionItem, CompletionItemKind}; + +/// Complete dot accesses, i.e. fields or methods (currently only fields). +pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { + let module = if let Some(module) = &ctx.module { + module + } else { + return Ok(()); + }; + let function = if let Some(fn_def) = ctx.enclosing_fn { + hir::source_binder::function_from_module(ctx.db, module, fn_def) + } else { + return Ok(()); + }; + let receiver = if let Some(receiver) = ctx.dot_receiver { + receiver + } else { + return Ok(()); + }; + let infer_result = function.infer(ctx.db)?; + let receiver_ty = if let Some(ty) = infer_result.type_of_node(receiver.syntax()) { + ty + } else { + return Ok(()); + }; + if !ctx.is_method_call { + complete_fields(acc, ctx, receiver_ty)?; + } + Ok(()) +} + +fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, ty: Ty) -> Cancelable<()> { + // TODO: autoderef etc. + match ty { + Ty::Adt { def_id, .. } => { + match def_id.resolve(ctx.db)? { + Def::Struct(s) => { + let variant_data = s.variant_data(ctx.db)?; + for field in variant_data.fields() { + CompletionItem::new(CompletionKind::Reference, field.name().to_string()) + .kind(CompletionItemKind::Field) + .add_to(acc); + } + } + // TODO unions + _ => {} + } + } + Ty::Tuple(fields) => { + for (i, _ty) in fields.iter().enumerate() { + CompletionItem::new(CompletionKind::Reference, i.to_string()) + .kind(CompletionItemKind::Field) + .add_to(acc); + } + } + _ => {} + }; + Ok(()) +} + +#[cfg(test)] +mod tests { + use crate::completion::*; + + fn check_ref_completion(code: &str, expected_completions: &str) { + check_completion(code, expected_completions, CompletionKind::Reference); + } + + #[test] + fn test_struct_field_completion() { + check_ref_completion( + r" + struct A { the_field: u32 } + fn foo(a: A) { + a.<|> + } + ", + r#"the_field"#, + ); + } + + #[test] + fn test_no_struct_field_completion_for_method_call() { + check_ref_completion( + r" + struct A { the_field: u32 } + fn foo(a: A) { + a.<|>() + } + ", + r#""#, + ); + } +} diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs index 064fbc6f7be8..12e98e870cd2 100644 --- a/crates/ra_analysis/src/completion/completion_context.rs +++ b/crates/ra_analysis/src/completion/completion_context.rs @@ -31,6 +31,10 @@ pub(super) struct CompletionContext<'a> { pub(super) is_stmt: bool, /// Something is typed at the "top" level, in module or impl/trait. pub(super) is_new_item: bool, + /// The receiver if this is a field or method access, i.e. writing something.<|> + pub(super) dot_receiver: Option>, + /// If this is a method call in particular, i.e. the () are already there. + pub(super) is_method_call: bool, } impl<'a> CompletionContext<'a> { @@ -54,6 +58,8 @@ impl<'a> CompletionContext<'a> { after_if: false, is_stmt: false, is_new_item: false, + dot_receiver: None, + is_method_call: false, }; ctx.fill(original_file, position.offset); Ok(Some(ctx)) @@ -105,6 +111,12 @@ impl<'a> CompletionContext<'a> { _ => (), } + self.enclosing_fn = self + .leaf + .ancestors() + .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) + .find_map(ast::FnDef::cast); + let parent = match name_ref.syntax().parent() { Some(it) => it, None => return, @@ -120,11 +132,6 @@ impl<'a> CompletionContext<'a> { } if path.qualifier().is_none() { self.is_trivial_path = true; - self.enclosing_fn = self - .leaf - .ancestors() - .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) - .find_map(ast::FnDef::cast); self.is_stmt = match name_ref .syntax() @@ -145,6 +152,23 @@ impl<'a> CompletionContext<'a> { } } } + if let Some(_field_expr) = ast::FieldExpr::cast(parent) { + self.dot_receiver = self + .leaf + .ancestors() + .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) + .find_map(ast::FieldExpr::cast) + .and_then(ast::FieldExpr::expr); + } + if let Some(_method_call_expr) = ast::MethodCallExpr::cast(parent) { + self.dot_receiver = self + .leaf + .ancestors() + .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) + .find_map(ast::MethodCallExpr::cast) + .and_then(ast::MethodCallExpr::expr); + self.is_method_call = true; + } } } diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs index 6d466c8bdd40..c9f9f495da88 100644 --- a/crates/ra_analysis/src/completion/completion_item.rs +++ b/crates/ra_analysis/src/completion/completion_item.rs @@ -30,6 +30,7 @@ pub enum CompletionItemKind { Struct, Enum, Binding, + Field, } #[derive(Debug, PartialEq, Eq)] diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index 03770ed7db4a..e65f8deb866c 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 796970d8a2de..68fdbb7ea185 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 f931f3c87217..83da13f1a0a3 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)? diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index af52893114c5..c0e4e3a36a86 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -58,6 +58,7 @@ impl Conv for CompletionItemKind { CompletionItemKind::Struct => Struct, CompletionItemKind::Enum => Enum, CompletionItemKind::Binding => Variable, + CompletionItemKind::Field => Field, } } } diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 4e0550487f79..6b2800a0e4d2 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -2030,6 +2030,10 @@ impl<'a> MethodCallExpr<'a> { pub fn expr(self) -> Option> { super::child_opt(self) } + + pub fn name_ref(self) -> Option> { + super::child_opt(self) + } } // Module diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 923da0324000..dcde32923ba9 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -403,7 +403,7 @@ Grammar( ), "MethodCallExpr": ( traits: ["ArgListOwner"], - options: [ "Expr" ], + options: [ "Expr", "NameRef" ], ), "IndexExpr": (), "FieldExpr": (options: ["Expr", "NameRef"]), From e5a8089b08478c59e20e219b53345dde5301d517 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 15:20:37 +0100 Subject: [PATCH 09/15] Add . to trigger characters --- crates/ra_lsp_server/src/caps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs index 560f64989509..5f7038f63119 100644 --- a/crates/ra_lsp_server/src/caps.rs +++ b/crates/ra_lsp_server/src/caps.rs @@ -19,7 +19,7 @@ pub fn server_capabilities() -> ServerCapabilities { hover_provider: Some(true), completion_provider: Some(CompletionOptions { resolve_provider: None, - trigger_characters: Some(vec![":".to_string()]), + trigger_characters: Some(vec![":".to_string(), ".".to_string()]), }), signature_help_provider: Some(SignatureHelpOptions { trigger_characters: Some(vec!["(".to_string(), ",".to_string(), ")".to_string()]), From 3befd1a9e82809fef5bc68950d3265dbcbbd5527 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 15:44:10 +0100 Subject: [PATCH 10/15] Cleanup --- crates/ra_hir/src/ty.rs | 162 +++++++++------------------------------- 1 file changed, 37 insertions(+), 125 deletions(-) diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 83da13f1a0a3..7b8dbe6b7596 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())?; } } } From 3e4d41d1e409315ce42cb3c3479236b5e73d0643 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 17:43:58 +0100 Subject: [PATCH 11/15] Determine receiver for completion in a more robust way Also rename a parameter. --- .../src/completion/complete_dot.rs | 4 +- .../src/completion/completion_context.rs | 48 +++++++++++-------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs index fa62da210523..93d657576813 100644 --- a/crates/ra_analysis/src/completion/complete_dot.rs +++ b/crates/ra_analysis/src/completion/complete_dot.rs @@ -33,9 +33,9 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Ca Ok(()) } -fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, ty: Ty) -> Cancelable<()> { +fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) -> Cancelable<()> { // TODO: autoderef etc. - match ty { + match receiver { Ty::Adt { def_id, .. } => { match def_id.resolve(ctx.db)? { Def::Struct(s) => { diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs index 12e98e870cd2..978772fd447f 100644 --- a/crates/ra_analysis/src/completion/completion_context.rs +++ b/crates/ra_analysis/src/completion/completion_context.rs @@ -1,12 +1,13 @@ use ra_editor::find_node_at_offset; use ra_text_edit::AtomTextEdit; use ra_syntax::{ - algo::find_leaf_at_offset, + algo::{find_leaf_at_offset, find_covering_node}, ast, AstNode, SyntaxNodeRef, SourceFileNode, TextUnit, + TextRange, SyntaxKind::*, }; use hir::source_binder; @@ -65,7 +66,7 @@ impl<'a> CompletionContext<'a> { Ok(Some(ctx)) } - fn fill(&mut self, original_file: &SourceFileNode, offset: TextUnit) { + fn fill(&mut self, original_file: &'a SourceFileNode, offset: TextUnit) { // Insert a fake ident to get a valid parse tree. We will use this file // to determine context, though the original_file will be used for // actual completion. @@ -82,7 +83,7 @@ impl<'a> CompletionContext<'a> { self.is_param = true; return; } - self.classify_name_ref(&file, name_ref); + self.classify_name_ref(original_file, name_ref); } // Otherwise, see if this is a declaration. We can use heuristics to @@ -94,7 +95,7 @@ impl<'a> CompletionContext<'a> { } } } - fn classify_name_ref(&mut self, file: &SourceFileNode, name_ref: ast::NameRef) { + fn classify_name_ref(&mut self, original_file: &'a SourceFileNode, name_ref: ast::NameRef) { let name_range = name_ref.syntax().range(); let top_node = name_ref .syntax() @@ -144,7 +145,9 @@ impl<'a> CompletionContext<'a> { }; if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { - if let Some(if_expr) = find_node_at_offset::(file.syntax(), off) { + if let Some(if_expr) = + find_node_at_offset::(original_file.syntax(), off) + { if if_expr.syntax().range().end() < name_ref.syntax().range().start() { self.after_if = true; } @@ -152,26 +155,33 @@ impl<'a> CompletionContext<'a> { } } } - if let Some(_field_expr) = ast::FieldExpr::cast(parent) { - self.dot_receiver = self - .leaf - .ancestors() - .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) - .find_map(ast::FieldExpr::cast) - .and_then(ast::FieldExpr::expr); + if let Some(field_expr) = ast::FieldExpr::cast(parent) { + // The receiver comes before the point of insertion of the fake + // ident, so it should have the same range in the non-modified file + self.dot_receiver = field_expr + .expr() + .map(|e| e.syntax().range()) + .and_then(|r| find_node_with_range(original_file.syntax(), r)); } - if let Some(_method_call_expr) = ast::MethodCallExpr::cast(parent) { - self.dot_receiver = self - .leaf - .ancestors() - .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) - .find_map(ast::MethodCallExpr::cast) - .and_then(ast::MethodCallExpr::expr); + if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { + // As above + self.dot_receiver = method_call_expr + .expr() + .map(|e| e.syntax().range()) + .and_then(|r| find_node_with_range(original_file.syntax(), r)); self.is_method_call = true; } } } +fn find_node_with_range<'a, N: AstNode<'a>>( + syntax: SyntaxNodeRef<'a>, + range: TextRange, +) -> Option { + let node = find_covering_node(syntax, range); + node.ancestors().find_map(N::cast) +} + fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { match node.ancestors().filter_map(N::cast).next() { None => false, From b96d3612390e070936a176571c946ad0cafa69a9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 17:55:50 +0100 Subject: [PATCH 12/15] 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(-) diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index e65f8deb866c..40a45b83129c 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 7b8dbe6b7596..3674688ef4cc 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) From 2870effd5c69941bbf32a44c0ee6d9d42e0b038d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 17:17:39 +0100 Subject: [PATCH 13/15] 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 +++++ .../ra_hir/src/ty/tests/data/0001_basics.txt | 4 +- crates/ra_hir/src/ty/tests/data/0005_refs.txt | 23 +++++ crates/ra_syntax/src/ast.rs | 39 +++++++++ crates/ra_syntax/src/ast/generated.rs | 18 +++- crates/ra_syntax/src/grammar.ron | 6 +- 7 files changed, 173 insertions(+), 23 deletions(-) create mode 100644 crates/ra_hir/src/ty/tests/data/0005_refs.txt diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 3674688ef4cc..54aa6715c8fb 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 9bb58ec850a5..a76925b58ee1 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 0c46f243a8e0..212e92e0008a 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 000000000000..296e955c19ef --- /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 diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 5dbf9b221aad..8fb6b64083f7 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -394,3 +394,42 @@ impl<'a> EnumVariant<'a> { StructFlavor::from_node(self) } } + +impl<'a> PointerType<'a> { + pub fn is_mut(&self) -> bool { + self.syntax().children().any(|n| n.kind() == MUT_KW) + } +} + +impl<'a> ReferenceType<'a> { + pub fn is_mut(&self) -> bool { + self.syntax().children().any(|n| n.kind() == MUT_KW) + } +} + +impl<'a> RefExpr<'a> { + pub fn is_mut(&self) -> bool { + self.syntax().children().any(|n| n.kind() == MUT_KW) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum PrefixOp { + /// The `*` operator for dereferencing + Deref, + /// The `!` operator for logical inversion + Not, + /// The `-` operator for negation + Neg, +} + +impl<'a> PrefixExpr<'a> { + pub fn op(&self) -> Option { + match self.syntax().first_child()?.kind() { + STAR => Some(PrefixOp::Deref), + EXCL => Some(PrefixOp::Not), + MINUS => Some(PrefixOp::Neg), + _ => None, + } + } +} diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 6b2800a0e4d2..535dcc97524a 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -2607,7 +2607,11 @@ impl> ParenTypeNode { } -impl<'a> ParenType<'a> {} +impl<'a> ParenType<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } +} // Pat #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -2972,7 +2976,11 @@ impl> PointerTypeNode { } -impl<'a> PointerType<'a> {} +impl<'a> PointerType<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } +} // PosField #[derive(Debug, Clone, Copy,)] @@ -3285,7 +3293,11 @@ impl> ReferenceTypeNode { } -impl<'a> ReferenceType<'a> {} +impl<'a> ReferenceType<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } +} // RetType #[derive(Debug, Clone, Copy,)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index dcde32923ba9..8b1bd6d1c1fd 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -303,14 +303,14 @@ Grammar( ] ), "ImplItem": (), - "ParenType": (), + "ParenType": (options: ["TypeRef"]), "TupleType": (), "NeverType": (), "PathType": (options: ["Path"]), - "PointerType": (), + "PointerType": (options: ["TypeRef"]), "ArrayType": (), "SliceType": (), - "ReferenceType": (), + "ReferenceType": (options: ["TypeRef"]), "PlaceholderType": (), "FnPointerType": (), "ForType": (), From cdca39706121b2d1734a94938a2372da881e10c6 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 21:14:13 +0100 Subject: [PATCH 14/15] Add a hir::TypeRef as an intermediate between ast::TypeRef and ty::Ty --- .../src/completion/complete_path.rs | 2 +- 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 ++++++++++++++ crates/ra_syntax/src/ast/generated.rs | 38 ++++- crates/ra_syntax/src/grammar.ron | 10 +- 10 files changed, 219 insertions(+), 95 deletions(-) create mode 100644 crates/ra_hir/src/type_ref.rs diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs index 8c00be499eda..aaa2c7ceec10 100644 --- a/crates/ra_analysis/src/completion/complete_path.rs +++ b/crates/ra_analysis/src/completion/complete_path.rs @@ -8,7 +8,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C (Some(path), Some(module)) => (path.clone(), module), _ => return Ok(()), }; - let def_id = match module.resolve_path(ctx.db, path)?.take_types() { + let def_id = match module.resolve_path(ctx.db, &path)?.take_types() { Some(it) => it, None => return Ok(()), }; diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index 40a45b83129c..dae04d258db4 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 68fdbb7ea185..f1cc0ccd085c 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 e1a0e4b59536..b9d36f01f0b3 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 33c9d93c2886..98cd225ddec5 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 e04d00900aa8..0b260072cc26 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 54aa6715c8fb..11b4caa23b44 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 + } + } +} diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 535dcc97524a..c22e026cf9dd 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -131,7 +131,15 @@ impl> ArrayTypeNode { } -impl<'a> ArrayType<'a> {} +impl<'a> ArrayType<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } + + pub fn expr(self) -> Option> { + super::child_opt(self) + } +} // Attr #[derive(Debug, Clone, Copy,)] @@ -1258,7 +1266,15 @@ impl> FnPointerTypeNode { } -impl<'a> FnPointerType<'a> {} +impl<'a> FnPointerType<'a> { + pub fn param_list(self) -> Option> { + super::child_opt(self) + } + + pub fn ret_type(self) -> Option> { + super::child_opt(self) + } +} // ForExpr #[derive(Debug, Clone, Copy,)] @@ -1341,7 +1357,11 @@ impl> ForTypeNode { } -impl<'a> ForType<'a> {} +impl<'a> ForType<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } +} // IfExpr #[derive(Debug, Clone, Copy,)] @@ -3490,7 +3510,11 @@ impl> SliceTypeNode { } -impl<'a> SliceType<'a> {} +impl<'a> SliceType<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } +} // SourceFile #[derive(Debug, Clone, Copy,)] @@ -4025,7 +4049,11 @@ impl> TupleTypeNode { } -impl<'a> TupleType<'a> {} +impl<'a> TupleType<'a> { + pub fn fields(self) -> impl Iterator> + 'a { + super::children(self) + } +} // TypeDef #[derive(Debug, Clone, Copy,)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 8b1bd6d1c1fd..4bcff4e144bf 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -304,16 +304,16 @@ Grammar( "ImplItem": (), "ParenType": (options: ["TypeRef"]), - "TupleType": (), + "TupleType": ( collections: [["fields", "TypeRef"]] ), "NeverType": (), "PathType": (options: ["Path"]), "PointerType": (options: ["TypeRef"]), - "ArrayType": (), - "SliceType": (), + "ArrayType": ( options: ["TypeRef", "Expr"] ), + "SliceType": ( options: ["TypeRef"] ), "ReferenceType": (options: ["TypeRef"]), "PlaceholderType": (), - "FnPointerType": (), - "ForType": (), + "FnPointerType": (options: ["ParamList", "RetType"]), + "ForType": (options: ["TypeRef"]), "ImplTraitType": (), "DynTraitType": (), From bc745a139674f289386f3081458793f756cab5b9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 25 Dec 2018 21:40:33 +0100 Subject: [PATCH 15/15] Resolve field types lazily I.e. not already when getting the HIR for the struct. --- crates/ra_analysis/src/db.rs | 1 + 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 +- 7 files changed, 76 insertions(+), 59 deletions(-) diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index 677745d57db7..036e284bfbb0 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs @@ -95,6 +95,7 @@ salsa::database_storage! { fn submodules() for hir::db::SubmodulesQuery; fn infer() for hir::db::InferQuery; fn type_for_def() for hir::db::TypeForDefQuery; + fn type_for_field() for hir::db::TypeForFieldQuery; fn struct_data() for hir::db::StructDataQuery; fn enum_data() for hir::db::EnumDataQuery; } diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index dae04d258db4..65c461148aa4 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 113790ee9b7e..e7f9afa77d5c 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 ead2b8414b3e..f6882cb77198 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 405e359f1ce0..4a7958a1224e 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 11b4caa23b44..67b523c2cc50 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 ae163313fbe1..b36bb35d8941 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 {