1use either::Either;
89use hir_def::{
90 AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
91 ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId,
92 Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
93 UseId, VariantId,
94 dyn_map::{
95 DynMap,
96 keys::{self, Key},
97 },
98 hir::{BindingId, Expr, LabelId},
99 nameres::{block_def_map, crate_def_map},
100};
101use hir_expand::{
102 EditionedFileId, ExpansionInfo, HirFileId, InMacroFile, MacroCallId, attrs::AttrId,
103 name::AsName,
104};
105use rustc_hash::FxHashMap;
106use smallvec::SmallVec;
107use span::FileId;
108use stdx::impl_from;
109use syntax::{
110 AstNode, AstPtr, SyntaxNode,
111 ast::{self, HasAttrs, HasName},
112};
113use tt::TextRange;
114
115use crate::{InFile, InlineAsmOperand, db::HirDatabase, semantics::child_by_source::ChildBySource};
116
117#[derive(Default)]
118pub(super) struct SourceToDefCache {
119 pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>,
120 expansion_info_cache: FxHashMap<MacroCallId, ExpansionInfo>,
121 pub(super) file_to_def_cache: FxHashMap<FileId, SmallVec<[ModuleId; 1]>>,
122 pub(super) included_file_cache: FxHashMap<EditionedFileId, Option<MacroCallId>>,
123 pub(super) root_to_file_cache: FxHashMap<SyntaxNode, HirFileId>,
125}
126
127impl SourceToDefCache {
128 pub(super) fn cache(
129 root_to_file_cache: &mut FxHashMap<SyntaxNode, HirFileId>,
130 root_node: SyntaxNode,
131 file_id: HirFileId,
132 ) {
133 assert!(root_node.parent().is_none());
134 let prev = root_to_file_cache.insert(root_node, file_id);
135 assert!(prev.is_none() || prev == Some(file_id));
136 }
137
138 pub(super) fn get_or_insert_include_for(
139 &mut self,
140 db: &dyn HirDatabase,
141 file: EditionedFileId,
142 ) -> Option<MacroCallId> {
143 if let Some(&m) = self.included_file_cache.get(&file) {
144 return m;
145 }
146 self.included_file_cache.insert(file, None);
147 for &crate_id in db.relevant_crates(file.file_id(db)).iter() {
148 db.include_macro_invoc(crate_id).iter().for_each(|&(macro_call_id, file_id)| {
149 self.included_file_cache.insert(file_id, Some(macro_call_id));
150 });
151 }
152 self.included_file_cache.get(&file).copied().flatten()
153 }
154
155 pub(super) fn get_or_insert_expansion(
156 &mut self,
157 db: &dyn HirDatabase,
158 macro_file: MacroCallId,
159 ) -> &ExpansionInfo {
160 self.expansion_info_cache.entry(macro_file).or_insert_with(|| {
161 let exp_info = macro_file.expansion_info(db);
162
163 let InMacroFile { file_id, value } = exp_info.expanded();
164 Self::cache(&mut self.root_to_file_cache, value, file_id.into());
165
166 exp_info
167 })
168 }
169}
170
171pub(super) struct SourceToDefCtx<'db, 'cache> {
172 pub(super) db: &'db dyn HirDatabase,
173 pub(super) cache: &'cache mut SourceToDefCache,
174}
175
176impl SourceToDefCtx<'_, '_> {
177 pub(super) fn file_to_def(&mut self, file: FileId) -> &SmallVec<[ModuleId; 1]> {
178 let _p = tracing::info_span!("SourceToDefCtx::file_to_def").entered();
179 self.cache.file_to_def_cache.entry(file).or_insert_with(|| {
180 let mut mods = SmallVec::new();
181
182 for &crate_id in self.db.relevant_crates(file).iter() {
183 let crate_def_map = crate_def_map(self.db, crate_id);
185 let n_mods = mods.len();
186 let modules = |file| crate_def_map.modules_for_file(self.db, file);
187 mods.extend(modules(file));
188 if mods.len() == n_mods {
189 mods.extend(
190 self.db
191 .include_macro_invoc(crate_id)
192 .iter()
193 .filter(|&&(_, file_id)| file_id.file_id(self.db) == file)
194 .flat_map(|&(macro_call_id, file_id)| {
195 self.cache.included_file_cache.insert(file_id, Some(macro_call_id));
196 modules(
197 macro_call_id
198 .lookup(self.db)
199 .kind
200 .file_id()
201 .original_file(self.db)
202 .file_id(self.db),
203 )
204 }),
205 );
206 }
207 }
208 if mods.is_empty() {
209 }
211 mods
212 })
213 }
214
215 pub(super) fn module_to_def(&mut self, src: InFile<&ast::Module>) -> Option<ModuleId> {
216 let _p = tracing::info_span!("module_to_def").entered();
217 let parent_declaration = self
218 .parent_ancestors_with_macros(src.syntax_ref(), |_, ancestor, _| {
219 ancestor.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose()
220 })
221 .map(|it| it.transpose());
222
223 let parent_module = match parent_declaration {
224 Some(Either::Right(parent_block)) => self
225 .block_to_def(parent_block.as_ref())
226 .map(|block| block_def_map(self.db, block).root_module_id()),
227 Some(Either::Left(parent_declaration)) => {
228 self.module_to_def(parent_declaration.as_ref())
229 }
230 None => {
231 let file_id = src.file_id.original_file(self.db);
232 self.file_to_def(file_id.file_id(self.db)).first().copied()
233 }
234 }?;
235
236 let child_name = src.value.name()?.as_name();
237 let def_map = parent_module.def_map(self.db);
238 let &child_id = def_map[parent_module].children.get(&child_name)?;
239 Some(child_id)
240 }
241
242 pub(super) fn source_file_to_def(&mut self, src: InFile<&ast::SourceFile>) -> Option<ModuleId> {
243 let _p = tracing::info_span!("source_file_to_def").entered();
244 let file_id = src.file_id.original_file(self.db);
245 self.file_to_def(file_id.file_id(self.db)).first().copied()
246 }
247
248 pub(super) fn trait_to_def(&mut self, src: InFile<&ast::Trait>) -> Option<TraitId> {
249 self.to_def(src, keys::TRAIT)
250 }
251 pub(super) fn impl_to_def(&mut self, src: InFile<&ast::Impl>) -> Option<ImplId> {
252 self.to_def(src, keys::IMPL)
253 }
254 pub(super) fn fn_to_def(&mut self, src: InFile<&ast::Fn>) -> Option<FunctionId> {
255 self.to_def(src, keys::FUNCTION)
256 }
257 pub(super) fn struct_to_def(&mut self, src: InFile<&ast::Struct>) -> Option<StructId> {
258 self.to_def(src, keys::STRUCT)
259 }
260 pub(super) fn enum_to_def(&mut self, src: InFile<&ast::Enum>) -> Option<EnumId> {
261 self.to_def(src, keys::ENUM)
262 }
263 pub(super) fn union_to_def(&mut self, src: InFile<&ast::Union>) -> Option<UnionId> {
264 self.to_def(src, keys::UNION)
265 }
266 pub(super) fn static_to_def(&mut self, src: InFile<&ast::Static>) -> Option<StaticId> {
267 self.to_def(src, keys::STATIC)
268 }
269 pub(super) fn const_to_def(&mut self, src: InFile<&ast::Const>) -> Option<ConstId> {
270 self.to_def(src, keys::CONST)
271 }
272 pub(super) fn type_alias_to_def(
273 &mut self,
274 src: InFile<&ast::TypeAlias>,
275 ) -> Option<TypeAliasId> {
276 self.to_def(src, keys::TYPE_ALIAS)
277 }
278 pub(super) fn record_field_to_def(
279 &mut self,
280 src: InFile<&ast::RecordField>,
281 ) -> Option<FieldId> {
282 self.to_def(src, keys::RECORD_FIELD)
283 }
284 pub(super) fn tuple_field_to_def(&mut self, src: InFile<&ast::TupleField>) -> Option<FieldId> {
285 self.to_def(src, keys::TUPLE_FIELD)
286 }
287 pub(super) fn block_to_def(&mut self, src: InFile<&ast::BlockExpr>) -> Option<BlockId> {
288 self.to_def(src, keys::BLOCK)
289 }
290 pub(super) fn enum_variant_to_def(
291 &mut self,
292 src: InFile<&ast::Variant>,
293 ) -> Option<EnumVariantId> {
294 self.to_def(src, keys::ENUM_VARIANT)
295 }
296 pub(super) fn extern_crate_to_def(
297 &mut self,
298 src: InFile<&ast::ExternCrate>,
299 ) -> Option<ExternCrateId> {
300 self.to_def(src, keys::EXTERN_CRATE)
301 }
302 pub(super) fn extern_block_to_def(
303 &mut self,
304 src: InFile<&ast::ExternBlock>,
305 ) -> Option<ExternBlockId> {
306 self.to_def(src, keys::EXTERN_BLOCK)
307 }
308 #[allow(dead_code)]
309 pub(super) fn use_to_def(&mut self, src: InFile<&ast::Use>) -> Option<UseId> {
310 self.to_def(src, keys::USE)
311 }
312 pub(super) fn adt_to_def(
313 &mut self,
314 InFile { file_id, value }: InFile<&ast::Adt>,
315 ) -> Option<AdtId> {
316 match value {
317 ast::Adt::Enum(it) => self.enum_to_def(InFile::new(file_id, it)).map(AdtId::EnumId),
318 ast::Adt::Struct(it) => {
319 self.struct_to_def(InFile::new(file_id, it)).map(AdtId::StructId)
320 }
321 ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId),
322 }
323 }
324
325 pub(super) fn asm_operand_to_def(
326 &mut self,
327 src: InFile<&ast::AsmOperandNamed>,
328 ) -> Option<InlineAsmOperand> {
329 let asm = src.value.syntax().parent().and_then(ast::AsmExpr::cast)?;
330 let index = asm
331 .asm_pieces()
332 .filter_map(|it| match it {
333 ast::AsmPiece::AsmOperandNamed(it) => Some(it),
334 _ => None,
335 })
336 .position(|it| it == *src.value)?;
337 let container = self.find_pat_or_label_container(src.syntax_ref())?;
338 let source_map = self.db.body_with_source_map(container).1;
339 let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?.as_expr()?;
340 Some(InlineAsmOperand { owner: container, expr, index })
341 }
342
343 pub(super) fn bind_pat_to_def(
344 &mut self,
345 src: InFile<&ast::IdentPat>,
346 ) -> Option<(DefWithBodyId, BindingId)> {
347 let container = self.find_pat_or_label_container(src.syntax_ref())?;
348 let (body, source_map) = self.db.body_with_source_map(container);
349 let src = src.cloned().map(ast::Pat::from);
350 let pat_id = source_map.node_pat(src.as_ref())?;
351 if let crate::Pat::Bind { id, .. } = body[pat_id.as_pat()?] {
353 Some((container, id))
354 } else {
355 None
356 }
357 }
358 pub(super) fn self_param_to_def(
359 &mut self,
360 src: InFile<&ast::SelfParam>,
361 ) -> Option<(DefWithBodyId, BindingId)> {
362 let container = self.find_pat_or_label_container(src.syntax_ref())?;
363 let body = self.db.body(container);
364 Some((container, body.self_param?))
365 }
366 pub(super) fn label_to_def(
367 &mut self,
368 src: InFile<&ast::Label>,
369 ) -> Option<(DefWithBodyId, LabelId)> {
370 let container = self.find_pat_or_label_container(src.syntax_ref())?;
371 let source_map = self.db.body_with_source_map(container).1;
372
373 let label_id = source_map.node_label(src)?;
374 Some((container, label_id))
375 }
376
377 pub(super) fn label_ref_to_def(
378 &mut self,
379 src: InFile<&ast::Lifetime>,
380 ) -> Option<(DefWithBodyId, LabelId)> {
381 let break_or_continue = ast::Expr::cast(src.value.syntax().parent()?)?;
382 let container = self.find_pat_or_label_container(src.syntax_ref())?;
383 let (body, source_map) = self.db.body_with_source_map(container);
384 let break_or_continue =
385 source_map.node_expr(src.with_value(&break_or_continue))?.as_expr()?;
386 let (Expr::Break { label, .. } | Expr::Continue { label }) = body[break_or_continue] else {
387 return None;
388 };
389 Some((container, label?))
390 }
391
392 pub(super) fn attr_to_derive_macro_call(
394 &mut self,
395 item: InFile<&ast::Adt>,
396 src: InFile<ast::Attr>,
397 ) -> Option<(AttrId, MacroCallId, &[Option<MacroCallId>])> {
398 let map = self.dyn_map(item)?;
399 map[keys::DERIVE_MACRO_CALL]
400 .get(&AstPtr::new(&src.value))
401 .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
402 }
403
404 pub(super) fn file_of_adt_has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool {
406 self.dyn_map(adt).as_ref().is_some_and(|map| !map[keys::DERIVE_MACRO_CALL].is_empty())
407 }
408
409 pub(super) fn derive_macro_calls<'slf>(
410 &'slf mut self,
411 adt: InFile<&ast::Adt>,
412 ) -> Option<impl Iterator<Item = (AttrId, MacroCallId, &'slf [Option<MacroCallId>])> + use<'slf>>
413 {
414 self.dyn_map(adt).as_ref().map(|&map| {
415 let dyn_map = &map[keys::DERIVE_MACRO_CALL];
416 adt.value
417 .attrs()
418 .filter_map(move |attr| dyn_map.get(&AstPtr::new(&attr)))
419 .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
420 })
421 }
422
423 fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
424 &mut self,
425 src: InFile<&Ast>,
426 key: Key<Ast, ID>,
427 ) -> Option<ID> {
428 self.dyn_map(src)?[key].get(&AstPtr::new(src.value)).copied()
429 }
430
431 fn dyn_map<Ast: AstNode + 'static>(&mut self, src: InFile<&Ast>) -> Option<&DynMap> {
432 let container = self.find_container(src.map(|it| it.syntax()))?;
433 Some(self.cache_for(container, src.file_id))
434 }
435
436 fn cache_for(&mut self, container: ChildContainer, file_id: HirFileId) -> &DynMap {
437 let db = self.db;
438 self.cache
439 .dynmap_cache
440 .entry((container, file_id))
441 .or_insert_with(|| container.child_by_source(db, file_id))
442 }
443
444 pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> {
445 self.to_def(src, keys::ATTR_MACRO_CALL)
446 }
447
448 pub(super) fn macro_call_to_macro_call(
449 &mut self,
450 src: InFile<&ast::MacroCall>,
451 ) -> Option<MacroCallId> {
452 self.to_def(src, keys::MACRO_CALL)
453 }
454
455 pub(super) fn type_param_to_def(
456 &mut self,
457 src: InFile<&ast::TypeParam>,
458 ) -> Option<TypeParamId> {
459 let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
460 let dyn_map = self.cache_for(container, src.file_id);
461 dyn_map[keys::TYPE_PARAM]
462 .get(&AstPtr::new(src.value))
463 .copied()
464 .map(TypeParamId::from_unchecked)
465 }
466
467 pub(super) fn lifetime_param_to_def(
468 &mut self,
469 src: InFile<&ast::LifetimeParam>,
470 ) -> Option<LifetimeParamId> {
471 let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
472 let dyn_map = self.cache_for(container, src.file_id);
473 dyn_map[keys::LIFETIME_PARAM].get(&AstPtr::new(src.value)).copied()
474 }
475
476 pub(super) fn const_param_to_def(
477 &mut self,
478 src: InFile<&ast::ConstParam>,
479 ) -> Option<ConstParamId> {
480 let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
481 let dyn_map = self.cache_for(container, src.file_id);
482 dyn_map[keys::CONST_PARAM]
483 .get(&AstPtr::new(src.value))
484 .copied()
485 .map(ConstParamId::from_unchecked)
486 }
487
488 pub(super) fn generic_param_to_def(
489 &mut self,
490 InFile { file_id, value }: InFile<&ast::GenericParam>,
491 ) -> Option<GenericParamId> {
492 match value {
493 ast::GenericParam::ConstParam(it) => {
494 self.const_param_to_def(InFile::new(file_id, it)).map(GenericParamId::ConstParamId)
495 }
496 ast::GenericParam::LifetimeParam(it) => self
497 .lifetime_param_to_def(InFile::new(file_id, it))
498 .map(GenericParamId::LifetimeParamId),
499 ast::GenericParam::TypeParam(it) => {
500 self.type_param_to_def(InFile::new(file_id, it)).map(GenericParamId::TypeParamId)
501 }
502 }
503 }
504
505 pub(super) fn macro_to_def(&mut self, src: InFile<&ast::Macro>) -> Option<MacroId> {
506 self.dyn_map(src).and_then(|it| match src.value {
507 ast::Macro::MacroRules(value) => {
508 it[keys::MACRO_RULES].get(&AstPtr::new(value)).copied().map(MacroId::from)
509 }
510 ast::Macro::MacroDef(value) => {
511 it[keys::MACRO2].get(&AstPtr::new(value)).copied().map(MacroId::from)
512 }
513 })
514 }
515
516 pub(super) fn proc_macro_to_def(&mut self, src: InFile<&ast::Fn>) -> Option<MacroId> {
517 self.dyn_map(src).and_then(|it| {
518 it[keys::PROC_MACRO].get(&AstPtr::new(src.value)).copied().map(MacroId::from)
519 })
520 }
521
522 pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
523 let _p = tracing::info_span!("find_container").entered();
524 let def = self.parent_ancestors_with_macros(src, |this, container, child| {
525 this.container_to_def(container, child)
526 });
527 if let Some(def) = def {
528 return Some(def);
529 }
530
531 let def = self
532 .file_to_def(src.file_id.original_file(self.db).file_id(self.db))
533 .first()
534 .copied()?;
535 Some(def.into())
536 }
537
538 fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
539 self.parent_ancestors_with_macros(src, |this, InFile { file_id, value }, _| {
540 let item = ast::Item::cast(value)?;
541 match &item {
542 ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into),
543 ast::Item::Struct(it) => {
544 this.struct_to_def(InFile::new(file_id, it)).map(Into::into)
545 }
546 ast::Item::Enum(it) => this.enum_to_def(InFile::new(file_id, it)).map(Into::into),
547 ast::Item::Trait(it) => this.trait_to_def(InFile::new(file_id, it)).map(Into::into),
548 ast::Item::TypeAlias(it) => {
549 this.type_alias_to_def(InFile::new(file_id, it)).map(Into::into)
550 }
551 ast::Item::Impl(it) => this.impl_to_def(InFile::new(file_id, it)).map(Into::into),
552 _ => None,
553 }
554 })
555 }
556
557 fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
559 self.parent_ancestors_with_macros(src, |this, InFile { file_id, value }, _| {
560 let item = match ast::Item::cast(value.clone()) {
561 Some(it) => it,
562 None => {
563 let variant = ast::Variant::cast(value)?;
564 return this
565 .enum_variant_to_def(InFile::new(file_id, &variant))
566 .map(Into::into);
567 }
568 };
569 match &item {
570 ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into),
571 ast::Item::Const(it) => this.const_to_def(InFile::new(file_id, it)).map(Into::into),
572 ast::Item::Static(it) => {
573 this.static_to_def(InFile::new(file_id, it)).map(Into::into)
574 }
575 _ => None,
576 }
577 })
578 }
579
580 fn parent_ancestors_with_macros<T>(
582 &mut self,
583 node: InFile<&SyntaxNode>,
584 mut cb: impl FnMut(
585 &mut Self,
586 InFile<SyntaxNode>,
587 &SyntaxNode,
588 ) -> Option<T>,
589 ) -> Option<T> {
590 let parent = |this: &mut Self, node: InFile<&SyntaxNode>| match node.value.parent() {
591 Some(parent) => Some(node.with_value(parent)),
592 None => {
593 let macro_file = node.file_id.macro_file()?;
594 let expansion_info = this.cache.get_or_insert_expansion(this.db, macro_file);
595 expansion_info.arg().map(|node| node?.parent()).transpose()
596 }
597 };
598 let mut deepest_child_in_same_file = node.cloned();
599 let mut node = node.cloned();
600 while let Some(parent) = parent(self, node.as_ref()) {
601 if parent.file_id != node.file_id {
602 deepest_child_in_same_file = parent.clone();
603 }
604 if let Some(res) = cb(self, parent.clone(), &deepest_child_in_same_file.value) {
605 return Some(res);
606 }
607 node = parent;
608 }
609 None
610 }
611
612 fn container_to_def(
613 &mut self,
614 container: InFile<SyntaxNode>,
615 child: &SyntaxNode,
616 ) -> Option<ChildContainer> {
617 let cont = if let Some(item) = ast::Item::cast(container.value.clone()) {
618 match &item {
619 ast::Item::Module(it) => self.module_to_def(container.with_value(it))?.into(),
620 ast::Item::Trait(it) => self.trait_to_def(container.with_value(it))?.into(),
621 ast::Item::Impl(it) => self.impl_to_def(container.with_value(it))?.into(),
622 ast::Item::Enum(it) => self.enum_to_def(container.with_value(it))?.into(),
623 ast::Item::TypeAlias(it) => ChildContainer::GenericDefId(
624 self.type_alias_to_def(container.with_value(it))?.into(),
625 ),
626 ast::Item::Struct(it) => {
627 let def = self.struct_to_def(container.with_value(it))?;
628 let is_in_body = it.field_list().is_some_and(|it| {
629 it.syntax().text_range().contains(child.text_range().start())
630 });
631 if is_in_body {
632 VariantId::from(def).into()
633 } else {
634 ChildContainer::GenericDefId(def.into())
635 }
636 }
637 ast::Item::Union(it) => {
638 let def = self.union_to_def(container.with_value(it))?;
639 let is_in_body = it.record_field_list().is_some_and(|it| {
640 it.syntax().text_range().contains(child.text_range().start())
641 });
642 if is_in_body {
643 VariantId::from(def).into()
644 } else {
645 ChildContainer::GenericDefId(def.into())
646 }
647 }
648 ast::Item::Fn(it) => {
649 let def = self.fn_to_def(container.with_value(it))?;
650 let child_offset = child.text_range().start();
651 let is_in_body =
652 it.body().is_some_and(|it| it.syntax().text_range().contains(child_offset));
653 let in_param_pat = || {
654 it.param_list().is_some_and(|it| {
655 it.self_param()
656 .and_then(|it| {
657 Some(TextRange::new(
658 it.syntax().text_range().start(),
659 it.name()?.syntax().text_range().end(),
660 ))
661 })
662 .is_some_and(|r| r.contains_inclusive(child_offset))
663 || it
664 .params()
665 .filter_map(|it| it.pat())
666 .any(|it| it.syntax().text_range().contains(child_offset))
667 })
668 };
669 if is_in_body || in_param_pat() {
670 DefWithBodyId::from(def).into()
671 } else {
672 ChildContainer::GenericDefId(def.into())
673 }
674 }
675 ast::Item::Static(it) => {
676 let def = self.static_to_def(container.with_value(it))?;
677 let is_in_body = it.body().is_some_and(|it| {
678 it.syntax().text_range().contains(child.text_range().start())
679 });
680 if is_in_body {
681 DefWithBodyId::from(def).into()
682 } else {
683 ChildContainer::GenericDefId(def.into())
684 }
685 }
686 ast::Item::Const(it) => {
687 let def = self.const_to_def(container.with_value(it))?;
688 let is_in_body = it.body().is_some_and(|it| {
689 it.syntax().text_range().contains(child.text_range().start())
690 });
691 if is_in_body {
692 DefWithBodyId::from(def).into()
693 } else {
694 ChildContainer::GenericDefId(def.into())
695 }
696 }
697 _ => return None,
698 }
699 } else if let Some(it) = ast::Variant::cast(container.value.clone()) {
700 let def = self.enum_variant_to_def(InFile::new(container.file_id, &it))?;
701 let is_in_body =
702 it.eq_token().is_some_and(|it| it.text_range().end() < child.text_range().start());
703 if is_in_body { DefWithBodyId::from(def).into() } else { VariantId::from(def).into() }
704 } else {
705 let it = match Either::<ast::Pat, ast::Name>::cast(container.value)? {
706 Either::Left(it) => ast::Param::cast(it.syntax().parent()?)?.syntax().parent(),
707 Either::Right(it) => ast::SelfParam::cast(it.syntax().parent()?)?.syntax().parent(),
708 }
709 .and_then(ast::ParamList::cast)?
710 .syntax()
711 .parent()
712 .and_then(ast::Fn::cast)?;
713 let def = self.fn_to_def(InFile::new(container.file_id, &it))?;
714 DefWithBodyId::from(def).into()
715 };
716 Some(cont)
717 }
718}
719
720#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
721pub(crate) enum ChildContainer {
722 DefWithBodyId(DefWithBodyId),
723 ModuleId(ModuleId),
724 TraitId(TraitId),
725 ImplId(ImplId),
726 EnumId(EnumId),
727 VariantId(VariantId),
728 GenericDefId(GenericDefId),
731}
732impl_from! {
733 DefWithBodyId,
734 ModuleId,
735 TraitId,
736 ImplId,
737 EnumId,
738 VariantId,
739 GenericDefId
740 for ChildContainer
741}
742
743impl ChildContainer {
744 fn child_by_source(self, db: &dyn HirDatabase, file_id: HirFileId) -> DynMap {
745 let _p = tracing::info_span!("ChildContainer::child_by_source").entered();
746 match self {
747 ChildContainer::DefWithBodyId(it) => it.child_by_source(db, file_id),
748 ChildContainer::ModuleId(it) => it.child_by_source(db, file_id),
749 ChildContainer::TraitId(it) => it.child_by_source(db, file_id),
750 ChildContainer::ImplId(it) => it.child_by_source(db, file_id),
751 ChildContainer::EnumId(it) => it.child_by_source(db, file_id),
752 ChildContainer::VariantId(it) => it.child_by_source(db, file_id),
753 ChildContainer::GenericDefId(it) => it.child_by_source(db, file_id),
754 }
755 }
756}