forge_lint/sol/info/
boolean_cst.rs1use super::BooleanCst;
2use crate::{
3 linter::{EarlyLintPass, LintContext},
4 sol::{Severity, SolLint, analysis::ast_bool_literal},
5};
6use solar::ast::{BinOpKind, Expr, ExprKind, Stmt, StmtKind, VariableDefinition};
7
8declare_forge_lint!(BOOLEAN_CST, Severity::Med, "boolean-cst", "misuse of a boolean constant");
9
10impl<'ast> EarlyLintPass<'ast> for BooleanCst {
11 fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) {
12 match &stmt.kind {
13 StmtKind::If(cond, ..)
14 | StmtKind::DoWhile(_, cond)
15 | StmtKind::For { cond: Some(cond), .. } => {
16 check_expr(ctx, cond, false);
17 }
18 StmtKind::While(cond, _) => check_expr(ctx, cond, ast_bool_literal(cond) == Some(true)),
20 StmtKind::DeclMulti(_, expr) | StmtKind::Expr(expr) | StmtKind::Return(Some(expr)) => {
21 check_expr(ctx, expr, true);
22 }
23 _ => {}
24 }
25 }
26
27 fn check_variable_definition(
28 &mut self,
29 ctx: &LintContext,
30 var: &'ast VariableDefinition<'ast>,
31 ) {
32 if let Some(initializer) = &var.initializer {
33 check_expr(ctx, initializer, true);
34 }
35 }
36}
37
38fn check_expr(ctx: &LintContext, expr: &Expr<'_>, allow_bare: bool) {
42 if ast_bool_literal(expr).is_some() {
43 if !allow_bare {
44 ctx.emit(&BOOLEAN_CST, expr.span);
45 }
46 return;
47 }
48 match &expr.kind {
49 ExprKind::Assign(_, _, rhs) => check_expr(ctx, rhs, true),
50 ExprKind::Binary(left, op, right)
52 if !(matches!(op.kind, BinOpKind::Eq | BinOpKind::Ne)
53 && (ast_bool_literal(left).is_some() || ast_bool_literal(right).is_some())) =>
54 {
55 check_expr(ctx, left, false);
56 check_expr(ctx, right, false);
57 }
58 ExprKind::Call(_, args) => args.exprs().for_each(|arg| check_expr(ctx, arg, true)),
59 ExprKind::Delete(expr) | ExprKind::Unary(_, expr) => check_expr(ctx, expr, false),
60 ExprKind::Ternary(cond, true_expr, false_expr) => {
61 check_expr(ctx, cond, false);
62 check_expr(ctx, true_expr, false);
63 check_expr(ctx, false_expr, false);
64 }
65 ExprKind::Tuple(exprs) => exprs
66 .iter()
67 .filter_map(|expr| Option::from(expr.as_deref()))
68 .for_each(|expr| check_expr(ctx, expr, false)),
69 _ => {}
70 }
71}