diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index d43cb32ee51..1a8ec9c6ec3 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1353,6 +1353,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option { } } +#[allow(clippy::too_many_lines)] fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; @@ -1427,7 +1428,18 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); cbrace_start = format!("{{\n{}", indent); } - }; + } + // If the parent is already an arm, and the body is another match statement, + // we need curly braces around suggestion + let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id); + if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { + if let ExprKind::Match(..) = arm.body.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } ( expr.span, format!( diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed new file mode 100644 index 00000000000..e73a85b73d7 --- /dev/null +++ b/tests/ui/match_single_binding2.fixed @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables)] + +fn main() { + // Lint (additional curly braces needed, see #6572) + struct AppendIter + where + I: Iterator, + { + inner: Option<(I, ::Item)>, + } + + #[allow(dead_code)] + fn size_hint(iter: &AppendIter) -> (usize, Option) { + match &iter.inner { + Some((iter, _item)) => { + let (min, max) = iter.size_hint(); + (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) + }, + None => (0, Some(0)), + } + } + + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + let (a, b) = get_tup(); + println!("a {:?} and b {:?}", a, b); + }, + None => println!("nothing"), + } +} diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs new file mode 100644 index 00000000000..7362cb390e5 --- /dev/null +++ b/tests/ui/match_single_binding2.rs @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables)] + +fn main() { + // Lint (additional curly braces needed, see #6572) + struct AppendIter + where + I: Iterator, + { + inner: Option<(I, ::Item)>, + } + + #[allow(dead_code)] + fn size_hint(iter: &AppendIter) -> (usize, Option) { + match &iter.inner { + Some((iter, _item)) => match iter.size_hint() { + (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), + }, + None => (0, Some(0)), + } + } + + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + match get_tup() { + (a, b) => println!("a {:?} and b {:?}", a, b), + } + }, + None => println!("nothing"), + } +} diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr new file mode 100644 index 00000000000..bc18d191aa3 --- /dev/null +++ b/tests/ui/match_single_binding2.stderr @@ -0,0 +1,34 @@ +error: this match could be written as a `let` statement + --> $DIR/match_single_binding2.rs:18:36 + | +LL | Some((iter, _item)) => match iter.size_hint() { + | ____________________________________^ +LL | | (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), +LL | | }, + | |_____________^ + | + = note: `-D clippy::match-single-binding` implied by `-D warnings` +help: consider using `let` statement + | +LL | Some((iter, _item)) => { +LL | let (min, max) = iter.size_hint(); +LL | (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) +LL | }, + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding2.rs:31:13 + | +LL | / match get_tup() { +LL | | (a, b) => println!("a {:?} and b {:?}", a, b), +LL | | } + | |_____________^ + | +help: consider using `let` statement + | +LL | let (a, b) = get_tup(); +LL | println!("a {:?} and b {:?}", a, b); + | + +error: aborting due to 2 previous errors +