Skip to main content

cairo_lang_defs/
db.rs

1use std::collections::VecDeque;
2use std::sync::Arc;
3
4use cairo_lang_diagnostics::{
5    DiagnosticNote, Maybe, MaybeAsRef, PluginFileDiagnosticNotes, ToMaybe, skip_diagnostic,
6};
7use cairo_lang_filesystem::db::{ExtAsVirtual, FilesGroup, files_group_input};
8use cairo_lang_filesystem::ids::{
9    BlobId, CrateId, CrateInput, Directory, FileId, FileKind, FileLongId, SmolStrId, Tracked,
10    VirtualFile,
11};
12use cairo_lang_parser::db::ParserGroup;
13use cairo_lang_syntax::attribute::consts::{
14    ALLOW_ATTR, ALLOW_ATTR_ATTR, DEPRECATED_ATTR, FEATURE_ATTR, FMT_SKIP_ATTR,
15    IMPLICIT_PRECEDENCE_ATTR, INLINE_ATTR, INTERNAL_ATTR, MUST_USE_ATTR, PATH_ATTR, PHANTOM_ATTR,
16    STARKNET_INTERFACE_ATTR, UNSTABLE_ATTR,
17};
18use cairo_lang_syntax::attribute::structured::AttributeStructurize;
19use cairo_lang_syntax::node::ast::MaybeModuleBody;
20use cairo_lang_syntax::node::helpers::QueryAttrs;
21use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
22use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast};
23use cairo_lang_utils::Intern;
24use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
25use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
26use itertools::{Itertools, chain};
27use salsa::{Database, Setter};
28
29use crate::cache::{
30    CacheBlobReader, DefCacheLoadingData, EXTERNAL_CACHE_SECTION, ExternalFileContentCached,
31    ExternalFileKey, load_cached_crate_modules,
32};
33use crate::ids::*;
34use crate::plugin::{DynGeneratedFileAuxData, MacroPlugin, MacroPluginMetadata, PluginDiagnostic};
35use crate::plugin_utils::try_extract_unnamed_arg;
36
37#[salsa::input]
38pub struct DefsGroupInput {
39    #[returns(ref)]
40    pub default_macro_plugins: Option<Vec<MacroPluginLongId>>,
41    #[returns(ref)]
42    pub macro_plugin_overrides: Option<OrderedHashMap<CrateInput, Arc<[MacroPluginLongId]>>>,
43    #[returns(ref)]
44    pub default_inline_macro_plugins: Option<OrderedHashMap<String, InlineMacroExprPluginLongId>>,
45    #[returns(ref)]
46    pub inline_macro_plugin_overrides: Option<
47        OrderedHashMap<CrateInput, Arc<OrderedHashMap<String, InlineMacroExprPluginLongId>>>,
48    >,
49}
50
51/// Returns a reference to the inputs of [DefsGroup].
52/// The reference is also used to set the inputs to new values.
53#[salsa::tracked(returns(ref))]
54pub fn defs_group_input(db: &dyn Database) -> DefsGroupInput {
55    DefsGroupInput::new(db, None, None, None, None)
56}
57
58/// Salsa database interface.
59/// See [`super::ids`] for further details.
60pub trait DefsGroup: Database {
61    fn default_macro_plugins_input(&self) -> &[MacroPluginLongId] {
62        defs_group_input(self.as_dyn_database()).default_macro_plugins(self).as_ref().unwrap()
63    }
64
65    fn macro_plugin_overrides_input(
66        &self,
67    ) -> &OrderedHashMap<CrateInput, Arc<[MacroPluginLongId]>> {
68        defs_group_input(self.as_dyn_database()).macro_plugin_overrides(self).as_ref().unwrap()
69    }
70
71    fn inline_macro_plugin_overrides_input(
72        &self,
73    ) -> &OrderedHashMap<CrateInput, Arc<OrderedHashMap<String, InlineMacroExprPluginLongId>>> {
74        defs_group_input(self.as_dyn_database())
75            .inline_macro_plugin_overrides(self)
76            .as_ref()
77            .unwrap()
78    }
79
80    fn default_inline_macro_plugins_input(
81        &self,
82    ) -> &OrderedHashMap<String, InlineMacroExprPluginLongId> {
83        defs_group_input(self.as_dyn_database())
84            .default_inline_macro_plugins(self)
85            .as_ref()
86            .unwrap()
87    }
88
89    // Plugins.
90    // ========
91
92    /// Interned version of `default_macro_plugins_input`.
93    fn default_macro_plugins<'db>(&'db self) -> &'db [MacroPluginId<'db>] {
94        default_macro_plugins(self.as_dyn_database())
95    }
96
97    /// Interned version of `macro_plugin_overrides_input`.
98    fn macro_plugin_overrides<'db>(
99        &'db self,
100    ) -> &'db OrderedHashMap<CrateId<'db>, Vec<MacroPluginId<'db>>> {
101        macro_plugin_overrides(self.as_dyn_database())
102    }
103
104    /// Returns [`MacroPluginId`]s of the plugins set for the crate with [`CrateId`].
105    /// Provides an override if it has been set with
106    /// [`DefsGroupEx::set_override_crate_macro_plugins`] or the default
107    /// ([`DefsGroup::default_macro_plugins`]) otherwise.
108    fn crate_macro_plugins<'db>(&'db self, crate_id: CrateId<'db>) -> &'db [MacroPluginId<'db>] {
109        crate_macro_plugins(self.as_dyn_database(), crate_id)
110    }
111
112    /// Interned version of `default_inline_macro_plugins_input`.
113    fn default_inline_macro_plugins<'db>(
114        &'db self,
115    ) -> &'db OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
116        default_inline_macro_plugins(self.as_dyn_database())
117    }
118
119    /// Interned version of `inline_macro_plugin_overrides_input`.
120    fn inline_macro_plugin_overrides<'db>(
121        &'db self,
122    ) -> &'db OrderedHashMap<CrateId<'db>, OrderedHashMap<String, InlineMacroExprPluginId<'db>>>
123    {
124        inline_macro_plugin_overrides(self.as_dyn_database())
125    }
126
127    /// Returns [`InlineMacroExprPluginId`]s of the plugins set for the crate with [`CrateId`].
128    /// Provides an override if it has been set with
129    /// [`DefsGroupEx::set_override_crate_inline_macro_plugins`] or the default
130    /// ([`DefsGroup::default_inline_macro_plugins`]) otherwise.
131    fn crate_inline_macro_plugins<'db>(
132        &'db self,
133        crate_id: CrateId<'db>,
134    ) -> &'db OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
135        crate_inline_macro_plugins(self.as_dyn_database(), crate_id)
136    }
137
138    /// Returns the set of attributes allowed anywhere.
139    /// An attribute on any item that is not in this set will be handled as an unknown attribute.
140    fn allowed_attributes<'db>(
141        &'db self,
142        crate_id: CrateId<'db>,
143    ) -> &'db OrderedHashSet<SmolStrId<'db>> {
144        allowed_attributes(self.as_dyn_database(), crate_id)
145    }
146
147    /// Returns the set of attributes allowed on statements.
148    /// An attribute on a statement that is not in this set will be handled as an unknown attribute.
149    fn allowed_statement_attributes<'db>(&'db self) -> &'db OrderedHashSet<SmolStrId<'db>> {
150        allowed_statement_attributes(self.as_dyn_database())
151    }
152
153    /// Returns the set of `derive` that were declared by a plugin.
154    /// A derive that is not in this set will be handled as an unknown derive.
155    fn declared_derives<'db>(
156        &'db self,
157        crate_id: CrateId<'db>,
158    ) -> &'db OrderedHashSet<SmolStrId<'db>> {
159        declared_derives(self.as_dyn_database(), crate_id)
160    }
161
162    /// Returns the set of attributes that were declared as phantom type attributes by a plugin,
163    /// i.e. a type marked with this attribute is considered a phantom type.
164    fn declared_phantom_type_attributes<'db>(
165        &'db self,
166        crate_id: CrateId<'db>,
167    ) -> &'db OrderedHashSet<SmolStrId<'db>> {
168        declared_phantom_type_attributes(self.as_dyn_database(), crate_id)
169    }
170
171    /// Checks whether the submodule is defined as inline.
172    fn is_submodule_inline<'db>(&self, submodule_id: SubmoduleId<'db>) -> bool {
173        is_submodule_inline(self.as_dyn_database(), submodule_id)
174    }
175
176    // Module to syntax.
177    /// Gets the main file of the module.
178    /// A module might have more virtual files generated by plugins.
179    fn module_main_file<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<FileId<'db>> {
180        module_main_file(self.as_dyn_database(), module_id)
181    }
182    /// Gets all the files of a module - main files and generated virtual files.
183    fn module_files<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [FileId<'db>]> {
184        module_files(self.as_dyn_database(), module_id)
185    }
186    /// Gets the directory of a module.
187    fn module_dir<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db Directory<'db>> {
188        module_dir(self.as_dyn_database(), module_id)
189    }
190
191    // File to module.
192    fn crate_modules<'db>(&'db self, crate_id: CrateId<'db>) -> &'db [ModuleId<'db>] {
193        crate_modules(self.as_dyn_database(), crate_id)
194    }
195    fn file_modules<'db>(&'db self, file_id: FileId<'db>) -> Maybe<&'db Vec<ModuleId<'db>>> {
196        file_modules(self.as_dyn_database(), file_id).maybe_as_ref()
197    }
198
199    /// Returns the [ModuleData] of all modules in the crate's cache, and the loading data of the
200    /// [DefsGroup] in the crate.
201    fn cached_crate_modules<'db>(
202        &'db self,
203        crate_id: CrateId<'db>,
204    ) -> Option<ModuleDataCacheAndLoadingData<'db>> {
205        cached_crate_modules(self.as_dyn_database(), crate_id)
206    }
207    fn module_submodules_ids<'db>(
208        &'db self,
209        module_id: ModuleId<'db>,
210    ) -> Maybe<&'db [SubmoduleId<'db>]> {
211        module_submodules_ids(self.as_dyn_database(), module_id)
212    }
213    fn module_constants_ids<'db>(
214        &'db self,
215        module_id: ModuleId<'db>,
216    ) -> Maybe<&'db [ConstantId<'db>]> {
217        module_constants_ids(self.as_dyn_database(), module_id)
218    }
219    fn module_constant_by_id<'db>(
220        &'db self,
221        constant_data: ConstantId<'db>,
222    ) -> Maybe<ast::ItemConstant<'db>> {
223        module_constant_by_id(self.as_dyn_database(), constant_data)
224    }
225    fn module_free_functions_ids<'db>(
226        &'db self,
227        module_id: ModuleId<'db>,
228    ) -> Maybe<&'db [FreeFunctionId<'db>]> {
229        module_free_functions_ids(self.as_dyn_database(), module_id)
230    }
231    fn module_free_function_by_id<'db>(
232        &'db self,
233        free_function_id: FreeFunctionId<'db>,
234    ) -> Maybe<ast::FunctionWithBody<'db>> {
235        module_free_function_by_id(self.as_dyn_database(), free_function_id)
236    }
237    /// Returns the stable ptr of the name of a module item.
238    fn module_item_name_stable_ptr<'db>(
239        &'db self,
240        module_id: ModuleId<'db>,
241        item_id: ModuleItemId<'db>,
242    ) -> Maybe<SyntaxStablePtrId<'db>> {
243        module_item_name_stable_ptr(self.as_dyn_database(), module_id, item_id)
244    }
245    fn module_uses_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [UseId<'db>]> {
246        module_uses_ids(self.as_dyn_database(), module_id)
247    }
248    fn module_use_by_id<'db>(&'db self, use_id: UseId<'db>) -> Maybe<ast::UsePathLeaf<'db>> {
249        module_use_by_id(self.as_dyn_database(), use_id)
250    }
251    fn module_global_use_by_id<'db>(
252        &'db self,
253        global_use_id: GlobalUseId<'db>,
254    ) -> Maybe<ast::UsePathStar<'db>> {
255        module_global_use_by_id(self.as_dyn_database(), global_use_id)
256    }
257    fn module_structs_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [StructId<'db>]> {
258        module_structs_ids(self.as_dyn_database(), module_id)
259    }
260    fn module_struct_by_id<'db>(
261        &'db self,
262        struct_id: StructId<'db>,
263    ) -> Maybe<ast::ItemStruct<'db>> {
264        module_struct_by_id(self.as_dyn_database(), struct_id)
265    }
266    fn module_enums_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [EnumId<'db>]> {
267        module_enums_ids(self.as_dyn_database(), module_id)
268    }
269    fn module_enum_by_id<'db>(&'db self, enum_id: EnumId<'db>) -> Maybe<ast::ItemEnum<'db>> {
270        module_enum_by_id(self.as_dyn_database(), enum_id)
271    }
272    fn module_type_aliases_ids<'db>(
273        &'db self,
274        module_id: ModuleId<'db>,
275    ) -> Maybe<&'db [ModuleTypeAliasId<'db>]> {
276        module_type_aliases_ids(self.as_dyn_database(), module_id)
277    }
278    fn module_type_alias_by_id<'db>(
279        &'db self,
280        module_type_alias_id: ModuleTypeAliasId<'db>,
281    ) -> Maybe<ast::ItemTypeAlias<'db>> {
282        module_type_alias_by_id(self.as_dyn_database(), module_type_alias_id)
283    }
284    fn module_impl_aliases_ids<'db>(
285        &'db self,
286        module_id: ModuleId<'db>,
287    ) -> Maybe<&'db [ImplAliasId<'db>]> {
288        module_impl_aliases_ids(self.as_dyn_database(), module_id)
289    }
290    fn module_impl_alias_by_id<'db>(
291        &'db self,
292        impl_alias_id: ImplAliasId<'db>,
293    ) -> Maybe<ast::ItemImplAlias<'db>> {
294        module_impl_alias_by_id(self.as_dyn_database(), impl_alias_id)
295    }
296    fn module_traits_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [TraitId<'db>]> {
297        module_traits_ids(self.as_dyn_database(), module_id)
298    }
299    fn module_trait_by_id<'db>(&'db self, trait_id: TraitId<'db>) -> Maybe<ast::ItemTrait<'db>> {
300        module_trait_by_id(self.as_dyn_database(), trait_id)
301    }
302    fn module_impls_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [ImplDefId<'db>]> {
303        module_impls_ids(self.as_dyn_database(), module_id)
304    }
305    fn module_impl_by_id<'db>(&'db self, impl_id: ImplDefId<'db>) -> Maybe<ast::ItemImpl<'db>> {
306        module_impl_by_id(self.as_dyn_database(), impl_id)
307    }
308    fn module_extern_types_ids<'db>(
309        &'db self,
310        module_id: ModuleId<'db>,
311    ) -> Maybe<&'db [ExternTypeId<'db>]> {
312        module_extern_types_ids(self.as_dyn_database(), module_id)
313    }
314    fn module_extern_type_by_id<'db>(
315        &'db self,
316        extern_type_id: ExternTypeId<'db>,
317    ) -> Maybe<ast::ItemExternType<'db>> {
318        module_extern_type_by_id(self.as_dyn_database(), extern_type_id)
319    }
320    fn module_extern_functions_ids<'db>(
321        &'db self,
322        module_id: ModuleId<'db>,
323    ) -> Maybe<&'db [ExternFunctionId<'db>]> {
324        module_extern_functions_ids(self.as_dyn_database(), module_id)
325    }
326    fn module_extern_function_by_id<'db>(
327        &'db self,
328        extern_function_id: ExternFunctionId<'db>,
329    ) -> Maybe<ast::ItemExternFunction<'db>> {
330        module_extern_function_by_id(self.as_dyn_database(), extern_function_id)
331    }
332
333    /// Returns the IDs of the macro declarations in the module.
334    fn module_macro_declarations_ids<'db>(
335        &'db self,
336        module_id: ModuleId<'db>,
337    ) -> Maybe<&'db [MacroDeclarationId<'db>]> {
338        module_macro_declarations_ids(self.as_dyn_database(), module_id)
339    }
340    /// Returns the macro declaration by its ID.
341    fn module_macro_declaration_by_id<'db>(
342        &'db self,
343        macro_declaration_id: MacroDeclarationId<'db>,
344    ) -> Maybe<ast::ItemMacroDeclaration<'db>> {
345        module_macro_declaration_by_id(self.as_dyn_database(), macro_declaration_id)
346    }
347
348    /// Returns the IDs of the macro calls in the module.
349    fn module_macro_calls_ids<'db>(
350        &'db self,
351        module_id: ModuleId<'db>,
352    ) -> Maybe<&'db [MacroCallId<'db>]> {
353        module_macro_calls_ids(self.as_dyn_database(), module_id)
354    }
355    /// Returns the macro call by its ID.
356    fn module_macro_call_by_id<'db>(
357        &'db self,
358        macro_call_id: MacroCallId<'db>,
359    ) -> Maybe<ast::ItemInlineMacro<'db>> {
360        module_macro_call_by_id(self.as_dyn_database(), macro_call_id)
361    }
362    /// Returns the ancestors of a module.
363    fn module_ancestors<'db>(
364        &'db self,
365        module_id: ModuleId<'db>,
366    ) -> &'db OrderedHashSet<ModuleId<'db>> {
367        module_ancestors_helper(self.as_dyn_database(), (), module_id)
368    }
369    /// Returns the module that the module is perceived as.
370    /// Specifically if this is a macro call, it returns the module that the macro call was called
371    /// from, including recursive calls.
372    fn module_perceived_module<'db>(&'db self, module_id: ModuleId<'db>) -> ModuleId<'db> {
373        module_perceived_module_helper(self.as_dyn_database(), (), module_id)
374    }
375}
376
377impl<T: Database + ?Sized> DefsGroup for T {}
378
379/// Initializes the [`DefsGroup`] database to a proper state.
380pub fn init_defs_group(db: &mut dyn Database) {
381    defs_group_input(db).set_macro_plugin_overrides(db).to(Some(OrderedHashMap::default()));
382    defs_group_input(db).set_inline_macro_plugin_overrides(db).to(Some(OrderedHashMap::default()));
383}
384
385#[salsa::tracked(returns(ref))]
386fn default_macro_plugins_helper<'db>(db: &'db dyn Database) -> Vec<MacroPluginId<'db>> {
387    db.default_macro_plugins_input()
388        .iter()
389        .map(|plugin| MacroPluginId::new(db, plugin.clone()))
390        .collect()
391}
392
393pub fn default_macro_plugins<'db>(db: &'db dyn Database) -> &'db [MacroPluginId<'db>] {
394    default_macro_plugins_helper(db)
395}
396
397#[salsa::tracked(returns(ref))]
398pub fn macro_plugin_overrides<'db>(
399    db: &'db dyn Database,
400) -> OrderedHashMap<CrateId<'db>, Vec<MacroPluginId<'db>>> {
401    let inp = db.macro_plugin_overrides_input();
402    inp.iter()
403        .map(|(crate_id, plugins)| {
404            (
405                crate_id.clone().into_crate_long_id(db).intern(db),
406                plugins.iter().map(|plugin| plugin.clone().intern(db)).collect(),
407            )
408        })
409        .collect()
410}
411
412#[salsa::tracked(returns(ref))]
413pub fn inline_macro_plugin_overrides<'db>(
414    db: &'db dyn Database,
415) -> OrderedHashMap<CrateId<'db>, OrderedHashMap<String, InlineMacroExprPluginId<'db>>> {
416    let inp = db.inline_macro_plugin_overrides_input();
417    inp.iter()
418        .map(|(crate_id, plugins)| {
419            (
420                crate_id.clone().into_crate_long_id(db).intern(db),
421                plugins
422                    .iter()
423                    .map(|(name, plugin)| (name.clone(), plugin.clone().intern(db)))
424                    .collect(),
425            )
426        })
427        .collect()
428}
429
430#[salsa::tracked(returns(ref))]
431pub fn default_inline_macro_plugins<'db>(
432    db: &'db dyn Database,
433) -> OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
434    let inp = db.default_inline_macro_plugins_input();
435    inp.iter().map(|(name, plugin)| (name.clone(), plugin.clone().intern(db))).collect()
436}
437
438fn crate_macro_plugins<'db>(
439    db: &'db dyn Database,
440    crate_id: CrateId<'db>,
441) -> &'db [MacroPluginId<'db>] {
442    macro_plugin_overrides(db).get(&crate_id).unwrap_or_else(|| default_macro_plugins_helper(db))
443}
444
445fn crate_inline_macro_plugins<'db>(
446    db: &'db dyn Database,
447    crate_id: CrateId<'db>,
448) -> &'db OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
449    db.inline_macro_plugin_overrides()
450        .get(&crate_id)
451        .unwrap_or_else(|| db.default_inline_macro_plugins())
452}
453
454#[salsa::tracked(returns(ref))]
455fn allowed_attributes<'db>(
456    db: &'db dyn Database,
457    crate_id: CrateId<'db>,
458) -> OrderedHashSet<SmolStrId<'db>> {
459    let base_attrs = [
460        INLINE_ATTR,
461        MUST_USE_ATTR,
462        UNSTABLE_ATTR,
463        DEPRECATED_ATTR,
464        INTERNAL_ATTR,
465        ALLOW_ATTR,
466        ALLOW_ATTR_ATTR,
467        FEATURE_ATTR,
468        PHANTOM_ATTR,
469        IMPLICIT_PRECEDENCE_ATTR,
470        FMT_SKIP_ATTR,
471        PATH_ATTR,
472        // TODO(orizi): Remove this once `starknet` is removed from corelib.
473        STARKNET_INTERFACE_ATTR,
474    ];
475
476    let crate_plugins = db.crate_macro_plugins(crate_id);
477
478    OrderedHashSet::from_iter(chain!(
479        base_attrs.map(|attr| SmolStrId::from(db, attr)),
480        crate_plugins.iter().flat_map(|plugin| plugin.long(db).declared_attributes(db))
481    ))
482}
483
484// TODO(eytan-starkware): Untrack this
485#[salsa::tracked(returns(ref))]
486fn allowed_statement_attributes<'db>(db: &'db dyn Database) -> OrderedHashSet<SmolStrId<'db>> {
487    let all_attributes = [FMT_SKIP_ATTR, ALLOW_ATTR, FEATURE_ATTR];
488    OrderedHashSet::from_iter(all_attributes.map(|attr| SmolStrId::from(db, attr)))
489}
490
491#[salsa::tracked(returns(ref))]
492fn declared_derives<'db>(
493    db: &'db dyn Database,
494    crate_id: CrateId<'db>,
495) -> OrderedHashSet<SmolStrId<'db>> {
496    OrderedHashSet::from_iter(
497        db.crate_macro_plugins(crate_id)
498            .iter()
499            .flat_map(|plugin| plugin.long(db).declared_derives(db)),
500    )
501}
502
503#[salsa::tracked(returns(ref))]
504fn declared_phantom_type_attributes<'db>(
505    db: &'db dyn Database,
506    crate_id: CrateId<'db>,
507) -> OrderedHashSet<SmolStrId<'db>> {
508    let crate_plugins = db.crate_macro_plugins(crate_id);
509
510    OrderedHashSet::from_iter(chain!(
511        [SmolStrId::from(db, PHANTOM_ATTR)],
512        crate_plugins.iter().flat_map(|plugin| plugin.long(db).phantom_type_attributes(db))
513    ))
514}
515
516#[salsa::tracked]
517fn is_submodule_inline<'db>(db: &'db dyn Database, submodule_id: SubmoduleId<'db>) -> bool {
518    match submodule_id.stable_ptr(db).lookup(db).body(db) {
519        MaybeModuleBody::Some(_) => true,
520        MaybeModuleBody::None(_) => false,
521    }
522}
523
524#[salsa::tracked]
525fn module_main_file_helper<'db>(
526    db: &'db dyn Database,
527    _tracked: Tracked,
528    module_id: ModuleId<'db>,
529) -> Maybe<FileId<'db>> {
530    Ok(match module_id {
531        ModuleId::CrateRoot(crate_id) => {
532            db.crate_config(crate_id).to_maybe()?.root.file(db, "lib.cairo")
533        }
534        ModuleId::Submodule(submodule_id) => {
535            let parent = submodule_id.parent_module(db);
536            if db.is_submodule_inline(submodule_id) {
537                // This is an inline module, we return the file where the inline module was
538                // defined. It can be either the file of the parent module
539                // or a plugin-generated virtual file.
540                submodule_id.stable_ptr(db).untyped().file_id(db)
541            } else if let Some(path_override) = submodule_path_override(db, submodule_id) {
542                path_override
543            } else {
544                db.module_dir(parent)?
545                    .file(db, &format!("{}.cairo", submodule_id.name(db).long(db)))
546            }
547        }
548        // This is a macro-generated module, so the main file is the generated file.
549        ModuleId::MacroCall { generated_file_id, .. } => generated_file_id,
550    })
551}
552
553fn module_main_file<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> Maybe<FileId<'db>> {
554    module_main_file_helper(db, (), module_id)
555}
556
557fn module_files<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> Maybe<&'db [FileId<'db>]> {
558    Ok(module_id.module_data(db)?.files(db))
559}
560
561// TODO(eytan-starkware): This doesn't really need to be tracked.
562#[salsa::tracked(returns(ref))]
563fn module_dir_helper<'db>(
564    db: &'db dyn Database,
565    _tracked: Tracked,
566    module_id: ModuleId<'db>,
567) -> Maybe<Directory<'db>> {
568    match module_id {
569        ModuleId::CrateRoot(crate_id) => {
570            db.crate_config(crate_id).to_maybe().map(|config| config.root.clone())
571        }
572        ModuleId::Submodule(submodule_id) => {
573            let parent = submodule_id.parent_module(db);
574            let name = submodule_id.name(db);
575            Ok(db.module_dir(parent)?.subdir(name.long(db)))
576        }
577        // This is a macro call, we return the directory for the file that contained the macro
578        // call, as it is considered the location of the macro itself.
579        ModuleId::MacroCall { id, .. } => db.module_dir(id.parent_module(db)).cloned(),
580    }
581}
582
583/// Extracts an override path from a `#[path("...")]` attribute on the submodule declaration, if
584/// any. The returned string can be a relative path (resolved against the parent module directory)
585/// or an absolute path.
586fn submodule_path_override<'db>(
587    db: &'db dyn Database,
588    submodule_id: SubmoduleId<'db>,
589) -> Option<FileId<'db>> {
590    // Get the parent module's AST for this submodule.
591    let parent = submodule_id.parent_module(db);
592    let parent_data = parent.module_data(db).ok()?;
593    let item_module_ast = parent_data.submodules(db).get(&submodule_id)?.clone();
594
595    let attr_ast = item_module_ast.find_attr(db, PATH_ATTR)?;
596    let terminal = extract_path_arg(db, &attr_ast.arguments(db))?;
597    let path = terminal.string_value(db)?;
598    /// Find the module where the parents of this module is defined.
599    fn find_base<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> ModuleId<'db> {
600        match module_id {
601            ModuleId::CrateRoot(crate_id) => ModuleId::CrateRoot(crate_id),
602            ModuleId::Submodule(submodule_id) => submodule_id.parent_module(db),
603            ModuleId::MacroCall { id, .. } => find_base(db, id.parent_module(db)),
604        }
605    }
606    let base = find_base(db, parent);
607    Some(db.module_dir(base).ok()?.file(db, &path))
608}
609
610fn module_dir<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> Maybe<&'db Directory<'db>> {
611    module_dir_helper(db, (), module_id).maybe_as_ref()
612}
613
614/// Appends all the modules under the given module, including nested modules.
615fn collect_modules_under<'db>(
616    db: &'db dyn Database,
617    modules: &mut Vec<ModuleId<'db>>,
618    module_id: ModuleId<'db>,
619) {
620    modules.push(module_id);
621    if let Ok(submodule_ids) = db.module_submodules_ids(module_id) {
622        for submodule_module_id in submodule_ids.iter().copied() {
623            collect_modules_under(db, modules, ModuleId::Submodule(submodule_module_id));
624        }
625    }
626}
627
628/// Returns all the modules in the crate, including recursively.
629#[salsa::tracked(returns(ref))]
630fn crate_modules<'db>(db: &'db dyn Database, crate_id: CrateId<'db>) -> Vec<ModuleId<'db>> {
631    let mut modules = Vec::new();
632    collect_modules_under(db, &mut modules, ModuleId::CrateRoot(crate_id));
633    modules
634}
635
636/// Returns a mapping from file IDs to the modules that contain them.
637#[salsa::tracked(returns(ref))]
638fn file_to_module_mapping<'db>(
639    db: &'db dyn Database,
640) -> OrderedHashMap<FileId<'db>, Vec<ModuleId<'db>>> {
641    let mut mapping = OrderedHashMap::<FileId<'db>, Vec<ModuleId<'db>>>::default();
642    for crate_id in db.crates() {
643        for module_id in db.crate_modules(*crate_id).iter().copied() {
644            if let Ok(files) = db.module_files(module_id) {
645                for file_id in files.iter().copied() {
646                    match mapping.get_mut(&file_id) {
647                        Some(file_modules) => {
648                            file_modules.push(module_id);
649                        }
650                        None => {
651                            mapping.insert(file_id, vec![module_id]);
652                        }
653                    }
654                }
655            }
656        }
657    }
658    mapping
659}
660
661#[salsa::tracked(returns(ref))]
662fn file_modules<'db>(db: &'db dyn Database, file_id: FileId<'db>) -> Maybe<Vec<ModuleId<'db>>> {
663    file_to_module_mapping(db).get(&file_id).cloned().to_maybe()
664}
665
666#[salsa::tracked]
667pub struct ModuleData<'db> {
668    /// The list of IDs of all items in the module. Each ID here is guaranteed to be a key in one
669    /// of the specific-item-kind maps.
670    #[returns(ref)]
671    pub items: Vec<ModuleItemId<'db>>,
672
673    // Splitting into parts to avoid overly large tuples in salsa, which fails to compile.
674    /// Data for type items.
675    #[returns(ref)]
676    types_data: ModuleTypesData<'db>,
677    /// Data for non-type named items.
678    #[returns(ref)]
679    named_items_data: ModuleNamedItemsData<'db>,
680    /// Data for unnamed items.
681    #[returns(ref)]
682    unnamed_items_data: ModuleUnnamedItemsData<'db>,
683    /// Data for files.
684    #[returns(ref)]
685    files_data: ModuleFilesData<'db>,
686}
687
688#[salsa::tracked]
689pub struct ModuleTypesData<'db> {
690    /// All the structs of the given module.
691    #[returns(ref)]
692    structs: OrderedHashMap<StructId<'db>, ast::ItemStruct<'db>>,
693    /// All the enums of the given module.
694    #[returns(ref)]
695    enums: OrderedHashMap<EnumId<'db>, ast::ItemEnum<'db>>,
696    /// All the type aliases of the given module.
697    #[returns(ref)]
698    type_aliases: OrderedHashMap<ModuleTypeAliasId<'db>, ast::ItemTypeAlias<'db>>,
699    /// All the extern types of the given module.
700    #[returns(ref)]
701    extern_types: OrderedHashMap<ExternTypeId<'db>, ast::ItemExternType<'db>>,
702}
703
704#[salsa::tracked]
705pub struct ModuleNamedItemsData<'db> {
706    /// All the constants of the given module.
707    #[returns(ref)]
708    constants: OrderedHashMap<ConstantId<'db>, ast::ItemConstant<'db>>,
709    /// All the *direct* submodules of the given module - including those generated by
710    /// macro plugins. To get all the submodules including nested modules, use
711    /// [`collect_modules_under`].
712    #[returns(ref)]
713    submodules: OrderedHashMap<SubmoduleId<'db>, ast::ItemModule<'db>>,
714    /// All the uses of the given module.
715    #[returns(ref)]
716    uses: OrderedHashMap<UseId<'db>, ast::UsePathLeaf<'db>>,
717    #[returns(ref)]
718    free_functions: OrderedHashMap<FreeFunctionId<'db>, ast::FunctionWithBody<'db>>,
719    /// All the impl aliases of the given module.
720    #[returns(ref)]
721    impl_aliases: OrderedHashMap<ImplAliasId<'db>, ast::ItemImplAlias<'db>>,
722    /// All the traits of the given module.
723    #[returns(ref)]
724    traits: OrderedHashMap<TraitId<'db>, ast::ItemTrait<'db>>,
725    /// All the impls of the given module.
726    #[returns(ref)]
727    impls: OrderedHashMap<ImplDefId<'db>, ast::ItemImpl<'db>>,
728    /// All the extern functions of the given module.
729    #[returns(ref)]
730    extern_functions: OrderedHashMap<ExternFunctionId<'db>, ast::ItemExternFunction<'db>>,
731    /// All the macro declarations of the given module.
732    #[returns(ref)]
733    macro_declarations: OrderedHashMap<MacroDeclarationId<'db>, ast::ItemMacroDeclaration<'db>>,
734}
735#[salsa::tracked]
736pub struct ModuleUnnamedItemsData<'db> {
737    /// All the global uses of the given module.
738    #[returns(ref)]
739    global_uses: OrderedHashMap<GlobalUseId<'db>, ast::UsePathStar<'db>>,
740    /// Calls to inline macros in the module (only those that were not handled by plugins).
741    #[returns(ref)]
742    macro_calls: OrderedHashMap<MacroCallId<'db>, ast::ItemInlineMacro<'db>>,
743}
744
745#[salsa::tracked]
746pub struct ModuleFilesData<'db> {
747    #[returns(ref)]
748    files: Vec<FileId<'db>>,
749    /// Generation info for each file. Virtual files have Some. Other files have None.
750    #[returns(ref)]
751    generated_file_aux_data: OrderedHashMap<FileId<'db>, Option<DynGeneratedFileAuxData>>,
752    #[returns(ref)]
753    plugin_diagnostics: Vec<(ModuleId<'db>, PluginDiagnostic<'db>)>,
754    /// Diagnostic notes for diagnostics originating in the plugin generated files identified by
755    /// [`FileId`].
756    /// Diagnostic notes are added with `note: ` prefix at the end of diagnostic display.
757    #[returns(ref)]
758    diagnostics_notes: PluginFileDiagnosticNotes<'db>,
759}
760
761impl<'db> ModuleData<'db> {
762    /// All the constants of the given module.
763    pub fn constants(
764        &self,
765        db: &'db dyn Database,
766    ) -> &'db OrderedHashMap<ConstantId<'db>, ast::ItemConstant<'db>> {
767        self.named_items_data(db).constants(db)
768    }
769    /// All the submodules of the given module.
770    pub fn submodules(
771        &self,
772        db: &'db dyn Database,
773    ) -> &'db OrderedHashMap<SubmoduleId<'db>, ast::ItemModule<'db>> {
774        self.named_items_data(db).submodules(db)
775    }
776    /// All the uses of the given module.
777    pub fn uses(
778        &self,
779        db: &'db dyn Database,
780    ) -> &'db OrderedHashMap<UseId<'db>, ast::UsePathLeaf<'db>> {
781        self.named_items_data(db).uses(db)
782    }
783    /// All the free functions of the given module.
784    pub fn free_functions(
785        &self,
786        db: &'db dyn Database,
787    ) -> &'db OrderedHashMap<FreeFunctionId<'db>, ast::FunctionWithBody<'db>> {
788        self.named_items_data(db).free_functions(db)
789    }
790    /// All the structs of the given module.
791    pub fn structs(
792        &self,
793        db: &'db dyn Database,
794    ) -> &'db OrderedHashMap<StructId<'db>, ast::ItemStruct<'db>> {
795        self.types_data(db).structs(db)
796    }
797
798    /// All the enums of the given module.
799    pub fn enums(
800        &self,
801        db: &'db dyn Database,
802    ) -> &'db OrderedHashMap<EnumId<'db>, ast::ItemEnum<'db>> {
803        self.types_data(db).enums(db)
804    }
805
806    /// All the type aliases of the given module.
807    pub fn type_aliases(
808        &self,
809        db: &'db dyn Database,
810    ) -> &'db OrderedHashMap<ModuleTypeAliasId<'db>, ast::ItemTypeAlias<'db>> {
811        self.types_data(db).type_aliases(db)
812    }
813
814    /// All the impl aliases of the given module.
815    pub fn impl_aliases(
816        &self,
817        db: &'db dyn Database,
818    ) -> &'db OrderedHashMap<ImplAliasId<'db>, ast::ItemImplAlias<'db>> {
819        self.named_items_data(db).impl_aliases(db)
820    }
821    /// All the traits of the given module.
822    pub fn traits(
823        &self,
824        db: &'db dyn Database,
825    ) -> &'db OrderedHashMap<TraitId<'db>, ast::ItemTrait<'db>> {
826        self.named_items_data(db).traits(db)
827    }
828    /// All the impls of the given module.
829    pub fn impls(
830        &self,
831        db: &'db dyn Database,
832    ) -> &'db OrderedHashMap<ImplDefId<'db>, ast::ItemImpl<'db>> {
833        self.named_items_data(db).impls(db)
834    }
835    /// All the extern types of the given module.
836    pub fn extern_types(
837        &self,
838        db: &'db dyn Database,
839    ) -> &'db OrderedHashMap<ExternTypeId<'db>, ast::ItemExternType<'db>> {
840        self.types_data(db).extern_types(db)
841    }
842    /// All the extern functions of the given module.
843    pub fn extern_functions(
844        &self,
845        db: &'db dyn Database,
846    ) -> &'db OrderedHashMap<ExternFunctionId<'db>, ast::ItemExternFunction<'db>> {
847        self.named_items_data(db).extern_functions(db)
848    }
849    /// All the macro declarations of the given module.
850    pub fn macro_declarations(
851        &self,
852        db: &'db dyn Database,
853    ) -> &'db OrderedHashMap<MacroDeclarationId<'db>, ast::ItemMacroDeclaration<'db>> {
854        self.named_items_data(db).macro_declarations(db)
855    }
856    /// All the global uses of the given module.
857    pub fn global_uses(
858        &self,
859        db: &'db dyn Database,
860    ) -> &'db OrderedHashMap<GlobalUseId<'db>, ast::UsePathStar<'db>> {
861        self.unnamed_items_data(db).global_uses(db)
862    }
863    /// Calls to inline macros in the module (only those that were not handled by plugins).
864    pub fn macro_calls(
865        &self,
866        db: &'db dyn Database,
867    ) -> &'db OrderedHashMap<MacroCallId<'db>, ast::ItemInlineMacro<'db>> {
868        self.unnamed_items_data(db).macro_calls(db)
869    }
870
871    /// All the files of the given module.
872    pub fn files(&self, db: &'db dyn Database) -> &'db Vec<FileId<'db>> {
873        self.files_data(db).files(db)
874    }
875
876    /// Generation info for each file. Virtual files have Some. Other files have None.
877    pub fn generated_file_aux_data(
878        &self,
879        db: &'db dyn Database,
880    ) -> &'db OrderedHashMap<FileId<'db>, Option<DynGeneratedFileAuxData>> {
881        self.files_data(db).generated_file_aux_data(db)
882    }
883
884    pub fn plugin_diagnostics(
885        &self,
886        db: &'db dyn Database,
887    ) -> &'db Vec<(ModuleId<'db>, PluginDiagnostic<'db>)> {
888        self.files_data(db).plugin_diagnostics(db)
889    }
890
891    /// Diagnostic notes for diagnostics originating in the plugin generated files identified by
892    /// [`FileId`].
893    /// Diagnostic notes are added with `note: ` prefix at the end of diagnostic display.
894    pub fn diagnostics_notes(&self, db: &'db dyn Database) -> &'db PluginFileDiagnosticNotes<'db> {
895        self.files_data(db).diagnostics_notes(db)
896    }
897}
898
899/// Information about generated files from running on a module file.
900#[derive(Clone, Debug, Eq, PartialEq, salsa::Update)]
901pub struct PrivModuleSubFiles<'db> {
902    /// The files generated by plugins running on items.
903    files: OrderedHashMap<FileId<'db>, VirtualFile<'db>>,
904    /// The aux data per such file.
905    aux_data: OrderedHashMap<FileId<'db>, Option<DynGeneratedFileAuxData>>,
906    /// The items not filtered out by plugins.
907    items: Vec<ast::ModuleItem<'db>>,
908    /// The diagnostics generated by the plugins.
909    plugin_diagnostics: Vec<PluginDiagnostic<'db>>,
910    /// Diagnostic notes for diagnostics originating in the plugin generated files identified by
911    /// [`FileId`].
912    /// Diagnostic notes are added with `note: ` prefix at the end of diagnostic display.
913    diagnostics_notes: PluginFileDiagnosticNotes<'db>,
914}
915
916#[salsa::tracked]
917fn priv_module_data_helper<'db>(
918    db: &'db dyn Database,
919    _tracked: Tracked,
920    module_id: ModuleId<'db>,
921) -> Maybe<ModuleData<'db>> {
922    let crate_id = module_id.owning_crate(db);
923
924    if let Some((map, _)) = db.cached_crate_modules(crate_id) {
925        if let Some(module_data) = map.get(&module_id) {
926            return Ok(*module_data);
927        } else {
928            panic!("module not found in cached modules_data {:?}", module_id.name(db));
929        }
930    };
931
932    let module_file = db.module_main_file(module_id)?;
933    let main_file_aux_data = if let ModuleId::Submodule(submodule_id) = module_id {
934        let parent_module_data = submodule_id.module_data(db)?;
935        let item_module_ast = &parent_module_data.submodules(db)[&submodule_id];
936        if matches!(item_module_ast.body(db), MaybeModuleBody::Some(_)) {
937            // TODO(spapini): Diagnostics in this module that get mapped to parent module
938            // should lie in that module's ModuleData, or be collected by its
939            // diagnostics collector function.
940
941            // If this is an inline module, copy its generation file info from the parent
942            // module, from the file where this submodule was defined.
943            parent_module_data.generated_file_aux_data(db)
944                [&item_module_ast.stable_ptr(db).untyped().file_id(db)]
945                .clone()
946        } else {
947            None
948        }
949    } else {
950        None
951    };
952    let mut file_queue = VecDeque::new();
953    file_queue.push_back(module_file);
954    let mut constants = OrderedHashMap::default();
955    let mut submodules = OrderedHashMap::default();
956    let mut uses = OrderedHashMap::default();
957    let mut free_functions = OrderedHashMap::default();
958    let mut structs = OrderedHashMap::default();
959    let mut enums = OrderedHashMap::default();
960    let mut type_aliases = OrderedHashMap::default();
961    let mut impl_aliases = OrderedHashMap::default();
962    let mut traits = OrderedHashMap::default();
963    let mut impls = OrderedHashMap::default();
964    let mut extern_types = OrderedHashMap::default();
965    let mut extern_functions = OrderedHashMap::default();
966    let mut macro_declarations = OrderedHashMap::default();
967    let mut macro_calls = OrderedHashMap::default();
968    let mut global_uses = OrderedHashMap::default();
969    let mut aux_data = OrderedHashMap::default();
970    let mut files = Vec::new();
971    let mut plugin_diagnostics = Vec::new();
972    let mut diagnostics_notes = OrderedHashMap::default();
973
974    let mut items = vec![];
975    aux_data.insert(module_file, main_file_aux_data);
976    while let Some(file_id) = file_queue.pop_front() {
977        files.push(file_id);
978
979        let priv_module_data = module_sub_files(db, module_id, file_id).maybe_as_ref()?;
980        diagnostics_notes
981            .extend(priv_module_data.diagnostics_notes.iter().map(|(k, v)| (*k, v.clone())));
982        file_queue.extend(priv_module_data.files.keys().copied());
983        plugin_diagnostics
984            .extend(priv_module_data.plugin_diagnostics.iter().map(|v| (module_id, v.clone())));
985        aux_data.extend(priv_module_data.aux_data.iter().map(|(k, v)| (*k, v.clone())));
986        for item_ast in &priv_module_data.items {
987            match item_ast.clone() {
988                ast::ModuleItem::Constant(constant) => {
989                    let item_id = ConstantLongId(module_id, constant.stable_ptr(db)).intern(db);
990                    constants.insert(item_id, constant);
991                    items.push(ModuleItemId::Constant(item_id));
992                }
993                ast::ModuleItem::Module(module) => {
994                    let item_id = SubmoduleLongId(module_id, module.stable_ptr(db)).intern(db);
995                    submodules.insert(item_id, module);
996                    items.push(ModuleItemId::Submodule(item_id));
997                }
998                ast::ModuleItem::Use(us) => {
999                    for leaf in get_all_path_leaves(db, &us) {
1000                        let id = UseLongId(module_id, leaf.stable_ptr(db)).intern(db);
1001                        uses.insert(id, leaf);
1002                        items.push(ModuleItemId::Use(id));
1003                    }
1004                    for star in get_all_path_stars(db, &us) {
1005                        let id = GlobalUseLongId(module_id, star.stable_ptr(db)).intern(db);
1006                        global_uses.insert(id, star);
1007                    }
1008                }
1009                ast::ModuleItem::FreeFunction(function) => {
1010                    let item_id = FreeFunctionLongId(module_id, function.stable_ptr(db)).intern(db);
1011                    free_functions.insert(item_id, function);
1012                    items.push(ModuleItemId::FreeFunction(item_id));
1013                }
1014                ast::ModuleItem::ExternFunction(extern_function) => {
1015                    let item_id =
1016                        ExternFunctionLongId(module_id, extern_function.stable_ptr(db)).intern(db);
1017                    extern_functions.insert(item_id, extern_function);
1018                    items.push(ModuleItemId::ExternFunction(item_id));
1019                }
1020                ast::ModuleItem::ExternType(extern_type) => {
1021                    let item_id =
1022                        ExternTypeLongId(module_id, extern_type.stable_ptr(db)).intern(db);
1023                    extern_types.insert(item_id, extern_type);
1024                    items.push(ModuleItemId::ExternType(item_id));
1025                }
1026                ast::ModuleItem::Trait(trt) => {
1027                    let item_id = TraitLongId(module_id, trt.stable_ptr(db)).intern(db);
1028                    traits.insert(item_id, trt);
1029                    items.push(ModuleItemId::Trait(item_id));
1030                }
1031                ast::ModuleItem::Impl(imp) => {
1032                    let item_id = ImplDefLongId(module_id, imp.stable_ptr(db)).intern(db);
1033                    impls.insert(item_id, imp);
1034                    items.push(ModuleItemId::Impl(item_id));
1035                }
1036                ast::ModuleItem::Struct(structure) => {
1037                    let item_id = StructLongId(module_id, structure.stable_ptr(db)).intern(db);
1038                    structs.insert(item_id, structure);
1039                    items.push(ModuleItemId::Struct(item_id));
1040                }
1041                ast::ModuleItem::Enum(enm) => {
1042                    let item_id = EnumLongId(module_id, enm.stable_ptr(db)).intern(db);
1043                    enums.insert(item_id, enm);
1044                    items.push(ModuleItemId::Enum(item_id));
1045                }
1046                ast::ModuleItem::TypeAlias(type_alias) => {
1047                    let item_id =
1048                        ModuleTypeAliasLongId(module_id, type_alias.stable_ptr(db)).intern(db);
1049                    type_aliases.insert(item_id, type_alias);
1050                    items.push(ModuleItemId::TypeAlias(item_id));
1051                }
1052                ast::ModuleItem::ImplAlias(impl_alias) => {
1053                    let item_id = ImplAliasLongId(module_id, impl_alias.stable_ptr(db)).intern(db);
1054                    impl_aliases.insert(item_id, impl_alias);
1055                    items.push(ModuleItemId::ImplAlias(item_id));
1056                }
1057                ast::ModuleItem::MacroDeclaration(macro_declaration) => {
1058                    let item_id =
1059                        MacroDeclarationLongId(module_id, macro_declaration.stable_ptr(db))
1060                            .intern(db);
1061                    macro_declarations.insert(item_id, macro_declaration);
1062                    items.push(ModuleItemId::MacroDeclaration(item_id));
1063                }
1064                ast::ModuleItem::InlineMacro(inline_macro_ast) => {
1065                    let item_id =
1066                        MacroCallLongId(module_id, inline_macro_ast.stable_ptr(db)).intern(db);
1067                    macro_calls.insert(item_id, inline_macro_ast.clone());
1068                }
1069                ast::ModuleItem::HeaderDoc(_) => {}
1070                ast::ModuleItem::Missing(_) => {}
1071            }
1072        }
1073    }
1074    let types_data = ModuleTypesData::new(db, structs, enums, type_aliases, extern_types);
1075    let named_items_data = ModuleNamedItemsData::new(
1076        db,
1077        constants,
1078        submodules,
1079        uses,
1080        free_functions,
1081        impl_aliases,
1082        traits,
1083        impls,
1084        extern_functions,
1085        macro_declarations,
1086    );
1087    let unnamed_items_data = ModuleUnnamedItemsData::new(db, global_uses, macro_calls);
1088    let files_data =
1089        ModuleFilesData::new(db, files, aux_data, plugin_diagnostics, diagnostics_notes);
1090    Ok(ModuleData::new(db, items, types_data, named_items_data, unnamed_items_data, files_data))
1091}
1092
1093pub(crate) fn module_data<'db>(
1094    db: &'db dyn Database,
1095    module_id: ModuleId<'db>,
1096) -> Maybe<ModuleData<'db>> {
1097    priv_module_data_helper(db, (), module_id)
1098}
1099
1100pub type ModuleDataCacheAndLoadingData<'db> =
1101    (Arc<OrderedHashMap<ModuleId<'db>, ModuleData<'db>>>, Arc<DefCacheLoadingData<'db>>);
1102
1103#[salsa::tracked]
1104fn cached_crate_modules<'db>(
1105    db: &'db dyn Database,
1106    crate_id: CrateId<'db>,
1107) -> Option<ModuleDataCacheAndLoadingData<'db>> {
1108    load_cached_crate_modules(db, crate_id)
1109}
1110
1111pub fn init_external_files<T: DefsGroup>(db: &mut T) {
1112    let ext_as_virtual_impl: ExtAsVirtual =
1113        Arc::new(|db: &dyn Database, external_id: salsa::Id| ext_as_virtual_impl(db, external_id));
1114    files_group_input(db).set_ext_as_virtual_obj(db).to(Some(ext_as_virtual_impl));
1115}
1116
1117/// The external-file content table from a crate's cache blob, keyed only on the blob — so reading
1118/// it from `ext_as_virtual` adds no module-data edge, keeping cache loading cycle-free.
1119#[salsa::tracked(returns(ref))]
1120fn external_file_contents<'db>(
1121    db: &'db dyn Database,
1122    blob_id: BlobId<'db>,
1123    crate_id: CrateId<'db>,
1124) -> OrderedHashMap<ExternalFileKey, ExternalFileContentCached> {
1125    let Some(content) = db.blob_content(blob_id) else {
1126        return Default::default();
1127    };
1128
1129    CacheBlobReader::read_section(db, content, EXTERNAL_CACHE_SECTION, crate_id)
1130}
1131
1132/// Rebuilds an external file's `VirtualFile` from the cache blob. `None` if the crate has no cache
1133/// blob or the blob lacks this file — `ext_as_virtual_impl` then falls back to `module_sub_files`.
1134#[salsa::tracked(returns(ref))]
1135fn cached_external_virtual_file<'db>(
1136    db: &'db dyn Database,
1137    external_file: FileId<'db>,
1138) -> Option<VirtualFile<'db>> {
1139    let FileLongId::External(raw_external_id) = external_file.long(db) else {
1140        return None;
1141    };
1142    let long_id = PluginGeneratedFileId::from_intern_id(*raw_external_id).long(db);
1143    let crate_id = long_id.module_id.owning_crate(db);
1144    let blob_id = db.crate_config(crate_id)?.cache_file?;
1145    // Parse/plugin/mint-free, so it cannot re-enter the cache. See `ExternalFileKey::new`.
1146    let key = ExternalFileKey::new(db, external_file);
1147    let content = external_file_contents(db, blob_id, crate_id).get(&key)?;
1148    Some(content.to_virtual_file(db, &long_id.name, Some(long_id.stable_ptr.span_in_file(db))))
1149}
1150
1151/// Returns the `VirtualFile` matching the given external id.
1152pub fn ext_as_virtual_impl<'db>(
1153    db: &'db dyn Database,
1154    external_id: salsa::Id,
1155) -> &'db VirtualFile<'db> {
1156    let file_id = FileLongId::External(external_id).intern(db);
1157    // Serve from the cache blob first (no module-data edge); see `external_file_contents`.
1158    if let Some(virtual_file) = cached_external_virtual_file(db, file_id) {
1159        return virtual_file;
1160    }
1161    let long_id = PluginGeneratedFileId::from_intern_id(external_id).long(db);
1162    // A loaded blob collected every external file at save time, so a miss means a stale/corrupt
1163    // cache; falling back to `module_sub_files` would re-enter the in-flight load (cycle), so fail
1164    // loud. An absent/unreadable blob means the crate isn't cache-backed — the fallback is safe.
1165    if let Some(blob_id) =
1166        db.crate_config(long_id.module_id.owning_crate(db)).and_then(|config| config.cache_file)
1167    {
1168        assert!(
1169            db.blob_content(blob_id).is_none(),
1170            "external file missing from a loaded crate cache: {:?}",
1171            file_id.long(db)
1172        );
1173    }
1174    let data =
1175        module_sub_files(db, long_id.module_id, long_id.stable_ptr.file_id(db)).as_ref().unwrap();
1176    &data.files[&file_id]
1177}
1178
1179#[salsa::tracked(returns(ref))]
1180/// Returns the information about sub-files generated by the file in the module.
1181fn module_sub_files<'db>(
1182    db: &'db dyn Database,
1183    module_id: ModuleId<'db>,
1184    file_id: FileId<'db>,
1185) -> Maybe<PrivModuleSubFiles<'db>> {
1186    let module_main_file = db.module_main_file(module_id)?;
1187    let file_syntax = db.file_module_syntax(file_id)?;
1188    let item_asts = if module_main_file == file_id {
1189        if let ModuleId::Submodule(submodule_id) = module_id {
1190            let data = submodule_id.module_data(db)?;
1191            if let MaybeModuleBody::Some(body) = data.submodules(db)[&submodule_id].body(db) {
1192                Some(body.items(db))
1193            } else {
1194                None
1195            }
1196        } else {
1197            None
1198        }
1199    } else {
1200        None
1201    }
1202    .unwrap_or_else(|| file_syntax.items(db));
1203
1204    let crate_id = module_id.owning_crate(db);
1205
1206    let allowed_attributes = db.allowed_attributes(crate_id);
1207    let declared_derives = db.declared_derives(crate_id);
1208    // The features allowed at the module scope; each item additionally grants its own
1209    // `#[feature]`s.
1210    let module_features = module_allowed_features(db, (), module_id);
1211
1212    let cfg_set = db
1213        .crate_config(crate_id)
1214        .and_then(|cfg| cfg.settings.cfg_set.as_ref())
1215        .unwrap_or(db.cfg_set());
1216    let edition = db
1217        .crate_config(module_id.owning_crate(db))
1218        .map(|cfg| cfg.settings.edition)
1219        .unwrap_or_default();
1220
1221    let mut files = OrderedHashMap::<_, _>::default();
1222    let mut aux_data = OrderedHashMap::default();
1223    let mut items = Vec::new();
1224    let mut plugin_diagnostics = Vec::new();
1225    let mut diagnostics_notes = OrderedHashMap::default();
1226    for item_ast in item_asts.elements(db) {
1227        let mut remove_original_item = false;
1228        // The features in scope for this item: the module's, plus the item's own `#[feature]`s.
1229        // Reuse the module set directly when the item adds nothing, to avoid a per-item allocation.
1230        let mut item_allowed_features = extract_allowed_features(db, &item_ast);
1231        let allowed_features = if item_allowed_features.is_empty() {
1232            module_features
1233        } else {
1234            item_allowed_features.extend(module_features.iter().copied());
1235            &item_allowed_features
1236        };
1237        let metadata = MacroPluginMetadata { cfg_set, declared_derives, allowed_features, edition };
1238        // Iterate through the plugins by their order. The first one to change something (either
1239        // generate new code, remove the original code, or both), breaks the loop. If more
1240        // plugins might act on the item, they can do it on the generated code.
1241        for plugin_id in db.crate_macro_plugins(crate_id).iter() {
1242            let plugin = plugin_id.long(db);
1243
1244            let result = plugin.generate_code(db, item_ast.clone(), &metadata);
1245            plugin_diagnostics.extend(result.diagnostics);
1246            if result.remove_original_item {
1247                remove_original_item = true;
1248            }
1249
1250            if let Some(generated) = result.code {
1251                let stable_ptr = item_ast.stable_ptr(db).untyped();
1252                let generated_file_id = FileLongId::External(
1253                    PluginGeneratedFileLongId {
1254                        module_id,
1255                        stable_ptr,
1256                        name: generated.name.clone(),
1257                    }
1258                    .intern(db)
1259                    .as_intern_id(),
1260                )
1261                .intern(db);
1262                if let Some(text) = generated.diagnostics_note {
1263                    diagnostics_notes
1264                        .insert(generated_file_id, DiagnosticNote { text, location: None });
1265                }
1266                files.insert(
1267                    generated_file_id,
1268                    VirtualFile {
1269                        parent: Some(stable_ptr.span_in_file(db)),
1270                        name: SmolStrId::from(db, generated.name),
1271                        content: SmolStrId::from(db, generated.content),
1272                        code_mappings: generated.code_mappings.into(),
1273                        kind: FileKind::Module,
1274                        original_item_removed: remove_original_item,
1275                    },
1276                );
1277                aux_data.insert(generated_file_id, generated.aux_data);
1278            }
1279            if remove_original_item {
1280                break;
1281            }
1282        }
1283        if remove_original_item {
1284            // Don't add the original item to the module data.
1285            continue;
1286        }
1287        validate_attributes(db, allowed_attributes, &item_ast, &mut plugin_diagnostics);
1288        items.push(item_ast);
1289    }
1290    Ok(PrivModuleSubFiles { files, aux_data, items, plugin_diagnostics, diagnostics_notes })
1291}
1292
1293/// Collects attributes allowed by `allow_attr` attribute.
1294fn collect_extra_allowed_attributes<'db>(
1295    db: &'db dyn Database,
1296    item: &impl QueryAttrs<'db>,
1297    plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
1298) -> OrderedHashSet<SmolStrId<'db>> {
1299    let mut extra_allowed_attributes = OrderedHashSet::default();
1300    for attr in item.query_attr(db, ALLOW_ATTR_ATTR) {
1301        let args = attr.clone().structurize(db).args;
1302        if args.is_empty() {
1303            plugin_diagnostics.push(PluginDiagnostic::error(
1304                attr.stable_ptr(db),
1305                "Expected arguments.".to_string(),
1306            ));
1307            continue;
1308        }
1309        for arg in args {
1310            if let Some(ast::Expr::Path(path)) = try_extract_unnamed_arg(db, &arg.arg)
1311                && let Ok(ast::PathSegment::Simple(segment)) =
1312                    path.segments(db).elements(db).exactly_one()
1313            {
1314                extra_allowed_attributes.insert(segment.ident(db).text(db));
1315                continue;
1316            }
1317            plugin_diagnostics.push(PluginDiagnostic::error(
1318                arg.arg.stable_ptr(db),
1319                "Expected simple identifier.".to_string(),
1320            ));
1321        }
1322    }
1323    extra_allowed_attributes
1324}
1325
1326/// Validates that all attributes on the given item are in the allowed set or adds diagnostics.
1327pub fn validate_attributes_flat<'db, Item: QueryAttrs<'db> + TypedSyntaxNode<'db>>(
1328    db: &'db dyn Database,
1329    allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1330    extra_allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1331    item: &Item,
1332    plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
1333) {
1334    let local_extra_attributes = collect_extra_allowed_attributes(db, item, plugin_diagnostics);
1335    for attr in item.attributes_elements(db) {
1336        let attr_text = attr.attr(db).as_syntax_node().get_text_without_trivia(db);
1337        if !(allowed_attributes.contains(&attr_text)
1338            || extra_allowed_attributes.contains(&attr_text)
1339            || local_extra_attributes.contains(&attr_text))
1340        {
1341            plugin_diagnostics.push(PluginDiagnostic::error(
1342                attr.stable_ptr(db),
1343                "Unsupported attribute.".to_string(),
1344            ));
1345        }
1346    }
1347
1348    // Additional semantic validation for `#[path("...")]` attribute.
1349    for attr in item.query_attr(db, PATH_ATTR) {
1350        let node = item.as_syntax_node();
1351        let Some(item_module) = ast::ItemModule::cast(db, node) else {
1352            plugin_diagnostics.push(PluginDiagnostic::error(
1353                attr.stable_ptr(db),
1354                "`#[path(..)]` is only allowed on module declarations.".to_string(),
1355            ));
1356            continue;
1357        };
1358        // Must be file-based (`mod name;`), not inline.
1359        if matches!(item_module.body(db), MaybeModuleBody::Some(_)) {
1360            plugin_diagnostics.push(PluginDiagnostic::error(
1361                attr.stable_ptr(db),
1362                "`#[path(..)]` requires a file-based module: use `mod name;` with a semicolon."
1363                    .to_string(),
1364            ));
1365            continue;
1366        }
1367
1368        let args = attr.arguments(db);
1369        if extract_path_arg(db, &args).is_none() {
1370            plugin_diagnostics.push(PluginDiagnostic::error(
1371                args.stable_ptr(db),
1372                "`#[path(..)]` expects exactly one string literal argument.".to_string(),
1373            ));
1374        }
1375    }
1376}
1377
1378/// Extracts the path argument from the given attribute arguments.
1379fn extract_path_arg<'db>(
1380    db: &'db dyn Database,
1381    args: &ast::OptionArgListParenthesized<'db>,
1382) -> Option<ast::TerminalString<'db>> {
1383    match args {
1384        ast::OptionArgListParenthesized::Empty(_) => None,
1385        ast::OptionArgListParenthesized::ArgListParenthesized(args) => {
1386            let arg = args.arguments(db).elements(db).exactly_one().ok()?;
1387            if let ast::Expr::String(path) = try_extract_unnamed_arg(db, &arg)? {
1388                Some(path)
1389            } else {
1390                None
1391            }
1392        }
1393    }
1394}
1395
1396/// Validates that all attributes on all items in the given element list are in the allowed set or
1397/// adds diagnostics.
1398fn validate_attributes_element_list<'db, Item: QueryAttrs<'db> + TypedSyntaxNode<'db>>(
1399    db: &'db dyn Database,
1400    allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1401    extra_allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1402    items: impl Iterator<Item = Item>,
1403    plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
1404) {
1405    for item in items {
1406        validate_attributes_flat(
1407            db,
1408            allowed_attributes,
1409            extra_allowed_attributes,
1410            &item,
1411            plugin_diagnostics,
1412        );
1413    }
1414}
1415
1416/// Validates that all attributes on an item and on items contained within it are in the allowed set
1417/// or adds diagnostics.
1418fn validate_attributes<'db>(
1419    db: &'db dyn Database,
1420    allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1421    item_ast: &ast::ModuleItem<'db>,
1422    plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
1423) {
1424    let extra_allowed_attributes =
1425        collect_extra_allowed_attributes(db, item_ast, plugin_diagnostics);
1426    validate_attributes_flat(
1427        db,
1428        allowed_attributes,
1429        &extra_allowed_attributes,
1430        item_ast,
1431        plugin_diagnostics,
1432    );
1433
1434    match item_ast {
1435        ast::ModuleItem::Trait(item) => {
1436            if let ast::MaybeTraitBody::Some(body) = item.body(db) {
1437                validate_attributes_element_list(
1438                    db,
1439                    allowed_attributes,
1440                    &extra_allowed_attributes,
1441                    body.items(db).elements(db),
1442                    plugin_diagnostics,
1443                );
1444            }
1445        }
1446        ast::ModuleItem::Impl(item) => {
1447            if let ast::MaybeImplBody::Some(body) = item.body(db) {
1448                validate_attributes_element_list(
1449                    db,
1450                    allowed_attributes,
1451                    &extra_allowed_attributes,
1452                    body.items(db).elements(db),
1453                    plugin_diagnostics,
1454                );
1455            }
1456        }
1457        ast::ModuleItem::Struct(item) => {
1458            validate_attributes_element_list(
1459                db,
1460                allowed_attributes,
1461                &extra_allowed_attributes,
1462                item.members(db).elements(db),
1463                plugin_diagnostics,
1464            );
1465        }
1466        ast::ModuleItem::Enum(item) => {
1467            validate_attributes_element_list(
1468                db,
1469                allowed_attributes,
1470                &extra_allowed_attributes,
1471                item.variants(db).elements(db),
1472                plugin_diagnostics,
1473            );
1474        }
1475        _ => {}
1476    }
1477}
1478
1479/// Returns all the path leaves under a given use item.
1480pub fn get_all_path_leaves<'db>(
1481    db: &'db dyn Database,
1482    use_item: &ast::ItemUse<'db>,
1483) -> Vec<ast::UsePathLeaf<'db>> {
1484    let mut res = vec![];
1485    let mut stack = vec![use_item.use_path(db)];
1486    while let Some(use_path) = stack.pop() {
1487        match use_path {
1488            ast::UsePath::Leaf(use_path) => res.push(use_path),
1489            ast::UsePath::Single(use_path) => stack.push(use_path.use_path(db)),
1490            ast::UsePath::Multi(use_path) => {
1491                stack.extend(use_path.use_paths(db).elements(db).rev())
1492            }
1493            ast::UsePath::Star(_) => {}
1494        }
1495    }
1496    res
1497}
1498
1499/// Returns all the path stars under a given use item.
1500pub fn get_all_path_stars<'db>(
1501    db: &'db dyn Database,
1502    use_item: &ast::ItemUse<'db>,
1503) -> Vec<ast::UsePathStar<'db>> {
1504    let mut res = vec![];
1505    let mut stack = vec![use_item.use_path(db)];
1506    while let Some(use_path) = stack.pop() {
1507        match use_path {
1508            ast::UsePath::Leaf(_) => {}
1509            ast::UsePath::Single(use_path) => stack.push(use_path.use_path(db)),
1510            ast::UsePath::Multi(use_path) => {
1511                stack.extend(use_path.use_paths(db).elements(db).rev())
1512            }
1513            ast::UsePath::Star(use_path) => res.push(use_path),
1514        }
1515    }
1516    res
1517}
1518
1519/// Collects the feature names granted by `#[feature("...")]` attributes on `item`.
1520///
1521/// The values are kept *with* their surrounding quotes, matching how `allowed_features` is
1522/// represented elsewhere. Malformed `#[feature]` attributes are ignored here; they are diagnosed by
1523/// the semantic model.
1524fn extract_allowed_features<'db>(
1525    db: &'db dyn Database,
1526    item: &impl QueryAttrs<'db>,
1527) -> OrderedHashSet<SmolStrId<'db>> {
1528    let mut allowed_features = OrderedHashSet::default();
1529    for attr in item.query_attr(db, FEATURE_ATTR) {
1530        for arg in attr.structurize(db).args {
1531            if let Some(ast::Expr::String(value)) = try_extract_unnamed_arg(db, &arg.arg) {
1532                allowed_features.insert(value.text(db));
1533            }
1534        }
1535    }
1536    allowed_features
1537}
1538
1539/// Returns the feature names allowed in `module_id`'s scope: the `#[feature("...")]` attributes on
1540/// the module declaration accumulated with those of all its ancestor modules, up to the crate root.
1541///
1542/// Mirrors the semantic feature-config walk, but at the defs level and without diagnostics (the
1543/// semantic model owns feature diagnostics). Memoized per `ModuleId` so the parent walk runs once.
1544#[salsa::tracked(returns(ref))]
1545fn module_allowed_features<'db>(
1546    db: &'db dyn Database,
1547    _tracked: Tracked,
1548    module_id: ModuleId<'db>,
1549) -> OrderedHashSet<SmolStrId<'db>> {
1550    let (parent_module, mut allowed_features) = match module_id {
1551        ModuleId::CrateRoot(_) => return Default::default(),
1552        ModuleId::Submodule(id) => {
1553            (id.parent_module(db), extract_allowed_features(db, &id.stable_ptr(db).lookup(db)))
1554        }
1555        ModuleId::MacroCall { id, .. } => {
1556            (id.parent_module(db), extract_allowed_features(db, &id.stable_ptr(db).lookup(db)))
1557        }
1558    };
1559    allowed_features.extend(module_allowed_features(db, (), parent_module).iter().copied());
1560    allowed_features
1561}
1562
1563#[salsa::tracked(returns(ref))]
1564fn module_constants_ids_helper<'db>(
1565    db: &'db dyn Database,
1566    module_data: ModuleData<'db>,
1567) -> Vec<ConstantId<'db>> {
1568    module_data.constants(db).keys().copied().collect_vec()
1569}
1570
1571pub fn module_constants_ids<'db>(
1572    db: &'db dyn Database,
1573    module_id: ModuleId<'db>,
1574) -> Maybe<&'db [ConstantId<'db>]> {
1575    Ok(module_constants_ids_helper(db, module_id.module_data(db)?))
1576}
1577
1578#[salsa::tracked]
1579pub fn module_constant_by_id<'db>(
1580    db: &'db dyn Database,
1581    constant_id: ConstantId<'db>,
1582) -> Maybe<ast::ItemConstant<'db>> {
1583    let module_constants = constant_id.module_data(db)?.constants(db);
1584    module_constants.get(&constant_id).cloned().ok_or_else(skip_diagnostic)
1585}
1586
1587#[salsa::tracked(returns(ref))]
1588fn module_submodules_ids_helper<'db>(
1589    db: &'db dyn Database,
1590    module_data: ModuleData<'db>,
1591) -> Vec<SubmoduleId<'db>> {
1592    module_data.submodules(db).keys().copied().collect_vec()
1593}
1594
1595fn module_submodules_ids<'db>(
1596    db: &'db dyn Database,
1597    module_id: ModuleId<'db>,
1598) -> Maybe<&'db [SubmoduleId<'db>]> {
1599    Ok(module_submodules_ids_helper(db, module_id.module_data(db)?))
1600}
1601
1602#[salsa::tracked]
1603pub fn module_submodule_by_id<'db>(
1604    db: &'db dyn Database,
1605    submodule_id: SubmoduleId<'db>,
1606) -> Maybe<ast::ItemModule<'db>> {
1607    let module_submodules = submodule_id.module_data(db)?.submodules(db);
1608    module_submodules.get(&submodule_id).cloned().ok_or_else(skip_diagnostic)
1609}
1610
1611#[salsa::tracked(returns(ref))]
1612fn module_free_functions_ids_helper<'db>(
1613    db: &'db dyn Database,
1614    module_data: ModuleData<'db>,
1615) -> Vec<FreeFunctionId<'db>> {
1616    module_data.free_functions(db).keys().copied().collect_vec()
1617}
1618
1619pub fn module_free_functions_ids<'db>(
1620    db: &'db dyn Database,
1621    module_id: ModuleId<'db>,
1622) -> Maybe<&'db [FreeFunctionId<'db>]> {
1623    Ok(module_free_functions_ids_helper(db, module_id.module_data(db)?))
1624}
1625
1626#[salsa::tracked]
1627pub fn module_free_function_by_id<'db>(
1628    db: &'db dyn Database,
1629    free_function_id: FreeFunctionId<'db>,
1630) -> Maybe<ast::FunctionWithBody<'db>> {
1631    let module_free_functions = free_function_id.module_data(db)?.free_functions(db);
1632    module_free_functions.get(&free_function_id).cloned().ok_or_else(skip_diagnostic)
1633}
1634
1635#[salsa::tracked(returns(ref))]
1636fn module_uses_ids_helper<'db>(
1637    db: &'db dyn Database,
1638    module_data: ModuleData<'db>,
1639) -> Vec<UseId<'db>> {
1640    module_data.uses(db).keys().copied().collect_vec()
1641}
1642pub fn module_uses_ids<'db>(
1643    db: &'db dyn Database,
1644    module_id: ModuleId<'db>,
1645) -> Maybe<&'db [UseId<'db>]> {
1646    Ok(module_uses_ids_helper(db, module_id.module_data(db)?))
1647}
1648
1649#[salsa::tracked]
1650pub fn module_use_by_id<'db>(
1651    db: &'db dyn Database,
1652    use_id: UseId<'db>,
1653) -> Maybe<ast::UsePathLeaf<'db>> {
1654    let module_uses = use_id.module_data(db)?.uses(db);
1655    module_uses.get(&use_id).cloned().ok_or_else(skip_diagnostic)
1656}
1657
1658/// Returns the `use *` of the given module, by its ID.
1659#[salsa::tracked]
1660pub fn module_global_use_by_id<'db>(
1661    db: &'db dyn Database,
1662    global_use_id: GlobalUseId<'db>,
1663) -> Maybe<ast::UsePathStar<'db>> {
1664    let module_global_uses = global_use_id.module_data(db)?.global_uses(db);
1665    module_global_uses.get(&global_use_id).cloned().ok_or_else(skip_diagnostic)
1666}
1667
1668#[salsa::tracked(returns(ref))]
1669fn module_structs_ids_helper<'db>(
1670    db: &'db dyn Database,
1671    module_data: ModuleData<'db>,
1672) -> Vec<StructId<'db>> {
1673    module_data.structs(db).keys().copied().collect_vec()
1674}
1675
1676pub fn module_structs_ids<'db>(
1677    db: &'db dyn Database,
1678    module_id: ModuleId<'db>,
1679) -> Maybe<&'db [StructId<'db>]> {
1680    Ok(module_structs_ids_helper(db, module_id.module_data(db)?))
1681}
1682
1683#[salsa::tracked]
1684pub fn module_struct_by_id<'db>(
1685    db: &'db dyn Database,
1686    struct_id: StructId<'db>,
1687) -> Maybe<ast::ItemStruct<'db>> {
1688    let module_structs = struct_id.module_data(db)?.structs(db);
1689    module_structs.get(&struct_id).cloned().ok_or_else(skip_diagnostic)
1690}
1691
1692#[salsa::tracked(returns(ref))]
1693fn module_enums_ids_helper<'db>(
1694    db: &'db dyn Database,
1695    module_data: ModuleData<'db>,
1696) -> Vec<EnumId<'db>> {
1697    module_data.enums(db).keys().copied().collect_vec()
1698}
1699
1700pub fn module_enums_ids<'db>(
1701    db: &'db dyn Database,
1702    module_id: ModuleId<'db>,
1703) -> Maybe<&'db [EnumId<'db>]> {
1704    Ok(module_enums_ids_helper(db, module_id.module_data(db)?))
1705}
1706
1707#[salsa::tracked]
1708pub fn module_enum_by_id<'db>(
1709    db: &'db dyn Database,
1710    enum_id: EnumId<'db>,
1711) -> Maybe<ast::ItemEnum<'db>> {
1712    let module_enums = enum_id.module_data(db)?.enums(db);
1713    module_enums.get(&enum_id).cloned().ok_or_else(skip_diagnostic)
1714}
1715
1716#[salsa::tracked(returns(ref))]
1717fn module_type_aliases_ids_helper<'db>(
1718    db: &'db dyn Database,
1719    module_data: ModuleData<'db>,
1720) -> Vec<ModuleTypeAliasId<'db>> {
1721    module_data.type_aliases(db).keys().copied().collect_vec()
1722}
1723
1724pub fn module_type_aliases_ids<'db>(
1725    db: &'db dyn Database,
1726    module_id: ModuleId<'db>,
1727) -> Maybe<&'db [ModuleTypeAliasId<'db>]> {
1728    Ok(module_type_aliases_ids_helper(db, module_id.module_data(db)?))
1729}
1730
1731#[salsa::tracked]
1732pub fn module_type_alias_by_id<'db>(
1733    db: &'db dyn Database,
1734    module_type_alias_id: ModuleTypeAliasId<'db>,
1735) -> Maybe<ast::ItemTypeAlias<'db>> {
1736    let module_type_aliases = module_type_alias_id.module_data(db)?.type_aliases(db);
1737    module_type_aliases.get(&module_type_alias_id).cloned().ok_or_else(skip_diagnostic)
1738}
1739
1740#[salsa::tracked(returns(ref))]
1741fn module_impl_aliases_ids_helper<'db>(
1742    db: &'db dyn Database,
1743    module_data: ModuleData<'db>,
1744) -> Vec<ImplAliasId<'db>> {
1745    module_data.impl_aliases(db).keys().copied().collect_vec()
1746}
1747
1748pub fn module_impl_aliases_ids<'db>(
1749    db: &'db dyn Database,
1750    module_id: ModuleId<'db>,
1751) -> Maybe<&'db [ImplAliasId<'db>]> {
1752    Ok(module_impl_aliases_ids_helper(db, module_id.module_data(db)?))
1753}
1754
1755#[salsa::tracked]
1756pub fn module_impl_alias_by_id<'db>(
1757    db: &'db dyn Database,
1758    impl_alias_id: ImplAliasId<'db>,
1759) -> Maybe<ast::ItemImplAlias<'db>> {
1760    let module_impl_aliases = impl_alias_id.module_data(db)?.impl_aliases(db);
1761    module_impl_aliases.get(&impl_alias_id).cloned().ok_or_else(skip_diagnostic)
1762}
1763
1764#[salsa::tracked(returns(ref))]
1765fn module_traits_ids_helper<'db>(
1766    db: &'db dyn Database,
1767    module_data: ModuleData<'db>,
1768) -> Vec<TraitId<'db>> {
1769    module_data.traits(db).keys().copied().collect_vec()
1770}
1771
1772pub fn module_traits_ids<'db>(
1773    db: &'db dyn Database,
1774    module_id: ModuleId<'db>,
1775) -> Maybe<&'db [TraitId<'db>]> {
1776    Ok(module_traits_ids_helper(db, module_id.module_data(db)?))
1777}
1778
1779#[salsa::tracked]
1780pub fn module_trait_by_id<'db>(
1781    db: &'db dyn Database,
1782    trait_id: TraitId<'db>,
1783) -> Maybe<ast::ItemTrait<'db>> {
1784    let module_traits = trait_id.module_data(db)?.traits(db);
1785    module_traits.get(&trait_id).cloned().ok_or_else(skip_diagnostic)
1786}
1787
1788#[salsa::tracked(returns(ref))]
1789fn module_impls_ids_helper<'db>(
1790    db: &'db dyn Database,
1791    module_data: ModuleData<'db>,
1792) -> Vec<ImplDefId<'db>> {
1793    module_data.impls(db).keys().copied().collect_vec()
1794}
1795
1796pub fn module_impls_ids<'db>(
1797    db: &'db dyn Database,
1798    module_id: ModuleId<'db>,
1799) -> Maybe<&'db [ImplDefId<'db>]> {
1800    Ok(module_impls_ids_helper(db, module_id.module_data(db)?))
1801}
1802
1803#[salsa::tracked]
1804pub fn module_impl_by_id<'db>(
1805    db: &'db dyn Database,
1806    impl_def_id: ImplDefId<'db>,
1807) -> Maybe<ast::ItemImpl<'db>> {
1808    let module_impls = impl_def_id.module_data(db)?.impls(db);
1809    module_impls.get(&impl_def_id).cloned().ok_or_else(skip_diagnostic)
1810}
1811
1812#[salsa::tracked(returns(ref))]
1813fn module_extern_types_ids_helper<'db>(
1814    db: &'db dyn Database,
1815    module_data: ModuleData<'db>,
1816) -> Vec<ExternTypeId<'db>> {
1817    module_data.extern_types(db).keys().copied().collect_vec()
1818}
1819pub fn module_extern_types_ids<'db>(
1820    db: &'db dyn Database,
1821    module_id: ModuleId<'db>,
1822) -> Maybe<&'db [ExternTypeId<'db>]> {
1823    Ok(module_extern_types_ids_helper(db, module_id.module_data(db)?))
1824}
1825
1826#[salsa::tracked]
1827pub fn module_extern_type_by_id<'db>(
1828    db: &'db dyn Database,
1829    extern_type_id: ExternTypeId<'db>,
1830) -> Maybe<ast::ItemExternType<'db>> {
1831    let module_extern_types = extern_type_id.module_data(db)?.extern_types(db);
1832    module_extern_types.get(&extern_type_id).cloned().ok_or_else(skip_diagnostic)
1833}
1834
1835#[salsa::tracked(returns(ref))]
1836fn module_macro_declarations_ids_helper<'db>(
1837    db: &'db dyn Database,
1838    module_data: ModuleData<'db>,
1839) -> Vec<MacroDeclarationId<'db>> {
1840    module_data.macro_declarations(db).keys().copied().collect_vec()
1841}
1842
1843/// Returns all the ids of the macro declarations of the given module.
1844pub fn module_macro_declarations_ids<'db>(
1845    db: &'db dyn Database,
1846    module_id: ModuleId<'db>,
1847) -> Maybe<&'db [MacroDeclarationId<'db>]> {
1848    Ok(module_macro_declarations_ids_helper(db, module_id.module_data(db)?))
1849}
1850
1851/// Returns the macro declaration of the given id.
1852#[salsa::tracked]
1853pub fn module_macro_declaration_by_id<'db>(
1854    db: &'db dyn Database,
1855    macro_declaration_id: MacroDeclarationId<'db>,
1856) -> Maybe<ast::ItemMacroDeclaration<'db>> {
1857    let module_macro_declarations = macro_declaration_id.module_data(db)?.macro_declarations(db);
1858    module_macro_declarations.get(&macro_declaration_id).cloned().ok_or_else(skip_diagnostic)
1859}
1860
1861#[salsa::tracked(returns(ref))]
1862fn module_macro_calls_ids_helper<'db>(
1863    db: &'db dyn Database,
1864    module_data: ModuleData<'db>,
1865) -> Vec<MacroCallId<'db>> {
1866    module_data.macro_calls(db).keys().copied().collect_vec()
1867}
1868
1869pub fn module_macro_calls_ids<'db>(
1870    db: &'db dyn Database,
1871    module_id: ModuleId<'db>,
1872) -> Maybe<&'db [MacroCallId<'db>]> {
1873    Ok(module_macro_calls_ids_helper(db, module_id.module_data(db)?))
1874}
1875
1876#[salsa::tracked]
1877fn module_macro_call_by_id<'db>(
1878    db: &'db dyn Database,
1879    macro_call_id: MacroCallId<'db>,
1880) -> Maybe<ast::ItemInlineMacro<'db>> {
1881    let module_macro_calls = macro_call_id.module_data(db)?.macro_calls(db);
1882    module_macro_calls.get(&macro_call_id).cloned().ok_or_else(skip_diagnostic)
1883}
1884
1885#[salsa::tracked(returns(ref))]
1886fn module_extern_functions_ids_helper<'db>(
1887    db: &'db dyn Database,
1888    module_data: ModuleData<'db>,
1889) -> Vec<ExternFunctionId<'db>> {
1890    module_data.extern_functions(db).keys().copied().collect_vec()
1891}
1892
1893pub fn module_extern_functions_ids<'db>(
1894    db: &'db dyn Database,
1895    module_id: ModuleId<'db>,
1896) -> Maybe<&'db [ExternFunctionId<'db>]> {
1897    Ok(module_extern_functions_ids_helper(db, module_id.module_data(db)?))
1898}
1899
1900#[salsa::tracked]
1901pub fn module_extern_function_by_id<'db>(
1902    db: &'db dyn Database,
1903    extern_function_id: ExternFunctionId<'db>,
1904) -> Maybe<ast::ItemExternFunction<'db>> {
1905    let module_extern_functions = extern_function_id.module_data(db)?.extern_functions(db);
1906    module_extern_functions.get(&extern_function_id).cloned().ok_or_else(skip_diagnostic)
1907}
1908
1909#[salsa::tracked(returns(ref))]
1910fn module_ancestors_helper<'db>(
1911    db: &'db dyn Database,
1912    _tracked: Tracked,
1913    module_id: ModuleId<'db>,
1914) -> OrderedHashSet<ModuleId<'db>> {
1915    let mut current = module_id;
1916    let mut ancestors = OrderedHashSet::default();
1917    loop {
1918        match current {
1919            ModuleId::CrateRoot(_) => {
1920                ancestors.insert(current);
1921                return ancestors;
1922            }
1923            ModuleId::Submodule(submodule_id) => {
1924                ancestors.insert(current);
1925                current = submodule_id.parent_module(db);
1926            }
1927            ModuleId::MacroCall { id, .. } => {
1928                current = id.parent_module(db);
1929            }
1930        }
1931    }
1932}
1933
1934#[salsa::tracked]
1935fn module_perceived_module_helper<'db>(
1936    db: &'db dyn Database,
1937    _tracked: Tracked,
1938    mut module_id: ModuleId<'db>,
1939) -> ModuleId<'db> {
1940    while let ModuleId::MacroCall { id, .. } = module_id {
1941        module_id = id.parent_module(db);
1942    }
1943    module_id
1944}
1945
1946fn module_item_name_stable_ptr<'db>(
1947    db: &'db dyn Database,
1948    module_id: ModuleId<'db>,
1949    item_id: ModuleItemId<'db>,
1950) -> Maybe<SyntaxStablePtrId<'db>> {
1951    let data = module_id.module_data(db)?;
1952    Ok(match &item_id {
1953        ModuleItemId::Constant(id) => data.constants(db)[id].name(db).stable_ptr(db).untyped(),
1954        ModuleItemId::Submodule(id) => data.submodules(db)[id].name(db).stable_ptr(db).untyped(),
1955        ModuleItemId::Use(id) => data.uses(db)[id].name_stable_ptr(db),
1956        ModuleItemId::FreeFunction(id) => {
1957            data.free_functions(db)[id].declaration(db).name(db).stable_ptr(db).untyped()
1958        }
1959        ModuleItemId::Struct(id) => data.structs(db)[id].name(db).stable_ptr(db).untyped(),
1960        ModuleItemId::Enum(id) => data.enums(db)[id].name(db).stable_ptr(db).untyped(),
1961        ModuleItemId::TypeAlias(id) => data.type_aliases(db)[id].name(db).stable_ptr(db).untyped(),
1962        ModuleItemId::ImplAlias(id) => data.impl_aliases(db)[id].name(db).stable_ptr(db).untyped(),
1963        ModuleItemId::Trait(id) => data.traits(db)[id].name(db).stable_ptr(db).untyped(),
1964        ModuleItemId::Impl(id) => data.impls(db)[id].name(db).stable_ptr(db).untyped(),
1965        ModuleItemId::ExternType(id) => data.extern_types(db)[id].name(db).stable_ptr(db).untyped(),
1966        ModuleItemId::ExternFunction(id) => {
1967            data.extern_functions(db)[id].declaration(db).name(db).stable_ptr(db).untyped()
1968        }
1969        ModuleItemId::MacroDeclaration(id) => {
1970            data.macro_declarations(db)[id].name(db).stable_ptr(db).untyped()
1971        }
1972    })
1973}
1974
1975pub trait DefsGroupEx: DefsGroup {
1976    /// Overrides the default macro plugins available for [`CrateId`] with `plugins`.
1977    ///
1978    /// *Note*: Sets the following Salsa input: `DefsGroup::macro_plugin_overrides`.
1979    fn set_override_crate_macro_plugins<'db>(
1980        &'db mut self,
1981        crate_id: CrateId<'db>,
1982        plugins: Arc<Vec<MacroPluginId<'db>>>,
1983    ) {
1984        let crate_input = self.crate_input(crate_id);
1985        let mut overrides = self.macro_plugin_overrides_input().clone();
1986        let plugins = plugins.iter().map(|plugin| plugin.long(self).clone()).collect();
1987        overrides.insert(crate_input.clone(), plugins);
1988        defs_group_input(self.as_dyn_database())
1989            .set_macro_plugin_overrides(self)
1990            .to(Some(overrides));
1991    }
1992
1993    /// Overrides the default inline macro plugins available for [`CrateId`] with `plugins`.
1994    ///
1995    /// *Note*: Sets the following Salsa input: `DefsGroup::inline_macro_plugin_overrides`.
1996    fn set_override_crate_inline_macro_plugins<'db>(
1997        &'db mut self,
1998        crate_id: CrateId<'db>,
1999        plugins: Arc<OrderedHashMap<String, InlineMacroExprPluginId<'db>>>,
2000    ) {
2001        let crate_input = self.crate_input(crate_id);
2002        let mut overrides = self.inline_macro_plugin_overrides_input().clone();
2003        let plugins = Arc::new(
2004            plugins
2005                .iter()
2006                .map(|(name, plugin)| (name.clone(), plugin.long(self).clone()))
2007                .collect(),
2008        );
2009        overrides.insert(crate_input.clone(), plugins);
2010        defs_group_input(self.as_dyn_database())
2011            .set_inline_macro_plugin_overrides(self)
2012            .to(Some(overrides));
2013    }
2014}
2015
2016impl<T: DefsGroup + ?Sized> DefsGroupEx for T {}