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);
131 res[keys::DERIVE_MACRO_CALL]
132 .insert(AstPtr::new(&attr), (attr_id, call_id, calls.into()));
133 });
134 },
135 );
136 self.iter_macro_invoc().filter(|(id, _)| id.file_id == file_id).for_each(
137 |(ast_id, &call)| {
138 let ast = ast_id.to_ptr(db);
139 res[keys::MACRO_CALL].insert(ast, call);
140 },
141 );
142 fn add_module_def(
143 db: &dyn DefDatabase,
144 map: &mut DynMap,
145 file_id: HirFileId,
146 item: ModuleDefId,
147 ) {
148 match item {
149 ModuleDefId::FunctionId(id) => {
150 insert_item_loc(db, map, file_id, id, keys::FUNCTION)
151 }
152 ModuleDefId::ConstId(id) => insert_item_loc(db, map, file_id, id, keys::CONST),
153 ModuleDefId::TypeAliasId(id) => {
154 insert_item_loc(db, map, file_id, id, keys::TYPE_ALIAS)
155 }
156 ModuleDefId::StaticId(id) => insert_item_loc(db, map, file_id, id, keys::STATIC),
157 ModuleDefId::TraitId(id) => insert_item_loc(db, map, file_id, id, keys::TRAIT),
158 ModuleDefId::AdtId(adt) => match adt {
159 AdtId::StructId(id) => insert_item_loc(db, map, file_id, id, keys::STRUCT),
160 AdtId::UnionId(id) => insert_item_loc(db, map, file_id, id, keys::UNION),
161 AdtId::EnumId(id) => insert_item_loc(db, map, file_id, id, keys::ENUM),
162 },
163 ModuleDefId::MacroId(id) => match id {
164 MacroId::Macro2Id(id) => insert_item_loc(db, map, file_id, id, keys::MACRO2),
165 MacroId::MacroRulesId(id) => {
166 insert_item_loc(db, map, file_id, id, keys::MACRO_RULES)
167 }
168 MacroId::ProcMacroId(id) => {
169 insert_item_loc(db, map, file_id, id, keys::PROC_MACRO)
170 }
171 },
172 ModuleDefId::ModuleId(_)
173 | ModuleDefId::EnumVariantId(_)
174 | ModuleDefId::BuiltinType(_) => (),
175 }
176 }
177 }
178}
179
180impl ChildBySource for VariantId {
181 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
182 let arena_map = self.child_source(db);
183 let arena_map = arena_map.as_ref();
184 let parent = *self;
185 for (local_id, source) in arena_map.value.iter() {
186 let id = FieldId { parent, local_id };
187 match source.clone() {
188 Either::Left(source) => res[keys::TUPLE_FIELD].insert(AstPtr::new(&source), id),
189 Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id),
190 }
191 }
192 let (_, sm) = self.fields_with_source_map(db);
193 sm.expansions().for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id));
194 }
195}
196
197impl ChildBySource for EnumId {
198 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
199 let loc = &self.lookup(db);
200 if file_id != loc.id.file_id {
201 return;
202 }
203
204 let ast_id_map = db.ast_id_map(loc.id.file_id);
205
206 self.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| {
207 res[keys::ENUM_VARIANT].insert(ast_id_map.get(variant.lookup(db).id.value), variant);
208 });
209 let (_, source_map) = EnumSignature::with_source_map(db, *self);
210 source_map
211 .expansions()
212 .filter(|(ast, _)| ast.file_id == file_id)
213 .for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id));
214 }
215}
216
217impl ChildBySource for DefWithBodyId {
218 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
219 let (body, sm) = Body::with_source_map(db, *self);
220 if let &DefWithBodyId::VariantId(v) = self {
221 VariantId::EnumVariantId(v).child_by_source_to(db, res, file_id)
222 }
223
224 sm.expansions().filter(|(ast, _)| ast.file_id == file_id).for_each(|(ast, &exp_id)| {
225 res[keys::MACRO_CALL].insert(ast.value, exp_id);
226 });
227
228 for (block, def_map) in body.blocks(db) {
229 def_map[def_map.root].scope.child_by_source_to(db, res, file_id);
232 res[keys::BLOCK].insert(block.lookup(db).ast_id.to_ptr(db), block);
233 }
234 }
235}
236
237impl ChildBySource for GenericDefId {
238 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
239 let (gfile_id, generic_params_list) = self.file_id_and_params_of(db);
240 if gfile_id != file_id {
241 return;
242 }
243
244 let (generic_params, _, source_map) = GenericParams::with_source_map(db, *self);
245 let mut toc_idx_iter = generic_params.iter_type_or_consts().map(|(idx, _)| idx);
246 let lts_idx_iter = generic_params.iter_lt().map(|(idx, _)| idx);
247
248 if let GenericDefId::TraitId(_) = *self {
250 toc_idx_iter.next().unwrap(); }
252
253 if let Some(generic_params_list) = generic_params_list {
254 for (local_id, ast_param) in
255 toc_idx_iter.zip(generic_params_list.type_or_const_params())
256 {
257 let id = TypeOrConstParamId { parent: *self, local_id };
258 match ast_param {
259 ast::TypeOrConstParam::Type(a) => {
260 res[keys::TYPE_PARAM].insert(AstPtr::new(&a), id)
261 }
262 ast::TypeOrConstParam::Const(a) => {
263 res[keys::CONST_PARAM].insert(AstPtr::new(&a), id)
264 }
265 }
266 }
267 for (local_id, ast_param) in lts_idx_iter.zip(generic_params_list.lifetime_params()) {
268 let id = LifetimeParamId { parent: *self, local_id };
269 res[keys::LIFETIME_PARAM].insert(AstPtr::new(&ast_param), id);
270 }
271 }
272
273 source_map
274 .expansions()
275 .filter(|(ast, _)| ast.file_id == file_id)
276 .for_each(|(ast, &exp_id)| res[keys::MACRO_CALL].insert(ast.value, exp_id));
277 }
278}
279
280fn insert_item_loc<ID, N, Data>(
281 db: &dyn DefDatabase,
282 res: &mut DynMap,
283 file_id: HirFileId,
284 id: ID,
285 key: Key<N, ID>,
286) where
287 ID: Lookup<Database = dyn DefDatabase, Data = Data> + 'static,
288 Data: AstIdLoc<Ast = N>,
289 N: AstIdNode + 'static,
290{
291 let loc = id.lookup(db);
292 if loc.ast_id().file_id == file_id {
293 res[key].insert(loc.ast_ptr(db).value, id)
294 }
295}
296
297fn add_assoc_item(db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId, item: AssocItemId) {
298 match item {
299 AssocItemId::FunctionId(func) => insert_item_loc(db, res, file_id, func, keys::FUNCTION),
300 AssocItemId::ConstId(konst) => insert_item_loc(db, res, file_id, konst, keys::CONST),
301 AssocItemId::TypeAliasId(ty) => insert_item_loc(db, res, file_id, ty, keys::TYPE_ALIAS),
302 }
303}