Repository: rust-lang/rustfmt (rust)

Issue:
Rustfmt breaks match arm starting with attributed block
If a match arm starts with a block with an attribute, rustfmt removes the brackets, resulting in code that cannot compile.

Before rustfmt:

```rust
match () {
        _ => {
            #[cfg(debug_assertions)]
            {
                println!("Foo");
            }
        }
    }

    match () {
        _ => {
            #[allow(unsafe_code)]
            unsafe {}
        }
    }
```

After rustfmt:

```rust
match () {
        _ =>
        #[cfg(debug_assertions)]
        {
            println!("Foo");
        }
    }

    match () {
        _ =>
            #[allow(unsafe_code)]
            unsafe {}
    }
```

Which results in the attributes being placed in invalid positions.

```rust
error[E0658]: attributes on expressions are experimental                                       
 --> src\main.rs:6:9
  |
6 |         #[cfg(debug_assertions)]
  |         ^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: for more information, see https://github.com/rust-lang/rust/issues/15701

error: removing an expression is not supported in this position
 --> src\main.rs:6:9
  |
6 |         #[cfg(debug_assertions)]
  |         ^^^^^^^^^^^^^^^^^^^^^^^^

error[E0658]: attributes on expressions are experimental
  --> src\main.rs:14:13
   |
14 |             #[allow(unsafe_code)]
   |             ^^^^^^^^^^^^^^^^^^^^^
   |
   = note: for more information, see https://github.com/rust-lang/rust/issues/15701

error: aborting due to 3 previous errors
```

I assume it's related to blocks, as the following code is not changed by rustfmt:

```rust
match () {
    _ => {
        #[cfg(debug_assertions)]
        println!("Foo");
    }
}
```

-----

rustfmt 1.4.11-stable (9eb4b564 2020-01-29)
rustfmt 1.4.12-nightly (9f53665f 2020-02-10)


Pull request description:
Do not flatten match arm block with leading attributes
This is a backport of #4124.

Fixes #4109

Patch:
diff --git a/src/matches.rs b/src/matches.rs
index 22d23fc1..85d9c5d2 100644
--- a/src/matches.rs
+++ b/src/matches.rs
@@ -322,7 +322,11 @@ fn flatten_arm_body<'a>(
     if let Some(block) = block_can_be_flattened(context, body) {
         if let ast::StmtKind::Expr(ref expr) = block.stmts[0].kind {
             if let ast::ExprKind::Block(..) = expr.kind {
-                flatten_arm_body(context, expr, None)
+                if expr.attrs.is_empty() {
+                    flatten_arm_body(context, expr, None)
+                } else {
+                    (true, body)
+                }
             } else {
                 let cond_becomes_muti_line = opt_shape
                     .and_then(|shape| rewrite_cond(context, expr, shape))


Test patch:
diff --git a/tests/source/match.rs b/tests/source/match.rs
index da205172..b5dc9957 100644
--- a/tests/source/match.rs
+++ b/tests/source/match.rs
@@ -568,3 +568,22 @@ fn issue_3774() {
         }
     }
 }
+
+// #4109
+fn issue_4109() {
+    match () {
+        _ => {
+#[cfg(debug_assertions)]
+{
+println!("Foo");
+}
+}
+}
+
+match () {
+_ => {
+#[allow(unsafe_code)]
+unsafe {}
+}
+}
+}
diff --git a/tests/target/match.rs b/tests/target/match.rs
index e2c522be..1bf3fb75 100644
--- a/tests/target/match.rs
+++ b/tests/target/match.rs
@@ -608,3 +608,22 @@ fn issue_3774() {
         }
     }
 }
+
+// #4109
+fn issue_4109() {
+    match () {
+        _ => {
+            #[cfg(debug_assertions)]
+            {
+                println!("Foo");
+            }
+        }
+    }
+
+    match () {
+        _ => {
+            #[allow(unsafe_code)]
+            unsafe {}
+        }
+    }
+}