Skip to content

Commit 4a39707

Browse files
committed
Parenthesize blocks in needless_bool suggestion
Because the `if .. {}` statement already puts the condition in expression scope, contained blocks would be parsed as complete statements, so any `&` binary expression whose left operand ended in a block would lead to a non-compiling suggestion. This adds a visitor to identify such expressions and add parentheses. This fixes #8052.
1 parent d5d830a commit 4a39707

File tree

4 files changed

+106
-12
lines changed

4 files changed

+106
-12
lines changed

clippy_lints/src/needless_bool.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
66
use clippy_utils::higher;
77
use clippy_utils::source::snippet_with_applicability;
88
use clippy_utils::sugg::Sugg;
9-
use clippy_utils::{is_else_clause, is_expn_of};
9+
use clippy_utils::{get_parent_node, is_else_clause, is_expn_of};
1010
use rustc_ast::ast::LitKind;
1111
use rustc_errors::Applicability;
12-
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
12+
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, StmtKind, UnOp};
1313
use rustc_lint::{LateContext, LateLintPass};
1414
use rustc_session::{declare_lint_pass, declare_tool_lint};
1515
use rustc_span::source_map::Spanned;
@@ -74,6 +74,36 @@ declare_clippy_lint! {
7474

7575
declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
7676

77+
fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
78+
let mut inner = e;
79+
while let ExprKind::Binary(_, i, _)
80+
| ExprKind::Call(i, _)
81+
| ExprKind::Cast(i, _)
82+
| ExprKind::Type(i, _)
83+
| ExprKind::Index(i, _) = inner.kind
84+
{
85+
if matches!(
86+
i.kind,
87+
ExprKind::Block(..)
88+
| ExprKind::ConstBlock(..)
89+
| ExprKind::If(..)
90+
| ExprKind::Loop(..)
91+
| ExprKind::Match(..)
92+
) {
93+
return true;
94+
}
95+
inner = i;
96+
}
97+
false
98+
}
99+
100+
fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
101+
matches!(
102+
get_parent_node(cx.tcx, id),
103+
Some(Node::Stmt(..) | Node::Block(Block { stmts: &[], .. }))
104+
)
105+
}
106+
77107
impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
78108
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
79109
use self::Expression::{Bool, RetBool};
@@ -99,6 +129,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
99129
snip = snip.blockify();
100130
}
101131

132+
if condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id) {
133+
snip = snip.maybe_par();
134+
}
135+
102136
span_lint_and_sugg(
103137
cx,
104138
NEEDLESS_BOOL,

tests/ui/needless_bool/fixable.fixed

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ fn main() {
5353
needless_bool(x);
5454
needless_bool2(x);
5555
needless_bool3(x);
56+
needless_bool_condition();
5657
}
5758

5859
fn bool_ret3(x: bool) -> bool {
@@ -98,3 +99,19 @@ fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_b
9899
true
99100
} else { !returns_bool() };
100101
}
102+
103+
unsafe fn no(v: u8) -> u8 {
104+
v
105+
}
106+
107+
#[allow(clippy::unnecessary_operation)]
108+
fn needless_bool_condition() -> bool {
109+
(unsafe { no(4) } & 1 != 0);
110+
let _brackets_unneeded = unsafe { no(4) } & 1 != 0;
111+
fn foo() -> bool {
112+
// parentheses are needed here
113+
(unsafe { no(4) } & 1 != 0)
114+
}
115+
116+
foo()
117+
}

tests/ui/needless_bool/fixable.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ fn main() {
6565
needless_bool(x);
6666
needless_bool2(x);
6767
needless_bool3(x);
68+
needless_bool_condition();
6869
}
6970

7071
fn bool_ret3(x: bool) -> bool {
@@ -130,3 +131,23 @@ fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_b
130131
true
131132
};
132133
}
134+
135+
unsafe fn no(v: u8) -> u8 {
136+
v
137+
}
138+
139+
#[allow(clippy::unnecessary_operation)]
140+
fn needless_bool_condition() -> bool {
141+
if unsafe { no(4) } & 1 != 0 {
142+
true
143+
} else {
144+
false
145+
};
146+
let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false };
147+
fn foo() -> bool {
148+
// parentheses are needed here
149+
if unsafe { no(4) } & 1 != 0 { true } else { false }
150+
}
151+
152+
foo()
153+
}

tests/ui/needless_bool/fixable.stderr

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ LL | | };
3131
| |_____^ help: you can reduce it to: `!(x && y)`
3232

3333
error: this if-then-else expression returns a bool literal
34-
--> $DIR/fixable.rs:71:5
34+
--> $DIR/fixable.rs:72:5
3535
|
3636
LL | / if x {
3737
LL | | return true;
@@ -41,7 +41,7 @@ LL | | };
4141
| |_____^ help: you can reduce it to: `return x`
4242

4343
error: this if-then-else expression returns a bool literal
44-
--> $DIR/fixable.rs:79:5
44+
--> $DIR/fixable.rs:80:5
4545
|
4646
LL | / if x {
4747
LL | | return false;
@@ -51,7 +51,7 @@ LL | | };
5151
| |_____^ help: you can reduce it to: `return !x`
5252

5353
error: this if-then-else expression returns a bool literal
54-
--> $DIR/fixable.rs:87:5
54+
--> $DIR/fixable.rs:88:5
5555
|
5656
LL | / if x && y {
5757
LL | | return true;
@@ -61,7 +61,7 @@ LL | | };
6161
| |_____^ help: you can reduce it to: `return x && y`
6262

6363
error: this if-then-else expression returns a bool literal
64-
--> $DIR/fixable.rs:95:5
64+
--> $DIR/fixable.rs:96:5
6565
|
6666
LL | / if x && y {
6767
LL | | return false;
@@ -71,33 +71,33 @@ LL | | };
7171
| |_____^ help: you can reduce it to: `return !(x && y)`
7272

7373
error: equality checks against true are unnecessary
74-
--> $DIR/fixable.rs:103:8
74+
--> $DIR/fixable.rs:104:8
7575
|
7676
LL | if x == true {};
7777
| ^^^^^^^^^ help: try simplifying it as shown: `x`
7878
|
7979
= note: `-D clippy::bool-comparison` implied by `-D warnings`
8080

8181
error: equality checks against false can be replaced by a negation
82-
--> $DIR/fixable.rs:107:8
82+
--> $DIR/fixable.rs:108:8
8383
|
8484
LL | if x == false {};
8585
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
8686

8787
error: equality checks against true are unnecessary
88-
--> $DIR/fixable.rs:117:8
88+
--> $DIR/fixable.rs:118:8
8989
|
9090
LL | if x == true {};
9191
| ^^^^^^^^^ help: try simplifying it as shown: `x`
9292

9393
error: equality checks against false can be replaced by a negation
94-
--> $DIR/fixable.rs:118:8
94+
--> $DIR/fixable.rs:119:8
9595
|
9696
LL | if x == false {};
9797
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
9898

9999
error: this if-then-else expression returns a bool literal
100-
--> $DIR/fixable.rs:127:12
100+
--> $DIR/fixable.rs:128:12
101101
|
102102
LL | } else if returns_bool() {
103103
| ____________^
@@ -107,5 +107,27 @@ LL | | true
107107
LL | | };
108108
| |_____^ help: you can reduce it to: `{ !returns_bool() }`
109109

110-
error: aborting due to 12 previous errors
110+
error: this if-then-else expression returns a bool literal
111+
--> $DIR/fixable.rs:141:5
112+
|
113+
LL | / if unsafe { no(4) } & 1 != 0 {
114+
LL | | true
115+
LL | | } else {
116+
LL | | false
117+
LL | | };
118+
| |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
119+
120+
error: this if-then-else expression returns a bool literal
121+
--> $DIR/fixable.rs:146:30
122+
|
123+
LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false };
124+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0`
125+
126+
error: this if-then-else expression returns a bool literal
127+
--> $DIR/fixable.rs:149:9
128+
|
129+
LL | if unsafe { no(4) } & 1 != 0 { true } else { false }
130+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
131+
132+
error: aborting due to 15 previous errors
111133

0 commit comments

Comments
 (0)