| use clippy_utils::diagnostics::span_lint; |
| use clippy_utils::eq_expr_value; |
| use rustc_hir::{BinOpKind, Expr, ExprKind}; |
| use rustc_lint::{LateContext, LateLintPass}; |
| use rustc_middle::ty; |
| use rustc_session::declare_lint_pass; |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Detects C-style underflow/overflow checks. |
| /// |
| /// ### Why is this bad? |
| /// These checks will, by default, panic in debug builds rather than check |
| /// whether the operation caused an overflow. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// # let a = 1i32; |
| /// # let b = 2i32; |
| /// if a + b < a { |
| /// // handle overflow |
| /// } |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// # let a = 1i32; |
| /// # let b = 2i32; |
| /// if a.checked_add(b).is_none() { |
| /// // handle overflow |
| /// } |
| /// ``` |
| /// |
| /// Or: |
| /// ```no_run |
| /// # let a = 1i32; |
| /// # let b = 2i32; |
| /// if a.overflowing_add(b).1 { |
| /// // handle overflow |
| /// } |
| /// ``` |
| #[clippy::version = "pre 1.29.0"] |
| pub PANICKING_OVERFLOW_CHECKS, |
| correctness, |
| "overflow checks which will panic in debug mode" |
| } |
| |
| declare_lint_pass!(PanickingOverflowChecks => [PANICKING_OVERFLOW_CHECKS]); |
| |
| impl<'tcx> LateLintPass<'tcx> for PanickingOverflowChecks { |
| // a + b < a, a > a + b, a < a - b, a - b > a |
| fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { |
| if let ExprKind::Binary(op, lhs, rhs) = expr.kind |
| && let (lt, gt) = match op.node { |
| BinOpKind::Lt => (lhs, rhs), |
| BinOpKind::Gt => (rhs, lhs), |
| _ => return, |
| } |
| && let ctxt = expr.span.ctxt() |
| && let (op_lhs, op_rhs, other, commutative) = match (<.kind, >.kind) { |
| (&ExprKind::Binary(op, lhs, rhs), _) if op.node == BinOpKind::Add && ctxt == lt.span.ctxt() => { |
| (lhs, rhs, gt, true) |
| }, |
| (_, &ExprKind::Binary(op, lhs, rhs)) if op.node == BinOpKind::Sub && ctxt == gt.span.ctxt() => { |
| (lhs, rhs, lt, false) |
| }, |
| _ => return, |
| } |
| && let typeck = cx.typeck_results() |
| && let ty = typeck.expr_ty(op_lhs) |
| && matches!(ty.kind(), ty::Uint(_)) |
| && ty == typeck.expr_ty(op_rhs) |
| && ty == typeck.expr_ty(other) |
| && !expr.span.in_external_macro(cx.tcx.sess.source_map()) |
| && (eq_expr_value(cx, op_lhs, other) || (commutative && eq_expr_value(cx, op_rhs, other))) |
| { |
| span_lint( |
| cx, |
| PANICKING_OVERFLOW_CHECKS, |
| expr.span, |
| "you are trying to use classic C overflow conditions that will fail in Rust", |
| ); |
| } |
| } |
| } |