Repository: rust-lang/rustfmt (rust)

Issue:
Rustfmt creates invalid syntax on extern block containing macro call
```rust
macro_rules! x {
    ($tt:tt) => {};
}

extern "C" {
    x!(-);
    x!(x);
}
```

Rustfmt loses the necessary `;` after *some* macro calls, which becomes invalid Rust syntax in the above input. The call `x!(x);` appears unaffected, regardless of the order of the two calls.

```diff
-     x!(-);
+     x!(-)
```

Self-contained repro as of current master (e0c7b7d5d30d5b47f9d6a9fd3c449fc610a330b9):

```console
$ echo 'extern { x!(-); x!(x); }' | cargo run --bin rustfmt
extern "C" {
    x!(-)
    x!(x);
}

$ !! | rustc -
error: macros that expand to items must be delimited with braces or followed by a semicolon
 --> <anon>:2:7
  |
2 |     x!(-)
  |       ^^^
  |
help: change the delimiters to curly braces
  |
2 |     x!{-}
  |       ~ ~
help: add a semicolon
  |
2 |     x!(-);
  |          +
```

Pull request:
Preserve semicolon after macro call inside foreign mod
Fixes #5281.

### Before:

```console
$ echo 'extern { x!(-); x!(x); }' | cargo run --bin rustfmt
extern "C" {
    x!(-)
    x!(x);
}
```

### After:

```console
$ echo 'extern { x!(-); x!(x); }' | cargo run --bin rustfmt
extern "C" {
    x!(-);
    x!(x);
}
```

Patch:
diff --git a/src/macros.rs b/src/macros.rs
index fdbe3374..664f152e 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -112,6 +112,7 @@ fn rewrite_macro_name(
 fn return_macro_parse_failure_fallback(
     context: &RewriteContext<'_>,
     indent: Indent,
+    position: MacroPosition,
     span: Span,
 ) -> Option<String> {
     // Mark this as a failure however we format it
@@ -140,7 +141,11 @@ fn return_macro_parse_failure_fallback(
     ));
 
     // Return the snippet unmodified if the macro is not block-like
-    Some(context.snippet(span).to_owned())
+    let mut snippet = context.snippet(span).to_owned();
+    if position == MacroPosition::Item {
+        snippet.push(';');
+    }
+    Some(snippet)
 }
 
 pub(crate) fn rewrite_macro(
@@ -233,7 +238,12 @@ fn rewrite_macro_inner(
     } = match parse_macro_args(context, ts, style, is_forced_bracket) {
         Some(args) => args,
         None => {
-            return return_macro_parse_failure_fallback(context, shape.indent, mac.span());
+            return return_macro_parse_failure_fallback(
+                context,
+                shape.indent,
+                position,
+                mac.span(),
+            );
         }
     };
 


Test patch:
diff --git a/tests/source/extern.rs b/tests/source/extern.rs
index d0a033b1..f51ba6e9 100644
--- a/tests/source/extern.rs
+++ b/tests/source/extern.rs
@@ -77,3 +77,16 @@ libc::c_long;
 extern {
 
 }
+
+macro_rules! x {
+    ($tt:tt) => {};
+}
+
+extern "macros" {
+    x!(ident);
+    x!(#);
+    x![ident];
+    x![#];
+    x! {ident}
+    x! {#}
+}
diff --git a/tests/target/extern.rs b/tests/target/extern.rs
index 44ed6d4b..d1741360 100644
--- a/tests/target/extern.rs
+++ b/tests/target/extern.rs
@@ -82,3 +82,16 @@ extern "C" {
 }
 
 extern "C" {}
+
+macro_rules! x {
+    ($tt:tt) => {};
+}
+
+extern "macros" {
+    x!(ident);
+    x!(#);
+    x![ident];
+    x![#];
+    x! {ident}
+    x! {#}
+}