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