Skip to content

Commit 4582296

Browse files
Add block_scrutinee lint
1 parent effe235 commit 4582296

7 files changed

Lines changed: 161 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6449,6 +6449,7 @@ Released 2018-09-13
64496449
[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
64506450
[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
64516451
[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
6452+
[`block_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_scrutinee
64526453
[`blocks_in_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_conditions
64536454
[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
64546455
[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet;
3+
use rustc_errors::Applicability;
4+
use rustc_hir::{Expr, ExprKind};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_session::declare_lint_pass;
7+
use rustc_span::edition::Edition;
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Warns when a match, if let, or while let scrutinee is wrapped in a block.
12+
///
13+
/// ### Why is this bad?
14+
/// Prior to the 2024 edition, wrapping the scrutinee in a block did not drop
15+
/// temporaries before the body executes.
16+
///
17+
/// ### Example
18+
/// ```rust,ignore
19+
/// if let Some(x) = { my_function() } { .. }
20+
/// ```
21+
#[clippy::version = "1.80.0"]
22+
pub BLOCK_SCRUTINEE,
23+
correctness,
24+
"warns when the scrutinee is wrapped in a block in older editions"
25+
}
26+
27+
declare_lint_pass!(BlockScrutinee => [BLOCK_SCRUTINEE]);
28+
29+
impl<'tcx> LateLintPass<'tcx> for BlockScrutinee {
30+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
31+
if cx.tcx.sess.edition() >= Edition::Edition2024 {
32+
return;
33+
}
34+
35+
let scrutinee = match expr.kind {
36+
ExprKind::Match(scrutinee, _, _) => scrutinee,
37+
ExprKind::Let(let_expr) => let_expr.init,
38+
_ => return,
39+
};
40+
41+
if let ExprKind::Block(block, _) = scrutinee.kind
42+
&& block.stmts.is_empty()
43+
&& let Some(inner_expr) = block.expr
44+
{
45+
let inner_snippet = snippet(cx, inner_expr.span, "..");
46+
47+
span_lint_and_sugg(
48+
cx,
49+
BLOCK_SCRUTINEE,
50+
scrutinee.span,
51+
"scrutinee is wrapped in a block which will not drop temporaries until the end of the statement in this edition",
52+
"remove the block",
53+
inner_snippet.to_string(),
54+
Applicability::MachineApplicable,
55+
);
56+
}
57+
}
58+
}

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
3333
crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO,
3434
crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO,
3535
crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO,
36+
crate::block_scrutinee::BLOCK_SCRUTINEE_INFO,
3637
crate::blocks_in_conditions::BLOCKS_IN_CONDITIONS_INFO,
3738
crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO,
3839
crate::bool_comparison::BOOL_COMPARISON_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ mod assigning_clones;
7272
mod async_yields_async;
7373
mod attrs;
7474
mod await_holding_invalid;
75+
mod block_scrutinee;
7576
mod blocks_in_conditions;
7677
mod bool_assert_comparison;
7778
mod bool_comparison;
@@ -867,6 +868,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
867868
Box::new(|_| Box::new(manual_checked_ops::ManualCheckedOps)),
868869
Box::new(move |tcx| Box::new(manual_pop_if::ManualPopIf::new(tcx, conf))),
869870
Box::new(move |_| Box::new(manual_noop_waker::ManualNoopWaker::new(conf))),
871+
Box::new(|_| Box::new(block_scrutinee::BlockScrutinee)),
870872
// add late passes here, used by `cargo dev new_lint`
871873
];
872874
store.late_passes.extend(late_lints);

tests/ui/block_scrutinee.fixed

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//@ edition: 2021
2+
#![warn(clippy::block_scrutinee)]
3+
#![allow(clippy::blocks_in_conditions)]
4+
5+
fn my_function() -> Option<i32> {
6+
Some(1)
7+
}
8+
9+
fn main() {
10+
if let Some(x) = my_function() {
11+
//~^ ERROR: scrutinee is wrapped in a block
12+
let _ = x;
13+
}
14+
15+
match my_function() {
16+
//~^ ERROR: scrutinee is wrapped in a block
17+
Some(1) => println!("one"),
18+
Some(_) => println!("other"),
19+
None => println!("none"),
20+
}
21+
22+
let mut v = vec![1, 2, 3];
23+
while let Some(x) = v.pop() {
24+
//~^ ERROR: scrutinee is wrapped in a block
25+
let _ = x;
26+
}
27+
28+
if let Some(x) = my_function() {
29+
let _ = x;
30+
}
31+
32+
if let Some(x) = {
33+
let _y = 2;
34+
my_function()
35+
} {
36+
let _ = x;
37+
}
38+
}

tests/ui/block_scrutinee.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//@ edition: 2021
2+
#![warn(clippy::block_scrutinee)]
3+
#![allow(clippy::blocks_in_conditions)]
4+
5+
fn my_function() -> Option<i32> {
6+
Some(1)
7+
}
8+
9+
fn main() {
10+
if let Some(x) = { my_function() } {
11+
//~^ ERROR: scrutinee is wrapped in a block
12+
let _ = x;
13+
}
14+
15+
match { my_function() } {
16+
//~^ ERROR: scrutinee is wrapped in a block
17+
Some(1) => println!("one"),
18+
Some(_) => println!("other"),
19+
None => println!("none"),
20+
}
21+
22+
let mut v = vec![1, 2, 3];
23+
while let Some(x) = { v.pop() } {
24+
//~^ ERROR: scrutinee is wrapped in a block
25+
let _ = x;
26+
}
27+
28+
if let Some(x) = my_function() {
29+
let _ = x;
30+
}
31+
32+
if let Some(x) = {
33+
let _y = 2;
34+
my_function()
35+
} {
36+
let _ = x;
37+
}
38+
}

tests/ui/block_scrutinee.stderr

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: scrutinee is wrapped in a block which will not drop temporaries until the end of the statement in this edition
2+
--> tests/ui/block_scrutinee.rs:10:22
3+
|
4+
LL | if let Some(x) = { my_function() } {
5+
| ^^^^^^^^^^^^^^^^^ help: remove the block: `my_function()`
6+
|
7+
= note: `-D clippy::block-scrutinee` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::block_scrutinee)]`
9+
10+
error: scrutinee is wrapped in a block which will not drop temporaries until the end of the statement in this edition
11+
--> tests/ui/block_scrutinee.rs:15:11
12+
|
13+
LL | match { my_function() } {
14+
| ^^^^^^^^^^^^^^^^^ help: remove the block: `my_function()`
15+
16+
error: scrutinee is wrapped in a block which will not drop temporaries until the end of the statement in this edition
17+
--> tests/ui/block_scrutinee.rs:23:25
18+
|
19+
LL | while let Some(x) = { v.pop() } {
20+
| ^^^^^^^^^^^ help: remove the block: `v.pop()`
21+
22+
error: aborting due to 3 previous errors
23+

0 commit comments

Comments
 (0)