about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/matches.rs14
-rw-r--r--tests/ui/match_single_binding2.fixed37
-rw-r--r--tests/ui/match_single_binding2.rs37
-rw-r--r--tests/ui/match_single_binding2.stderr34
4 files changed, 121 insertions, 1 deletions
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<bool> {
     }
 }
 
+#[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<I>
+    where
+        I: Iterator,
+    {
+        inner: Option<(I, <I as Iterator>::Item)>,
+    }
+
+    #[allow(dead_code)]
+    fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
+        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<I>
+    where
+        I: Iterator,
+    {
+        inner: Option<(I, <I as Iterator>::Item)>,
+    }
+
+    #[allow(dead_code)]
+    fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
+        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
+