87 lines
2.8 KiB
Rust
87 lines
2.8 KiB
Rust
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::lint::in_external_macro;
|
|
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)
|
|
&& !in_external_macro(cx.tcx.sess, expr.span)
|
|
&& (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",
|
|
);
|
|
}
|
|
}
|
|
}
|