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 780a84291a..7043a0f4d0 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 0000000000..a2d228593a --- /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 d94f75857f..113790ee9b 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 a0d99a84df..7e9824de9c 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 b5a9971707..ead2b8414b 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 b654af9204..72440d1d6f 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 c759d4c8b1..f86b749ec1 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 0000000000..70ad055ff3 --- /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 ad4d68a332..8c00be499e 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 911f08468e..6d466c8bdd 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 7043a0f4d0..677745d57d 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 7e9824de9c..81526fe9ca 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 8911199530..e1a0e4b595 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 0b152a4062..33c9d93c28 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 3e29c39541..03ea5c1d6c 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 f86b749ec1..429292cfc9 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 70ad055ff3..a4371c5a53 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 051f1f995b..af52893114 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 a2d228593a..ee270ac459 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 429292cfc9..386af8120a 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 170eef1471..9bb58ec850 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 a4371c5a53..41357749f2 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 c735338619..334da67ef0 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 e3b9032a0c..0da8b8183d 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 f12479fb46..5dbf9b221a 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 334da67ef0..35a9770a6f 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 0da8b8183d..e4e41d0776 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 ee270ac459..b44f59f0b8 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 d36477b48d..01f0f3a662 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 81526fe9ca..796970d8a2 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 72440d1d6f..405e359f1c 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 386af8120a..e2428a37f6 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 b44f59f0b8..03770ed7db 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 e2428a37f6..f931f3c872 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 41357749f2..cc8f3665bf 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 35a9770a6f..4e0550487f 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 e4e41d0776..923da03240 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 da78d85a2f..2d1f174912 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 0000000000..a7cdc17bb1 --- /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 0000000000..fd2a3f37b7 --- /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 d742d62955..fe580700ff 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 0000000000..fa62da2105 --- /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 064fbc6f7b..12e98e870c 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 6d466c8bdd..c9f9f495da 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 03770ed7db..e65f8deb86 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 796970d8a2..68fdbb7ea1 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 f931f3c872..83da13f1a0 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 af52893114..c0e4e3a36a 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 4e0550487f..6b2800a0e4 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 923da03240..dcde32923b 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 560f649895..5f7038f631 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 83da13f1a0..7b8dbe6b75 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 fa62da2105..93d6575768 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 12e98e870c..978772fd44 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 e65f8deb86..40a45b8312 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 7b8dbe6b75..3674688ef4 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 3674688ef4..54aa6715c8 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 9bb58ec850..a76925b58e 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 0c46f243a8..212e92e000 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 0000000000..296e955c19 --- /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 5dbf9b221a..8fb6b64083 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 6b2800a0e4..535dcc9752 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 dcde32923b..8b1bd6d1c1 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 8c00be499e..aaa2c7ceec 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 40a45b8312..dae04d258d 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 68fdbb7ea1..f1cc0ccd08 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 e1a0e4b595..b9d36f01f0 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 33c9d93c28..98cd225dde 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 e04d00900a..0b260072cc 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 54aa6715c8..11b4caa23b 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 535dcc9752..c22e026cf9 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 8b1bd6d1c1..4bcff4e144 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 677745d57d..036e284bfb 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 dae04d258d..65c461148a 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 113790ee9b..e7f9afa77d 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 ead2b8414b..f6882cb771 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 405e359f1c..4a7958a122 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 11b4caa23b..67b523c2cc 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 ae163313fb..b36bb35d89 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 {