Skip to main content

forge_lint/sol/info/
boolean_equal.rs

1use super::BooleanEqual;
2use crate::{
3    linter::{EarlyLintPass, LintContext, Suggestion},
4    sol::{Severity, SolLint, analysis::ast_bool_literal},
5};
6use solar::{
7    ast::{BinOpKind, Expr, ExprKind},
8    interface::diagnostics::Applicability,
9};
10
11declare_forge_lint!(
12    BOOLEAN_EQUAL,
13    Severity::Info,
14    "boolean-equal",
15    "boolean comparison to a constant can be simplified"
16);
17
18impl<'ast> EarlyLintPass<'ast> for BooleanEqual {
19    fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) {
20        let ExprKind::Binary(left, op, right) = &expr.kind else { return };
21        if !matches!(op.kind, BinOpKind::Eq | BinOpKind::Ne) {
22            return;
23        }
24        let simplified = match (ast_bool_literal(left), ast_bool_literal(right)) {
25            (None, None) => return,
26            (Some(_), Some(_)) => None,
27            (Some(constant), None) => simplify(ctx, right, op.kind, constant),
28            (None, Some(constant)) => simplify(ctx, left, op.kind, constant),
29        };
30        match simplified {
31            Some(simplified) => ctx.emit_with_suggestion(
32                &BOOLEAN_EQUAL,
33                expr.span,
34                Suggestion::fix(simplified, Applicability::MachineApplicable)
35                    .with_desc("consider simplifying to"),
36            ),
37            None => ctx.emit(&BOOLEAN_EQUAL, expr.span),
38        }
39    }
40}
41
42/// `x == true` / `x != false` simplify to `x`, the other two forms to `!x`.
43fn simplify(ctx: &LintContext, expr: &Expr<'_>, op: BinOpKind, constant: bool) -> Option<String> {
44    let snippet = ctx.span_to_snippet(expr.span)?;
45    let negate = (op == BinOpKind::Eq) != constant;
46    let atomic = matches!(
47        expr.peel_parens().kind,
48        ExprKind::Call(..)
49            | ExprKind::CallOptions(..)
50            | ExprKind::Ident(_)
51            | ExprKind::Index(..)
52            | ExprKind::Lit(..)
53            | ExprKind::Member(..)
54    );
55    Some(match (negate, atomic) {
56        (false, _) => snippet,
57        (true, true) => format!("!{snippet}"),
58        (true, false) => format!("!({snippet})"),
59    })
60}