1use either::Either;
8use hir_expand::HirFileId;
9use span::AstIdNode;
10use syntax::{AstPtr, ast};
11
12use hir_def::{
13 AdtId, AssocItemId, AstIdLoc, DefWithBodyId, EnumId, FieldId, GenericDefId, ImplId,
14 LifetimeParamId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, TypeOrConstParamId,
15 VariantId,
16 db::DefDatabase,
17 dyn_map::{
18 DynMap,
19 keys::{self, Key},
20 },
21 expr_store::Body,
22 hir::generics::GenericParams,
23 item_scope::ItemScope,
24 signatures::{EnumSignature, ImplSignature, TraitSignature},
25 src::{HasChildSource, HasSource},
26};
27
28pub(crate) trait ChildBySource {
29 fn child_by_source(&self, db: &dyn DefDatabase, file_id: HirFileId) -> DynMap {
30 let mut res = DynMap::default();
31 self.child_by_source_to(db, &mut res, file_id);
32 res
33 }
34 fn child_by_source_to(&self, db: &dyn DefDatabase, map: &mut DynMap, file_id: HirFileId);
35}
36
37impl ChildBySource for TraitId {
38 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
39 let data = self.trait_items(db);
40
41 data.macro_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
42 |(ast_id, call_id)| {
43 let ptr = ast_id.to_ptr(db);
44 if let Some(ptr) = ptr.cast::<ast::MacroCall>() {
45 res[keys::MACRO_CALL].insert(ptr, call_id);
46 } else {
47 res[keys::ATTR_MACRO_CALL].insert(ptr, call_id);
48 }
49 },
50 );
51 data.items.iter().for_each(|&(_, item)| {
52 add_assoc_item(db, res, file_id, item);
53 });
54 let (_, source_map) = TraitSignature::with_source_map(db, *self);
55 source_map.expansions().filter(|(ast, _)| ast.file_id == file_id).for_each(
56 |(ast, &exp_id)| {
57 res[keys::MACRO_CALL].insert(ast.value, exp_id);
58 },
59 );
60 }
61}
62
63impl ChildBySource for ImplId {
64 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
65 let data = self.impl_items(db);
66 data.macro_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
67 |(ast_id, call_id)| {
68 let ptr = ast_id.to_ptr(db);
69 if let Some(ptr) = ptr.cast::<ast::MacroCall>() {
70 res[keys::MACRO_CALL].insert(ptr, call_id);
71 } else {
72 res[keys::ATTR_MACRO_CALL].insert(ptr, call_id);
73 }
74 },
75 );
76 data.items.iter().for_each(|&(_, item)| {
77 add_assoc_item(db, res, file_id, item);
78 });
79 let (_, source_map) = ImplSignature::with_source_map(db, *self);
80 source_map.expansions().filter(|(ast, _)| ast.file_id == file_id).for_each(
81 |(ast, &exp_id)| {
82 res[keys::MACRO_CALL].insert(ast.value, exp_id);
83 },
84 );
85 }
86}
87
88impl ChildBySource for ModuleId {
89 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
90 let def_map = self.def_map(db);
91 let module_data = &def_map[*self];
92 module_data.scope.child_by_source_to(db, res, file_id);
93 }
94}
95
96impl ChildBySource for ItemScope {
97 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
98 self.declarations().for_each(|item| add_module_def(db, res, file_id, item));
99 self.impls().for_each(|imp| insert_item_loc(db, res, file_id, imp, keys::IMPL));
100 self.extern_blocks().for_each(|extern_block| {
101 insert_item_loc(db, res, file_id, extern_block, keys::EXTERN_BLOCK)
102 });
103 self.extern_crate_decls()
104 .for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::EXTERN_CRATE));
105 self.use_decls().for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::USE));
106 self.unnamed_consts()
107 .for_each(|konst| insert_item_loc(db, res, file_id, konst, keys::CONST));
108 self.attr_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
109 |(ast_id, call_id)| {
110 res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db), call_id);
111 },
112 );
113 self.legacy_macros().for_each(|(_, ids)| {
114 ids.iter().for_each(|&id| {
115 if let MacroId::MacroRulesId(id) = id {
116 let loc = id.lookup(db);
117 if loc.id.file_id == file_id {
118 res[keys::MACRO_RULES].insert(loc.ast_ptr(db).value, id);
119 }
120 }
121 })
122 });
123 self.derive_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
124 |(ast_id, calls)| {
125 let adt = ast_id.to_node(db);
126 calls.for_each(|(attr_id, call_id, calls)| {
127 let krate = call_id.lookup(db).krate;
129 let (_, attr) = attr_id.find_attr_range_with_source(db, krate, &adt);
130 res[keys::DERIVE_MACRO_CALL]
131 .insert(AstPtr::new(&attr), (attr_id, call_id, calls.into()));
132 });
133 },
134 );
135 self.iter_macro_invoc().filter(|(id, _)| id.file_id == file_id).for_each(
136 |(ast_id, &call)| {
137 let ast = ast_id.to_ptr(db);
138 res[keys::MACRO_CALL].insert(ast, call);
139 },
140 );
141 fn add_module_def(
142 db: &dyn DefDatabase,
143 map: &mut DynMap,
144 file_id: HirFileId,
145 item: ModuleDefId,
146 ) {
147 match item {
148 ModuleDefId::FunctionId(id) => {
149 insert_item_loc(db, map, file_id, id, keys::FUNCTION)
150 }
151 ModuleDefId::ConstId(id) => insert_item_loc(db, map, file_id, id, keys::CONST),
152 ModuleDefId::TypeAliasId(id) => {
153 insert_item_loc(db, map, file_id, id, keys::TYPE_ALIAS)
154 }
155 ModuleDefId::StaticId(id) => insert_item_loc(db, map, file_id, id, keys::STATIC),
156 ModuleDefId::TraitId(id) => insert_item_loc(db, map, file_id, id, keys::TRAIT),
157 ModuleDefId::AdtId(adt) => match adt {
158 AdtId::StructId(id) => insert_item_loc(db, map, file_id, id, keys::STRUCT),
159 AdtId::UnionId(id) => insert_item_loc(db, map, file_id, id, keys::UNION),
160 AdtId::EnumId(id) => insert_item_loc(db, map, file_id, id, keys::ENUM),
161 },
162 ModuleDefId::MacroId(id) => match id {
163 MacroId::Macro2Id(id) => insert_item_loc(db, map, file_id, id, keys::MACRO2),
164 MacroId::MacroRulesId(id) => {
165 insert_item_loc(db, map, file_id, id, keys::MACRO_RULES)
166 }
167 MacroId::ProcMacroId(id) => {
168 insert_item_loc(db, map, file_id, id, keys::PROC_MACRO)
169 }
170 },
171 ModuleDefId::ModuleId(_)
172 | ModuleDefId::EnumVariantId(_)
173 | ModuleDefId::BuiltinType(_) => (),
174 }
175 }
176 }
177}
178
179impl ChildBySource for VariantId {
180 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
181 let arena_map = self.child_source(db);
182 let arena_map = arena_map.as_ref();
183 let parent = *self;
184 for (local_id, source) in arena_map.value.iter() {
185 let id = FieldId { parent, local_id };
186 match source.clone() {
187 Either::Left(source) => res[keys::TUPLE_FIELD].insert(AstPtr::new(&source), id),
188 Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id),
189 }
190 }
191 let (_, sm) = self.fields_with_source_map(db);
192 sm.expansions().for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id));
193 }
194}
195
196impl ChildBySource for EnumId {
197 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
198 let loc = &self.lookup(db);
199 if file_id != loc.id.file_id {
200 return;
201 }
202
203 let ast_id_map = db.ast_id_map(loc.id.file_id);
204
205 self.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| {
206 res[keys::ENUM_VARIANT].insert(ast_id_map.get(variant.lookup(db).id.value), variant);
207 });
208 let (_, source_map) = EnumSignature::with_source_map(db, *self);
209 source_map
210 .expansions()
211 .filter(|(ast, _)| ast.file_id == file_id)
212 .for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id));
213 }
214}
215
216impl ChildBySource for DefWithBodyId {
217 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
218 let (body, sm) = Body::with_source_map(db, *self);
219 if let &DefWithBodyId::VariantId(v) = self {
220 VariantId::EnumVariantId(v).child_by_source_to(db, res, file_id)
221 }
222
223 sm.expansions().filter(|(ast, _)| ast.file_id == file_id).for_each(|(ast, &exp_id)| {
224 res[keys::MACRO_CALL].insert(ast.value, exp_id);
225 });
226
227 for (block, def_map) in body.blocks(db) {
228 def_map[def_map.root].scope.child_by_source_to(db, res, file_id);
231 res[keys::BLOCK].insert(block.lookup(db).ast_id.to_ptr(db), block);
232 }
233 }
234}
235
236impl ChildBySource for GenericDefId {
237 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
238 let (gfile_id, generic_params_list) = self.file_id_and_params_of(db);
239 if gfile_id != file_id {
240 return;
241 }
242
243 let (generic_params, _, source_map) = GenericParams::with_source_map(db, *self);
244 let mut toc_idx_iter = generic_params.iter_type_or_consts().map(|(idx, _)| idx);
245 let lts_idx_iter = generic_params.iter_lt().map(|(idx, _)| idx);
246
247 if let GenericDefId::TraitId(_) = *self {
249 toc_idx_iter.next().unwrap(); }
251
252 if let Some(generic_params_list) = generic_params_list {
253 for (local_id, ast_param) in
254 toc_idx_iter.zip(generic_params_list.type_or_const_params())
255 {
256 let id = TypeOrConstParamId { parent: *self, local_id };
257 match ast_param {
258 ast::TypeOrConstParam::Type(a) => {
259 res[keys::TYPE_PARAM].insert(AstPtr::new(&a), id)
260 }
261 ast::TypeOrConstParam::Const(a) => {
262 res[keys::CONST_PARAM].insert(AstPtr::new(&a), id)
263 }
264 }
265 }
266 for (local_id, ast_param) in lts_idx_iter.zip(generic_params_list.lifetime_params()) {
267 let id = LifetimeParamId { parent: *self, local_id };
268 res[keys::LIFETIME_PARAM].insert(AstPtr::new(&ast_param), id);
269 }
270 }
271
272 source_map
273 .expansions()
274 .filter(|(ast, _)| ast.file_id == file_id)
275 .for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id));
276 }
277}
278
279fn insert_item_loc<ID, N, Data>(
280 db: &dyn DefDatabase,
281 res: &mut DynMap,
282 file_id: HirFileId,
283 id: ID,
284 key: Key<N, ID>,
285) where
286 ID: Lookup<Database = dyn DefDatabase, Data = Data> + 'static,
287 Data: AstIdLoc<Ast = N>,
288 N: AstIdNode + 'static,
289{
290 let loc = id.lookup(db);
291 if loc.ast_id().file_id == file_id {
292 res[key].insert(loc.ast_ptr(db).value, id)
293 }
294}
295
296fn add_assoc_item(db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId, item: AssocItemId) {
297 match item {
298 AssocItemId::FunctionId(func) => insert_item_loc(db, res, file_id, func, keys::FUNCTION),
299 AssocItemId::ConstId(konst) => insert_item_loc(db, res, file_id, konst, keys::CONST),
300 AssocItemId::TypeAliasId(ty) => insert_item_loc(db, res, file_id, ty, keys::TYPE_ALIAS),
301 }
302}