Skip to main content

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