]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/child_by_source.rs
Merge #11145
[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::HasDocComments;
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_CALL].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_CALL].insert(item, call_id);
124         });
125         self.derive_macro_invocs().for_each(|(ast_id, calls)| {
126             let adt = ast_id.to_node(db.upcast());
127             for (attr_id, calls) in calls {
128                 if let Some(Either::Right(attr)) =
129                     adt.doc_comments_and_attrs().nth(attr_id.ast_index as usize)
130                 {
131                     res[keys::DERIVE_MACRO_CALL]
132                         .insert(ast_id.with_value(attr), (attr_id, calls.into()));
133                 }
134             }
135         });
136
137         fn add_module_def(
138             db: &dyn DefDatabase,
139             file_id: HirFileId,
140             map: &mut DynMap,
141             item: ModuleDefId,
142         ) {
143             match item {
144                 ModuleDefId::FunctionId(func) => {
145                     let loc = func.lookup(db);
146                     if loc.id.file_id() == file_id {
147                         let src = loc.source(db);
148                         map[keys::FUNCTION].insert(src, func)
149                     }
150                 }
151                 ModuleDefId::ConstId(konst) => {
152                     let loc = konst.lookup(db);
153                     if loc.id.file_id() == file_id {
154                         let src = loc.source(db);
155                         map[keys::CONST].insert(src, konst)
156                     }
157                 }
158                 ModuleDefId::StaticId(statik) => {
159                     let loc = statik.lookup(db);
160                     if loc.id.file_id() == file_id {
161                         let src = loc.source(db);
162                         map[keys::STATIC].insert(src, statik)
163                     }
164                 }
165                 ModuleDefId::TypeAliasId(ty) => {
166                     let loc = ty.lookup(db);
167                     if loc.id.file_id() == file_id {
168                         let src = loc.source(db);
169                         map[keys::TYPE_ALIAS].insert(src, ty)
170                     }
171                 }
172                 ModuleDefId::TraitId(trait_) => {
173                     let loc = trait_.lookup(db);
174                     if loc.id.file_id() == file_id {
175                         let src = loc.source(db);
176                         map[keys::TRAIT].insert(src, trait_)
177                     }
178                 }
179                 ModuleDefId::AdtId(adt) => match adt {
180                     AdtId::StructId(strukt) => {
181                         let loc = strukt.lookup(db);
182                         if loc.id.file_id() == file_id {
183                             let src = loc.source(db);
184                             map[keys::STRUCT].insert(src, strukt)
185                         }
186                     }
187                     AdtId::UnionId(union_) => {
188                         let loc = union_.lookup(db);
189                         if loc.id.file_id() == file_id {
190                             let src = loc.source(db);
191                             map[keys::UNION].insert(src, union_)
192                         }
193                     }
194                     AdtId::EnumId(enum_) => {
195                         let loc = enum_.lookup(db);
196                         if loc.id.file_id() == file_id {
197                             let src = loc.source(db);
198                             map[keys::ENUM].insert(src, enum_)
199                         }
200                     }
201                 },
202                 _ => (),
203             }
204         }
205         fn add_impl(db: &dyn DefDatabase, file_id: HirFileId, map: &mut DynMap, imp: ImplId) {
206             let loc = imp.lookup(db);
207             if loc.id.file_id() == file_id {
208                 let src = loc.source(db);
209                 map[keys::IMPL].insert(src, imp)
210             }
211         }
212     }
213 }
214
215 impl ChildBySource for VariantId {
216     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
217         let arena_map = self.child_source(db);
218         let arena_map = arena_map.as_ref();
219         let parent = *self;
220         for (local_id, source) in arena_map.value.iter() {
221             let id = FieldId { parent, local_id };
222             match source.clone() {
223                 Either::Left(source) => {
224                     res[keys::TUPLE_FIELD].insert(arena_map.with_value(source), id)
225                 }
226                 Either::Right(source) => {
227                     res[keys::RECORD_FIELD].insert(arena_map.with_value(source), id)
228                 }
229             }
230         }
231     }
232 }
233
234 impl ChildBySource for EnumId {
235     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
236         let arena_map = self.child_source(db);
237         let arena_map = arena_map.as_ref();
238         for (local_id, source) in arena_map.value.iter() {
239             let id = EnumVariantId { parent: *self, local_id };
240             res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
241         }
242     }
243 }
244
245 impl ChildBySource for DefWithBodyId {
246     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
247         let body = db.body(*self);
248         for (_, def_map) in body.blocks(db) {
249             // All block expressions are merged into the same map, because they logically all add
250             // inner items to the containing `DefWithBodyId`.
251             def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);
252         }
253     }
254 }