cairo_lint_core/
plugin.rs1use cairo_lang_defs::ids::{LanguageElementId, ModuleId};
2use cairo_lang_defs::plugin::PluginDiagnostic;
3use cairo_lang_filesystem::ids::FileLongId;
4use cairo_lang_semantic::db::SemanticGroup;
5use cairo_lang_semantic::plugin::{AnalyzerPlugin, PluginSuite};
6use cairo_lang_syntax::node::db::SyntaxGroup;
7use cairo_lang_syntax::node::helpers::QueryAttrs;
8use cairo_lang_syntax::node::SyntaxNode;
9use cairo_lang_utils::LookupIntern;
10
11use crate::context::{
12 get_all_checking_functions, get_name_for_diagnostic_message, get_unique_allowed_names,
13};
14
15pub fn cairo_lint_plugin_suite() -> PluginSuite {
16 let mut suite = PluginSuite::default();
17 suite.add_analyzer_plugin::<CairoLint>();
18 suite
19}
20
21pub fn cairo_lint_allow_plugin_suite() -> PluginSuite {
22 let mut suite = PluginSuite::default();
23 suite.add_analyzer_plugin::<CairoLintAllow>();
24 suite
25}
26
27#[derive(Debug, Default)]
28pub struct CairoLint {
29 include_compiler_generated_files: bool,
30}
31
32impl CairoLint {
33 pub fn new(include_compiler_generated_files: bool) -> Self {
34 Self {
35 include_compiler_generated_files,
36 }
37 }
38}
39
40impl AnalyzerPlugin for CairoLint {
41 fn declared_allows(&self) -> Vec<String> {
42 get_unique_allowed_names()
43 .iter()
44 .map(ToString::to_string)
45 .collect()
46 }
47
48 fn diagnostics(&self, db: &dyn SemanticGroup, module_id: ModuleId) -> Vec<PluginDiagnostic> {
49 let mut diags = Vec::new();
50 let Ok(items) = db.module_items(module_id) else {
51 return diags;
52 };
53 for item in &*items {
54 if !self.include_compiler_generated_files
56 && matches!(
57 item.stable_location(db.upcast())
58 .file_id(db.upcast())
59 .lookup_intern(db),
60 FileLongId::Virtual(_) | FileLongId::External(_)
61 )
62 {
63 continue;
64 }
65
66 let checking_functions = get_all_checking_functions();
67
68 for checking_function in checking_functions {
69 checking_function(db, item, &mut diags);
70 }
71 }
72
73 diags
74 .into_iter()
75 .filter(|diag| {
76 let node = diag.stable_ptr.lookup(db.upcast());
77 let allowed_name = get_name_for_diagnostic_message(&diag.message).unwrap();
78 !node_has_ascendants_with_allow_name_attr(db.upcast(), node, allowed_name)
79 })
80 .collect()
81 }
82}
83
84#[derive(Debug, Default)]
88pub struct CairoLintAllow;
89
90impl AnalyzerPlugin for CairoLintAllow {
91 fn diagnostics(&self, _db: &dyn SemanticGroup, _module_id: ModuleId) -> Vec<PluginDiagnostic> {
92 Vec::new()
93 }
94
95 fn declared_allows(&self) -> Vec<String> {
96 get_unique_allowed_names()
97 .iter()
98 .map(ToString::to_string)
99 .collect()
100 }
101}
102
103fn node_has_ascendants_with_allow_name_attr(
104 db: &dyn SyntaxGroup,
105 node: SyntaxNode,
106 allowed_name: &'static str,
107) -> bool {
108 let mut current_node = node;
109 while let Some(node) = current_node.parent() {
110 if node.has_attr_with_arg(db, "allow", allowed_name) {
111 return true;
112 }
113 current_node = node;
114 }
115 false
116}