1use anyhow::{Result, anyhow};
2use cairo_lang_compiler::{
3 db::validate_corelib,
4 project::{ProjectConfig, update_crate_roots_from_project_config},
5};
6use cairo_lang_defs::{
7 db::{defs_group_input, init_defs_group, init_external_files},
8 ids::{InlineMacroExprPluginLongId, MacroPluginLongId},
9};
10use cairo_lang_filesystem::flag::FlagsGroup;
11use cairo_lang_filesystem::{
12 cfg::CfgSet,
13 db::{FilesGroup, init_dev_corelib, init_files_group},
14 detect::detect_corelib,
15 flag::Flag,
16 ids::FlagLongId,
17};
18use cairo_lang_semantic::{
19 db::{init_semantic_group, semantic_group_input},
20 ids::AnalyzerPluginLongId,
21 inline_macros::get_default_plugin_suite,
22 plugin::PluginSuite,
23};
24
25use crate::plugin::cairo_lint_allow_plugin_suite;
26use salsa::Setter;
27
28#[salsa::db]
29#[derive(Clone)]
30pub struct LinterAnalysisDatabase {
31 storage: salsa::Storage<Self>,
32}
33
34impl LinterAnalysisDatabase {
35 pub fn builder() -> LinterAnalysisDatabaseBuilder {
36 LinterAnalysisDatabaseBuilder::new()
37 }
38
39 fn new(mut default_plugin_suite: PluginSuite) -> Self {
40 let mut res = Self {
41 storage: Default::default(),
42 };
43 init_files_group(&mut res);
44 init_defs_group(&mut res);
45 init_semantic_group(&mut res);
46 init_external_files(&mut res);
47
48 default_plugin_suite.add(cairo_lint_allow_plugin_suite());
49
50 defs_group_input(&res)
51 .set_default_macro_plugins(&mut res)
52 .to(Some(
53 default_plugin_suite
54 .plugins
55 .into_iter()
56 .map(MacroPluginLongId)
57 .collect(),
58 ));
59 defs_group_input(&res)
60 .set_default_inline_macro_plugins(&mut res)
61 .to(Some(
62 default_plugin_suite
63 .inline_macro_plugins
64 .into_iter()
65 .map(|(name, value)| (name, InlineMacroExprPluginLongId(value)))
66 .collect(),
67 ));
68 semantic_group_input(&res)
69 .set_default_analyzer_plugins(&mut res)
70 .to(Some(
71 default_plugin_suite
72 .analyzer_plugins
73 .into_iter()
74 .map(AnalyzerPluginLongId)
75 .collect(),
76 ));
77
78 res
79 }
80}
81
82impl salsa::Database for LinterAnalysisDatabase {}
83
84#[derive(Clone, Debug)]
85pub struct LinterAnalysisDatabaseBuilder {
86 default_plugin_suite: PluginSuite,
87 detect_corelib: bool,
88 auto_withdraw_gas: bool,
89 panic_backtrace: bool,
90 unsafe_panic: bool,
91 project_config: Option<Box<ProjectConfig>>,
92 cfg_set: Option<CfgSet>,
93}
94
95impl LinterAnalysisDatabaseBuilder {
96 fn new() -> Self {
97 Self {
98 default_plugin_suite: get_default_plugin_suite(),
99 detect_corelib: false,
100 auto_withdraw_gas: true,
101 panic_backtrace: false,
102 unsafe_panic: false,
103 project_config: None,
104 cfg_set: None,
105 }
106 }
107
108 pub fn with_default_plugin_suite(&mut self, suite: PluginSuite) -> &mut Self {
109 self.default_plugin_suite.add(suite);
110 self
111 }
112
113 pub fn clear_plugins(&mut self) -> &mut Self {
114 self.default_plugin_suite = get_default_plugin_suite();
115 self
116 }
117
118 pub fn detect_corelib(&mut self) -> &mut Self {
119 self.detect_corelib = true;
120 self
121 }
122
123 pub fn with_project_config(&mut self, config: ProjectConfig) -> &mut Self {
124 self.project_config = Some(Box::new(config));
125 self
126 }
127
128 pub fn with_cfg(&mut self, cfg_set: impl Into<CfgSet>) -> &mut Self {
129 self.cfg_set = Some(cfg_set.into());
130 self
131 }
132
133 pub fn skip_auto_withdraw_gas(&mut self) -> &mut Self {
134 self.auto_withdraw_gas = false;
135 self
136 }
137
138 pub fn with_panic_backtrace(&mut self) -> &mut Self {
139 self.panic_backtrace = true;
140 self
141 }
142
143 pub fn with_unsafe_panic(&mut self) -> &mut Self {
144 self.unsafe_panic = true;
145 self
146 }
147
148 pub fn build(&mut self) -> Result<LinterAnalysisDatabase> {
149 let mut db = LinterAnalysisDatabase::new(self.default_plugin_suite.clone());
154
155 if let Some(cfg_set) = &self.cfg_set {
156 db.use_cfg(cfg_set);
157 }
158
159 if self.detect_corelib {
160 let path =
161 detect_corelib().ok_or_else(|| anyhow!("Failed to find development corelib."))?;
162 init_dev_corelib(&mut db, path)
163 }
164
165 db.set_flag(
166 FlagLongId("add_withdraw_gas".to_string()),
167 Some(Flag::AddWithdrawGas(self.auto_withdraw_gas)),
168 );
169
170 db.set_flag(
171 FlagLongId("panic_backtrace".to_string()),
172 Some(Flag::PanicBacktrace(self.panic_backtrace)),
173 );
174
175 db.set_flag(
176 FlagLongId("unsafe_panic".to_string()),
177 Some(Flag::UnsafePanic(self.unsafe_panic)),
178 );
179
180 if let Some(config) = &self.project_config {
181 update_crate_roots_from_project_config(&mut db, config.as_ref());
182 }
183 validate_corelib(&db)?;
184
185 Ok(db)
186 }
187}