1 use clippy_utils
::consts
::constant_simple
;
2 use clippy_utils
::diagnostics
::span_lint
;
4 use rustc_lint
::{LateContext, LateLintPass}
;
5 use rustc_session
::{declare_tool_lint, impl_lint_pass}
;
6 use rustc_span
::source_map
::Span
;
10 /// Checks for integer arithmetic operations which could overflow or panic.
12 /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
13 /// of overflowing according to the [Rust
14 /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
15 /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
18 /// ### Why is this bad?
19 /// Integer overflow will trigger a panic in debug builds or will wrap in
20 /// release mode. Division by zero will cause a panic in either mode. In some applications one
21 /// wants explicitly checked, wrapping or saturating arithmetic.
28 #[clippy::version = "pre 1.29.0"]
29 pub INTEGER_ARITHMETIC
,
31 "any integer arithmetic expression which could overflow or panic"
34 declare_clippy_lint
! {
36 /// Checks for float arithmetic.
38 /// ### Why is this bad?
39 /// For some embedded systems or kernel development, it
40 /// can be useful to rule out floating-point numbers.
47 #[clippy::version = "pre 1.29.0"]
50 "any floating-point arithmetic statement"
53 #[derive(Copy, Clone, Default)]
54 pub struct Arithmetic
{
55 expr_span
: Option
<Span
>,
56 /// This field is used to check whether expressions are constants, such as in enum discriminants
58 const_span
: Option
<Span
>,
61 impl_lint_pass
!(Arithmetic
=> [INTEGER_ARITHMETIC
, FLOAT_ARITHMETIC
]);
63 impl<'tcx
> LateLintPass
<'tcx
> for Arithmetic
{
64 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx hir
::Expr
<'_
>) {
65 if self.expr_span
.is_some() {
69 if let Some(span
) = self.const_span
{
70 if span
.contains(expr
.span
) {
75 hir
::ExprKind
::Binary(op
, l
, r
) | hir
::ExprKind
::AssignOp(op
, l
, r
) => {
79 | hir
::BinOpKind
::BitAnd
80 | hir
::BinOpKind
::BitOr
81 | hir
::BinOpKind
::BitXor
87 | hir
::BinOpKind
::Gt
=> return,
91 let (l_ty
, r_ty
) = (cx
.typeck_results().expr_ty(l
), cx
.typeck_results().expr_ty(r
));
92 if l_ty
.peel_refs().is_integral() && r_ty
.peel_refs().is_integral() {
94 hir
::BinOpKind
::Div
| hir
::BinOpKind
::Rem
=> match &r
.kind
{
95 hir
::ExprKind
::Lit(_lit
) => (),
96 hir
::ExprKind
::Unary(hir
::UnOp
::Neg
, expr
) => {
97 if let hir
::ExprKind
::Lit(lit
) = &expr
.kind
{
98 if let rustc_ast
::ast
::LitKind
::Int(1, _
) = lit
.node
{
99 span_lint(cx
, INTEGER_ARITHMETIC
, expr
.span
, "integer arithmetic detected");
100 self.expr_span
= Some(expr
.span
);
105 span_lint(cx
, INTEGER_ARITHMETIC
, expr
.span
, "integer arithmetic detected");
106 self.expr_span
= Some(expr
.span
);
110 span_lint(cx
, INTEGER_ARITHMETIC
, expr
.span
, "integer arithmetic detected");
111 self.expr_span
= Some(expr
.span
);
114 } else if r_ty
.peel_refs().is_floating_point() && r_ty
.peel_refs().is_floating_point() {
115 span_lint(cx
, FLOAT_ARITHMETIC
, expr
.span
, "floating-point arithmetic detected");
116 self.expr_span
= Some(expr
.span
);
119 hir
::ExprKind
::Unary(hir
::UnOp
::Neg
, arg
) => {
120 let ty
= cx
.typeck_results().expr_ty(arg
);
121 if constant_simple(cx
, cx
.typeck_results(), expr
).is_none() {
122 if ty
.is_integral() {
123 span_lint(cx
, INTEGER_ARITHMETIC
, expr
.span
, "integer arithmetic detected");
124 self.expr_span
= Some(expr
.span
);
125 } else if ty
.is_floating_point() {
126 span_lint(cx
, FLOAT_ARITHMETIC
, expr
.span
, "floating-point arithmetic detected");
127 self.expr_span
= Some(expr
.span
);
135 fn check_expr_post(&mut self, _
: &LateContext
<'tcx
>, expr
: &'tcx hir
::Expr
<'_
>) {
136 if Some(expr
.span
) == self.expr_span
{
137 self.expr_span
= None
;
141 fn check_body(&mut self, cx
: &LateContext
<'_
>, body
: &hir
::Body
<'_
>) {
142 let body_owner
= cx
.tcx
.hir().body_owner_def_id(body
.id());
144 match cx
.tcx
.hir().body_owner_kind(body_owner
) {
145 hir
::BodyOwnerKind
::Static(_
) | hir
::BodyOwnerKind
::Const
=> {
146 let body_span
= cx
.tcx
.def_span(body_owner
);
148 if let Some(span
) = self.const_span
{
149 if span
.contains(body_span
) {
153 self.const_span
= Some(body_span
);
155 hir
::BodyOwnerKind
::Fn
| hir
::BodyOwnerKind
::Closure
=> (),
159 fn check_body_post(&mut self, cx
: &LateContext
<'_
>, body
: &hir
::Body
<'_
>) {
160 let body_owner
= cx
.tcx
.hir().body_owner(body
.id());
161 let body_span
= cx
.tcx
.hir().span(body_owner
);
163 if let Some(span
) = self.const_span
{
164 if span
.contains(body_span
) {
168 self.const_span
= None
;