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