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_hir/src/impl_block.rs | 172 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 crates/ra_hir/src/impl_block.rs (limited to 'crates/ra_hir/src/impl_block.rs') 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)) +} -- 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 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'crates/ra_hir/src/impl_block.rs') 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, + } } } -- 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/impl_block.rs | 52 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 27 deletions(-) (limited to 'crates/ra_hir/src/impl_block.rs') 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