Skip to content

Commit f5d429c

Browse files
committed
Auto merge of #5820 - ThibsG:FixSuspiciousArithmeticImpl, r=flip1995
Fix FP for `suspicious_arithmetic_impl` from `suspicious_trait_impl` … As discussed in #3215, the `suspicious_trait_impl` lint causes too many false positives, as it is complex to find out if binary operations are suspicious or not. This PR restricts the number of binary operations to at most one, otherwise we don't lint. This can be seen as very conservative, but at least FP can be reduced to bare minimum. Fixes: #3215 changelog: limit the `suspicious_arithmetic_impl` lint to one binop, to avoid many FPs
2 parents da5a6fb + 442c8ae commit f5d429c

File tree

2 files changed

+47
-20
lines changed

2 files changed

+47
-20
lines changed

clippy_lints/src/suspicious_trait_impl.rs

+17-20
Original file line numberDiff line numberDiff line change
@@ -64,26 +64,22 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
6464
| hir::BinOpKind::Gt => return,
6565
_ => {},
6666
}
67-
// Check if the binary expression is part of another bi/unary expression
68-
// or operator assignment as a child node
69-
let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id);
70-
while parent_expr != hir::CRATE_HIR_ID {
71-
if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) {
72-
match e.kind {
73-
hir::ExprKind::Binary(..)
74-
| hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
75-
| hir::ExprKind::AssignOp(..) => return,
76-
_ => {},
67+
68+
// Check for more than one binary operation in the implemented function
69+
// Linting when multiple operations are involved can result in false positives
70+
if_chain! {
71+
let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
72+
if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn);
73+
if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind;
74+
let body = cx.tcx.hir().body(body_id);
75+
let mut visitor = BinaryExprVisitor { nb_binops: 0 };
76+
77+
then {
78+
walk_expr(&mut visitor, &body.value);
79+
if visitor.nb_binops > 1 {
80+
return;
7781
}
7882
}
79-
parent_expr = cx.tcx.hir().get_parent_node(parent_expr);
80-
}
81-
// as a parent node
82-
let mut visitor = BinaryExprVisitor { in_binary_expr: false };
83-
walk_expr(&mut visitor, expr);
84-
85-
if visitor.in_binary_expr {
86-
return;
8783
}
8884

8985
if let Some(impl_trait) = check_binop(
@@ -181,7 +177,7 @@ fn check_binop(
181177
}
182178

183179
struct BinaryExprVisitor {
184-
in_binary_expr: bool,
180+
nb_binops: u32,
185181
}
186182

187183
impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
@@ -191,12 +187,13 @@ impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
191187
match expr.kind {
192188
hir::ExprKind::Binary(..)
193189
| hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
194-
| hir::ExprKind::AssignOp(..) => self.in_binary_expr = true,
190+
| hir::ExprKind::AssignOp(..) => self.nb_binops += 1,
195191
_ => {},
196192
}
197193

198194
walk_expr(self, expr);
199195
}
196+
200197
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
201198
NestedVisitorMap::None
202199
}

tests/ui/suspicious_arithmetic_impl.rs

+30
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,33 @@ fn main() {}
8888
fn do_nothing(x: u32) -> u32 {
8989
x
9090
}
91+
92+
struct MultipleBinops(u32);
93+
94+
impl Add for MultipleBinops {
95+
type Output = MultipleBinops;
96+
97+
// OK: multiple Binops in `add` impl
98+
fn add(self, other: Self) -> Self::Output {
99+
let mut result = self.0 + other.0;
100+
if result >= u32::max_value() {
101+
result -= u32::max_value();
102+
}
103+
MultipleBinops(result)
104+
}
105+
}
106+
107+
impl Mul for MultipleBinops {
108+
type Output = MultipleBinops;
109+
110+
// OK: multiple Binops in `mul` impl
111+
fn mul(self, other: Self) -> Self::Output {
112+
let mut result: u32 = 0;
113+
let size = std::cmp::max(self.0, other.0) as usize;
114+
let mut v = vec![0; size + 1];
115+
for i in 0..size + 1 {
116+
result *= i as u32;
117+
}
118+
MultipleBinops(result)
119+
}
120+
}

0 commit comments

Comments
 (0)