]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/child_by_source.rs
Expand attribute macros on impl and trait items
[rust.git] / crates / hir_def / src / child_by_source.rs
1 //! When *constructing* `hir`, we start at some parent syntax node and recursively
2 //! lower the children.
3 //!
4 //! This modules allows one to go in the opposite direction: start with a syntax
5 //! node for a *child*, and get its hir.
6
7 use either::Either;
8 use hir_expand::HirFileId;
9 use syntax::ast::HasAttrs;
10
11 use crate::{
12     db::DefDatabase,
13     dyn_map::DynMap,
14     item_scope::ItemScope,
15     keys,
16     src::{HasChildSource, HasSource},
17     AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, ModuleDefId,
18     ModuleId, TraitId, VariantId,
19 };
20
21 pub trait ChildBySource {
22     fn child_by_source(&self, db: &dyn DefDatabase, file_id: HirFileId) -> DynMap {
23         let mut res = DynMap::default();
24         self.child_by_source_to(db, &mut res, file_id);
25         res
26     }
27     fn child_by_source_to(&self, db: &dyn DefDatabase, map: &mut DynMap, file_id: HirFileId);
28 }
29
30 impl ChildBySource for TraitId {
31     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
32         let data = db.trait_data(*self);
33         // FIXME attribute macros
34         for (_name, item) in data.items.iter() {
35             match *item {
36                 AssocItemId::FunctionId(func) => {
37                     let loc = func.lookup(db);
38                     if loc.id.file_id() == file_id {
39                         let src = loc.source(db);
40                         res[keys::FUNCTION].insert(src, func)
41                     }
42                 }
43                 AssocItemId::ConstId(konst) => {
44                     let loc = konst.lookup(db);
45                     if loc.id.file_id() == file_id {
46                         let src = loc.source(db);
47                         res[keys::CONST].insert(src, konst)
48                     }
49                 }
50                 AssocItemId::TypeAliasId(ty) => {
51                     let loc = ty.lookup(db);
52                     if loc.id.file_id() == file_id {
53                         let src = loc.source(db);
54                         res[keys::TYPE_ALIAS].insert(src, ty)
55                     }
56                 }
57             }
58         }
59     }
60 }
61
62 impl ChildBySource for ImplId {
63     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
64         let data = db.impl_data(*self);
65         // FIXME attribute macros
66         for &item in data.items.iter() {
67             match item {
68                 AssocItemId::FunctionId(func) => {
69                     let loc = func.lookup(db);
70                     if loc.id.file_id() == file_id {
71                         let src = loc.source(db);
72                         res[keys::FUNCTION].insert(src, func)
73                     }
74                 }
75                 AssocItemId::ConstId(konst) => {
76                     let loc = konst.lookup(db);
77                     if loc.id.file_id() == file_id {
78                         let src = loc.source(db);
79                         res[keys::CONST].insert(src, konst)
80                     }
81                 }
82                 AssocItemId::TypeAliasId(ty) => {
83                     let loc = ty.lookup(db);
84                     if loc.id.file_id() == file_id {
85                         let src = loc.source(db);
86                         res[keys::TYPE_ALIAS].insert(src, ty)
87                     }
88                 }
89             }
90         }
91     }
92 }
93
94 impl ChildBySource for ModuleId {
95     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
96         let def_map = self.def_map(db);
97         let module_data = &def_map[self.local_id];
98         module_data.scope.child_by_source_to(db, res, file_id);
99     }
100 }
101
102 impl ChildBySource for ItemScope {
103     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
104         self.declarations().for_each(|item| add_module_def(db, file_id, res, item));
105         self.macros().for_each(|(_, makro)| {
106             let ast_id = makro.ast_id();
107             if ast_id.either(|it| it.file_id, |it| it.file_id) == file_id {
108                 let src = match ast_id {
109                     Either::Left(ast_id) => ast_id.with_value(ast_id.to_node(db.upcast())),
110                     // FIXME: Do we need to add proc-macros into a PROCMACRO dynmap here?
111                     Either::Right(_fn) => return,
112                 };
113                 res[keys::MACRO].insert(src, makro);
114             }
115         });
116         self.unnamed_consts().for_each(|konst| {
117             let src = konst.lookup(db).source(db);
118             res[keys::CONST].insert(src, konst);
119         });
120         self.impls().for_each(|imp| add_impl(db, file_id, res, imp));
121         self.attr_macro_invocs().for_each(|(ast_id, call_id)| {
122             let item = ast_id.with_value(ast_id.to_node(db.upcast()));
123             res[keys::ATTR_MACRO].insert(item, call_id);
124         });
125         self.derive_macro_invocs().for_each(|(ast_id, calls)| {
126             let item = ast_id.to_node(db.upcast());
127             for (attr_id, calls) in calls {
128                 if let Some(attr) = item.attrs().nth(attr_id.ast_index as usize) {
129                     res[keys::DERIVE_MACRO].insert(ast_id.with_value(attr), calls.into());
130                 }
131             }
132         });
133
134         fn add_module_def(
135             db: &dyn DefDatabase,
136             file_id: HirFileId,
137             map: &mut DynMap,
138             item: ModuleDefId,
139         ) {
140             match item {
141                 ModuleDefId::FunctionId(func) => {
142                     let loc = func.lookup(db);
143                     if loc.id.file_id() == file_id {
144                         let src = loc.source(db);
145                         map[keys::FUNCTION].insert(src, func)
146                     }
147                 }
148                 ModuleDefId::ConstId(konst) => {
149                     let loc = konst.lookup(db);
150                     if loc.id.file_id() == file_id {
151                         let src = loc.source(db);
152                         map[keys::CONST].insert(src, konst)
153                     }
154                 }
155                 ModuleDefId::StaticId(statik) => {
156                     let loc = statik.lookup(db);
157                     if loc.id.file_id() == file_id {
158                         let src = loc.source(db);
159                         map[keys::STATIC].insert(src, statik)
160                     }
161                 }
162                 ModuleDefId::TypeAliasId(ty) => {
163                     let loc = ty.lookup(db);
164                     if loc.id.file_id() == file_id {
165                         let src = loc.source(db);
166                         map[keys::TYPE_ALIAS].insert(src, ty)
167                     }
168                 }
169                 ModuleDefId::TraitId(trait_) => {
170                     let loc = trait_.lookup(db);
171                     if loc.id.file_id() == file_id {
172                         let src = loc.source(db);
173                         map[keys::TRAIT].insert(src, trait_)
174                     }
175                 }
176                 ModuleDefId::AdtId(adt) => match adt {
177                     AdtId::StructId(strukt) => {
178                         let loc = strukt.lookup(db);
179                         if loc.id.file_id() == file_id {
180                             let src = loc.source(db);
181                             map[keys::STRUCT].insert(src, strukt)
182                         }
183                     }
184                     AdtId::UnionId(union_) => {
185                         let loc = union_.lookup(db);
186                         if loc.id.file_id() == file_id {
187                             let src = loc.source(db);
188                             map[keys::UNION].insert(src, union_)
189                         }
190                     }
191                     AdtId::EnumId(enum_) => {
192                         let loc = enum_.lookup(db);
193                         if loc.id.file_id() == file_id {
194                             let src = loc.source(db);
195                             map[keys::ENUM].insert(src, enum_)
196                         }
197                     }
198                 },
199                 _ => (),
200             }
201         }
202         fn add_impl(db: &dyn DefDatabase, file_id: HirFileId, map: &mut DynMap, imp: ImplId) {
203             let loc = imp.lookup(db);
204             if loc.id.file_id() == file_id {
205                 let src = loc.source(db);
206                 map[keys::IMPL].insert(src, imp)
207             }
208         }
209     }
210 }
211
212 impl ChildBySource for VariantId {
213     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
214         let arena_map = self.child_source(db);
215         let arena_map = arena_map.as_ref();
216         let parent = *self;
217         for (local_id, source) in arena_map.value.iter() {
218             let id = FieldId { parent, local_id };
219             match source.clone() {
220                 Either::Left(source) => {
221                     res[keys::TUPLE_FIELD].insert(arena_map.with_value(source), id)
222                 }
223                 Either::Right(source) => {
224                     res[keys::RECORD_FIELD].insert(arena_map.with_value(source), id)
225                 }
226             }
227         }
228     }
229 }
230
231 impl ChildBySource for EnumId {
232     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
233         let arena_map = self.child_source(db);
234         let arena_map = arena_map.as_ref();
235         for (local_id, source) in arena_map.value.iter() {
236             let id = EnumVariantId { parent: *self, local_id };
237             res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
238         }
239     }
240 }
241
242 impl ChildBySource for DefWithBodyId {
243     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
244         let body = db.body(*self);
245         for (_, def_map) in body.blocks(db) {
246             // All block expressions are merged into the same map, because they logically all add
247             // inner items to the containing `DefWithBodyId`.
248             def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);
249         }
250     }
251 }