aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/impl_block.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/impl_block.rs')
-rw-r--r--crates/ra_hir/src/impl_block.rs180
1 files changed, 180 insertions, 0 deletions
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs
new file mode 100644
index 000000000..01afa84c4
--- /dev/null
+++ b/crates/ra_hir/src/impl_block.rs
@@ -0,0 +1,180 @@
1use std::sync::Arc;
2use rustc_hash::FxHashMap;
3
4use ra_arena::{Arena, RawId, impl_arena_id};
5use ra_syntax::ast::{self, AstNode};
6use ra_db::{LocationIntener, Cancelable, SourceRootId};
7
8use crate::{
9 DefId, DefLoc, DefKind, SourceItemId, SourceFileItems,
10 Module, Function,
11 db::HirDatabase,
12 type_ref::TypeRef,
13 module::{ModuleSourceNode, ModuleId},
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ImplBlock {
18 module_impl_blocks: Arc<ModuleImplBlocks>,
19 impl_id: ImplId,
20}
21
22impl ImplBlock {
23 pub(crate) fn containing(
24 module_impl_blocks: Arc<ModuleImplBlocks>,
25 def_id: DefId,
26 ) -> Option<ImplBlock> {
27 let impl_id = *module_impl_blocks.impls_by_def.get(&def_id)?;
28 Some(ImplBlock {
29 module_impl_blocks,
30 impl_id,
31 })
32 }
33
34 fn impl_data(&self) -> &ImplData {
35 &self.module_impl_blocks.impls[self.impl_id]
36 }
37
38 pub fn target_trait(&self) -> Option<&TypeRef> {
39 self.impl_data().target_trait.as_ref()
40 }
41
42 pub fn target_type(&self) -> &TypeRef {
43 &self.impl_data().target_type
44 }
45
46 pub fn items(&self) -> &[ImplItem] {
47 &self.impl_data().items
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct ImplData {
53 target_trait: Option<TypeRef>,
54 target_type: TypeRef,
55 items: Vec<ImplItem>,
56}
57
58impl ImplData {
59 pub(crate) fn from_ast(
60 db: &impl AsRef<LocationIntener<DefLoc, DefId>>,
61 file_items: &SourceFileItems,
62 module: &Module,
63 node: ast::ImplBlock,
64 ) -> Self {
65 let target_trait = node.target_type().map(TypeRef::from_ast);
66 let target_type = TypeRef::from_ast_opt(node.target_type());
67 let file_id = module.source().file_id();
68 let items = if let Some(item_list) = node.item_list() {
69 item_list
70 .impl_items()
71 .map(|item_node| {
72 let kind = match item_node {
73 ast::ImplItem::FnDef(..) => DefKind::Function,
74 ast::ImplItem::ConstDef(..) => DefKind::Item,
75 ast::ImplItem::TypeDef(..) => DefKind::Item,
76 };
77 let item_id = file_items.id_of_unchecked(item_node.syntax());
78 let def_loc = DefLoc {
79 kind,
80 source_root_id: module.source_root_id,
81 module_id: module.module_id,
82 source_item_id: SourceItemId {
83 file_id,
84 item_id: Some(item_id),
85 },
86 };
87 let def_id = def_loc.id(db);
88 match item_node {
89 ast::ImplItem::FnDef(..) => ImplItem::Method(Function::new(def_id)),
90 ast::ImplItem::ConstDef(..) => ImplItem::Const(def_id),
91 ast::ImplItem::TypeDef(..) => ImplItem::Type(def_id),
92 }
93 })
94 .collect()
95 } else {
96 Vec::new()
97 };
98 ImplData {
99 target_trait,
100 target_type,
101 items,
102 }
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub enum ImplItem {
108 Method(Function),
109 // these don't have their own types yet
110 Const(DefId),
111 Type(DefId),
112 // Existential
113}
114
115impl ImplItem {
116 pub fn def_id(&self) -> DefId {
117 match self {
118 ImplItem::Method(f) => f.def_id(),
119 ImplItem::Const(def_id) => *def_id,
120 ImplItem::Type(def_id) => *def_id,
121 }
122 }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
126pub struct ImplId(pub RawId);
127impl_arena_id!(ImplId);
128
129/// Collection of impl blocks is a two-step process: First we collect the blocks
130/// per-module; then we build an index of all impl blocks in the crate. This
131/// way, we avoid having to do this process for the whole crate whenever someone
132/// types in any file; as long as the impl blocks in the file don't change, we
133/// don't need to do the second step again.
134///
135/// (The second step does not yet exist currently.)
136#[derive(Debug, PartialEq, Eq)]
137pub struct ModuleImplBlocks {
138 impls: Arena<ImplId, ImplData>,
139 impls_by_def: FxHashMap<DefId, ImplId>,
140}
141
142impl ModuleImplBlocks {
143 fn new() -> Self {
144 ModuleImplBlocks {
145 impls: Arena::default(),
146 impls_by_def: FxHashMap::default(),
147 }
148 }
149
150 fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> {
151 let module_source_node = module.source().resolve(db);
152 let node = match &module_source_node {
153 ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(),
154 ModuleSourceNode::Module(node) => node.borrowed().syntax(),
155 };
156
157 let source_file_items = db.file_items(module.source().file_id());
158
159 for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
160 let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast);
161 let id = self.impls.alloc(impl_block);
162 for impl_item in &self.impls[id].items {
163 self.impls_by_def.insert(impl_item.def_id(), id);
164 }
165 }
166
167 Ok(())
168 }
169}
170
171pub(crate) fn impls_in_module(
172 db: &impl HirDatabase,
173 source_root_id: SourceRootId,
174 module_id: ModuleId,
175) -> Cancelable<Arc<ModuleImplBlocks>> {
176 let mut result = ModuleImplBlocks::new();
177 let module = Module::new(db, source_root_id, module_id)?;
178 result.collect(db, module)?;
179 Ok(Arc::new(result))
180}