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.rs172
1 files changed, 172 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..22f0a4461
--- /dev/null
+++ b/crates/ra_hir/src/impl_block.rs
@@ -0,0 +1,172 @@
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};
7
8use crate::{
9 Crate, DefId, DefLoc, DefKind, SourceItemId, SourceFileItems,
10 Module, Function,
11 db::HirDatabase,
12 type_ref::TypeRef,
13 module::{ModuleSourceNode},
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ImplBlock {
18 crate_impl_blocks: Arc<CrateImplBlocks>,
19 impl_id: ImplId,
20}
21
22impl ImplBlock {
23 pub(crate) fn containing(
24 crate_impl_blocks: Arc<CrateImplBlocks>,
25 def_id: DefId,
26 ) -> Option<ImplBlock> {
27 let impl_id = *crate_impl_blocks.impls_by_def.get(&def_id)?;
28 Some(ImplBlock {
29 crate_impl_blocks,
30 impl_id,
31 })
32 }
33
34 fn impl_data(&self) -> &ImplData {
35 &self.crate_impl_blocks.impls[self.impl_id]
36 }
37
38 pub fn target(&self) -> &TypeRef {
39 &self.impl_data().impl_for
40 }
41
42 pub fn items(&self) -> &[ImplItem] {
43 &self.impl_data().items
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct ImplData {
49 impl_for: TypeRef,
50 items: Vec<ImplItem>,
51}
52
53impl ImplData {
54 pub(crate) fn from_ast(
55 db: &impl AsRef<LocationIntener<DefLoc, DefId>>,
56 file_items: &SourceFileItems,
57 module: &Module,
58 node: ast::ImplBlock,
59 ) -> Self {
60 let impl_for = TypeRef::from_ast_opt(node.target_type());
61 let file_id = module.source().file_id();
62 let items = if let Some(item_list) = node.item_list() {
63 item_list
64 .impl_items()
65 .map(|item_node| {
66 let kind = match item_node {
67 ast::ImplItem::FnDef(..) => DefKind::Function,
68 ast::ImplItem::ConstDef(..) => DefKind::Item,
69 ast::ImplItem::TypeDef(..) => DefKind::Item,
70 };
71 let item_id = file_items.id_of_unchecked(item_node.syntax());
72 let def_loc = DefLoc {
73 kind,
74 source_root_id: module.source_root_id,
75 module_id: module.module_id,
76 source_item_id: SourceItemId {
77 file_id,
78 item_id: Some(item_id),
79 },
80 };
81 let def_id = def_loc.id(db);
82 match item_node {
83 ast::ImplItem::FnDef(..) => ImplItem::Method(Function::new(def_id)),
84 ast::ImplItem::ConstDef(..) => ImplItem::Const(def_id),
85 ast::ImplItem::TypeDef(..) => ImplItem::Type(def_id),
86 }
87 })
88 .collect()
89 } else {
90 Vec::new()
91 };
92 ImplData { impl_for, items }
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
97pub enum ImplItem {
98 Method(Function),
99 // these don't have their own types yet
100 Const(DefId),
101 Type(DefId),
102 // Existential
103}
104
105impl ImplItem {
106 pub fn def_id(&self) -> DefId {
107 match self {
108 ImplItem::Method(f) => f.def_id(),
109 ImplItem::Const(def_id) => *def_id,
110 ImplItem::Type(def_id) => *def_id,
111 }
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
116pub struct ImplId(pub RawId);
117impl_arena_id!(ImplId);
118
119/// We have to collect all impl blocks in a crate, to later be able to find
120/// impls for specific types.
121#[derive(Debug, PartialEq, Eq)]
122pub struct CrateImplBlocks {
123 impls: Arena<ImplId, ImplData>,
124 impls_by_def: FxHashMap<DefId, ImplId>,
125}
126
127impl CrateImplBlocks {
128 fn new() -> Self {
129 CrateImplBlocks {
130 impls: Arena::default(),
131 impls_by_def: FxHashMap::default(),
132 }
133 }
134
135 fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> {
136 let module_source_node = module.source().resolve(db);
137 let node = match &module_source_node {
138 ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(),
139 ModuleSourceNode::Module(node) => node.borrowed().syntax(),
140 };
141
142 let source_file_items = db.file_items(module.source().file_id());
143
144 for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
145 let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast);
146 let id = self.impls.alloc(impl_block);
147 for impl_item in &self.impls[id].items {
148 self.impls_by_def.insert(impl_item.def_id(), id);
149 }
150 }
151
152 for (_, child) in module.children() {
153 self.collect(db, child)?;
154 }
155
156 Ok(())
157 }
158}
159
160pub(crate) fn impls_in_crate(
161 db: &impl HirDatabase,
162 krate: Crate,
163) -> Cancelable<Arc<CrateImplBlocks>> {
164 let mut result = CrateImplBlocks::new();
165 let root_module = if let Some(root) = krate.root_module(db)? {
166 root
167 } else {
168 return Ok(Arc::new(result));
169 };
170 result.collect(db, root_module)?;
171 Ok(Arc::new(result))
172}