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