Skip to content

Commit 0bdaa77

Browse files
committed
Auto merge of rust-lang#6941 - ThibsG:suggMatchSingleBinding, r=llogiq
Fix bad suggestion for `match_single_binding` lint Fix a bad suggestion that needs curly braces when the target `match` is the body of an arm. Fixes rust-lang#6572 changelog: none
2 parents 478f258 + 7d45d8a commit 0bdaa77

File tree

4 files changed

+121
-1
lines changed

4 files changed

+121
-1
lines changed

clippy_lints/src/matches.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1353,6 +1353,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
13531353
}
13541354
}
13551355

1356+
#[allow(clippy::too_many_lines)]
13561357
fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
13571358
if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
13581359
return;
@@ -1427,7 +1428,18 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
14271428
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
14281429
cbrace_start = format!("{{\n{}", indent);
14291430
}
1430-
};
1431+
}
1432+
// If the parent is already an arm, and the body is another match statement,
1433+
// we need curly braces around suggestion
1434+
let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
1435+
if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
1436+
if let ExprKind::Match(..) = arm.body.kind {
1437+
cbrace_end = format!("\n{}}}", indent);
1438+
// Fix body indent due to the match
1439+
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
1440+
cbrace_start = format!("{{\n{}", indent);
1441+
}
1442+
}
14311443
(
14321444
expr.span,
14331445
format!(

tests/ui/match_single_binding2.fixed

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::match_single_binding)]
4+
#![allow(unused_variables)]
5+
6+
fn main() {
7+
// Lint (additional curly braces needed, see #6572)
8+
struct AppendIter<I>
9+
where
10+
I: Iterator,
11+
{
12+
inner: Option<(I, <I as Iterator>::Item)>,
13+
}
14+
15+
#[allow(dead_code)]
16+
fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
17+
match &iter.inner {
18+
Some((iter, _item)) => {
19+
let (min, max) = iter.size_hint();
20+
(min.saturating_add(1), max.and_then(|max| max.checked_add(1)))
21+
},
22+
None => (0, Some(0)),
23+
}
24+
}
25+
26+
// Lint (no additional curly braces needed)
27+
let opt = Some((5, 2));
28+
let get_tup = || -> (i32, i32) { (1, 2) };
29+
match opt {
30+
#[rustfmt::skip]
31+
Some((first, _second)) => {
32+
let (a, b) = get_tup();
33+
println!("a {:?} and b {:?}", a, b);
34+
},
35+
None => println!("nothing"),
36+
}
37+
}

tests/ui/match_single_binding2.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::match_single_binding)]
4+
#![allow(unused_variables)]
5+
6+
fn main() {
7+
// Lint (additional curly braces needed, see #6572)
8+
struct AppendIter<I>
9+
where
10+
I: Iterator,
11+
{
12+
inner: Option<(I, <I as Iterator>::Item)>,
13+
}
14+
15+
#[allow(dead_code)]
16+
fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
17+
match &iter.inner {
18+
Some((iter, _item)) => match iter.size_hint() {
19+
(min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))),
20+
},
21+
None => (0, Some(0)),
22+
}
23+
}
24+
25+
// Lint (no additional curly braces needed)
26+
let opt = Some((5, 2));
27+
let get_tup = || -> (i32, i32) { (1, 2) };
28+
match opt {
29+
#[rustfmt::skip]
30+
Some((first, _second)) => {
31+
match get_tup() {
32+
(a, b) => println!("a {:?} and b {:?}", a, b),
33+
}
34+
},
35+
None => println!("nothing"),
36+
}
37+
}

tests/ui/match_single_binding2.stderr

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error: this match could be written as a `let` statement
2+
--> $DIR/match_single_binding2.rs:18:36
3+
|
4+
LL | Some((iter, _item)) => match iter.size_hint() {
5+
| ____________________________________^
6+
LL | | (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))),
7+
LL | | },
8+
| |_____________^
9+
|
10+
= note: `-D clippy::match-single-binding` implied by `-D warnings`
11+
help: consider using `let` statement
12+
|
13+
LL | Some((iter, _item)) => {
14+
LL | let (min, max) = iter.size_hint();
15+
LL | (min.saturating_add(1), max.and_then(|max| max.checked_add(1)))
16+
LL | },
17+
|
18+
19+
error: this match could be written as a `let` statement
20+
--> $DIR/match_single_binding2.rs:31:13
21+
|
22+
LL | / match get_tup() {
23+
LL | | (a, b) => println!("a {:?} and b {:?}", a, b),
24+
LL | | }
25+
| |_____________^
26+
|
27+
help: consider using `let` statement
28+
|
29+
LL | let (a, b) = get_tup();
30+
LL | println!("a {:?} and b {:?}", a, b);
31+
|
32+
33+
error: aborting due to 2 previous errors
34+

0 commit comments

Comments
 (0)