Skip to main content

cairo_lang_semantic/
db.rs

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