]>
Commit | Line | Data |
---|---|---|
17df50a5 | 1 | use clippy_utils::consts::constant_simple; |
cdc7bbd5 | 2 | use clippy_utils::diagnostics::span_lint; |
f20569fa XL |
3 | use rustc_hir as hir; |
4 | use rustc_lint::{LateContext, LateLintPass}; | |
5 | use rustc_session::{declare_tool_lint, impl_lint_pass}; | |
6 | use rustc_span::source_map::Span; | |
7 | ||
8 | declare_clippy_lint! { | |
94222f64 XL |
9 | /// ### What it does |
10 | /// Checks for integer arithmetic operations which could overflow or panic. | |
f20569fa XL |
11 | /// |
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 | |
16 | /// attempted. | |
17 | /// | |
94222f64 XL |
18 | /// ### Why is this bad? |
19 | /// Integer overflow will trigger a panic in debug builds or will wrap in | |
f20569fa XL |
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. | |
22 | /// | |
94222f64 | 23 | /// ### Example |
f20569fa XL |
24 | /// ```rust |
25 | /// # let a = 0; | |
26 | /// a + 1; | |
27 | /// ``` | |
28 | pub INTEGER_ARITHMETIC, | |
29 | restriction, | |
30 | "any integer arithmetic expression which could overflow or panic" | |
31 | } | |
32 | ||
33 | declare_clippy_lint! { | |
94222f64 XL |
34 | /// ### What it does |
35 | /// Checks for float arithmetic. | |
f20569fa | 36 | /// |
94222f64 XL |
37 | /// ### Why is this bad? |
38 | /// For some embedded systems or kernel development, it | |
f20569fa XL |
39 | /// can be useful to rule out floating-point numbers. |
40 | /// | |
94222f64 | 41 | /// ### Example |
f20569fa XL |
42 | /// ```rust |
43 | /// # let a = 0.0; | |
44 | /// a + 1.0; | |
45 | /// ``` | |
46 | pub FLOAT_ARITHMETIC, | |
47 | restriction, | |
48 | "any floating-point arithmetic statement" | |
49 | } | |
50 | ||
51 | #[derive(Copy, Clone, Default)] | |
52 | pub struct Arithmetic { | |
53 | expr_span: Option<Span>, | |
54 | /// This field is used to check whether expressions are constants, such as in enum discriminants | |
55 | /// and consts | |
56 | const_span: Option<Span>, | |
57 | } | |
58 | ||
59 | impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]); | |
60 | ||
61 | impl<'tcx> LateLintPass<'tcx> for Arithmetic { | |
62 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { | |
63 | if self.expr_span.is_some() { | |
64 | return; | |
65 | } | |
66 | ||
67 | if let Some(span) = self.const_span { | |
68 | if span.contains(expr.span) { | |
69 | return; | |
70 | } | |
71 | } | |
72 | match &expr.kind { | |
73 | hir::ExprKind::Binary(op, l, r) | hir::ExprKind::AssignOp(op, l, r) => { | |
74 | match op.node { | |
75 | hir::BinOpKind::And | |
76 | | hir::BinOpKind::Or | |
77 | | hir::BinOpKind::BitAnd | |
78 | | hir::BinOpKind::BitOr | |
79 | | hir::BinOpKind::BitXor | |
80 | | hir::BinOpKind::Eq | |
81 | | hir::BinOpKind::Lt | |
82 | | hir::BinOpKind::Le | |
83 | | hir::BinOpKind::Ne | |
84 | | hir::BinOpKind::Ge | |
85 | | hir::BinOpKind::Gt => return, | |
86 | _ => (), | |
87 | } | |
88 | ||
89 | let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); | |
90 | if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { | |
91 | match op.node { | |
92 | hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { | |
93 | hir::ExprKind::Lit(_lit) => (), | |
94 | hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { | |
95 | if let hir::ExprKind::Lit(lit) = &expr.kind { | |
96 | if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { | |
97 | span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); | |
98 | self.expr_span = Some(expr.span); | |
99 | } | |
100 | } | |
101 | }, | |
102 | _ => { | |
103 | span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); | |
104 | self.expr_span = Some(expr.span); | |
105 | }, | |
106 | }, | |
107 | _ => { | |
108 | span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); | |
109 | self.expr_span = Some(expr.span); | |
110 | }, | |
111 | } | |
112 | } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { | |
113 | span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); | |
114 | self.expr_span = Some(expr.span); | |
115 | } | |
116 | }, | |
117 | hir::ExprKind::Unary(hir::UnOp::Neg, arg) => { | |
118 | let ty = cx.typeck_results().expr_ty(arg); | |
119 | if constant_simple(cx, cx.typeck_results(), expr).is_none() { | |
120 | if ty.is_integral() { | |
121 | span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); | |
122 | self.expr_span = Some(expr.span); | |
123 | } else if ty.is_floating_point() { | |
124 | span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); | |
125 | self.expr_span = Some(expr.span); | |
126 | } | |
127 | } | |
128 | }, | |
129 | _ => (), | |
130 | } | |
131 | } | |
132 | ||
133 | fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { | |
134 | if Some(expr.span) == self.expr_span { | |
135 | self.expr_span = None; | |
136 | } | |
137 | } | |
138 | ||
139 | fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { | |
140 | let body_owner = cx.tcx.hir().body_owner(body.id()); | |
141 | ||
142 | match cx.tcx.hir().body_owner_kind(body_owner) { | |
143 | hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => { | |
144 | let body_span = cx.tcx.hir().span(body_owner); | |
145 | ||
146 | if let Some(span) = self.const_span { | |
147 | if span.contains(body_span) { | |
148 | return; | |
149 | } | |
150 | } | |
151 | self.const_span = Some(body_span); | |
152 | }, | |
153 | hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (), | |
154 | } | |
155 | } | |
156 | ||
157 | fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { | |
158 | let body_owner = cx.tcx.hir().body_owner(body.id()); | |
159 | let body_span = cx.tcx.hir().span(body_owner); | |
160 | ||
161 | if let Some(span) = self.const_span { | |
162 | if span.contains(body_span) { | |
163 | return; | |
164 | } | |
165 | } | |
166 | self.const_span = None; | |
167 | } | |
168 | } |