cairo_lint/lang/
db.rs

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        // NOTE: Order of operations matters here!
151        // Errors if something is not OK are very subtle, mostly this results in missing
152        // identifier diagnostics, or panics regarding lack of corelib items.
153
154        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}