cairo_lang_semantic/
db.rs

1use std::sync::Arc;
2
3use cairo_lang_defs::db::{DefsGroup, DefsGroupEx, defs_group_input};
4use cairo_lang_defs::diagnostic_utils::StableLocation;
5use cairo_lang_defs::ids::{
6    ImplAliasId, InlineMacroExprPluginId, InlineMacroExprPluginLongId, LanguageElementId,
7    LookupItemId, MacroPluginId, MacroPluginLongId, ModuleId, ModuleItemId, UseId,
8};
9use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder, Maybe};
10use cairo_lang_filesystem::db::FilesGroup;
11use cairo_lang_filesystem::ids::{CrateId, CrateInput, FileId, FileLongId, SmolStrId, Tracked};
12use cairo_lang_syntax::attribute::consts::{DEPRECATED_ATTR, UNUSED_IMPORTS, UNUSED_VARIABLES};
13use cairo_lang_syntax::node::{TypedStablePtr, ast};
14use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
15use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
16use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
17use cairo_lang_utils::{Intern, require};
18use itertools::{Itertools, chain};
19use salsa::{Database, Setter};
20
21use crate::SemanticDiagnostic;
22use crate::cache::{SemanticCacheLoadingData, load_cached_crate_modules_semantic};
23use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnosticsBuilder};
24use crate::ids::{AnalyzerPluginId, AnalyzerPluginLongId};
25use crate::items::constant::ConstantSemantic;
26use crate::items::enm::EnumSemantic;
27use crate::items::extern_function::ExternFunctionSemantic;
28use crate::items::extern_type::ExternTypeSemantic;
29use crate::items::free_function::FreeFunctionSemantic;
30use crate::items::imp::{ImplId, ImplSemantic};
31use crate::items::impl_alias::ImplAliasSemantic;
32use crate::items::macro_call::{MacroCallSemantic, module_macro_modules};
33use crate::items::macro_declaration::MacroDeclarationSemantic;
34use crate::items::module::{ModuleSemantic, ModuleSemanticData};
35use crate::items::module_type_alias::ModuleTypeAliasSemantic;
36use crate::items::structure::StructSemantic;
37use crate::items::trt::TraitSemantic;
38use crate::items::us::{SemanticUseEx, UseSemantic};
39use crate::items::visibility::Visibility;
40use crate::plugin::{AnalyzerPlugin, InternedPluginSuite, PluginSuite};
41use crate::resolve::{ResolvedConcreteItem, ResolvedGenericItem, ResolverData};
42
43#[salsa::input]
44pub struct SemanticGroupInput {
45    #[returns(ref)]
46    pub default_analyzer_plugins: Option<Vec<AnalyzerPluginLongId>>,
47    #[returns(ref)]
48    pub analyzer_plugin_overrides: Option<OrderedHashMap<CrateInput, Arc<[AnalyzerPluginLongId]>>>,
49}
50
51#[salsa::tracked(returns(ref))]
52pub fn semantic_group_input(db: &dyn Database) -> SemanticGroupInput {
53    SemanticGroupInput::new(db, None, None)
54}
55
56fn default_analyzer_plugins_input(db: &dyn Database) -> &[AnalyzerPluginLongId] {
57    semantic_group_input(db).default_analyzer_plugins(db).as_ref().unwrap()
58}
59
60fn analyzer_plugin_overrides_input(
61    db: &dyn Database,
62) -> &OrderedHashMap<CrateInput, Arc<[AnalyzerPluginLongId]>> {
63    semantic_group_input(db).analyzer_plugin_overrides(db).as_ref().unwrap()
64}
65
66// Salsa database interface.
67// All queries starting with priv_ are for internal use only by this crate.
68// They appear in the public API because of salsa limitations.
69// We differentiate between the declaration and the definition of each item:
70// Declarations and definitions must not depend on other definitions, only other declarations.
71// This prevents cycles where there shouldn't be any.
72pub trait SemanticGroup: Database {
73    // Lookups.
74    // ========
75    fn lookup_resolved_generic_item_by_ptr<'db>(
76        &'db self,
77        id: LookupItemId<'db>,
78        ptr: ast::TerminalIdentifierPtr<'db>,
79    ) -> Option<ResolvedGenericItem<'db>> {
80        lookup_resolved_generic_item_by_ptr(self.as_dyn_database(), id, ptr)
81    }
82
83    fn lookup_resolved_concrete_item_by_ptr<'db>(
84        &'db self,
85        id: LookupItemId<'db>,
86        ptr: ast::TerminalIdentifierPtr<'db>,
87    ) -> Option<ResolvedConcreteItem<'db>> {
88        lookup_resolved_concrete_item_by_ptr(self.as_dyn_database(), id, ptr)
89    }
90
91    // Diagnostics.
92    // ============
93    /// Aggregates module level semantic diagnostics.
94    fn module_semantic_diagnostics<'db>(
95        &'db self,
96        module_id: ModuleId<'db>,
97    ) -> Maybe<Diagnostics<'db, SemanticDiagnostic<'db>>> {
98        module_semantic_diagnostics_tracked(self.as_dyn_database(), (), module_id)
99    }
100
101    /// Aggregates file-level semantic diagnostics.
102    fn file_semantic_diagnostics<'db>(
103        &'db self,
104        file_id: FileId<'db>,
105    ) -> Maybe<Diagnostics<'db, SemanticDiagnostic<'db>>> {
106        file_semantic_diagnostics(self.as_dyn_database(), file_id)
107    }
108
109    // Analyzer plugins.
110    // ========
111    fn default_analyzer_plugins_input(&self) -> &[AnalyzerPluginLongId] {
112        default_analyzer_plugins_input(self.as_dyn_database())
113    }
114
115    /// Interned version of `default_analyzer_plugins`.
116    fn default_analyzer_plugins<'db>(&'db self) -> Arc<Vec<AnalyzerPluginId<'db>>> {
117        default_analyzer_plugins(self.as_dyn_database())
118    }
119
120    fn analyzer_plugin_overrides_input(
121        &self,
122    ) -> &OrderedHashMap<CrateInput, Arc<[AnalyzerPluginLongId]>> {
123        analyzer_plugin_overrides_input(self.as_dyn_database())
124    }
125
126    /// Interned version of `analyzer_plugin_overrides_input`.
127    fn analyzer_plugin_overrides<'db>(
128        &'db self,
129    ) -> Arc<OrderedHashMap<CrateId<'db>, Arc<Vec<AnalyzerPluginId<'db>>>>> {
130        analyzer_plugin_overrides(self.as_dyn_database())
131    }
132
133    /// Returns [`AnalyzerPluginId`]s of the plugins set for the crate with [`CrateId`].
134    /// Returns
135    /// [`SemanticGroupEx::set_override_crate_analyzer_plugins`] if it has been set,
136    /// or the ([`SemanticGroup::default_analyzer_plugins`]) otherwise.
137    fn crate_analyzer_plugins<'db>(
138        &'db self,
139        crate_id: CrateId<'db>,
140    ) -> Arc<Vec<AnalyzerPluginId<'db>>> {
141        crate_analyzer_plugins(self.as_dyn_database(), crate_id)
142    }
143
144    /// Returns the set of `allow` that were declared as by a plugin.
145    /// An allow that is not in this set will be handled as an unknown allow.
146    fn declared_allows<'db>(&self, crate_id: CrateId<'db>) -> Arc<OrderedHashSet<String>> {
147        declared_allows(self.as_dyn_database(), crate_id)
148    }
149
150    /// Returns the [ModuleSemanticData] of all modules in the crate's cache, and the loading data
151    /// of the [SemanticGroup] in the crate.
152    fn cached_crate_semantic_data<'db>(
153        &'db self,
154        crate_id: CrateId<'db>,
155    ) -> Option<ModuleSemanticDataCacheAndLoadingData<'db>> {
156        cached_crate_semantic_data(self.as_dyn_database(), crate_id)
157    }
158}
159
160impl<T: Database + ?Sized> SemanticGroup for T {}
161
162/// Initializes the [`SemanticGroup`] database to a proper state.
163pub fn init_semantic_group(db: &mut dyn Database) {
164    semantic_group_input(db).set_analyzer_plugin_overrides(db).to(Some(OrderedHashMap::default()));
165}
166
167#[salsa::tracked]
168fn default_analyzer_plugins(db: &dyn Database) -> Arc<Vec<AnalyzerPluginId<'_>>> {
169    let inp = db.default_analyzer_plugins_input();
170    Arc::new(inp.iter().map(|plugin| plugin.clone().intern(db)).collect_vec())
171}
172
173#[salsa::tracked]
174fn analyzer_plugin_overrides(
175    db: &dyn Database,
176) -> Arc<OrderedHashMap<CrateId<'_>, Arc<Vec<AnalyzerPluginId<'_>>>>> {
177    let inp = db.analyzer_plugin_overrides_input();
178    Arc::new(
179        inp.iter()
180            .map(|(crate_input, plugins)| {
181                (
182                    crate_input.clone().into_crate_long_id(db).intern(db),
183                    Arc::new(plugins.iter().map(|plugin| plugin.clone().intern(db)).collect_vec()),
184                )
185            })
186            .collect(),
187    )
188}
189
190#[salsa::tracked]
191fn module_semantic_diagnostics_tracked<'db>(
192    db: &'db dyn Database,
193    _tracked: Tracked,
194    module_id: ModuleId<'db>,
195) -> Maybe<Diagnostics<'db, SemanticDiagnostic<'db>>> {
196    module_semantic_diagnostics(db, module_id)
197}
198
199fn module_semantic_diagnostics<'db>(
200    db: &'db dyn Database,
201    module_id: ModuleId<'db>,
202) -> Maybe<Diagnostics<'db, SemanticDiagnostic<'db>>> {
203    let mut diagnostics = DiagnosticsBuilder::default();
204    for (_module_id, plugin_diag) in
205        module_id.module_data(db)?.plugin_diagnostics(db).iter().cloned()
206    {
207        diagnostics.add(SemanticDiagnostic::new(
208            match plugin_diag.inner_span {
209                None => StableLocation::new(plugin_diag.stable_ptr),
210                Some(inner_span) => {
211                    StableLocation::with_inner_span(plugin_diag.stable_ptr, inner_span)
212                }
213            },
214            SemanticDiagnosticKind::PluginDiagnostic(plugin_diag),
215        ));
216    }
217    let data = db.priv_module_semantic_data(module_id)?;
218    diagnostics.extend(data.diagnostics.clone());
219    // TODO(Gil): Aggregate diagnostics for subitems with semantic model (i.e. impl function, trait
220    // functions and generic params) directly and not via the parent item.
221    for item in module_id.module_data(db)?.items(db).iter() {
222        match item {
223            ModuleItemId::Constant(const_id) => {
224                diagnostics.extend(db.constant_semantic_diagnostics(*const_id));
225            }
226            // Add signature diagnostics.
227            ModuleItemId::Use(use_id) => {
228                diagnostics.extend(db.use_semantic_diagnostics(*use_id));
229            }
230            ModuleItemId::FreeFunction(free_function) => {
231                diagnostics.extend(db.free_function_declaration_diagnostics(*free_function));
232                diagnostics.extend(db.free_function_body_diagnostics(*free_function));
233            }
234            ModuleItemId::Struct(struct_id) => {
235                diagnostics.extend(db.struct_declaration_diagnostics(*struct_id));
236                diagnostics.extend(db.struct_definition_diagnostics(*struct_id));
237            }
238            ModuleItemId::Enum(enum_id) => {
239                diagnostics.extend(db.enum_definition_diagnostics(*enum_id));
240                diagnostics.extend(db.enum_declaration_diagnostics(*enum_id));
241            }
242            ModuleItemId::Trait(trait_id) => {
243                diagnostics.extend(db.trait_semantic_declaration_diagnostics(*trait_id));
244                diagnostics.extend(db.trait_semantic_definition_diagnostics(*trait_id));
245            }
246            ModuleItemId::Impl(impl_def_id) => {
247                diagnostics.extend(db.impl_semantic_declaration_diagnostics(*impl_def_id));
248                diagnostics.extend(db.impl_semantic_definition_diagnostics(*impl_def_id));
249            }
250            ModuleItemId::Submodule(submodule_id) => {
251                // Note that the parent module does not report the diagnostics of its submodules.
252                if let Ok(file_id) = db.module_main_file(ModuleId::Submodule(*submodule_id))
253                    && db.file_content(file_id).is_none()
254                {
255                    // Note that the error location is in the parent module, not the
256                    // submodule.
257
258                    let path = match file_id.long(db) {
259                        FileLongId::OnDisk(path) => path.display().to_string(),
260                        FileLongId::Virtual(_) | FileLongId::External(_) => {
261                            panic!("Expected OnDisk file.")
262                        }
263                    };
264
265                    let stable_location =
266                        StableLocation::new(submodule_id.stable_ptr(db).untyped());
267                    diagnostics.add(SemanticDiagnostic::new(
268                        stable_location,
269                        SemanticDiagnosticKind::ModuleFileNotFound(path),
270                    ));
271                }
272            }
273            ModuleItemId::ExternType(extern_type) => {
274                diagnostics.extend(db.extern_type_declaration_diagnostics(*extern_type));
275            }
276            ModuleItemId::ExternFunction(extern_function) => {
277                diagnostics.extend(db.extern_function_declaration_diagnostics(*extern_function));
278            }
279            ModuleItemId::TypeAlias(type_alias) => {
280                diagnostics.extend(db.module_type_alias_semantic_diagnostics(*type_alias));
281            }
282            ModuleItemId::ImplAlias(type_alias) => {
283                diagnostics.extend(db.impl_alias_semantic_diagnostics(*type_alias));
284            }
285            ModuleItemId::MacroDeclaration(macro_declaration) => {
286                diagnostics.extend(db.macro_declaration_diagnostics(*macro_declaration));
287            }
288        }
289    }
290    for global_use in module_id.module_data(db)?.global_uses(db).keys() {
291        diagnostics.extend(db.global_use_semantic_diagnostics(*global_use));
292    }
293    for macro_call in db.module_macro_calls_ids(module_id)?.iter() {
294        diagnostics.extend(db.macro_call_diagnostics(*macro_call));
295        if let Ok(macro_module_id) = db.macro_call_module_id(*macro_call)
296            && let Ok(semantic_diags) = db.module_semantic_diagnostics(macro_module_id)
297        {
298            diagnostics.extend(semantic_diags);
299        }
300    }
301    add_unused_item_diagnostics(db, module_id, data, &mut diagnostics);
302    add_duplicated_names_from_macro_expansions_diagnostics(db, module_id, &mut diagnostics);
303    for analyzer_plugin_id in db.crate_analyzer_plugins(module_id.owning_crate(db)).iter() {
304        let analyzer_plugin = analyzer_plugin_id.long(db);
305
306        for diag in analyzer_plugin.diagnostics(db, module_id) {
307            diagnostics.add(SemanticDiagnostic::new(
308                StableLocation::new(diag.stable_ptr),
309                SemanticDiagnosticKind::PluginDiagnostic(diag),
310            ));
311        }
312    }
313
314    Ok(diagnostics.build())
315}
316
317/// Adds diagnostics for duplicated names from macro expansions.
318fn add_duplicated_names_from_macro_expansions_diagnostics<'db>(
319    db: &'db dyn Database,
320    module_id: ModuleId<'db>,
321    diagnostics: &mut DiagnosticsBuilder<'db, SemanticDiagnostic<'db>>,
322) {
323    if matches!(module_id, ModuleId::MacroCall { .. }) {
324        // Macro calls are handled by the caller.
325        return;
326    }
327    let mut names = UnorderedHashSet::<SmolStrId<'_>>::default();
328    for defined_module in chain!([&module_id], module_macro_modules(db, false, module_id)) {
329        let Ok(data) = db.priv_module_semantic_data(*defined_module) else {
330            continue;
331        };
332        for (name, info) in data.items.iter() {
333            if !names.insert(*name)
334                && let Ok(stable_ptr) =
335                    db.module_item_name_stable_ptr(*defined_module, info.item_id)
336            {
337                diagnostics
338                    .report(stable_ptr, SemanticDiagnosticKind::NameDefinedMultipleTimes(*name));
339            }
340        }
341    }
342}
343
344fn crate_analyzer_plugins<'db>(
345    db: &'db dyn Database,
346    crate_id: CrateId<'db>,
347) -> Arc<Vec<AnalyzerPluginId<'db>>> {
348    db.analyzer_plugin_overrides()
349        .get(&crate_id)
350        .cloned()
351        .unwrap_or_else(|| db.default_analyzer_plugins())
352}
353
354#[salsa::tracked]
355fn declared_allows(db: &dyn Database, crate_id: CrateId<'_>) -> Arc<OrderedHashSet<String>> {
356    let base_lints = [DEPRECATED_ATTR, UNUSED_IMPORTS, UNUSED_VARIABLES];
357
358    let crate_analyzer_plugins = db.crate_analyzer_plugins(crate_id);
359
360    Arc::new(OrderedHashSet::from_iter(chain!(
361        base_lints.map(|attr| attr.into()),
362        crate_analyzer_plugins.iter().flat_map(|plugin| plugin.long(db).declared_allows())
363    )))
364}
365
366/// Adds diagnostics for unused items in a module.
367///
368/// Returns `None` if skipped attempt to add diagnostics.
369fn add_unused_item_diagnostics<'db>(
370    db: &'db dyn Database,
371    module_id: ModuleId<'db>,
372    data: &ModuleSemanticData<'db>,
373    diagnostics: &mut DiagnosticsBuilder<'db, SemanticDiagnostic<'db>>,
374) {
375    let Ok(all_used_uses) = db.module_all_used_uses(module_id) else {
376        return;
377    };
378    for info in data.items.values() {
379        if info.visibility == Visibility::Public {
380            continue;
381        }
382        if let ModuleItemId::Use(use_id) = info.item_id {
383            add_unused_import_diagnostics(db, all_used_uses, use_id, diagnostics);
384        };
385    }
386}
387
388/// Adds diagnostics for unused imports.
389fn add_unused_import_diagnostics<'db>(
390    db: &'db dyn Database,
391    all_used_uses: &OrderedHashSet<UseId<'db>>,
392    use_id: UseId<'db>,
393    diagnostics: &mut DiagnosticsBuilder<'db, SemanticDiagnostic<'db>>,
394) {
395    let _iife = (|| {
396        let item = db.use_resolved_item(use_id).ok()?;
397        // TODO(orizi): Properly handle usages of impls, and then add warnings on their usages as
398        // well.
399        require(!matches!(
400            item,
401            ResolvedGenericItem::Impl(_) | ResolvedGenericItem::GenericImplAlias(_)
402        ))?;
403        require(!all_used_uses.contains(&use_id))?;
404        let resolver_data = db.use_resolver_data(use_id).ok()?;
405
406        require(
407            !resolver_data
408                .feature_config
409                .allowed_lints
410                .contains(&SmolStrId::from(db, UNUSED_IMPORTS)),
411        )?;
412        Some(diagnostics.add(SemanticDiagnostic::new(
413            StableLocation::new(use_id.untyped_stable_ptr(db)),
414            SemanticDiagnosticKind::UnusedImport(use_id),
415        )))
416    })();
417}
418
419#[salsa::tracked]
420fn file_semantic_diagnostics<'db>(
421    db: &'db dyn Database,
422    file_id: FileId<'db>,
423) -> Maybe<Diagnostics<'db, SemanticDiagnostic<'db>>> {
424    let mut diagnostics = DiagnosticsBuilder::default();
425    for module_id in db.file_modules(file_id)?.iter().copied() {
426        if let Ok(module_diagnostics) = db.module_semantic_diagnostics(module_id) {
427            diagnostics.extend(module_diagnostics)
428        }
429    }
430    Ok(diagnostics.build())
431}
432
433#[salsa::tracked]
434pub fn lookup_resolved_generic_item_by_ptr<'db>(
435    db: &'db dyn Database,
436    id: LookupItemId<'db>,
437    ptr: ast::TerminalIdentifierPtr<'db>,
438) -> Option<ResolvedGenericItem<'db>> {
439    get_resolver_data_options(id, db)
440        .into_iter()
441        .find_map(|resolver_data| resolver_data.resolved_items.generic.get(&ptr).cloned())
442}
443
444#[salsa::tracked]
445pub fn lookup_resolved_concrete_item_by_ptr<'db>(
446    db: &'db dyn Database,
447    id: LookupItemId<'db>,
448    ptr: ast::TerminalIdentifierPtr<'db>,
449) -> Option<ResolvedConcreteItem<'db>> {
450    get_resolver_data_options(id, db)
451        .into_iter()
452        .find_map(|resolver_data| resolver_data.resolved_items.concrete.get(&ptr).cloned())
453}
454
455pub fn get_resolver_data_options<'db>(
456    id: LookupItemId<'db>,
457    db: &'db dyn Database,
458) -> Vec<Arc<ResolverData<'db>>> {
459    match id {
460        LookupItemId::ModuleItem(module_item) => match module_item {
461            ModuleItemId::Constant(id) => vec![db.constant_resolver_data(id)],
462            ModuleItemId::Submodule(_) => vec![],
463            ModuleItemId::Use(id) => vec![db.use_resolver_data(id)],
464            ModuleItemId::FreeFunction(id) => vec![
465                db.free_function_declaration_resolver_data(id),
466                db.free_function_body_resolver_data(id),
467            ],
468            ModuleItemId::Struct(id) => vec![
469                db.struct_declaration_resolver_data(id),
470                db.struct_definition_resolver_data(id),
471            ],
472            ModuleItemId::Enum(id) => {
473                vec![db.enum_definition_resolver_data(id), db.enum_declaration_resolver_data(id)]
474            }
475            ModuleItemId::TypeAlias(id) => vec![db.module_type_alias_resolver_data(id)],
476            ModuleItemId::ImplAlias(id) => vec![db.impl_alias_resolver_data(id)],
477            ModuleItemId::Trait(id) => vec![db.trait_resolver_data(id)],
478            ModuleItemId::Impl(id) => vec![db.impl_def_resolver_data(id)],
479            ModuleItemId::ExternType(_) => vec![],
480            ModuleItemId::ExternFunction(id) => {
481                vec![db.extern_function_declaration_resolver_data(id)]
482            }
483            ModuleItemId::MacroDeclaration(id) => vec![db.macro_declaration_resolver_data(id)],
484        },
485        LookupItemId::TraitItem(id) => match id {
486            cairo_lang_defs::ids::TraitItemId::Function(id) => {
487                let mut res = vec![db.trait_function_resolver_data(id)];
488                if let Ok(Some(resolver_data)) = db.trait_function_body_resolver_data(id) {
489                    res.push(Ok(resolver_data));
490                }
491                res
492            }
493            cairo_lang_defs::ids::TraitItemId::Type(id) => vec![db.trait_type_resolver_data(id)],
494            cairo_lang_defs::ids::TraitItemId::Constant(id) => {
495                vec![db.trait_constant_resolver_data(id)]
496            }
497            cairo_lang_defs::ids::TraitItemId::Impl(id) => vec![db.trait_impl_resolver_data(id)],
498        },
499        LookupItemId::ImplItem(id) => match id {
500            cairo_lang_defs::ids::ImplItemId::Function(id) => {
501                vec![db.impl_function_resolver_data(id), db.impl_function_body_resolver_data(id)]
502            }
503            cairo_lang_defs::ids::ImplItemId::Type(id) => vec![db.impl_type_def_resolver_data(id)],
504            cairo_lang_defs::ids::ImplItemId::Constant(id) => {
505                vec![db.impl_constant_def_resolver_data(id)]
506            }
507            cairo_lang_defs::ids::ImplItemId::Impl(id) => vec![db.impl_impl_def_resolver_data(id)],
508        },
509    }
510    .into_iter()
511    .flatten()
512    .collect()
513}
514
515pub trait SemanticGroupEx: Database {
516    /// Overrides the default analyzer plugins available for [`CrateId`] with `plugins`.
517    ///
518    /// *Note*: Sets the following Salsa input: `SemanticGroup::analyzer_plugin_overrides`.
519    fn set_override_crate_analyzer_plugins(
520        &mut self,
521        crate_id: CrateId<'_>,
522        plugins: Arc<[AnalyzerPluginId<'_>]>,
523    ) {
524        let mut overrides = self.analyzer_plugin_overrides_input().clone();
525        let plugins = plugins.iter().map(|plugin| plugin.long(self).clone()).collect_vec();
526        overrides.insert(self.crate_input(crate_id).clone(), Arc::from(plugins));
527        let db_ref = self.as_dyn_database();
528        semantic_group_input(db_ref).set_analyzer_plugin_overrides(self).to(Some(overrides));
529    }
530}
531
532impl<T: Database + ?Sized> SemanticGroupEx for T {}
533
534/// An extension trait for [`SemanticGroup`] to manage plugin setters.
535pub trait PluginSuiteInput: Database {
536    /// Interns each plugin from the [`PluginSuite`] into the database.
537    fn intern_plugin_suite<'r>(&'r mut self, suite: PluginSuite) -> InternedPluginSuite<'r> {
538        let PluginSuite { plugins, inline_macro_plugins, analyzer_plugins } = suite;
539
540        let macro_plugins = plugins
541            .into_iter()
542            .map(|plugin| MacroPluginId::new(self, MacroPluginLongId(plugin)))
543            .collect::<Arc<[_]>>();
544
545        let inline_macro_plugins = Arc::new(
546            inline_macro_plugins
547                .into_iter()
548                .map(|(name, plugin)| {
549                    (name, InlineMacroExprPluginId::new(self, InlineMacroExprPluginLongId(plugin)))
550                })
551                .collect::<OrderedHashMap<_, _>>(),
552        );
553
554        let analyzer_plugins = analyzer_plugins
555            .into_iter()
556            .map(|plugin| AnalyzerPluginId::new(self, AnalyzerPluginLongId(plugin)))
557            .collect::<Arc<[_]>>();
558
559        InternedPluginSuite { macro_plugins, inline_macro_plugins, analyzer_plugins }
560    }
561
562    /// Sets macro, inline macro and analyzer plugins specified in the [`PluginSuite`] as default
563    /// for all crates.
564    ///
565    /// *Note*: Sets the following Salsa inputs: [`DefsGroup::default_macro_plugins`],
566    /// [`DefsGroup::default_inline_macro_plugins`], and
567    /// [`SemanticGroup::default_analyzer_plugins`].
568    fn set_default_plugins_from_suite(&mut self, suite: PluginSuite) {
569        let PluginSuite { plugins, inline_macro_plugins, analyzer_plugins } = suite;
570        // let interned = self.intern_plugin_suite(suite);
571
572        let macro_plugins = plugins.into_iter().map(MacroPluginLongId).collect_vec();
573
574        let inline_macro_plugins = inline_macro_plugins
575            .into_iter()
576            .map(|(name, plugin)| (name, InlineMacroExprPluginLongId(plugin)))
577            .collect::<OrderedHashMap<_, _>>();
578
579        let analyzer_plugins =
580            analyzer_plugins.into_iter().map(AnalyzerPluginLongId).collect::<Vec<_>>();
581
582        defs_group_input(self.as_dyn_database())
583            .set_default_macro_plugins(self)
584            .to(Some(macro_plugins));
585        defs_group_input(self.as_dyn_database())
586            .set_default_inline_macro_plugins(self)
587            .to(Some(inline_macro_plugins));
588        semantic_group_input(self.as_dyn_database())
589            .set_default_analyzer_plugins(self)
590            .to(Some(analyzer_plugins));
591    }
592
593    /// Sets macro, inline macro and analyzer plugins present in the [`PluginSuite`] for a crate
594    /// pointed to by the [`CrateId`], overriding the defaults for that crate.
595    ///
596    /// *Note*: Sets the following Salsa inputs: [`DefsGroup::macro_plugin_overrides`],
597    /// [`DefsGroup::inline_macro_plugin_overrides`], and
598    /// [`SemanticGroup::analyzer_plugin_overrides`].
599    fn set_override_crate_plugins_from_suite(
600        &mut self,
601        crate_id: CrateId<'_>,
602        suite: InternedPluginSuite<'_>,
603    ) {
604        let InternedPluginSuite { macro_plugins, inline_macro_plugins, analyzer_plugins } = suite;
605
606        self.set_override_crate_macro_plugins(crate_id, Arc::new(macro_plugins.to_vec()));
607        self.set_override_crate_inline_macro_plugins(crate_id, inline_macro_plugins);
608        self.set_override_crate_analyzer_plugins(crate_id, analyzer_plugins);
609    }
610}
611
612impl<T: Database + ?Sized> PluginSuiteInput for T {}
613
614/// Returns all ancestors (parents) of the given module, including the module itself, in order from
615/// closest to farthest.
616pub fn module_ancestors<'db>(
617    db: &'db dyn Database,
618    mut module_id: ModuleId<'db>,
619) -> Vec<ModuleId<'db>> {
620    let mut ancestors = Vec::new();
621    ancestors.push(module_id); // Include the module itself first
622    while let ModuleId::Submodule(submodule_id) = module_id {
623        let parent = submodule_id.parent_module(db);
624        ancestors.push(parent);
625        module_id = parent;
626    }
627    ancestors
628}
629
630/// Returns all ancestors (parents) and all macro-generated modules within them.
631pub fn module_fully_accessible_modules<'db>(
632    db: &'db dyn Database,
633    module_id: ModuleId<'db>,
634) -> OrderedHashSet<ModuleId<'db>> {
635    let mut result: Vec<ModuleId<'db>> = module_ancestors(db, module_id);
636    let mut index = 0;
637    while let Some(curr) = result.get(index).copied() {
638        index += 1;
639        if let Ok(macro_call_ids) = db.module_macro_calls_ids(curr) {
640            for macro_call_id in macro_call_ids.iter() {
641                if let Ok(generated_module_id) = db.macro_call_module_id(*macro_call_id) {
642                    result.push(generated_module_id);
643                }
644            }
645        }
646    }
647    result.into_iter().collect()
648}
649
650/// Cache for the semantic data of a crate.
651#[derive(PartialEq, Eq, Clone, salsa::Update)]
652pub struct ModuleSemanticDataCacheAndLoadingData<'db> {
653    /// Semantic data of the modules in the crate.
654    pub modules_semantic_data: Arc<OrderedHashMap<ModuleId<'db>, ModuleSemanticData<'db>>>,
655    /// Resolved implementations of the impl aliases in the crate.
656    pub impl_aliases_resolved_impls: Arc<OrderedHashMap<ImplAliasId<'db>, ImplId<'db>>>,
657    /// Loading data of the semantic cache.
658    pub loading_data: Arc<SemanticCacheLoadingData<'db>>,
659}
660
661#[salsa::tracked]
662fn cached_crate_semantic_data<'db>(
663    db: &'db dyn Database,
664    crate_id: CrateId<'db>,
665) -> Option<ModuleSemanticDataCacheAndLoadingData<'db>> {
666    load_cached_crate_modules_semantic(db, crate_id)
667}