hir/semantics/
source_to_def.rs

1//! Maps *syntax* of various definitions to their semantic ids.
2//!
3//! This is a very interesting module, and, in some sense, can be considered the
4//! heart of the IDE parts of rust-analyzer.
5//!
6//! This module solves the following problem:
7//!
8//! > Given a piece of syntax, find the corresponding semantic definition (def).
9//!
10//! This problem is a part of more-or-less every IDE feature implemented. Every
11//! IDE functionality (like goto to definition), conceptually starts with a
12//! specific cursor position in a file. Starting with this text offset, we first
13//! figure out what syntactic construct are we at: is this a pattern, an
14//! expression, an item definition.
15//!
16//! Knowing only the syntax gives us relatively little info. For example,
17//! looking at the syntax of the function we can realize that it is a part of an
18//! `impl` block, but we won't be able to tell what trait function the current
19//! function overrides, and whether it does that correctly. For that, we need to
20//! go from [`ast::Fn`] to [`crate::Function`], and that's exactly what this
21//! module does.
22//!
23//! As syntax trees are values and don't know their place of origin/identity,
24//! this module also requires [`InFile`] wrappers to understand which specific
25//! real or macro-expanded file the tree comes from.
26//!
27//! The actual algorithm to resolve syntax to def is curious in two aspects:
28//!
29//! * It is recursive
30//! * It uses the inverse algorithm (what is the syntax for this def?)
31//!
32//! Specifically, the algorithm goes like this:
33//!
34//! 1. Find the syntactic container for the syntax. For example, field's
35//!    container is the struct, and structs container is a module.
36//! 2. Recursively get the def corresponding to container.
37//! 3. Ask the container def for all child defs. These child defs contain
38//!    the answer and answer's siblings.
39//! 4. For each child def, ask for it's source.
40//! 5. The child def whose source is the syntax node we've started with
41//!    is the answer.
42//!
43//! It's interesting that both Roslyn and Kotlin contain very similar code
44//! shape.
45//!
46//! Let's take a look at Roslyn:
47//!
48//!   <https://github.com/dotnet/roslyn/blob/36a0c338d6621cc5fe34b79d414074a95a6a489c/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs#L1403-L1429>
49//!   <https://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Compilation/SyntaxTreeSemanticModel.cs,1403>
50//!
51//! The `GetDeclaredType` takes `Syntax` as input, and returns `Symbol` as
52//! output. First, it retrieves a `Symbol` for parent `Syntax`:
53//!
54//! * <https://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Compilation/SyntaxTreeSemanticModel.cs,1423>
55//!
56//! Then, it iterates parent symbol's children, looking for one which has the
57//! same text span as the original node:
58//!
59//!   <https://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Compilation/SyntaxTreeSemanticModel.cs,1786>
60//!
61//! Now, let's look at Kotlin:
62//!
63//!   <https://github.com/JetBrains/kotlin/blob/a288b8b00e4754a1872b164999c6d3f3b8c8994a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt#L93-L125>
64//!
65//! This function starts with a syntax node (`KtExpression` is syntax, like all
66//! `Kt` nodes), and returns a def. It uses
67//! `getNonLocalContainingOrThisDeclaration` to get syntactic container for a
68//! current node. Then, `findSourceNonLocalFirDeclaration` gets `Fir` for this
69//! parent. Finally, `findElementIn` function traverses `Fir` children to find
70//! one with the same source we originally started with.
71//!
72//! One question is left though -- where does the recursion stops? This happens
73//! when we get to the file syntax node, which doesn't have a syntactic parent.
74//! In that case, we loop through all the crates that might contain this file
75//! and look for a module whose source is the given file.
76//!
77//! Note that the logic in this module is somewhat fundamentally imprecise --
78//! due to conditional compilation and `#[path]` attributes, there's no
79//! injective mapping from syntax nodes to defs. This is not an edge case --
80//! more or less every item in a `lib.rs` is a part of two distinct crates: a
81//! library with `--cfg test` and a library without.
82//!
83//! At the moment, we don't really handle this well and return the first answer
84//! that works. Ideally, we should first let the caller to pick a specific
85//! active crate for a given position, and then provide an API to resolve all
86//! syntax nodes against this specific crate.
87
88use 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    /// Rootnode to HirFileId cache
125    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                // Note: `mod` declarations in block modules cannot be supported here
185                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                // FIXME: detached file
211            }
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        // the pattern could resolve to a constant, verify that this is not the case
353        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    /// (AttrId, derive attribute call id, derive call ids)
397    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    // FIXME: Make this more fine grained! This should be a `adt_has_derives`!
409    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    /// Skips the attributed item that caused the macro invocation we are climbing up
565    fn parent_ancestors_with_macros<T>(
566        &mut self,
567        node: InFile<&SyntaxNode>,
568        mut cb: impl FnMut(
569            &mut Self,
570            /*parent: */ InFile<SyntaxNode>,
571            /*child: */ &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    /// XXX: this might be the same def as, for example an `EnumId`. However,
713    /// here the children are generic parameters, and not, eg enum variants.
714    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}