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