From 226e31dae94f2c72f5cf650564e521b792793629 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 26 Dec 2018 21:28:05 +0100 Subject: Add test for self type inference --- crates/ra_hir/src/ty/tests.rs | 19 +++++++++++++++++++ crates/ra_hir/src/ty/tests/data/0007_self.txt | 4 ++++ 2 files changed, 23 insertions(+) create mode 100644 crates/ra_hir/src/ty/tests/data/0007_self.txt diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 93bf431c4..fb53fcf0b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -134,6 +134,25 @@ fn test() -> &mut &f64 { ); } +#[test] +fn infer_self() { + check_inference( + r#" +struct S; + +impl S { + fn test(&self) { + self; + } + fn test2(self: &Self) { + self; + } +} +"#, + "0007_self.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/0007_self.txt b/crates/ra_hir/src/ty/tests/data/0007_self.txt new file mode 100644 index 000000000..18cd796c2 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0007_self.txt @@ -0,0 +1,4 @@ +[50; 54) 'self': [unknown] +[40; 61) '{ ... }': () +[88; 109) '{ ... }': () +[98; 102) 'self': [unknown] -- cgit v1.2.3 From ae9530addc4c5e9bbfd5c0287d3c3adb2de95e40 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 28 Dec 2018 14:34:00 +0100 Subject: Add HIR for impl blocks Since we need to be able to go from def to containing impl block, as well as the other direction, and to find all impls for a certain type, a design similar to the one for modules, where we collect all impls for the whole crate and keep them in an arena, seemed fitting. The ImplBlock type, which provides the public interface, then consists only of an Arc to the arena containing all impls, and the index into it. --- crates/ra_analysis/src/db.rs | 1 + crates/ra_hir/src/db.rs | 8 +- crates/ra_hir/src/function.rs | 17 +++- crates/ra_hir/src/ids.rs | 13 ++- crates/ra_hir/src/impl_block.rs | 172 ++++++++++++++++++++++++++++++++++ crates/ra_hir/src/krate.rs | 2 +- crates/ra_hir/src/lib.rs | 2 + crates/ra_hir/src/mock.rs | 1 + crates/ra_hir/src/module.rs | 15 +++ crates/ra_syntax/src/ast/generated.rs | 40 +++++++- crates/ra_syntax/src/grammar.ron | 6 +- 11 files changed, 269 insertions(+), 8 deletions(-) create mode 100644 crates/ra_hir/src/impl_block.rs diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index d7740f0c4..d7e51a597 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs @@ -105,6 +105,7 @@ salsa::database_storage! { fn type_for_field() for hir::db::TypeForFieldQuery; fn struct_data() for hir::db::StructDataQuery; fn enum_data() for hir::db::EnumDataQuery; + fn impls_in_crate() for hir::db::ImplsInCrateQuery; } } } diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 73a4cdc5c..6d5235ba4 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -4,7 +4,7 @@ use ra_syntax::{SyntaxNode, SourceFileNode}; use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable}; use crate::{ - DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, + Crate, DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, SourceFileItems, SourceItemId, query_definitions, FnScopes, @@ -13,6 +13,7 @@ use crate::{ nameres::{ItemMap, InputModuleItems}}, ty::{InferenceResult, Ty}, adt::{StructData, EnumData}, + impl_block::CrateImplBlocks, }; salsa::query_group! { @@ -87,6 +88,11 @@ pub trait HirDatabase: SyntaxDatabase type ModuleTreeQuery; use fn crate::module::imp::module_tree; } + + fn impls_in_crate(krate: Crate) -> Cancelable> { + type ImplsInCrateQuery; + use fn crate::impl_block::impls_in_crate; + } } } diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs index 5a44132fc..75ef308ae 100644 --- a/crates/ra_hir/src/function.rs +++ b/crates/ra_hir/src/function.rs @@ -11,11 +11,11 @@ use ra_syntax::{ ast::{self, AstNode, DocCommentsOwner, NameOwner}, }; -use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module}; +use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock}; pub use self::scope::FnScopes; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Function { def_id: DefId, } @@ -25,6 +25,10 @@ impl Function { Function { def_id } } + pub fn def_id(&self) -> DefId { + self.def_id + } + pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode { let def_loc = self.def_id.loc(db); assert!(def_loc.kind == DefKind::Function); @@ -48,6 +52,15 @@ impl Function { pub fn module(&self, db: &impl HirDatabase) -> Cancelable { self.def_id.module(db) } + + pub fn krate(&self, db: &impl HirDatabase) -> Cancelable> { + self.def_id.krate(db) + } + + /// The containing impl block, if this is a method. + pub fn impl_block(&self, db: &impl HirDatabase) -> Cancelable> { + self.def_id.impl_block(db) + } } #[derive(Debug, Clone)] diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 66adacc7d..c98be66f9 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -2,7 +2,7 @@ use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId}; use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; use ra_arena::{Arena, RawId, impl_arena_id}; -use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum}; +use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, ImplBlock, Crate}; /// hir makes a heavy use of ids: integer (u32) handlers to various things. You /// can think of id as a pointer (but without a lifetime) or a file descriptor @@ -177,6 +177,17 @@ impl DefId { let loc = self.loc(db); Module::new(db, loc.source_root_id, loc.module_id) } + + /// Returns the containing crate. + pub fn krate(&self, db: &impl HirDatabase) -> Cancelable> { + Ok(self.module(db)?.krate(db)) + } + + /// Returns the containing impl block, if this is an impl item. + pub fn impl_block(self, db: &impl HirDatabase) -> Cancelable> { + let crate_impls = db.impls_in_crate(ctry!(self.krate(db)?))?; + Ok(ImplBlock::containing(crate_impls, self)) + } } impl DefLoc { diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs new file mode 100644 index 000000000..22f0a4461 --- /dev/null +++ b/crates/ra_hir/src/impl_block.rs @@ -0,0 +1,172 @@ +use std::sync::Arc; +use rustc_hash::FxHashMap; + +use ra_arena::{Arena, RawId, impl_arena_id}; +use ra_syntax::ast::{self, AstNode}; +use ra_db::{LocationIntener, Cancelable}; + +use crate::{ + Crate, DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, + Module, Function, + db::HirDatabase, + type_ref::TypeRef, + module::{ModuleSourceNode}, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImplBlock { + crate_impl_blocks: Arc, + impl_id: ImplId, +} + +impl ImplBlock { + pub(crate) fn containing( + crate_impl_blocks: Arc, + def_id: DefId, + ) -> Option { + let impl_id = *crate_impl_blocks.impls_by_def.get(&def_id)?; + Some(ImplBlock { + crate_impl_blocks, + impl_id, + }) + } + + fn impl_data(&self) -> &ImplData { + &self.crate_impl_blocks.impls[self.impl_id] + } + + pub fn target(&self) -> &TypeRef { + &self.impl_data().impl_for + } + + pub fn items(&self) -> &[ImplItem] { + &self.impl_data().items + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImplData { + impl_for: TypeRef, + items: Vec, +} + +impl ImplData { + pub(crate) fn from_ast( + db: &impl AsRef>, + file_items: &SourceFileItems, + module: &Module, + node: ast::ImplBlock, + ) -> Self { + let impl_for = TypeRef::from_ast_opt(node.target_type()); + let file_id = module.source().file_id(); + let items = if let Some(item_list) = node.item_list() { + item_list + .impl_items() + .map(|item_node| { + let kind = match item_node { + ast::ImplItem::FnDef(..) => DefKind::Function, + ast::ImplItem::ConstDef(..) => DefKind::Item, + ast::ImplItem::TypeDef(..) => DefKind::Item, + }; + let item_id = file_items.id_of_unchecked(item_node.syntax()); + let def_loc = DefLoc { + kind, + source_root_id: module.source_root_id, + module_id: module.module_id, + source_item_id: SourceItemId { + file_id, + item_id: Some(item_id), + }, + }; + let def_id = def_loc.id(db); + match item_node { + ast::ImplItem::FnDef(..) => ImplItem::Method(Function::new(def_id)), + ast::ImplItem::ConstDef(..) => ImplItem::Const(def_id), + ast::ImplItem::TypeDef(..) => ImplItem::Type(def_id), + } + }) + .collect() + } else { + Vec::new() + }; + ImplData { impl_for, items } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ImplItem { + Method(Function), + // these don't have their own types yet + Const(DefId), + Type(DefId), + // Existential +} + +impl ImplItem { + pub fn def_id(&self) -> DefId { + match self { + ImplItem::Method(f) => f.def_id(), + ImplItem::Const(def_id) => *def_id, + ImplItem::Type(def_id) => *def_id, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ImplId(pub RawId); +impl_arena_id!(ImplId); + +/// We have to collect all impl blocks in a crate, to later be able to find +/// impls for specific types. +#[derive(Debug, PartialEq, Eq)] +pub struct CrateImplBlocks { + impls: Arena, + impls_by_def: FxHashMap, +} + +impl CrateImplBlocks { + fn new() -> Self { + CrateImplBlocks { + impls: Arena::default(), + impls_by_def: FxHashMap::default(), + } + } + + fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> { + let module_source_node = module.source().resolve(db); + let node = match &module_source_node { + ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(), + ModuleSourceNode::Module(node) => node.borrowed().syntax(), + }; + + let source_file_items = db.file_items(module.source().file_id()); + + for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) { + let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast); + let id = self.impls.alloc(impl_block); + for impl_item in &self.impls[id].items { + self.impls_by_def.insert(impl_item.def_id(), id); + } + } + + for (_, child) in module.children() { + self.collect(db, child)?; + } + + Ok(()) + } +} + +pub(crate) fn impls_in_crate( + db: &impl HirDatabase, + krate: Crate, +) -> Cancelable> { + let mut result = CrateImplBlocks::new(); + let root_module = if let Some(root) = krate.root_module(db)? { + root + } else { + return Ok(Arc::new(result)); + }; + result.collect(db, root_module)?; + Ok(Arc::new(result)) +} diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs index a0821d15d..5194e280b 100644 --- a/crates/ra_hir/src/krate.rs +++ b/crates/ra_hir/src/krate.rs @@ -5,7 +5,7 @@ use crate::{HirDatabase, Module, Name, AsName, HirFileId}; /// hir::Crate describes a single crate. It's the main inteface with which /// crate's dependencies interact. Mostly, it should be just a proxy for the /// root module. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Crate { crate_id: CrateId, } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 344b543b6..2abcec441 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -31,6 +31,7 @@ mod function; mod adt; mod type_ref; mod ty; +mod impl_block; use crate::{ db::HirDatabase, @@ -48,6 +49,7 @@ pub use self::{ function::{Function, FnScopes}, adt::{Struct, Enum}, ty::Ty, + impl_block::{ImplBlock, ImplItem}, }; pub use self::function::FnSignatureInfo; diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 89b18194a..ef245ec7a 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -203,6 +203,7 @@ salsa::database_storage! { fn type_for_field() for db::TypeForFieldQuery; fn struct_data() for db::StructDataQuery; fn enum_data() for db::EnumDataQuery; + fn impls_in_crate() for db::ImplsInCrateQuery; } } } diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index c70dc54dd..b9821115c 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs @@ -71,6 +71,21 @@ impl Module { }) } + /// Returns an iterator of all children of this module. + pub fn children<'a>(&'a self) -> impl Iterator + 'a { + self.module_id + .children(&self.tree) + .map(move |(name, module_id)| { + ( + name, + Module { + module_id, + ..self.clone() + }, + ) + }) + } + /// Returns the crate this module is part of. pub fn krate(&self, db: &impl HirDatabase) -> Option { let root_id = self.module_id.crate_root(&self.tree); diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index c1c63f555..91de17ddf 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -1442,7 +1442,39 @@ impl> ImplBlockNode { } -impl<'a> ImplBlock<'a> {} +impl<'a> ImplBlock<'a> { + pub fn item_list(self) -> Option> { + super::child_opt(self) + } +} + +// ImplItem +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ImplItem<'a> { + FnDef(FnDef<'a>), + TypeDef(TypeDef<'a>), + ConstDef(ConstDef<'a>), +} + +impl<'a> AstNode<'a> for ImplItem<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + FN_DEF => Some(ImplItem::FnDef(FnDef { syntax })), + TYPE_DEF => Some(ImplItem::TypeDef(TypeDef { syntax })), + CONST_DEF => Some(ImplItem::ConstDef(ConstDef { syntax })), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { + match self { + ImplItem::FnDef(inner) => inner.syntax(), + ImplItem::TypeDef(inner) => inner.syntax(), + ImplItem::ConstDef(inner) => inner.syntax(), + } + } +} + +impl<'a> ImplItem<'a> {} // ImplTraitType #[derive(Debug, Clone, Copy,)] @@ -1555,7 +1587,11 @@ impl> ItemListNode { impl<'a> ast::FnDefOwner<'a> for ItemList<'a> {} impl<'a> ast::ModuleItemOwner<'a> for ItemList<'a> {} -impl<'a> ItemList<'a> {} +impl<'a> ItemList<'a> { + pub fn impl_items(self) -> impl Iterator> + 'a { + super::children(self) + } +} // Label #[derive(Debug, Clone, Copy,)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 9a4a96fac..688a4af1e 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -284,6 +284,7 @@ Grammar( options: [ "ItemList" ] ), "ItemList": ( + collections: [["impl_items", "ImplItem"]], traits: [ "FnDefOwner", "ModuleItemOwner" ], ), "ConstDef": ( traits: [ @@ -307,7 +308,7 @@ Grammar( "AttrsOwner", "DocCommentsOwner" ] ), - "ImplBlock": (collections: []), + "ImplBlock": (options: ["ItemList"]), "ParenType": (options: ["TypeRef"]), "TupleType": ( collections: [["fields", "TypeRef"]] ), @@ -351,6 +352,9 @@ Grammar( enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "TypeDef", "ImplBlock", "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ] ), + "ImplItem": ( + enum: ["FnDef", "TypeDef", "ConstDef"] + ), "TupleExpr": (), "ArrayExpr": (), -- cgit v1.2.3 From 111126ed3c4f6358e0c833f80226e5192778f749 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 29 Dec 2018 21:32:07 +0100 Subject: Type the self parameter --- crates/ra_hir/src/mock.rs | 4 +++ crates/ra_hir/src/ty.rs | 38 ++++++++++++++++++---- crates/ra_hir/src/ty/tests/data/0007_self.txt | 2 ++ crates/ra_syntax/src/ast.rs | 31 ++++++++++++++++++ crates/ra_syntax/src/ast/generated.rs | 47 ++++++++++++++++++++++++++- crates/ra_syntax/src/grammar.ron | 3 +- 6 files changed, 116 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index ef245ec7a..2419d256a 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -30,6 +30,10 @@ impl MockDatabase { let file_id = db.add_file(&mut source_root, "/main.rs", text); db.query_mut(ra_db::SourceRootQuery) .set(WORKSPACE, Arc::new(source_root.clone())); + + let mut crate_graph = CrateGraph::default(); + crate_graph.add_crate_root(file_id); + db.set_crate_graph(crate_graph); (db, source_root, file_id) } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 719b3f7cd..c762ec606 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -918,22 +918,46 @@ pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable ty, + ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), + ast::SelfParamFlavor::MutRef => Ty::Ref(Arc::new(ty), Mutability::Mut), + }; + ctx.insert_type_vars(ty) + } + } else { + log::debug!( + "No impl block found, but self param for function {:?}", + def_id + ); + ctx.new_type_var() + }; + if let Some(self_kw) = self_param.self_kw() { + ctx.type_of + .insert(LocalSyntaxPtr::new(self_kw.syntax()), self_type); + } + } for param in param_list.params() { let pat = if let Some(pat) = param.pat() { pat } else { continue; }; - if let Some(type_ref) = param.type_ref() { + let ty = if let Some(type_ref) = param.type_ref() { let ty = Ty::from_ast(db, &ctx.module, type_ref)?; - let ty = ctx.insert_type_vars(ty); - ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); + ctx.insert_type_vars(ty) } else { - // TODO self param - let type_var = ctx.new_type_var(); - ctx.type_of - .insert(LocalSyntaxPtr::new(pat.syntax()), type_var); + // missing type annotation + ctx.new_type_var() }; + ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); } } diff --git a/crates/ra_hir/src/ty/tests/data/0007_self.txt b/crates/ra_hir/src/ty/tests/data/0007_self.txt index 18cd796c2..3d3c8b260 100644 --- a/crates/ra_hir/src/ty/tests/data/0007_self.txt +++ b/crates/ra_hir/src/ty/tests/data/0007_self.txt @@ -1,4 +1,6 @@ [50; 54) 'self': [unknown] +[34; 38) 'self': &S [40; 61) '{ ... }': () [88; 109) '{ ... }': () [98; 102) 'self': [unknown] +[75; 79) 'self': &[unknown] diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 7f986d322..2a3bd27e2 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -482,6 +482,37 @@ impl<'a> PrefixExpr<'a> { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum SelfParamFlavor { + /// self + Owned, + /// &self + Ref, + /// &mut self + MutRef, +} + +impl<'a> SelfParam<'a> { + pub fn flavor(&self) -> SelfParamFlavor { + let borrowed = self.syntax().children().any(|n| n.kind() == AMP); + if borrowed { + // check for a `mut` coming after the & -- `mut &self` != `&mut self` + if self + .syntax() + .children() + .skip_while(|n| n.kind() != AMP) + .any(|n| n.kind() == MUT_KW) + { + SelfParamFlavor::MutRef + } else { + SelfParamFlavor::Ref + } + } else { + SelfParamFlavor::Owned + } + } +} + #[test] fn test_doc_comment_of_items() { let file = SourceFileNode::parse( diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 91de17ddf..7df6a9c46 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -3488,6 +3488,43 @@ impl<'a> ReturnExpr<'a> { } } +// SelfKw +#[derive(Debug, Clone, Copy,)] +pub struct SelfKwNode = OwnedRoot> { + pub(crate) syntax: SyntaxNode, +} +pub type SelfKw<'a> = SelfKwNode>; + +impl, R2: TreeRoot> PartialEq> for SelfKwNode { + fn eq(&self, other: &SelfKwNode) -> bool { self.syntax == other.syntax } +} +impl> Eq for SelfKwNode {} +impl> Hash for SelfKwNode { + fn hash(&self, state: &mut H) { self.syntax.hash(state) } +} + +impl<'a> AstNode<'a> for SelfKw<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + SELF_KW => Some(SelfKw { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl> SelfKwNode { + pub fn borrowed(&self) -> SelfKw { + SelfKwNode { syntax: self.syntax.borrowed() } + } + pub fn owned(&self) -> SelfKwNode { + SelfKwNode { syntax: self.syntax.owned() } + } +} + + +impl<'a> SelfKw<'a> {} + // SelfParam #[derive(Debug, Clone, Copy,)] pub struct SelfParamNode = OwnedRoot> { @@ -3523,7 +3560,15 @@ impl> SelfParamNode { } -impl<'a> SelfParam<'a> {} +impl<'a> SelfParam<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } + + pub fn self_kw(self) -> Option> { + super::child_opt(self) + } +} // SlicePat #[derive(Debug, Clone, Copy,)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 688a4af1e..c55e9e07a 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -534,7 +534,8 @@ Grammar( ["params", "Param"] ] ), - "SelfParam": (), + "SelfParam": (options: ["TypeRef", "SelfKw"]), + "SelfKw": (), "Param": ( options: [ "Pat", "TypeRef" ], ), -- cgit v1.2.3 From d4db61b9a151a2a46c4067e61b0a4b1a9e3c73ec Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 29 Dec 2018 22:59:18 +0100 Subject: Resolve the self parameter during type inference --- crates/ra_hir/src/path.rs | 5 +++++ crates/ra_hir/src/ty.rs | 14 ++++++++++++-- crates/ra_hir/src/ty/tests/data/0007_self.txt | 4 ++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 93f7203fe..9fdfa0d13 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -70,6 +70,11 @@ impl Path { self.kind == PathKind::Plain && self.segments.len() == 1 } + /// `true` if this path is just a standalone `self` + pub fn is_self(&self) -> bool { + self.kind == PathKind::Self_ && self.segments.len() == 0 + } + /// If this path is a single identifier, like `foo`, return its name. pub fn as_ident(&self) -> Option<&Name> { if self.kind != PathKind::Plain || self.segments.len() > 1 { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index c762ec606..5ea62a14c 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -496,6 +496,8 @@ impl InferenceResult { struct InferenceContext<'a, D: HirDatabase> { db: &'a D, scopes: Arc, + /// The self param for the current method, if it exists. + self_param: Option, module: Module, var_unification_table: InPlaceUnificationTable, type_of: FxHashMap, @@ -506,6 +508,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { InferenceContext { type_of: FxHashMap::default(), var_unification_table: InPlaceUnificationTable::new(), + self_param: None, // set during parameter typing db, scopes, module, @@ -628,6 +631,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty = self.resolve_ty_as_possible(ty.clone()); return Ok(Some(ty)); }; + } else if path.is_self() { + // resolve `self` param + let self_param = ctry!(self.self_param); + let ty = ctry!(self.type_of.get(&self_param)); + let ty = self.resolve_ty_as_possible(ty.clone()); + return Ok(Some(ty)); }; // resolve in module @@ -940,8 +949,9 @@ pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable Date: Sat, 29 Dec 2018 23:20:12 +0100 Subject: Resolve the Self type --- crates/ra_hir/src/name.rs | 5 +- crates/ra_hir/src/ty.rs | 86 ++++++++++++++++++++------- crates/ra_hir/src/ty/tests/data/0007_self.txt | 4 +- 3 files changed, 71 insertions(+), 24 deletions(-) diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 51e8b3da8..017caf442 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -51,6 +51,7 @@ impl Name { "u128" => KnownName::U128, "f32" => KnownName::F32, "f64" => KnownName::F64, + "Self" => KnownName::Self_, _ => return None, }; Some(name) @@ -84,7 +85,7 @@ impl AsName for ra_db::Dependency { // const ISIZE: Name = Name::new("isize") // ``` // but const-fn is not that powerful yet. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub(crate) enum KnownName { Isize, I8, @@ -102,4 +103,6 @@ pub(crate) enum KnownName { F32, F64, + + Self_, } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 5ea62a14c..d11f80cff 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -31,9 +31,10 @@ use ra_syntax::{ }; use crate::{ - Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, + Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock, db::HirDatabase, type_ref::{TypeRef, Mutability}, + name::KnownName, }; /// The ID of a type variable. @@ -235,6 +236,7 @@ impl Ty { pub(crate) fn from_hir( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, type_ref: &TypeRef, ) -> Cancelable { Ok(match type_ref { @@ -242,29 +244,29 @@ impl Ty { TypeRef::Tuple(inner) => { let inner_tys = inner .iter() - .map(|tr| Ty::from_hir(db, module, tr)) + .map(|tr| Ty::from_hir(db, module, impl_block, tr)) .collect::>>()?; Ty::Tuple(inner_tys.into()) } - TypeRef::Path(path) => Ty::from_hir_path(db, module, path)?, + TypeRef::Path(path) => Ty::from_hir_path(db, module, impl_block, path)?, TypeRef::RawPtr(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, inner)?; + let inner_ty = Ty::from_hir(db, module, impl_block, 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)?; + let inner_ty = Ty::from_hir(db, module, impl_block, inner)?; Ty::Slice(Arc::new(inner_ty)) } TypeRef::Reference(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, inner)?; + let inner_ty = Ty::from_hir(db, module, impl_block, inner)?; Ty::Ref(Arc::new(inner_ty), *mutability) } TypeRef::Placeholder => Ty::Unknown, TypeRef::Fn(params) => { let mut inner_tys = params .iter() - .map(|tr| Ty::from_hir(db, module, tr)) + .map(|tr| Ty::from_hir(db, module, impl_block, tr)) .collect::>>()?; let return_ty = inner_tys .pop() @@ -279,9 +281,21 @@ impl Ty { }) } + pub(crate) fn from_hir_opt( + db: &impl HirDatabase, + module: &Module, + impl_block: Option<&ImplBlock>, + type_ref: Option<&TypeRef>, + ) -> Cancelable { + type_ref + .map(|t| Ty::from_hir(db, module, impl_block, t)) + .unwrap_or(Ok(Ty::Unknown)) + } + pub(crate) fn from_hir_path( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, path: &Path, ) -> Cancelable { if let Some(name) = path.as_ident() { @@ -291,6 +305,8 @@ impl Ty { return Ok(Ty::Uint(uint_ty)); } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { return Ok(Ty::Float(float_ty)); + } else if name.as_known_name() == Some(KnownName::Self_) { + return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target())); } } @@ -308,18 +324,20 @@ impl Ty { pub(crate) fn from_ast_opt( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, node: Option, ) -> Cancelable { - node.map(|n| Ty::from_ast(db, module, n)) + node.map(|n| Ty::from_ast(db, module, impl_block, n)) .unwrap_or(Ok(Ty::Unknown)) } pub(crate) fn from_ast( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, node: ast::TypeRef, ) -> Cancelable { - Ty::from_hir(db, module, &TypeRef::from_ast(node)) + Ty::from_hir(db, module, impl_block, &TypeRef::from_ast(node)) } pub fn unit() -> Self { @@ -402,18 +420,19 @@ impl fmt::Display for Ty { fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { let syntax = f.syntax(db); let module = f.module(db)?; + let impl_block = f.impl_block(db)?; let node = syntax.borrowed(); // TODO we ignore type parameters for now let input = node .param_list() .map(|pl| { pl.params() - .map(|p| Ty::from_ast_opt(db, &module, p.type_ref())) + .map(|p| Ty::from_ast_opt(db, &module, impl_block.as_ref(), p.type_ref())) .collect() }) .unwrap_or_else(|| Ok(Vec::new()))?; let output = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) { - Ty::from_ast(db, &module, type_ref)? + Ty::from_ast(db, &module, impl_block.as_ref(), type_ref)? } else { Ty::unit() }; @@ -467,12 +486,13 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) ), }; let module = def_id.module(db)?; + let impl_block = def_id.impl_block(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) + Ty::from_hir(db, &module, impl_block.as_ref(), &type_ref) } /// The result of type inference: A mapping from expressions and patterns to types. @@ -499,12 +519,18 @@ struct InferenceContext<'a, D: HirDatabase> { /// The self param for the current method, if it exists. self_param: Option, module: Module, + impl_block: Option, var_unification_table: InPlaceUnificationTable, type_of: FxHashMap, } impl<'a, D: HirDatabase> InferenceContext<'a, D> { - fn new(db: &'a D, scopes: Arc, module: Module) -> Self { + fn new( + db: &'a D, + scopes: Arc, + module: Module, + impl_block: Option, + ) -> Self { InferenceContext { type_of: FxHashMap::default(), var_unification_table: InPlaceUnificationTable::new(), @@ -512,6 +538,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { db, scopes, module, + impl_block, } } @@ -835,7 +862,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } ast::Expr::CastExpr(e) => { let _inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; - let cast_ty = Ty::from_ast_opt(self.db, &self.module, e.type_ref())?; + let cast_ty = Ty::from_ast_opt( + self.db, + &self.module, + self.impl_block.as_ref(), + e.type_ref(), + )?; let cast_ty = self.insert_type_vars(cast_ty); // TODO do the coercion... cast_ty @@ -889,7 +921,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { for stmt in node.statements() { match stmt { ast::Stmt::LetStmt(stmt) => { - let decl_ty = Ty::from_ast_opt(self.db, &self.module, stmt.type_ref())?; + let decl_ty = Ty::from_ast_opt( + self.db, + &self.module, + self.impl_block.as_ref(), + stmt.type_ref(), + )?; let decl_ty = self.insert_type_vars(decl_ty); let ty = if let Some(expr) = stmt.initializer() { let expr_ty = self.infer_expr(expr, &Expectation::has_type(decl_ty))?; @@ -921,19 +958,26 @@ pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable ty, ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), @@ -961,7 +1005,7 @@ pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable Cancelable Date: Sat, 29 Dec 2018 23:35:57 +0100 Subject: Refactor a bit --- crates/ra_hir/src/ty.rs | 120 +++++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d11f80cff..45a01679c 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -522,6 +522,8 @@ struct InferenceContext<'a, D: HirDatabase> { impl_block: Option, var_unification_table: InPlaceUnificationTable, type_of: FxHashMap, + /// The return type of the function being inferred. + return_ty: Ty, } impl<'a, D: HirDatabase> InferenceContext<'a, D> { @@ -534,7 +536,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { InferenceContext { type_of: FxHashMap::default(), var_unification_table: InPlaceUnificationTable::new(), - self_param: None, // set during parameter typing + self_param: None, // set during parameter typing + return_ty: Ty::Unknown, // set in collect_fn_signature db, scopes, module, @@ -555,6 +558,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.type_of.insert(LocalSyntaxPtr::new(node), ty); } + fn make_ty(&self, type_ref: &TypeRef) -> Cancelable { + Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), type_ref) + } + + fn make_ty_opt(&self, type_ref: Option<&TypeRef>) -> Cancelable { + Ty::from_hir_opt(self.db, &self.module, self.impl_block.as_ref(), type_ref) + } + fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { match (ty1, ty2) { (Ty::Unknown, ..) => true, @@ -952,78 +963,71 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.write_ty(node.syntax(), ty.clone()); Ok(ty) } -} - -pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable> { - let function = Function::new(def_id); // TODO: consts also need inference - let scopes = function.scopes(db); - let module = function.module(db)?; - let impl_block = function.impl_block(db)?; - let mut ctx = InferenceContext::new(db, scopes, module, impl_block); - let syntax = function.syntax(db); - let node = syntax.borrowed(); - - if let Some(param_list) = node.param_list() { - if let Some(self_param) = param_list.self_param() { - let self_type = if let Some(impl_block) = &ctx.impl_block { - if let Some(type_ref) = self_param.type_ref() { - let ty = Ty::from_ast(db, &ctx.module, ctx.impl_block.as_ref(), type_ref)?; - ctx.insert_type_vars(ty) + fn collect_fn_signature(&mut self, node: ast::FnDef) -> Cancelable<()> { + if let Some(param_list) = node.param_list() { + if let Some(self_param) = param_list.self_param() { + let self_type = if let Some(type_ref) = self_param.type_ref() { + let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; + self.insert_type_vars(ty) } else { // TODO this should be handled by desugaring during HIR conversion - let ty = Ty::from_hir( - db, - &ctx.module, - ctx.impl_block.as_ref(), - impl_block.target(), - )?; + let ty = self.make_ty_opt(self.impl_block.as_ref().map(|i| i.target()))?; let ty = match self_param.flavor() { ast::SelfParamFlavor::Owned => ty, ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), ast::SelfParamFlavor::MutRef => Ty::Ref(Arc::new(ty), Mutability::Mut), }; - ctx.insert_type_vars(ty) + self.insert_type_vars(ty) + }; + if let Some(self_kw) = self_param.self_kw() { + let self_param = LocalSyntaxPtr::new(self_kw.syntax()); + self.self_param = Some(self_param); + self.type_of.insert(self_param, self_type); } - } else { - log::debug!( - "No impl block found, but self param for function {:?}", - def_id - ); - ctx.new_type_var() - }; - if let Some(self_kw) = self_param.self_kw() { - let self_param = LocalSyntaxPtr::new(self_kw.syntax()); - ctx.self_param = Some(self_param); - ctx.type_of.insert(self_param, self_type); + } + for param in param_list.params() { + let pat = if let Some(pat) = param.pat() { + pat + } else { + continue; + }; + let ty = if let Some(type_ref) = param.type_ref() { + let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; + self.insert_type_vars(ty) + } else { + // missing type annotation + self.new_type_var() + }; + self.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); } } - for param in param_list.params() { - let pat = if let Some(pat) = param.pat() { - pat - } else { - continue; - }; - let ty = if let Some(type_ref) = param.type_ref() { - let ty = Ty::from_ast(db, &ctx.module, ctx.impl_block.as_ref(), type_ref)?; - ctx.insert_type_vars(ty) - } else { - // missing type annotation - ctx.new_type_var() - }; - ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); - } + + self.return_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) { + let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; + self.insert_type_vars(ty) + } else { + Ty::unit() + }; + + Ok(()) } +} - let ret_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) { - let ty = Ty::from_ast(db, &ctx.module, ctx.impl_block.as_ref(), type_ref)?; - ctx.insert_type_vars(ty) - } else { - Ty::unit() - }; +pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable> { + let function = Function::new(def_id); // TODO: consts also need inference + let scopes = function.scopes(db); + let module = function.module(db)?; + let impl_block = function.impl_block(db)?; + let mut ctx = InferenceContext::new(db, scopes, module, impl_block); + + let syntax = function.syntax(db); + let node = syntax.borrowed(); + + ctx.collect_fn_signature(node)?; if let Some(block) = node.body() { - ctx.infer_block(block, &Expectation::has_type(ret_ty))?; + ctx.infer_block(block, &Expectation::has_type(ctx.return_ty.clone()))?; } Ok(Arc::new(ctx.resolve_all())) -- cgit v1.2.3 From 0ad13b9477fc6b15cbfbd521a79ea97bc0e79953 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 30 Dec 2018 00:03:52 +0100 Subject: Add a test for self field completion Needed to add a default crate graph in the analysis for that. --- crates/ra_analysis/src/completion/complete_dot.rs | 15 +++++++++++++++ crates/ra_analysis/src/mock_analysis.rs | 7 ++++++- crates/ra_analysis/tests/test/main.rs | 8 ++++---- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs index f24835d17..031d8b98f 100644 --- a/crates/ra_analysis/src/completion/complete_dot.rs +++ b/crates/ra_analysis/src/completion/complete_dot.rs @@ -72,6 +72,21 @@ mod tests { ); } + #[test] + fn test_struct_field_completion_self() { + check_ref_completion( + r" + struct A { the_field: u32 } + impl A { + fn foo(self) { + self.<|> + } + } + ", + r#"the_field"#, + ); + } + #[test] fn test_no_struct_field_completion_for_method_call() { check_ref_completion( diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs index 960529404..846c76cfe 100644 --- a/crates/ra_analysis/src/mock_analysis.rs +++ b/crates/ra_analysis/src/mock_analysis.rs @@ -4,7 +4,7 @@ use relative_path::RelativePathBuf; use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; use ra_db::mock::FileMap; -use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, FileRange, SourceRootId}; +use crate::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FilePosition, FileRange, SourceRootId}; /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis /// from a set of in-memory files. @@ -87,12 +87,17 @@ impl MockAnalysis { let source_root = SourceRootId(0); let mut change = AnalysisChange::new(); change.add_root(source_root, true); + let mut crate_graph = CrateGraph::default(); for (path, contents) in self.files.into_iter() { assert!(path.starts_with('/')); let path = RelativePathBuf::from_path(&path[1..]).unwrap(); let file_id = file_map.add(path.clone()); + if path == "/lib.rs" || path == "/main.rs" { + crate_graph.add_crate_root(file_id); + } change.add_file(source_root, file_id, path, Arc::new(contents)); } + change.set_crate_graph(crate_graph); // change.set_file_resolver(Arc::new(file_map)); host.apply_change(change); host diff --git a/crates/ra_analysis/tests/test/main.rs b/crates/ra_analysis/tests/test/main.rs index 859778024..beeae1e19 100644 --- a/crates/ra_analysis/tests/test/main.rs +++ b/crates/ra_analysis/tests/test/main.rs @@ -138,14 +138,14 @@ fn test_resolve_parent_module_for_inline() { fn test_resolve_crate_root() { let mock = MockAnalysis::with_files( " - //- /lib.rs + //- /bar.rs mod foo; - //- /foo.rs + //- /bar/foo.rs // emtpy <|> ", ); - let root_file = mock.id_of("/lib.rs"); - let mod_file = mock.id_of("/foo.rs"); + let root_file = mock.id_of("/bar.rs"); + let mod_file = mock.id_of("/bar/foo.rs"); let mut host = mock.analysis_host(); assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); -- cgit v1.2.3 From bb029cd29b8496e69ca625fabc3612e4c1fe9142 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 30 Dec 2018 18:38:44 +0100 Subject: Rename traits::impl_item -> impl_block as well, as well as the tests --- crates/ra_syntax/src/grammar/items.rs | 2 +- crates/ra_syntax/src/grammar/items/traits.rs | 6 +++--- .../data/parser/inline/ok/0063_impl_block_neg.rs | 1 + .../data/parser/inline/ok/0063_impl_block_neg.txt | 23 ++++++++++++++++++++++ .../data/parser/inline/ok/0063_impl_item_neg.rs | 1 - .../data/parser/inline/ok/0063_impl_item_neg.txt | 23 ---------------------- .../tests/data/parser/inline/ok/0079_impl_block.rs | 1 + .../data/parser/inline/ok/0079_impl_block.txt | 14 +++++++++++++ .../tests/data/parser/inline/ok/0079_impl_item.rs | 1 - .../tests/data/parser/inline/ok/0079_impl_item.txt | 14 ------------- 10 files changed, 43 insertions(+), 43 deletions(-) create mode 100644 crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.rs create mode 100644 crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.txt delete mode 100644 crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.rs delete mode 100644 crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.txt create mode 100644 crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.rs create mode 100644 crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.txt delete mode 100644 crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.rs delete mode 100644 crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.txt diff --git a/crates/ra_syntax/src/grammar/items.rs b/crates/ra_syntax/src/grammar/items.rs index b9a00b565..265e84570 100644 --- a/crates/ra_syntax/src/grammar/items.rs +++ b/crates/ra_syntax/src/grammar/items.rs @@ -151,7 +151,7 @@ pub(super) fn maybe_item(p: &mut Parser, flavor: ItemFlavor) -> MaybeItem { // test unsafe_default_impl // unsafe default impl Foo {} IMPL_KW => { - traits::impl_item(p); + traits::impl_block(p); IMPL_BLOCK } _ => { diff --git a/crates/ra_syntax/src/grammar/items/traits.rs b/crates/ra_syntax/src/grammar/items/traits.rs index d4da8b2f7..0a0621753 100644 --- a/crates/ra_syntax/src/grammar/items/traits.rs +++ b/crates/ra_syntax/src/grammar/items/traits.rs @@ -40,9 +40,9 @@ pub(crate) fn trait_item_list(p: &mut Parser) { m.complete(p, ITEM_LIST); } -// test impl_item +// test impl_block // impl Foo {} -pub(super) fn impl_item(p: &mut Parser) { +pub(super) fn impl_block(p: &mut Parser) { assert!(p.at(IMPL_KW)); p.bump(); if choose_type_params_over_qpath(p) { @@ -52,7 +52,7 @@ pub(super) fn impl_item(p: &mut Parser) { // TODO: never type // impl ! {} - // test impl_item_neg + // test impl_block_neg // impl !Send for X {} p.eat(EXCL); impl_type(p); diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.rs new file mode 100644 index 000000000..b7527c870 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.rs @@ -0,0 +1 @@ +impl !Send for X {} diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.txt new file mode 100644 index 000000000..563e43508 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.txt @@ -0,0 +1,23 @@ +SOURCE_FILE@[0; 20) + IMPL_BLOCK@[0; 19) + IMPL_KW@[0; 4) + WHITESPACE@[4; 5) + EXCL@[5; 6) + PATH_TYPE@[6; 10) + PATH@[6; 10) + PATH_SEGMENT@[6; 10) + NAME_REF@[6; 10) + IDENT@[6; 10) "Send" + WHITESPACE@[10; 11) + FOR_KW@[11; 14) + WHITESPACE@[14; 15) + PATH_TYPE@[15; 16) + PATH@[15; 16) + PATH_SEGMENT@[15; 16) + NAME_REF@[15; 16) + IDENT@[15; 16) "X" + WHITESPACE@[16; 17) + ITEM_LIST@[17; 19) + L_CURLY@[17; 18) + R_CURLY@[18; 19) + WHITESPACE@[19; 20) diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.rs deleted file mode 100644 index b7527c870..000000000 --- a/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.rs +++ /dev/null @@ -1 +0,0 @@ -impl !Send for X {} diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.txt deleted file mode 100644 index 563e43508..000000000 --- a/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.txt +++ /dev/null @@ -1,23 +0,0 @@ -SOURCE_FILE@[0; 20) - IMPL_BLOCK@[0; 19) - IMPL_KW@[0; 4) - WHITESPACE@[4; 5) - EXCL@[5; 6) - PATH_TYPE@[6; 10) - PATH@[6; 10) - PATH_SEGMENT@[6; 10) - NAME_REF@[6; 10) - IDENT@[6; 10) "Send" - WHITESPACE@[10; 11) - FOR_KW@[11; 14) - WHITESPACE@[14; 15) - PATH_TYPE@[15; 16) - PATH@[15; 16) - PATH_SEGMENT@[15; 16) - NAME_REF@[15; 16) - IDENT@[15; 16) "X" - WHITESPACE@[16; 17) - ITEM_LIST@[17; 19) - L_CURLY@[17; 18) - R_CURLY@[18; 19) - WHITESPACE@[19; 20) diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.rs new file mode 100644 index 000000000..d6337f6b3 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.rs @@ -0,0 +1 @@ +impl Foo {} diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.txt new file mode 100644 index 000000000..a2c218aa9 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.txt @@ -0,0 +1,14 @@ +SOURCE_FILE@[0; 12) + IMPL_BLOCK@[0; 11) + IMPL_KW@[0; 4) + WHITESPACE@[4; 5) + PATH_TYPE@[5; 8) + PATH@[5; 8) + PATH_SEGMENT@[5; 8) + NAME_REF@[5; 8) + IDENT@[5; 8) "Foo" + WHITESPACE@[8; 9) + ITEM_LIST@[9; 11) + L_CURLY@[9; 10) + R_CURLY@[10; 11) + WHITESPACE@[11; 12) diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.rs deleted file mode 100644 index d6337f6b3..000000000 --- a/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.rs +++ /dev/null @@ -1 +0,0 @@ -impl Foo {} diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.txt deleted file mode 100644 index a2c218aa9..000000000 --- a/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.txt +++ /dev/null @@ -1,14 +0,0 @@ -SOURCE_FILE@[0; 12) - IMPL_BLOCK@[0; 11) - IMPL_KW@[0; 4) - WHITESPACE@[4; 5) - PATH_TYPE@[5; 8) - PATH@[5; 8) - PATH_SEGMENT@[5; 8) - NAME_REF@[5; 8) - IDENT@[5; 8) "Foo" - WHITESPACE@[8; 9) - ITEM_LIST@[9; 11) - L_CURLY@[9; 10) - R_CURLY@[10; 11) - WHITESPACE@[11; 12) -- cgit v1.2.3 From 334ca0d9a790d14414301daa896848bf9a880982 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 30 Dec 2018 19:59:49 +0100 Subject: Rename ImplBlock::target -> target_type, and add target_trait already --- crates/ra_hir/src/impl_block.rs | 20 +++++++++++++++----- crates/ra_hir/src/ty.rs | 4 ++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 22f0a4461..77fab24d0 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -35,8 +35,12 @@ impl ImplBlock { &self.crate_impl_blocks.impls[self.impl_id] } - pub fn target(&self) -> &TypeRef { - &self.impl_data().impl_for + pub fn target_trait(&self) -> Option<&TypeRef> { + self.impl_data().target_trait.as_ref() + } + + pub fn target_type(&self) -> &TypeRef { + &self.impl_data().target_type } pub fn items(&self) -> &[ImplItem] { @@ -46,7 +50,8 @@ impl ImplBlock { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ImplData { - impl_for: TypeRef, + target_trait: Option, + target_type: TypeRef, items: Vec, } @@ -57,7 +62,8 @@ impl ImplData { module: &Module, node: ast::ImplBlock, ) -> Self { - let impl_for = TypeRef::from_ast_opt(node.target_type()); + let target_trait = node.target_type().map(TypeRef::from_ast); + let target_type = TypeRef::from_ast_opt(node.target_type()); let file_id = module.source().file_id(); let items = if let Some(item_list) = node.item_list() { item_list @@ -89,7 +95,11 @@ impl ImplData { } else { Vec::new() }; - ImplData { impl_for, items } + ImplData { + target_trait, + target_type, + items, + } } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 45a01679c..e33762e0d 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -306,7 +306,7 @@ impl Ty { } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { return Ok(Ty::Float(float_ty)); } else if name.as_known_name() == Some(KnownName::Self_) { - return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target())); + return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); } } @@ -972,7 +972,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.insert_type_vars(ty) } else { // TODO this should be handled by desugaring during HIR conversion - let ty = self.make_ty_opt(self.impl_block.as_ref().map(|i| i.target()))?; + let ty = self.make_ty_opt(self.impl_block.as_ref().map(|i| i.target_type()))?; let ty = match self_param.flavor() { ast::SelfParamFlavor::Owned => ty, ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), -- cgit v1.2.3 From 443ddb73c395a311b4ddff3bd8267a0eb7079216 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 4 Jan 2019 19:29:53 +0100 Subject: Do impl collection per module, not per crate --- crates/ra_hir/src/db.rs | 8 +++---- crates/ra_hir/src/ids.rs | 5 ++-- crates/ra_hir/src/impl_block.rs | 52 ++++++++++++++++++++--------------------- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 6d5235ba4..a045bbb12 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -4,7 +4,7 @@ use ra_syntax::{SyntaxNode, SourceFileNode}; use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable}; use crate::{ - Crate, DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, + DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, SourceFileItems, SourceItemId, query_definitions, FnScopes, @@ -13,7 +13,7 @@ use crate::{ nameres::{ItemMap, InputModuleItems}}, ty::{InferenceResult, Ty}, adt::{StructData, EnumData}, - impl_block::CrateImplBlocks, + impl_block::ModuleImplBlocks, }; salsa::query_group! { @@ -89,9 +89,9 @@ pub trait HirDatabase: SyntaxDatabase use fn crate::module::imp::module_tree; } - fn impls_in_crate(krate: Crate) -> Cancelable> { + fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable> { type ImplsInCrateQuery; - use fn crate::impl_block::impls_in_crate; + use fn crate::impl_block::impls_in_module; } } diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index c98be66f9..4d6378e02 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -185,8 +185,9 @@ impl DefId { /// Returns the containing impl block, if this is an impl item. pub fn impl_block(self, db: &impl HirDatabase) -> Cancelable> { - let crate_impls = db.impls_in_crate(ctry!(self.krate(db)?))?; - Ok(ImplBlock::containing(crate_impls, self)) + let loc = self.loc(db); + let module_impls = db.impls_in_module(loc.source_root_id, loc.module_id)?; + Ok(ImplBlock::containing(module_impls, self)) } } diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 77fab24d0..01afa84c4 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -3,36 +3,36 @@ use rustc_hash::FxHashMap; use ra_arena::{Arena, RawId, impl_arena_id}; use ra_syntax::ast::{self, AstNode}; -use ra_db::{LocationIntener, Cancelable}; +use ra_db::{LocationIntener, Cancelable, SourceRootId}; use crate::{ - Crate, DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, + DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, Module, Function, db::HirDatabase, type_ref::TypeRef, - module::{ModuleSourceNode}, + module::{ModuleSourceNode, ModuleId}, }; #[derive(Debug, Clone, PartialEq, Eq)] pub struct ImplBlock { - crate_impl_blocks: Arc, + module_impl_blocks: Arc, impl_id: ImplId, } impl ImplBlock { pub(crate) fn containing( - crate_impl_blocks: Arc, + module_impl_blocks: Arc, def_id: DefId, ) -> Option { - let impl_id = *crate_impl_blocks.impls_by_def.get(&def_id)?; + let impl_id = *module_impl_blocks.impls_by_def.get(&def_id)?; Some(ImplBlock { - crate_impl_blocks, + module_impl_blocks, impl_id, }) } fn impl_data(&self) -> &ImplData { - &self.crate_impl_blocks.impls[self.impl_id] + &self.module_impl_blocks.impls[self.impl_id] } pub fn target_trait(&self) -> Option<&TypeRef> { @@ -126,17 +126,22 @@ impl ImplItem { pub struct ImplId(pub RawId); impl_arena_id!(ImplId); -/// We have to collect all impl blocks in a crate, to later be able to find -/// impls for specific types. +/// Collection of impl blocks is a two-step process: First we collect the blocks +/// per-module; then we build an index of all impl blocks in the crate. This +/// way, we avoid having to do this process for the whole crate whenever someone +/// types in any file; as long as the impl blocks in the file don't change, we +/// don't need to do the second step again. +/// +/// (The second step does not yet exist currently.) #[derive(Debug, PartialEq, Eq)] -pub struct CrateImplBlocks { +pub struct ModuleImplBlocks { impls: Arena, impls_by_def: FxHashMap, } -impl CrateImplBlocks { +impl ModuleImplBlocks { fn new() -> Self { - CrateImplBlocks { + ModuleImplBlocks { impls: Arena::default(), impls_by_def: FxHashMap::default(), } @@ -159,24 +164,17 @@ impl CrateImplBlocks { } } - for (_, child) in module.children() { - self.collect(db, child)?; - } - Ok(()) } } -pub(crate) fn impls_in_crate( +pub(crate) fn impls_in_module( db: &impl HirDatabase, - krate: Crate, -) -> Cancelable> { - let mut result = CrateImplBlocks::new(); - let root_module = if let Some(root) = krate.root_module(db)? { - root - } else { - return Ok(Arc::new(result)); - }; - result.collect(db, root_module)?; + source_root_id: SourceRootId, + module_id: ModuleId, +) -> Cancelable> { + let mut result = ModuleImplBlocks::new(); + let module = Module::new(db, source_root_id, module_id)?; + result.collect(db, module)?; Ok(Arc::new(result)) } -- cgit v1.2.3 From e6aeabf96f9cf339c81f3e79502d477269d141ed Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 4 Jan 2019 19:52:07 +0100 Subject: Rename ImplsInCrateQuery as well --- crates/ra_analysis/src/db.rs | 2 +- crates/ra_hir/src/db.rs | 2 +- crates/ra_hir/src/mock.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index d7e51a597..5422a400b 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs @@ -105,7 +105,7 @@ salsa::database_storage! { fn type_for_field() for hir::db::TypeForFieldQuery; fn struct_data() for hir::db::StructDataQuery; fn enum_data() for hir::db::EnumDataQuery; - fn impls_in_crate() for hir::db::ImplsInCrateQuery; + fn impls_in_module() for hir::db::ImplsInModuleQuery; } } } diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index a045bbb12..58296fc6f 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -90,7 +90,7 @@ pub trait HirDatabase: SyntaxDatabase } fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable> { - type ImplsInCrateQuery; + type ImplsInModuleQuery; use fn crate::impl_block::impls_in_module; } } diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 2419d256a..a9db932ff 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -207,7 +207,7 @@ salsa::database_storage! { fn type_for_field() for db::TypeForFieldQuery; fn struct_data() for db::StructDataQuery; fn enum_data() for db::EnumDataQuery; - fn impls_in_crate() for db::ImplsInCrateQuery; + fn impls_in_module() for db::ImplsInModuleQuery; } } } -- cgit v1.2.3