use std::collections::HashSet;
use std::ops::Not;
use std::sync::Arc;
use cairo_lang_defs::db::{DefsGroup, defs_group_input, init_defs_group, init_external_files};
use cairo_lang_defs::ids::{InlineMacroExprPluginLongId, MacroPluginLongId};
use cairo_lang_defs::plugin::MacroPlugin;
use cairo_lang_executable_plugin::executable_plugin_suite;
use cairo_lang_filesystem::cfg::{Cfg, CfgSet};
use cairo_lang_filesystem::db::{FilesGroup, files_group_input, init_files_group};
use cairo_lang_filesystem::ids::{CrateInput, CrateLongId};
use cairo_lang_lowering::db::init_lowering_group;
use cairo_lang_lowering::optimizations::config::Optimizations;
use cairo_lang_lowering::utils::InliningStrategy;
use cairo_lang_plugins::plugins::ConfigPlugin;
use cairo_lang_semantic::db::{
PluginSuiteInput, SemanticGroup, init_semantic_group, semantic_group_input,
};
use cairo_lang_semantic::ids::AnalyzerPluginLongId;
use cairo_lang_semantic::inline_macros::get_default_plugin_suite;
use cairo_lang_semantic::plugin::PluginSuite;
use cairo_lang_starknet::starknet_plugin_suite;
use cairo_lang_test_plugin::test_plugin_suite;
use cairo_lang_utils::Intern;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use cairo_lint::plugin::cairo_lint_allow_plugin_suite;
use itertools::Itertools;
use salsa::{Database, Durability, Setter};
pub use self::migration::*;
pub use self::semantic::*;
pub use self::swapper::*;
pub use self::syntax::*;
mod migration;
mod semantic;
mod swapper;
mod syntax;
#[salsa::db]
#[derive(Clone)]
pub struct AnalysisDatabase {
storage: salsa::Storage<Self>,
}
impl Default for AnalysisDatabase {
fn default() -> Self {
Self::new()
}
}
impl AnalysisDatabase {
pub fn new() -> Self {
let mut db = Self { storage: Default::default() };
init_external_files(&mut db);
init_files_group(&mut db);
init_defs_group(&mut db);
init_semantic_group(&mut db);
init_lowering_group(
&mut db,
Optimizations::enabled_with_default_movable_functions(InliningStrategy::Default),
None,
);
files_group_input(&db).set_cfg_set(&mut db).to(Some(Self::initial_cfg_set()));
let default_plugin_suite = Self::default_global_plugin_suite();
db.set_default_plugins_from_suite(default_plugin_suite);
let core_plugin_suite = Self::default_corelib_plugin_suite();
db.set_override_crate_plugins_from_suite(
CrateLongId::core(&db).into_crate_input(&db),
core_plugin_suite,
);
db
}
pub fn set_override_crate_plugins_from_suite(
&mut self,
crate_input: CrateInput,
plugins: PluginSuite,
) {
let mut overrides = self.macro_plugin_overrides_input().clone();
overrides.insert(
crate_input.clone(),
plugins.plugins.into_iter().map(MacroPluginLongId).collect(),
);
defs_group_input(self).set_macro_plugin_overrides(self).to(Some(overrides));
let mut overrides = self.analyzer_plugin_overrides_input().clone();
overrides.insert(
crate_input.clone(),
plugins.analyzer_plugins.into_iter().map(AnalyzerPluginLongId).collect(),
);
semantic_group_input(self).set_analyzer_plugin_overrides(self).to(Some(overrides));
let mut overrides = self.inline_macro_plugin_overrides_input().clone();
overrides.insert(
crate_input,
Arc::new(
plugins
.inline_macro_plugins
.into_iter()
.map(|(key, value)| (key, InlineMacroExprPluginLongId(value)))
.collect(),
),
);
defs_group_input(self).set_inline_macro_plugin_overrides(self).to(Some(overrides));
}
pub(crate) fn initial_cfg_set() -> CfgSet {
CfgSet::from_iter([Cfg::name("test"), Cfg::kv("target", "test")])
}
pub(crate) fn initial_cfg_set_for_deps() -> CfgSet {
CfgSet::from_iter([Cfg::kv("target", "test")])
}
pub fn cancel_all(&mut self) {
self.synthetic_write(Durability::LOW);
}
pub fn remove_crate_plugin_suite(&mut self, crate_input: CrateInput, plugins: PluginSuite) {
self.with_plugins_mut(
crate_input,
|macro_plugins, analyzer_plugins, inline_macro_plugins| {
let macro_plugins_set: HashSet<_> =
plugins.plugins.into_iter().map(MacroPluginLongId).collect();
let analyzer_plugins_set: HashSet<_> =
plugins.analyzer_plugins.into_iter().map(AnalyzerPluginLongId).collect();
let inline_macro_plugin_set: HashSet<_> = plugins
.inline_macro_plugins
.into_iter()
.map(|(_, arc)| InlineMacroExprPluginLongId(arc))
.collect();
macro_plugins.retain(|plugin| !macro_plugins_set.contains(plugin));
analyzer_plugins.retain(|plugin| !analyzer_plugins_set.contains(plugin));
inline_macro_plugins.retain(|_, plugin| !inline_macro_plugin_set.contains(plugin));
},
)
}
pub fn add_proc_macro_plugin_suite(&mut self, crate_input: CrateInput, plugins: PluginSuite) {
self.with_plugins_mut(
crate_input,
move |macro_plugins, analyzer_plugins, inline_macro_plugins| {
let maybe_cfg_plugin =
macro_plugins.is_empty().not().then(|| macro_plugins.remove(0));
*macro_plugins = maybe_cfg_plugin
.into_iter()
.chain(plugins.plugins.into_iter().map(MacroPluginLongId))
.chain(macro_plugins.iter().cloned())
.collect();
*analyzer_plugins = plugins
.analyzer_plugins
.into_iter()
.map(AnalyzerPluginLongId)
.chain(analyzer_plugins.iter().cloned())
.collect();
*inline_macro_plugins = plugins
.inline_macro_plugins
.into_iter()
.map(|(key, arc)| (key, InlineMacroExprPluginLongId(arc)))
.chain(inline_macro_plugins.iter().map(|(s, id)| (s.clone(), id.clone())))
.collect();
},
)
}
fn with_plugins_mut(
&mut self,
crate_input: CrateInput,
action: impl FnOnce(
&mut Vec<MacroPluginLongId>,
&mut Vec<AnalyzerPluginLongId>,
&mut OrderedHashMap<String, InlineMacroExprPluginLongId>,
),
) {
if !self
.crate_configs()
.keys()
.contains(&crate_input.clone().into_crate_long_id(self).intern(self))
{
return;
}
let mut macro_plugin_overrides_input = self.macro_plugin_overrides_input().clone();
let mut macro_plugins =
macro_plugin_overrides_input.get(&crate_input).map(|a| a.to_vec()).unwrap_or_default();
let mut analyzer_plugin_overrides_input = self.analyzer_plugin_overrides_input().clone();
let mut analyzer_plugins = analyzer_plugin_overrides_input
.get(&crate_input)
.map(|a| a.to_vec())
.unwrap_or_default();
let mut inline_macro_plugin_overrides_input =
self.inline_macro_plugin_overrides_input().clone();
let mut inline_macro_plugins = inline_macro_plugin_overrides_input
.get(&crate_input)
.map(|a| (**a).clone())
.unwrap_or_default();
action(&mut macro_plugins, &mut analyzer_plugins, &mut inline_macro_plugins);
assert!(
macro_plugins
.first()
.is_none_or(|id| id.plugin_type_id() == ConfigPlugin::default().plugin_type_id()),
"cfg plugin must be the first macro plugin"
);
macro_plugin_overrides_input.insert(crate_input.clone(), macro_plugins.into());
analyzer_plugin_overrides_input.insert(crate_input.clone(), analyzer_plugins.into());
inline_macro_plugin_overrides_input
.insert(crate_input.clone(), inline_macro_plugins.into());
defs_group_input(self)
.set_macro_plugin_overrides(self)
.to(Some(macro_plugin_overrides_input));
defs_group_input(self)
.set_inline_macro_plugin_overrides(self)
.to(Some(inline_macro_plugin_overrides_input));
semantic_group_input(self)
.set_analyzer_plugin_overrides(self)
.to(Some(analyzer_plugin_overrides_input));
}
fn default_global_plugin_suite() -> PluginSuite {
[
get_default_plugin_suite(),
starknet_plugin_suite(),
test_plugin_suite(),
executable_plugin_suite(),
cairo_lint_allow_plugin_suite(),
]
.into_iter()
.fold(PluginSuite::default(), |mut acc, suite| {
acc.add(suite);
acc
})
}
fn default_corelib_plugin_suite() -> PluginSuite {
[
get_default_plugin_suite(),
test_plugin_suite(),
executable_plugin_suite(),
cairo_lint_allow_plugin_suite(),
]
.into_iter()
.fold(PluginSuite::default(), |mut acc, suite| {
acc.add(suite);
acc
})
}
}
impl salsa::Database for AnalysisDatabase {}