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