]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/child_by_source.rs
feat: allow attributes on all expressions
[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 itertools::Itertools;
10 use syntax::ast::AttrsOwner;
11
12 use crate::{
13     db::DefDatabase,
14     dyn_map::DynMap,
15     item_scope::ItemScope,
16     keys,
17     src::{HasChildSource, HasSource},
18     AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, ModuleDefId,
19     ModuleId, TraitId, VariantId,
20 };
21
22 pub trait ChildBySource {
23     fn child_by_source(&self, db: &dyn DefDatabase, file_id: HirFileId) -> DynMap {
24         let mut res = DynMap::default();
25         self.child_by_source_to(db, &mut res, file_id);
26         res
27     }
28     fn child_by_source_to(&self, db: &dyn DefDatabase, map: &mut DynMap, file_id: HirFileId);
29 }
30
31 impl ChildBySource for TraitId {
32     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
33         let data = db.trait_data(*self);
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         for &item in data.items.iter() {
66             match item {
67                 AssocItemId::FunctionId(func) => {
68                     let loc = func.lookup(db);
69                     if loc.id.file_id() == file_id {
70                         let src = loc.source(db);
71                         res[keys::FUNCTION].insert(src, func)
72                     }
73                 }
74                 AssocItemId::ConstId(konst) => {
75                     let loc = konst.lookup(db);
76                     if loc.id.file_id() == file_id {
77                         let src = loc.source(db);
78                         res[keys::CONST].insert(src, konst)
79                     }
80                 }
81                 AssocItemId::TypeAliasId(ty) => {
82                     let loc = ty.lookup(db);
83                     if loc.id.file_id() == file_id {
84                         let src = loc.source(db);
85                         res[keys::TYPE_ALIAS].insert(src, ty)
86                     }
87                 }
88             }
89         }
90     }
91 }
92
93 impl ChildBySource for ModuleId {
94     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
95         let def_map = self.def_map(db);
96         let module_data = &def_map[self.local_id];
97         module_data.scope.child_by_source_to(db, res, file_id);
98     }
99 }
100
101 impl ChildBySource for ItemScope {
102     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
103         self.declarations().for_each(|item| add_module_def(db, file_id, res, item));
104         self.unnamed_consts().for_each(|konst| {
105             let src = konst.lookup(db).source(db);
106             res[keys::CONST].insert(src, konst);
107         });
108         self.impls().for_each(|imp| add_impl(db, file_id, res, imp));
109         self.attr_macro_invocs().for_each(|(ast_id, call_id)| {
110             let item = ast_id.with_value(ast_id.to_node(db.upcast()));
111             res[keys::ATTR_MACRO].insert(item, call_id);
112         });
113         self.derive_macro_invocs().for_each(|(ast_id, calls)| {
114             let item = ast_id.to_node(db.upcast());
115             let grouped = calls.iter().copied().into_group_map();
116             for (attr_id, calls) in grouped {
117                 if let Some(attr) = item.attrs().nth(attr_id.ast_index as usize) {
118                     res[keys::DERIVE_MACRO].insert(ast_id.with_value(attr), calls.into());
119                 }
120             }
121         });
122
123         fn add_module_def(
124             db: &dyn DefDatabase,
125             file_id: HirFileId,
126             map: &mut DynMap,
127             item: ModuleDefId,
128         ) {
129             match item {
130                 ModuleDefId::FunctionId(func) => {
131                     let loc = func.lookup(db);
132                     if loc.id.file_id() == file_id {
133                         let src = loc.source(db);
134                         map[keys::FUNCTION].insert(src, func)
135                     }
136                 }
137                 ModuleDefId::ConstId(konst) => {
138                     let loc = konst.lookup(db);
139                     if loc.id.file_id() == file_id {
140                         let src = loc.source(db);
141                         map[keys::CONST].insert(src, konst)
142                     }
143                 }
144                 ModuleDefId::StaticId(statik) => {
145                     let loc = statik.lookup(db);
146                     if loc.id.file_id() == file_id {
147                         let src = loc.source(db);
148                         map[keys::STATIC].insert(src, statik)
149                     }
150                 }
151                 ModuleDefId::TypeAliasId(ty) => {
152                     let loc = ty.lookup(db);
153                     if loc.id.file_id() == file_id {
154                         let src = loc.source(db);
155                         map[keys::TYPE_ALIAS].insert(src, ty)
156                     }
157                 }
158                 ModuleDefId::TraitId(trait_) => {
159                     let loc = trait_.lookup(db);
160                     if loc.id.file_id() == file_id {
161                         let src = loc.source(db);
162                         map[keys::TRAIT].insert(src, trait_)
163                     }
164                 }
165                 ModuleDefId::AdtId(adt) => match adt {
166                     AdtId::StructId(strukt) => {
167                         let loc = strukt.lookup(db);
168                         if loc.id.file_id() == file_id {
169                             let src = loc.source(db);
170                             map[keys::STRUCT].insert(src, strukt)
171                         }
172                     }
173                     AdtId::UnionId(union_) => {
174                         let loc = union_.lookup(db);
175                         if loc.id.file_id() == file_id {
176                             let src = loc.source(db);
177                             map[keys::UNION].insert(src, union_)
178                         }
179                     }
180                     AdtId::EnumId(enum_) => {
181                         let loc = enum_.lookup(db);
182                         if loc.id.file_id() == file_id {
183                             let src = loc.source(db);
184                             map[keys::ENUM].insert(src, enum_)
185                         }
186                     }
187                 },
188                 _ => (),
189             }
190         }
191         fn add_impl(db: &dyn DefDatabase, file_id: HirFileId, map: &mut DynMap, imp: ImplId) {
192             let loc = imp.lookup(db);
193             if loc.id.file_id() == file_id {
194                 let src = loc.source(db);
195                 map[keys::IMPL].insert(src, imp)
196             }
197         }
198     }
199 }
200
201 impl ChildBySource for VariantId {
202     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
203         let arena_map = self.child_source(db);
204         let arena_map = arena_map.as_ref();
205         let parent = *self;
206         for (local_id, source) in arena_map.value.iter() {
207             let id = FieldId { parent, local_id };
208             match source.clone() {
209                 Either::Left(source) => {
210                     res[keys::TUPLE_FIELD].insert(arena_map.with_value(source), id)
211                 }
212                 Either::Right(source) => {
213                     res[keys::RECORD_FIELD].insert(arena_map.with_value(source), id)
214                 }
215             }
216         }
217     }
218 }
219
220 impl ChildBySource for EnumId {
221     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
222         let arena_map = self.child_source(db);
223         let arena_map = arena_map.as_ref();
224         for (local_id, source) in arena_map.value.iter() {
225             let id = EnumVariantId { parent: *self, local_id };
226             res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
227         }
228     }
229 }
230
231 impl ChildBySource for DefWithBodyId {
232     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
233         let body = db.body(*self);
234         for (_, def_map) in body.blocks(db) {
235             // All block expressions are merged into the same map, because they logically all add
236             // inner items to the containing `DefWithBodyId`.
237             def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);
238         }
239     }
240 }