Skip to content

Commit 41f79f7

Browse files
fix incorrect suggestion for !(a >= b) as i32 == c
1 parent a81f1c8 commit 41f79f7

File tree

4 files changed

+124
-4
lines changed

4 files changed

+124
-4
lines changed

clippy_lints/src/booleans.rs

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
22
use clippy_utils::eq_expr_value;
33
use clippy_utils::source::SpanRangeExt;
4+
use clippy_utils::sugg::{make_binop, Sugg};
45
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
56
use rustc_ast::ast::LitKind;
67
use rustc_errors::Applicability;
@@ -83,7 +84,11 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
8384
_: Span,
8485
_: LocalDefId,
8586
) {
86-
NonminimalBoolVisitor { cx }.visit_body(body);
87+
NonminimalBoolVisitor {
88+
cx,
89+
parent_expr: vec![],
90+
}
91+
.visit_body(body);
8792
}
8893

8994
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@@ -176,13 +181,65 @@ fn check_inverted_bool_in_condition(
176181
);
177182
}
178183

179-
fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
184+
fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>, parent_expr: Option<&Expr<'_>>) {
180185
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
181186
&& !expr.span.from_expansion()
182187
&& !inner.span.from_expansion()
183188
&& let Some(suggestion) = simplify_not(cx, inner)
184189
&& cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
185190
{
191+
let need_parens = 'block: {
192+
let Some(parent_expr) = parent_expr else {
193+
break 'block false;
194+
};
195+
match parent_expr.kind {
196+
ExprKind::Cast(_, ty) => {
197+
let new_expr = Expr {
198+
kind: ExprKind::Cast(inner, ty),
199+
..*parent_expr
200+
};
201+
let Some(new_sugg) = Sugg::hir_opt(cx, &new_expr) else {
202+
break 'block false;
203+
};
204+
let Sugg::BinOp(_, lhs, _) = new_sugg else {
205+
break 'block false;
206+
};
207+
lhs.starts_with('(') && lhs.ends_with(')')
208+
},
209+
ExprKind::Binary(binop, lhs, rhs) if lhs.span == expr.span => {
210+
let Some(lhs_sugg) = Sugg::hir_opt(cx, inner) else {
211+
break 'block false;
212+
};
213+
let Some(rhs_sugg) = Sugg::hir_opt(cx, rhs) else {
214+
break 'block false;
215+
};
216+
let new_sugg = make_binop(binop.node, &lhs_sugg, &rhs_sugg);
217+
let Sugg::BinOp(_, lhs, _) = new_sugg else {
218+
break 'block false;
219+
};
220+
lhs.starts_with('(') && lhs.ends_with(')')
221+
},
222+
ExprKind::Binary(binop, lhs, rhs) if rhs.span == expr.span => {
223+
let Some(lhs_sugg) = Sugg::hir_opt(cx, lhs) else {
224+
break 'block false;
225+
};
226+
let Some(rhs_sugg) = Sugg::hir_opt(cx, inner) else {
227+
break 'block false;
228+
};
229+
let new_sugg = make_binop(binop.node, &lhs_sugg, &rhs_sugg);
230+
let Sugg::BinOp(_, _, rhs) = new_sugg else {
231+
break 'block false;
232+
};
233+
rhs.starts_with('(') && rhs.ends_with(')')
234+
},
235+
_ => false,
236+
}
237+
};
238+
let suggestion = if need_parens {
239+
format!("({suggestion})")
240+
} else {
241+
suggestion
242+
};
186243
span_lint_and_sugg(
187244
cx,
188245
NONMINIMAL_BOOL,
@@ -196,6 +253,7 @@ fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
196253
}
197254

198255
struct NonminimalBoolVisitor<'a, 'tcx> {
256+
parent_expr: Vec<&'tcx Expr<'tcx>>, // parent stack
199257
cx: &'a LateContext<'tcx>,
200258
}
201259

@@ -569,7 +627,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
569627
}
570628
};
571629
if improvements.is_empty() {
572-
check_simplify_not(self.cx, e);
630+
check_simplify_not(self.cx, e, self.parent_expr.last().copied());
573631
} else {
574632
nonminimal_bool_lint(
575633
improvements
@@ -602,7 +660,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
602660
_ => {},
603661
}
604662
}
663+
self.parent_expr.push(e);
605664
walk_expr(self, e);
665+
self.parent_expr.pop();
606666
}
607667
}
608668

tests/ui/nonminimal_bool_methods.fixed

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,16 @@ fn issue_12625() {
115115
if a as u64 > b {} //~ ERROR: this boolean expression can be simplified
116116
}
117117

118+
fn issue_12761() {
119+
let a = 0;
120+
let b = 0;
121+
let c = 0;
122+
if (a < b) as i32 == c {} //~ ERROR: this boolean expression can be simplified
123+
if (a < b) | (a > c) {} //~ ERROR: this boolean expression can be simplified
124+
let opt: Option<usize> = Some(1);
125+
let res: Result<usize, usize> = Ok(1);
126+
if res.is_err() as i32 == c {} //~ ERROR: this boolean expression can be simplified
127+
if res.is_err() | opt.is_some() {} //~ ERROR: this boolean expression can be simplified
128+
}
129+
118130
fn main() {}

tests/ui/nonminimal_bool_methods.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,16 @@ fn issue_12625() {
115115
if !(a as u64 <= b) {} //~ ERROR: this boolean expression can be simplified
116116
}
117117

118+
fn issue_12761() {
119+
let a = 0;
120+
let b = 0;
121+
let c = 0;
122+
if !(a >= b) as i32 == c {} //~ ERROR: this boolean expression can be simplified
123+
if !(a >= b) | !(a <= c) {} //~ ERROR: this boolean expression can be simplified
124+
let opt: Option<usize> = Some(1);
125+
let res: Result<usize, usize> = Ok(1);
126+
if !res.is_ok() as i32 == c {} //~ ERROR: this boolean expression can be simplified
127+
if !res.is_ok() | !opt.is_none() {} //~ ERROR: this boolean expression can be simplified
128+
}
129+
118130
fn main() {}

tests/ui/nonminimal_bool_methods.stderr

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,41 @@ error: this boolean expression can be simplified
9797
LL | if !(a as u64 <= b) {}
9898
| ^^^^^^^^^^^^^^^^ help: try: `a as u64 > b`
9999

100-
error: aborting due to 16 previous errors
100+
error: this boolean expression can be simplified
101+
--> tests/ui/nonminimal_bool_methods.rs:122:8
102+
|
103+
LL | if !(a >= b) as i32 == c {}
104+
| ^^^^^^^^^ help: try: `(a < b)`
105+
106+
error: this boolean expression can be simplified
107+
--> tests/ui/nonminimal_bool_methods.rs:123:8
108+
|
109+
LL | if !(a >= b) | !(a <= c) {}
110+
| ^^^^^^^^^ help: try: `(a < b)`
111+
112+
error: this boolean expression can be simplified
113+
--> tests/ui/nonminimal_bool_methods.rs:123:20
114+
|
115+
LL | if !(a >= b) | !(a <= c) {}
116+
| ^^^^^^^^^ help: try: `(a > c)`
117+
118+
error: this boolean expression can be simplified
119+
--> tests/ui/nonminimal_bool_methods.rs:126:8
120+
|
121+
LL | if !res.is_ok() as i32 == c {}
122+
| ^^^^^^^^^^^^ help: try: `res.is_err()`
123+
124+
error: this boolean expression can be simplified
125+
--> tests/ui/nonminimal_bool_methods.rs:127:8
126+
|
127+
LL | if !res.is_ok() | !opt.is_none() {}
128+
| ^^^^^^^^^^^^ help: try: `res.is_err()`
129+
130+
error: this boolean expression can be simplified
131+
--> tests/ui/nonminimal_bool_methods.rs:127:23
132+
|
133+
LL | if !res.is_ok() | !opt.is_none() {}
134+
| ^^^^^^^^^^^^^^ help: try: `opt.is_some()`
135+
136+
error: aborting due to 22 previous errors
101137

0 commit comments

Comments
 (0)