cairo_lint/lints/performance/
inefficient_while_comp.rs1use cairo_lang_defs::ids::ModuleItemId;
2use cairo_lang_defs::plugin::PluginDiagnostic;
3use cairo_lang_diagnostics::Severity;
4use cairo_lang_semantic::{Arenas, Condition, Expr, ExprWhile};
5
6use crate::context::{CairoLintKind, Lint};
7
8use crate::queries::{get_all_function_bodies, get_all_while_expressions};
9use salsa::Database;
10
11pub struct InefficientWhileComparison;
12
13impl Lint for InefficientWhileComparison {
39 fn allowed_name(&self) -> &'static str {
40 "inefficient_while_comp"
41 }
42
43 fn diagnostic_message(&self) -> &'static str {
44 "using [`<`, `<=`, `>=`, `>`] exit conditions is inefficient. Consider \
45 switching to `!=` or using ArrayTrait::multi_pop_front."
46 }
47
48 fn kind(&self) -> CairoLintKind {
49 CairoLintKind::Performance
50 }
51
52 fn is_enabled(&self) -> bool {
53 false
54 }
55}
56
57const PARTIAL_ORD_PATTERNS: [&str; 4] = [
59 "PartialOrd::lt\"",
60 "PartialOrd::le\"",
61 "PartialOrd::gt\"",
62 "PartialOrd::ge\"",
63];
64
65#[tracing::instrument(skip_all, level = "trace")]
66pub fn check_inefficient_while_comp<'db>(
67 db: &'db dyn Database,
68 item: &ModuleItemId<'db>,
69 diagnostics: &mut Vec<PluginDiagnostic<'db>>,
70) {
71 let function_bodies = get_all_function_bodies(db, item);
72 for function_body in function_bodies {
73 let while_exprs = get_all_while_expressions(function_body);
74 let arenas = &function_body.arenas;
75 for while_expr in while_exprs.iter() {
76 check_single_inefficient_while_comp(db, while_expr, diagnostics, arenas);
77 }
78 }
79}
80
81fn check_single_inefficient_while_comp<'db>(
82 db: &'db dyn Database,
83 while_expr: &ExprWhile<'db>,
84 diagnostics: &mut Vec<PluginDiagnostic<'db>>,
85 arenas: &Arenas<'db>,
86) {
87 if let Condition::BoolExpr(expr_cond) = while_expr.condition {
91 check_expression(db, &arenas.exprs[expr_cond], diagnostics, arenas);
92 }
93}
94
95fn check_expression<'db>(
96 db: &'db dyn Database,
97 expr: &Expr<'db>,
98 diagnostics: &mut Vec<PluginDiagnostic<'db>>,
99 arenas: &Arenas<'db>,
100) {
101 match expr {
102 Expr::FunctionCall(func_call) => {
103 let func_name = func_call.function.name(db);
104 if PARTIAL_ORD_PATTERNS.iter().any(|p| func_name.ends_with(p)) {
105 diagnostics.push(PluginDiagnostic {
106 stable_ptr: func_call.stable_ptr.into(),
107 message: InefficientWhileComparison.diagnostic_message().to_owned(),
108 severity: Severity::Warning,
109 inner_span: None,
110 error_code: None,
111 });
112 }
113 }
114 Expr::LogicalOperator(expr_logical) => {
115 check_expression(db, &arenas.exprs[expr_logical.lhs], diagnostics, arenas);
116 check_expression(db, &arenas.exprs[expr_logical.rhs], diagnostics, arenas);
117 }
118 _ => {}
119 }
120}