Repository: rust-lang/rustfmt (rust)

Issue:
`rustfmt` is not idempotent and not consistent
When formatting the following code, 

```rust
mod test {
    extern "C" {fn test();}
}
```
The first diff suggested by `rustfmt` is 
```diff
-    extern "C" {fn test();}
+    extern "C" {
+        fn test();    }
```
After running this formatting, the next suggestion is 
```diff
-        fn test();    }
+        fn test();
+    }
```
The final result seems to be correct, but should most likely be applied at once, not in two steps.

Additionally, removing the `mod` block causes the changes to look like this:
```rust
extern "C" {fn test();}
```
```diff
-extern "C" {fn test();}
+extern "C" {
+    fn test();}
```
This change _is_ idempotent, but I would expect the closing `}` to be moved to the next line (which is what happens if a whitespace is put in between the final `;` and `}`).

Pull request description:
resolve idempotency issue in extern body elements
Fixes #4963 

Patch:
diff --git a/src/missed_spans.rs b/src/missed_spans.rs
index 263d8407..28edcb78 100644
--- a/src/missed_spans.rs
+++ b/src/missed_spans.rs
@@ -51,6 +51,14 @@ impl<'a> FmtVisitor<'a> {
     }
 
     pub(crate) fn format_missing_with_indent(&mut self, end: BytePos) {
+        self.format_missing_indent(end, true)
+    }
+
+    pub(crate) fn format_missing_no_indent(&mut self, end: BytePos) {
+        self.format_missing_indent(end, false)
+    }
+
+    fn format_missing_indent(&mut self, end: BytePos, should_indent: bool) {
         let config = self.config;
         self.format_missing_inner(end, |this, last_snippet, snippet| {
             this.push_str(last_snippet.trim_end());
@@ -58,14 +66,10 @@ impl<'a> FmtVisitor<'a> {
                 // No new lines in the snippet.
                 this.push_str("\n");
             }
-            let indent = this.block_indent.to_string(config);
-            this.push_str(&indent);
-        })
-    }
-
-    pub(crate) fn format_missing_no_indent(&mut self, end: BytePos) {
-        self.format_missing_inner(end, |this, last_snippet, _| {
-            this.push_str(last_snippet.trim_end());
+            if should_indent {
+                let indent = this.block_indent.to_string(config);
+                this.push_str(&indent);
+            }
         })
     }
 


Test patch:
diff --git a/tests/source/issue_4963.rs b/tests/source/issue_4963.rs
new file mode 100644
index 00000000..32e1f6cd
--- /dev/null
+++ b/tests/source/issue_4963.rs
@@ -0,0 +1,5 @@
+mod test {
+    extern "C" {fn test();}
+}
+
+extern "C" {fn test();}
\ No newline at end of file
diff --git a/tests/target/issue_4963.rs b/tests/target/issue_4963.rs
new file mode 100644
index 00000000..0c3c1357
--- /dev/null
+++ b/tests/target/issue_4963.rs
@@ -0,0 +1,9 @@
+mod test {
+    extern "C" {
+        fn test();
+    }
+}
+
+extern "C" {
+    fn test();
+}