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