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