cairo_lint/lints/
panic.rs1use cairo_lang_defs::diagnostic_utils::StableLocation;
2use cairo_lang_defs::ids::ModuleItemId;
3use cairo_lang_defs::plugin::PluginDiagnostic;
4use cairo_lang_diagnostics::Severity;
5use cairo_lang_filesystem::db::get_originating_location;
6use cairo_lang_parser::db::ParserGroup;
7use cairo_lang_semantic::ExprFunctionCall;
8use cairo_lang_semantic::items::functions::GenericFunctionId;
9use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};
10use if_chain::if_chain;
11use itertools::Itertools;
12
13use crate::context::{CairoLintKind, Lint};
14
15use crate::LinterGroup;
16use crate::helper::ASSERT_FORMATTER_NAME;
17use crate::queries::{get_all_function_bodies, get_all_function_calls};
18use cairo_lang_filesystem::ids::SpanInFile;
19use salsa::Database;
20
21pub struct PanicInCode;
22
23impl Lint for PanicInCode {
35 fn allowed_name(&self) -> &'static str {
36 "panic"
37 }
38
39 fn diagnostic_message(&self) -> &'static str {
40 "Leaving `panic` in the code is discouraged."
41 }
42
43 fn kind(&self) -> CairoLintKind {
44 CairoLintKind::Panic
45 }
46
47 fn is_enabled(&self) -> bool {
48 false
49 }
50}
51
52#[tracing::instrument(skip_all, level = "trace")]
53pub fn check_panic_usage<'db>(
54 db: &'db dyn Database,
55 item: &ModuleItemId<'db>,
56 diagnostics: &mut Vec<PluginDiagnostic<'db>>,
57) {
58 let function_bodies = get_all_function_bodies(db, item);
59 for function_body in function_bodies.iter() {
60 let function_call_exprs = get_all_function_calls(function_body);
61 for function_call_expr in function_call_exprs.unique() {
62 check_single_panic_usage(db, &function_call_expr, diagnostics);
63 }
64 }
65}
66
67fn check_single_panic_usage<'db>(
68 db: &'db dyn Database,
69 function_call_expr: &ExprFunctionCall<'db>,
70 diagnostics: &mut Vec<PluginDiagnostic<'db>>,
71) {
72 let init_node = function_call_expr.stable_ptr.lookup(db).as_syntax_node();
73
74 let concrete_function_id = function_call_expr
75 .function
76 .get_concrete(db)
77 .generic_function;
78
79 let corelib_context = db.corelib_context();
80
81 let is_panic = if let GenericFunctionId::Extern(id) = concrete_function_id
83 && id == corelib_context.get_panic_function_id()
84 {
85 true
86 } else {
87 false
88 };
89
90 let is_panic_with_byte_array = if let GenericFunctionId::Free(id) = concrete_function_id
92 && id == corelib_context.get_panic_with_byte_array_function_id()
93 {
94 true
95 } else {
96 false
97 };
98
99 let is_assert_panic = is_panic_with_byte_array
101 && function_call_expr
102 .stable_ptr
103 .lookup(db)
104 .as_syntax_node()
105 .get_text(db)
106 .contains(ASSERT_FORMATTER_NAME);
107
108 if !(is_panic || is_panic_with_byte_array) || is_assert_panic {
110 return;
111 }
112
113 let initial_file_id = StableLocation::new(function_call_expr.stable_ptr.untyped()).file_id(db);
116 let SpanInFile { file_id, span } = get_originating_location(
117 db,
118 SpanInFile {
119 file_id: initial_file_id,
120 span: function_call_expr
121 .stable_ptr
122 .lookup(db)
123 .as_syntax_node()
124 .span(db),
125 },
126 None,
127 );
128 if initial_file_id == file_id {
130 diagnostics.push(PluginDiagnostic {
131 stable_ptr: init_node.stable_ptr(db),
132 message: PanicInCode.diagnostic_message().to_owned(),
133 severity: Severity::Warning,
134 inner_span: None,
135 error_code: None,
136 });
137 } else {
138 if_chain! {
141 if let Some(text_position) = span.position_in_file(db, file_id);
142 if let Ok(file_node) = db.file_syntax(file_id);
143 then {
144 let syntax_node = file_node.lookup_position(db, text_position.start);
145 diagnostics.push(PluginDiagnostic {
146 stable_ptr: syntax_node.stable_ptr(db),
147 message: PanicInCode.diagnostic_message().to_owned(),
148 severity: Severity::Warning,
149 inner_span: None,
150 error_code: None,
151 });
152 }
153 }
154 }
155}