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