Skip to content

Commit cd143da

Browse files
committed
[Arithmetic] Consider literal integers
1 parent 5735a3b commit cd143da

File tree

7 files changed

+147
-83
lines changed

7 files changed

+147
-83
lines changed

clippy_lints/src/operators/arithmetic.rs

Lines changed: 79 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55

66
use super::ARITHMETIC;
77
use clippy_utils::{consts::constant_simple, diagnostics::span_lint};
8+
use rustc_ast as ast;
89
use rustc_data_structures::fx::FxHashSet;
910
use rustc_hir as hir;
1011
use rustc_lint::{LateContext, LateLintPass};
12+
use rustc_middle::ty::Ty;
1113
use rustc_session::impl_lint_pass;
12-
use rustc_span::source_map::Span;
14+
use rustc_span::source_map::{Span, Spanned};
1315

1416
const HARD_CODED_ALLOWED: &[&str] = &["std::num::Saturating", "std::string::String", "std::num::Wrapping"];
1517

@@ -50,36 +52,83 @@ impl Arithmetic {
5052
span_lint(cx, ARITHMETIC, expr.span, "arithmetic detected");
5153
self.expr_span = Some(expr.span);
5254
}
55+
56+
/// Checks assign operators (+=, -=, *=, /=) already handled by the CTFE.
57+
fn has_valid_assign_op(op: &Spanned<hir::BinOpKind>, rhs: &hir::Expr<'_>) -> bool {
58+
match op.node {
59+
hir::BinOpKind::Div | hir::BinOpKind::Rem => match rhs.kind {
60+
hir::ExprKind::Lit(_) => true,
61+
hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
62+
let hir::ExprKind::Lit(lit) = &expr.kind else {
63+
return true;
64+
};
65+
if let ast::LitKind::Int(1, _) = lit.node {
66+
return false;
67+
}
68+
true
69+
},
70+
_ => false,
71+
},
72+
_ => false,
73+
}
74+
}
75+
76+
/// Checks binary operators (+, -, *, /) already handled by the CTFE.
77+
fn has_valid_op(lhs: &hir::Expr<'_>, lhs_refs: Ty<'_>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool {
78+
let lhs_is_integral = lhs_refs.is_integral();
79+
let lhs_is_literal = matches!(lhs.kind, hir::ExprKind::Lit(_));
80+
let rhs_is_integral = rhs_refs.is_integral();
81+
let rhs_is_literal = matches!(rhs.kind, hir::ExprKind::Lit(_));
82+
lhs_is_integral && lhs_is_literal && rhs_is_integral && rhs_is_literal
83+
}
84+
85+
/// Manages when the lint should be triggered. Literal integers are ignored because they are
86+
/// already handled by the CTFE.
87+
fn manage_bin_ops(
88+
&mut self,
89+
cx: &LateContext<'_>,
90+
expr: &hir::Expr<'_>,
91+
op: &Spanned<hir::BinOpKind>,
92+
lhs: &hir::Expr<'_>,
93+
rhs: &hir::Expr<'_>,
94+
) {
95+
let (
96+
hir::BinOpKind::Add
97+
| hir::BinOpKind::Sub
98+
| hir::BinOpKind::Mul
99+
| hir::BinOpKind::Div
100+
| hir::BinOpKind::Rem
101+
| hir::BinOpKind::Shl
102+
| hir::BinOpKind::Shr
103+
) = op.node else {
104+
return;
105+
};
106+
if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
107+
return;
108+
}
109+
let lhs_refs = cx.typeck_results().expr_ty(lhs).peel_refs();
110+
let rhs_refs = cx.typeck_results().expr_ty(rhs).peel_refs();
111+
if lhs_refs.is_floating_point() && rhs_refs.is_floating_point() {
112+
self.issue_lint(cx, expr);
113+
return;
114+
}
115+
if Self::has_valid_assign_op(op, rhs) || Self::has_valid_op(lhs, lhs_refs, rhs, rhs_refs) {
116+
return;
117+
}
118+
self.issue_lint(cx, expr);
119+
}
53120
}
54121

55122
impl<'tcx> LateLintPass<'tcx> for Arithmetic {
56123
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
57-
if self.expr_span.is_some() {
58-
return;
59-
}
60-
if let Some(span) = self.const_span && span.contains(expr.span) {
124+
if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) {
61125
return;
62126
}
63127
match &expr.kind {
64128
hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => {
65-
let (
66-
hir::BinOpKind::Add
67-
| hir::BinOpKind::Sub
68-
| hir::BinOpKind::Mul
69-
| hir::BinOpKind::Div
70-
| hir::BinOpKind::Rem
71-
| hir::BinOpKind::Shl
72-
| hir::BinOpKind::Shr
73-
) = op.node else {
74-
return;
75-
};
76-
if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
77-
return;
78-
}
79-
self.issue_lint(cx, expr);
129+
self.manage_bin_ops(cx, expr, op, lhs, rhs)
80130
},
81131
hir::ExprKind::Unary(hir::UnOp::Neg, _) => {
82-
// CTFE already takes care of things like `-1` that do not overflow.
83132
if constant_simple(cx, cx.typeck_results(), expr).is_none() {
84133
self.issue_lint(cx, expr);
85134
}
@@ -89,16 +138,15 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic {
89138
}
90139

91140
fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
92-
let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
93-
match cx.tcx.hir().body_owner_kind(body_owner) {
94-
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => {
95-
let body_span = cx.tcx.def_span(body_owner);
96-
if let Some(span) = self.const_span && span.contains(body_span) {
97-
return;
98-
}
99-
self.const_span = Some(body_span);
100-
},
101-
hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => {},
141+
let body_owner = cx.tcx.hir().body_owner(body.id());
142+
let body_owner_def_id = cx.tcx.hir().local_def_id(body_owner);
143+
let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
144+
if let hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) = body_owner_kind {
145+
let body_span = cx.tcx.hir().span_with_body(body_owner);
146+
if let Some(span) = self.const_span && span.contains(body_span) {
147+
return;
148+
}
149+
self.const_span = Some(body_span);
102150
}
103151
}
104152

tests/ui/arithmetic.fixed

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// run-rustfix
22

33
#![allow(clippy::unnecessary_owned_empty_strings)]
4-
#![feature(saturating_int_impl)]
4+
#![feature(inline_const, saturating_int_impl)]
55
#![warn(clippy::arithmetic)]
66

77
use core::num::{Saturating, Wrapping};
@@ -24,4 +24,12 @@ pub fn hard_coded_allowed() {
2424
let _ = inferred_wrapping + inferred_wrapping;
2525
}
2626

27+
pub fn const_integer_ops() {
28+
const _A: i32 = 1 + 1;
29+
30+
let _a = 1 + 2;
31+
32+
let _a = const { 1 + 2 };
33+
}
34+
2735
fn main() {}

tests/ui/arithmetic.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// run-rustfix
22

33
#![allow(clippy::unnecessary_owned_empty_strings)]
4-
#![feature(saturating_int_impl)]
4+
#![feature(inline_const, saturating_int_impl)]
55
#![warn(clippy::arithmetic)]
66

77
use core::num::{Saturating, Wrapping};
@@ -24,4 +24,12 @@ pub fn hard_coded_allowed() {
2424
let _ = inferred_wrapping + inferred_wrapping;
2525
}
2626

27+
pub fn const_integer_ops() {
28+
const _A: i32 = 1 + 1;
29+
30+
let _a = 1 + 2;
31+
32+
let _a = const { 1 + 2 };
33+
}
34+
2735
fn main() {}

tests/ui/float_arithmetic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)]
1+
#![warn(clippy::arithmetic)]
22
#![allow(
33
unused,
44
clippy::shadow_reuse,

tests/ui/float_arithmetic.stderr

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,102 @@
1-
error: floating-point arithmetic detected
1+
error: arithmetic detected
22
--> $DIR/float_arithmetic.rs:15:5
33
|
44
LL | f * 2.0;
55
| ^^^^^^^
66
|
7-
= note: `-D clippy::float-arithmetic` implied by `-D warnings`
7+
= note: `-D clippy::arithmetic` implied by `-D warnings`
88

9-
error: floating-point arithmetic detected
9+
error: arithmetic detected
1010
--> $DIR/float_arithmetic.rs:17:5
1111
|
1212
LL | 1.0 + f;
1313
| ^^^^^^^
1414

15-
error: floating-point arithmetic detected
15+
error: arithmetic detected
1616
--> $DIR/float_arithmetic.rs:18:5
1717
|
1818
LL | f * 2.0;
1919
| ^^^^^^^
2020

21-
error: floating-point arithmetic detected
21+
error: arithmetic detected
2222
--> $DIR/float_arithmetic.rs:19:5
2323
|
2424
LL | f / 2.0;
2525
| ^^^^^^^
2626

27-
error: floating-point arithmetic detected
27+
error: arithmetic detected
2828
--> $DIR/float_arithmetic.rs:20:5
2929
|
3030
LL | f - 2.0 * 4.2;
3131
| ^^^^^^^^^^^^^
3232

33-
error: floating-point arithmetic detected
33+
error: arithmetic detected
3434
--> $DIR/float_arithmetic.rs:21:5
3535
|
3636
LL | -f;
3737
| ^^
3838

39-
error: floating-point arithmetic detected
39+
error: arithmetic detected
4040
--> $DIR/float_arithmetic.rs:23:5
4141
|
4242
LL | f += 1.0;
4343
| ^^^^^^^^
4444

45-
error: floating-point arithmetic detected
45+
error: arithmetic detected
4646
--> $DIR/float_arithmetic.rs:24:5
4747
|
4848
LL | f -= 1.0;
4949
| ^^^^^^^^
5050

51-
error: floating-point arithmetic detected
51+
error: arithmetic detected
5252
--> $DIR/float_arithmetic.rs:25:5
5353
|
5454
LL | f *= 2.0;
5555
| ^^^^^^^^
5656

57-
error: floating-point arithmetic detected
57+
error: arithmetic detected
5858
--> $DIR/float_arithmetic.rs:26:5
5959
|
6060
LL | f /= 2.0;
6161
| ^^^^^^^^
6262

63-
error: floating-point arithmetic detected
63+
error: arithmetic detected
6464
--> $DIR/float_arithmetic.rs:32:5
6565
|
6666
LL | 3.1_f32 + &1.2_f32;
6767
| ^^^^^^^^^^^^^^^^^^
6868

69-
error: floating-point arithmetic detected
69+
error: arithmetic detected
7070
--> $DIR/float_arithmetic.rs:33:5
7171
|
7272
LL | &3.4_f32 + 1.5_f32;
7373
| ^^^^^^^^^^^^^^^^^^
7474

75-
error: floating-point arithmetic detected
75+
error: arithmetic detected
7676
--> $DIR/float_arithmetic.rs:34:5
7777
|
7878
LL | &3.5_f32 + &1.3_f32;
7979
| ^^^^^^^^^^^^^^^^^^^
8080

81-
error: floating-point arithmetic detected
81+
error: arithmetic detected
8282
--> $DIR/float_arithmetic.rs:39:5
8383
|
8484
LL | a + f
8585
| ^^^^^
8686

87-
error: floating-point arithmetic detected
87+
error: arithmetic detected
8888
--> $DIR/float_arithmetic.rs:43:5
8989
|
9090
LL | f1 + f2
9191
| ^^^^^^^
9292

93-
error: floating-point arithmetic detected
93+
error: arithmetic detected
9494
--> $DIR/float_arithmetic.rs:47:5
9595
|
9696
LL | f1 + f2
9797
| ^^^^^^^
9898

99-
error: floating-point arithmetic detected
99+
error: arithmetic detected
100100
--> $DIR/float_arithmetic.rs:51:5
101101
|
102102
LL | (&f1 + &f2)

tests/ui/integer_arithmetic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)]
1+
#![warn(clippy::arithmetic)]
22
#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref)]
33

44
#[rustfmt::skip]

0 commit comments

Comments
 (0)