|
| 1 | +use clippy_utils::consts::is_zero_integer_const; |
| 2 | +use clippy_utils::diagnostics::span_lint_and_then; |
| 3 | +use clippy_utils::sugg::Sugg; |
| 4 | +use rustc_errors::Applicability; |
| 5 | +use rustc_hir::{BinOpKind, Expr, ExprKind}; |
| 6 | +use rustc_lint::LateContext; |
| 7 | +use rustc_middle::ty; |
| 8 | + |
| 9 | +use super::UNSIGNED_SUBTRACTION_GT_ZERO; |
| 10 | + |
| 11 | +pub(super) fn check<'tcx>( |
| 12 | + cx: &LateContext<'tcx>, |
| 13 | + expr: &'tcx Expr<'tcx>, |
| 14 | + op: BinOpKind, |
| 15 | + lhs: &'tcx Expr<'tcx>, |
| 16 | + rhs: &'tcx Expr<'tcx>, |
| 17 | +) { |
| 18 | + // Avoid linting macro-generated code to reduce noise |
| 19 | + if expr.span.from_expansion() { |
| 20 | + return; |
| 21 | + } |
| 22 | + |
| 23 | + // Only consider strict relational comparisons where one side is zero and the other is a subtraction |
| 24 | + let sub_expr = match op { |
| 25 | + BinOpKind::Gt if is_zero_integer_const(cx, rhs, expr.span.ctxt()) => lhs, |
| 26 | + BinOpKind::Lt if is_zero_integer_const(cx, lhs, expr.span.ctxt()) => rhs, |
| 27 | + _ => return, |
| 28 | + }; |
| 29 | + |
| 30 | + // Ensure the compared expression is a subtraction |
| 31 | + let (a, b) = match sub_expr.kind { |
| 32 | + ExprKind::Binary(sub_op, a, b) if sub_op.node == BinOpKind::Sub => (a, b), |
| 33 | + _ => return, |
| 34 | + }; |
| 35 | + |
| 36 | + // Subtraction result type must be an unsigned primitive |
| 37 | + if !matches!(cx.typeck_results().expr_ty(sub_expr).peel_refs().kind(), ty::Uint(_)) { |
| 38 | + return; |
| 39 | + } |
| 40 | + |
| 41 | + // Suggest `a > b` preserving user formatting with parentheses as needed |
| 42 | + let mut app = Applicability::MaybeIncorrect; |
| 43 | + let (left_sugg, right_sugg) = ( |
| 44 | + Sugg::hir_with_applicability(cx, a, "_", &mut app).maybe_paren(), |
| 45 | + Sugg::hir_with_applicability(cx, b, "_", &mut app).maybe_paren(), |
| 46 | + ); |
| 47 | + let replacement = format!("{left_sugg} > {right_sugg}"); |
| 48 | + let neq_suggestion = format!("{left_sugg} != {right_sugg}"); |
| 49 | + |
| 50 | + span_lint_and_then( |
| 51 | + cx, |
| 52 | + UNSIGNED_SUBTRACTION_GT_ZERO, |
| 53 | + expr.span, |
| 54 | + "suspicious comparison of unsigned subtraction to zero", |
| 55 | + |diag| { |
| 56 | + diag.help("`a - b > 0` will panic in debug when a < b and wrap in release; `a > b` is clearer"); |
| 57 | + diag.help(format!("if you meant inequality, use {neq_suggestion}")); |
| 58 | + diag.span_suggestion(expr.span, "try", replacement, app); |
| 59 | + }, |
| 60 | + ); |
| 61 | +} |
0 commit comments