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