diff options
Diffstat (limited to 'crates/ra_hir/src/impl_block.rs')
-rw-r--r-- | crates/ra_hir/src/impl_block.rs | 260 |
1 files changed, 23 insertions, 237 deletions
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index b1a014074..0c2bb8fee 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs | |||
@@ -1,88 +1,38 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use rustc_hash::FxHashMap; | 3 | use hir_def::{type_ref::TypeRef, AstItemDef}; |
4 | use std::sync::Arc; | 4 | use ra_syntax::ast::{self}; |
5 | |||
6 | use hir_def::{attr::Attr, type_ref::TypeRef}; | ||
7 | use hir_expand::hygiene::Hygiene; | ||
8 | use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; | ||
9 | use ra_cfg::CfgOptions; | ||
10 | use ra_syntax::{ | ||
11 | ast::{self, AstNode}, | ||
12 | AstPtr, | ||
13 | }; | ||
14 | 5 | ||
15 | use crate::{ | 6 | use crate::{ |
16 | code_model::{Module, ModuleSource}, | ||
17 | db::{AstDatabase, DefDatabase, HirDatabase}, | 7 | db::{AstDatabase, DefDatabase, HirDatabase}, |
18 | generics::HasGenericParams, | 8 | generics::HasGenericParams, |
19 | ids::LocationCtx, | ||
20 | ids::MacroCallLoc, | ||
21 | resolve::Resolver, | 9 | resolve::Resolver, |
22 | ty::Ty, | 10 | ty::Ty, |
23 | AssocItem, AstId, Const, Function, HasSource, HirFileId, MacroFileKind, Path, Source, TraitRef, | 11 | AssocItem, Crate, HasSource, ImplBlock, Module, Source, TraitRef, |
24 | TypeAlias, | ||
25 | }; | 12 | }; |
26 | 13 | ||
27 | #[derive(Debug, Default, PartialEq, Eq)] | ||
28 | pub struct ImplSourceMap { | ||
29 | map: ArenaMap<ImplId, Source<AstPtr<ast::ImplBlock>>>, | ||
30 | } | ||
31 | |||
32 | impl ImplSourceMap { | ||
33 | fn insert(&mut self, impl_id: ImplId, file_id: HirFileId, impl_block: &ast::ImplBlock) { | ||
34 | let source = Source { file_id, ast: AstPtr::new(impl_block) }; | ||
35 | self.map.insert(impl_id, source) | ||
36 | } | ||
37 | |||
38 | pub fn get(&self, db: &impl AstDatabase, impl_id: ImplId) -> Source<ast::ImplBlock> { | ||
39 | let src = self.map[impl_id]; | ||
40 | let root = src.file_syntax(db); | ||
41 | src.map(|ptr| ptr.to_node(&root)) | ||
42 | } | ||
43 | } | ||
44 | |||
45 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
46 | pub struct ImplBlock { | ||
47 | module: Module, | ||
48 | impl_id: ImplId, | ||
49 | } | ||
50 | |||
51 | impl HasSource for ImplBlock { | 14 | impl HasSource for ImplBlock { |
52 | type Ast = ast::ImplBlock; | 15 | type Ast = ast::ImplBlock; |
53 | fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::ImplBlock> { | 16 | fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::ImplBlock> { |
54 | let source_map = db.impls_in_module_with_source_map(self.module).1; | 17 | self.id.source(db) |
55 | source_map.get(db, self.impl_id) | ||
56 | } | 18 | } |
57 | } | 19 | } |
58 | 20 | ||
59 | impl ImplBlock { | 21 | impl ImplBlock { |
60 | pub(crate) fn containing( | 22 | pub(crate) fn containing(db: &impl DefDatabase, item: AssocItem) -> Option<ImplBlock> { |
61 | module_impl_blocks: Arc<ModuleImplBlocks>, | 23 | let module = item.module(db); |
62 | item: AssocItem, | 24 | let crate_def_map = db.crate_def_map(module.id.krate); |
63 | ) -> Option<ImplBlock> { | 25 | crate_def_map[module.id.module_id].impls.iter().copied().map(ImplBlock::from).find(|it| { |
64 | let impl_id = *module_impl_blocks.impls_by_def.get(&item)?; | 26 | db.impl_data(it.id).items().iter().copied().map(AssocItem::from).any(|it| it == item) |
65 | Some(ImplBlock { module: module_impl_blocks.module, impl_id }) | 27 | }) |
66 | } | ||
67 | |||
68 | pub(crate) fn from_id(module: Module, impl_id: ImplId) -> ImplBlock { | ||
69 | ImplBlock { module, impl_id } | ||
70 | } | ||
71 | |||
72 | pub fn id(&self) -> ImplId { | ||
73 | self.impl_id | ||
74 | } | ||
75 | |||
76 | pub fn module(&self) -> Module { | ||
77 | self.module | ||
78 | } | 28 | } |
79 | 29 | ||
80 | pub fn target_trait(&self, db: &impl DefDatabase) -> Option<TypeRef> { | 30 | pub fn target_trait(&self, db: &impl DefDatabase) -> Option<TypeRef> { |
81 | db.impls_in_module(self.module).impls[self.impl_id].target_trait().cloned() | 31 | db.impl_data(self.id).target_trait().cloned() |
82 | } | 32 | } |
83 | 33 | ||
84 | pub fn target_type(&self, db: &impl DefDatabase) -> TypeRef { | 34 | pub fn target_type(&self, db: &impl DefDatabase) -> TypeRef { |
85 | db.impls_in_module(self.module).impls[self.impl_id].target_type().clone() | 35 | db.impl_data(self.id).target_type().clone() |
86 | } | 36 | } |
87 | 37 | ||
88 | pub fn target_ty(&self, db: &impl HirDatabase) -> Ty { | 38 | pub fn target_ty(&self, db: &impl HirDatabase) -> Ty { |
@@ -95,15 +45,23 @@ impl ImplBlock { | |||
95 | } | 45 | } |
96 | 46 | ||
97 | pub fn items(&self, db: &impl DefDatabase) -> Vec<AssocItem> { | 47 | pub fn items(&self, db: &impl DefDatabase) -> Vec<AssocItem> { |
98 | db.impls_in_module(self.module).impls[self.impl_id].items().to_vec() | 48 | db.impl_data(self.id).items().iter().map(|it| (*it).into()).collect() |
99 | } | 49 | } |
100 | 50 | ||
101 | pub fn is_negative(&self, db: &impl DefDatabase) -> bool { | 51 | pub fn is_negative(&self, db: &impl DefDatabase) -> bool { |
102 | db.impls_in_module(self.module).impls[self.impl_id].negative | 52 | db.impl_data(self.id).is_negative() |
53 | } | ||
54 | |||
55 | pub fn module(&self, db: &impl DefDatabase) -> Module { | ||
56 | self.id.module(db).into() | ||
57 | } | ||
58 | |||
59 | pub fn krate(&self, db: &impl DefDatabase) -> Crate { | ||
60 | Crate { crate_id: self.module(db).id.krate } | ||
103 | } | 61 | } |
104 | 62 | ||
105 | pub(crate) fn resolver(&self, db: &impl DefDatabase) -> Resolver { | 63 | pub(crate) fn resolver(&self, db: &impl DefDatabase) -> Resolver { |
106 | let r = self.module().resolver(db); | 64 | let r = self.module(db).resolver(db); |
107 | // add generic params, if present | 65 | // add generic params, if present |
108 | let p = self.generic_params(db); | 66 | let p = self.generic_params(db); |
109 | let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r }; | 67 | let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r }; |
@@ -111,175 +69,3 @@ impl ImplBlock { | |||
111 | r | 69 | r |
112 | } | 70 | } |
113 | } | 71 | } |
114 | |||
115 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
116 | pub struct ImplData { | ||
117 | target_trait: Option<TypeRef>, | ||
118 | target_type: TypeRef, | ||
119 | items: Vec<AssocItem>, | ||
120 | negative: bool, | ||
121 | } | ||
122 | |||
123 | impl ImplData { | ||
124 | pub(crate) fn from_ast( | ||
125 | db: &(impl DefDatabase + AstDatabase), | ||
126 | file_id: HirFileId, | ||
127 | module: Module, | ||
128 | node: &ast::ImplBlock, | ||
129 | ) -> Self { | ||
130 | let target_trait = node.target_trait().map(TypeRef::from_ast); | ||
131 | let target_type = TypeRef::from_ast_opt(node.target_type()); | ||
132 | let ctx = LocationCtx::new(db, module.id, file_id); | ||
133 | let negative = node.is_negative(); | ||
134 | let items = if let Some(item_list) = node.item_list() { | ||
135 | item_list | ||
136 | .impl_items() | ||
137 | .map(|item_node| match item_node { | ||
138 | ast::ImplItem::FnDef(it) => Function { id: ctx.to_def(&it) }.into(), | ||
139 | ast::ImplItem::ConstDef(it) => Const { id: ctx.to_def(&it) }.into(), | ||
140 | ast::ImplItem::TypeAliasDef(it) => TypeAlias { id: ctx.to_def(&it) }.into(), | ||
141 | }) | ||
142 | .collect() | ||
143 | } else { | ||
144 | Vec::new() | ||
145 | }; | ||
146 | ImplData { target_trait, target_type, items, negative } | ||
147 | } | ||
148 | |||
149 | pub fn target_trait(&self) -> Option<&TypeRef> { | ||
150 | self.target_trait.as_ref() | ||
151 | } | ||
152 | |||
153 | pub fn target_type(&self) -> &TypeRef { | ||
154 | &self.target_type | ||
155 | } | ||
156 | |||
157 | pub fn items(&self) -> &[AssocItem] { | ||
158 | &self.items | ||
159 | } | ||
160 | } | ||
161 | |||
162 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
163 | pub struct ImplId(pub RawId); | ||
164 | impl_arena_id!(ImplId); | ||
165 | |||
166 | /// The collection of impl blocks is a two-step process: first we collect the | ||
167 | /// blocks per-module; then we build an index of all impl blocks in the crate. | ||
168 | /// This way, we avoid having to do this process for the whole crate whenever | ||
169 | /// a file is changed; as long as the impl blocks in the file don't change, | ||
170 | /// we don't need to do the second step again. | ||
171 | #[derive(Debug, PartialEq, Eq)] | ||
172 | pub struct ModuleImplBlocks { | ||
173 | pub(crate) module: Module, | ||
174 | pub(crate) impls: Arena<ImplId, ImplData>, | ||
175 | impls_by_def: FxHashMap<AssocItem, ImplId>, | ||
176 | } | ||
177 | |||
178 | impl ModuleImplBlocks { | ||
179 | pub(crate) fn impls_in_module_with_source_map_query( | ||
180 | db: &(impl DefDatabase + AstDatabase), | ||
181 | module: Module, | ||
182 | ) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) { | ||
183 | let mut source_map = ImplSourceMap::default(); | ||
184 | let crate_graph = db.crate_graph(); | ||
185 | let cfg_options = crate_graph.cfg_options(module.id.krate); | ||
186 | |||
187 | let result = ModuleImplBlocks::collect(db, cfg_options, module, &mut source_map); | ||
188 | (Arc::new(result), Arc::new(source_map)) | ||
189 | } | ||
190 | |||
191 | pub(crate) fn impls_in_module_query( | ||
192 | db: &impl DefDatabase, | ||
193 | module: Module, | ||
194 | ) -> Arc<ModuleImplBlocks> { | ||
195 | db.impls_in_module_with_source_map(module).0 | ||
196 | } | ||
197 | |||
198 | fn collect( | ||
199 | db: &(impl DefDatabase + AstDatabase), | ||
200 | cfg_options: &CfgOptions, | ||
201 | module: Module, | ||
202 | source_map: &mut ImplSourceMap, | ||
203 | ) -> Self { | ||
204 | let mut m = ModuleImplBlocks { | ||
205 | module, | ||
206 | impls: Arena::default(), | ||
207 | impls_by_def: FxHashMap::default(), | ||
208 | }; | ||
209 | |||
210 | let src = m.module.definition_source(db); | ||
211 | match &src.ast { | ||
212 | ModuleSource::SourceFile(node) => { | ||
213 | m.collect_from_item_owner(db, cfg_options, source_map, node, src.file_id) | ||
214 | } | ||
215 | ModuleSource::Module(node) => { | ||
216 | let item_list = node.item_list().expect("inline module should have item list"); | ||
217 | m.collect_from_item_owner(db, cfg_options, source_map, &item_list, src.file_id) | ||
218 | } | ||
219 | }; | ||
220 | m | ||
221 | } | ||
222 | |||
223 | fn collect_from_item_owner( | ||
224 | &mut self, | ||
225 | db: &(impl DefDatabase + AstDatabase), | ||
226 | cfg_options: &CfgOptions, | ||
227 | source_map: &mut ImplSourceMap, | ||
228 | owner: &dyn ast::ModuleItemOwner, | ||
229 | file_id: HirFileId, | ||
230 | ) { | ||
231 | let hygiene = Hygiene::new(db, file_id); | ||
232 | for item in owner.items_with_macros() { | ||
233 | match item { | ||
234 | ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => { | ||
235 | let attrs = Attr::from_attrs_owner(&impl_block_ast, &hygiene); | ||
236 | if attrs.map_or(false, |attrs| { | ||
237 | attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) | ||
238 | }) { | ||
239 | continue; | ||
240 | } | ||
241 | |||
242 | let impl_block = ImplData::from_ast(db, file_id, self.module, &impl_block_ast); | ||
243 | let id = self.impls.alloc(impl_block); | ||
244 | for &impl_item in &self.impls[id].items { | ||
245 | self.impls_by_def.insert(impl_item, id); | ||
246 | } | ||
247 | |||
248 | source_map.insert(id, file_id, &impl_block_ast); | ||
249 | } | ||
250 | ast::ItemOrMacro::Item(_) => (), | ||
251 | ast::ItemOrMacro::Macro(macro_call) => { | ||
252 | let attrs = Attr::from_attrs_owner(¯o_call, &hygiene); | ||
253 | if attrs.map_or(false, |attrs| { | ||
254 | attrs.iter().any(|attr| attr.is_cfg_enabled(cfg_options) == Some(false)) | ||
255 | }) { | ||
256 | continue; | ||
257 | } | ||
258 | |||
259 | //FIXME: we should really cut down on the boilerplate required to process a macro | ||
260 | let ast_id = AstId::new(file_id, db.ast_id_map(file_id).ast_id(¯o_call)); | ||
261 | if let Some(path) = | ||
262 | macro_call.path().and_then(|path| Path::from_src(path, &hygiene)) | ||
263 | { | ||
264 | if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path) | ||
265 | { | ||
266 | let call_id = db.intern_macro(MacroCallLoc { def: def.id, ast_id }); | ||
267 | let file_id = call_id.as_file(MacroFileKind::Items); | ||
268 | if let Some(item_list) = | ||
269 | db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) | ||
270 | { | ||
271 | self.collect_from_item_owner( | ||
272 | db, | ||
273 | cfg_options, | ||
274 | source_map, | ||
275 | &item_list, | ||
276 | file_id, | ||
277 | ) | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | } | ||