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