diff options
Diffstat (limited to 'crates/ra_hir/src/impl_block.rs')
-rw-r--r-- | crates/ra_hir/src/impl_block.rs | 172 |
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 @@ | |||
1 | use std::sync::Arc; | ||
2 | use rustc_hash::FxHashMap; | ||
3 | |||
4 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
5 | use ra_syntax::ast::{self, AstNode}; | ||
6 | use ra_db::{LocationIntener, Cancelable}; | ||
7 | |||
8 | use 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)] | ||
17 | pub struct ImplBlock { | ||
18 | crate_impl_blocks: Arc<CrateImplBlocks>, | ||
19 | impl_id: ImplId, | ||
20 | } | ||
21 | |||
22 | impl 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)] | ||
48 | pub struct ImplData { | ||
49 | impl_for: TypeRef, | ||
50 | items: Vec<ImplItem>, | ||
51 | } | ||
52 | |||
53 | impl 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)] | ||
97 | pub enum ImplItem { | ||
98 | Method(Function), | ||
99 | // these don't have their own types yet | ||
100 | Const(DefId), | ||
101 | Type(DefId), | ||
102 | // Existential | ||
103 | } | ||
104 | |||
105 | impl 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)] | ||
116 | pub struct ImplId(pub RawId); | ||
117 | impl_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)] | ||
122 | pub struct CrateImplBlocks { | ||
123 | impls: Arena<ImplId, ImplData>, | ||
124 | impls_by_def: FxHashMap<DefId, ImplId>, | ||
125 | } | ||
126 | |||
127 | impl 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 | |||
160 | pub(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 | } | ||