Repository: rust-lang/rustfmt (rust)

Issue:
`empty_item_single_line` should apply to trait definitions as well
1.4.37-nightly

Using this rustfmt.toml 

```toml
brace_style = "AlwaysNextLine"
empty_item_single_line = true
```
rustfmt will format the following code like this:

```rust
fn function() {}

struct Struct {}

enum Enum {}

trait Trait
{
}

impl<T> Trait for T {}
```

This is inconsistent, and empty traits end up sticking out like a sore thumb. Intended behaviour is probably this:

```rust
fn function() {}
struct Struct {}
enum Enum {}
trait Trait {}
impl<T> Trait for T {}
```

This is important, as empty traits are often quite useful. For instance, you might want something like this:

```rust
trait Trait: Send + Clone {}
impl<T: Send + Clone> Trait for T {}
```

which will look quite ugly at the moment.

Pull request:
Put empty trait braces on same line if possible
Fixes #5047.

Patch:
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index b3dda091..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-environment:
-  global:
-    PROJECT_NAME: rustfmt
-
-build: false
-
-test_script:
-  - echo Why does no one have access to delete me?
diff --git a/intellij.md b/intellij.md
index 7aea6222..6a711c01 100644
--- a/intellij.md
+++ b/intellij.md
@@ -3,19 +3,28 @@
 ## Installation
 
 - Install [CLion](https://www.jetbrains.com/clion/), [IntelliJ Ultimate or CE](https://www.jetbrains.com/idea/) through the direct download link or using the [JetBrains Toolbox](https://www.jetbrains.com/toolbox/).
-  CLion provides a built-in debugger interface but its not free like IntelliJ CE - which does not provide the debugger interface. (IntelliJ seems to lack the toolchain for that, see this discussion [intellij-rust/issues/535](https://github.com/intellij-rust/intellij-rust/issues/535))
-  
-- Install the [Rust Plugin](https://intellij-rust.github.io/) by navigating to File -> Settings -> Plugins and press "Install JetBrains Plugin"
-  ![plugins](https://user-images.githubusercontent.com/1133787/47240861-f40af680-d3e9-11e8-9b82-cdd5c8d5f5b8.png)
+  CLion and IntelliJ Ultimate [provide a built-in debugger interface](https://github.com/intellij-rust/intellij-rust#compatible-ides) but they are not free like IntelliJ CE.
+
+- Install the [Rust Plugin](https://intellij-rust.github.io/) by navigating to File → Settings → Plugins and searching the plugin in the Marketplace
+  ![plugins](https://user-images.githubusercontent.com/6505554/83944518-6f1e5c00-a81d-11ea-9c35-e16948811ba8.png)
 
-- Press "Install" on the rust plugin
-  ![install rust](https://user-images.githubusercontent.com/1133787/47240803-c0c86780-d3e9-11e8-9265-22f735e4d7ed.png)
+- Press "Install" on the Rust plugin
+  ![install rust](https://user-images.githubusercontent.com/6505554/83944533-82c9c280-a81d-11ea-86b3-ee2e31bc7d12.png)
   
 - Restart CLion/IntelliJ
 
 ## Configuration
 
-- Open the settings window (File -> Settings) and search for "reformat"
+### Run Rustfmt on save
+
+- Open Rustfmt settings (File → Settings → Languages & Frameworks → Rust → Rustfmt) and enable "Run rustfmt on Save"
+  ![run_rustfmt_on_save](https://user-images.githubusercontent.com/6505554/83944610-3468f380-a81e-11ea-9c34-0cbd18dd4969.png)
+
+- IntellJ uses autosave, so now your files will always be formatted according to rustfmt. Alternatively you can use Ctrl+S to reformat file manually
+
+### Bind shortcut to "Reformat File with Rustfmt" action
+
+- Open the settings window (File → Settings) and search for "reformat"
   ![keymap](https://user-images.githubusercontent.com/1133787/47240922-2ae10c80-d3ea-11e8-9d8f-c798d9749240.png)
 - Right-click on "Reformat File with Rustfmt" and assign a keyboard shortcut
 
diff --git a/src/config/options.rs b/src/config/options.rs
index a17d4834..bce9e5d0 100644
--- a/src/config/options.rs
+++ b/src/config/options.rs
@@ -20,7 +20,7 @@ pub enum NewlineStyle {
     Windows,
     /// Force CR (`\n).
     Unix,
-    /// `\r\n` in Windows, `\n`` on other platforms.
+    /// `\r\n` in Windows, `\n` on other platforms.
     Native,
 }
 
diff --git a/src/items.rs b/src/items.rs
index 1c7899b3..e37a1b69 100644
--- a/src/items.rs
+++ b/src/items.rs
@@ -1122,12 +1122,24 @@ pub(crate) fn format_trait(
             }
         }
 
+        let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi());
+        let snippet = context.snippet(block_span);
+        let open_pos = snippet.find_uncommented("{")? + 1;
+
         match context.config.brace_style() {
             _ if last_line_contains_single_line_comment(&result)
                 || last_line_width(&result) + 2 > context.budget(offset.width()) =>
             {
                 result.push_str(&offset.to_string_with_newline(context.config));
             }
+            _ if context.config.empty_item_single_line()
+                && trait_items.is_empty()
+                && !result.contains('\n')
+                && !contains_comment(&snippet[open_pos..]) =>
+            {
+                result.push_str(" {}");
+                return Some(result);
+            }
             BraceStyle::AlwaysNextLine => {
                 result.push_str(&offset.to_string_with_newline(context.config));
             }
@@ -1144,9 +1156,6 @@ pub(crate) fn format_trait(
         }
         result.push('{');
 
-        let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi());
-        let snippet = context.snippet(block_span);
-        let open_pos = snippet.find_uncommented("{")? + 1;
         let outer_indent_str = offset.block_only().to_string_with_newline(context.config);
 
         if !trait_items.is_empty() || contains_comment(&snippet[open_pos..]) {
diff --git a/src/modules.rs b/src/modules.rs
index 9e75f41a..b1f229d9 100644
--- a/src/modules.rs
+++ b/src/modules.rs
@@ -321,7 +321,9 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
             (Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _))), _) => {
                 self.visit_mod_from_ast(items)
             }
-            (Some(Cow::Owned(..)), Cow::Owned(items)) => self.visit_mod_outside_ast(items),
+            (Some(Cow::Owned(ast::ModKind::Loaded(items, _, _))), _) | (_, Cow::Owned(items)) => {
+                self.visit_mod_outside_ast(items)
+            }
             (_, _) => Ok(()),
         }
     }


Test patch:
diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs
index e0b55e3e..ae4a0d0f 100644
--- a/src/test/mod_resolver.rs
+++ b/src/test/mod_resolver.rs
@@ -5,21 +5,39 @@ use super::read_config;
 
 use crate::{FileName, Input, Session};
 
-#[test]
-fn nested_out_of_line_mods_loaded() {
-    // See also https://github.com/rust-lang/rustfmt/issues/4874
-    let filename = "tests/mod-resolver/issue-4874/main.rs";
-    let input_file = PathBuf::from(filename);
+fn verify_mod_resolution(input_file_name: &str, exp_misformatted_files: &[&str]) {
+    let input_file = PathBuf::from(input_file_name);
     let config = read_config(&input_file);
     let mut session = Session::<io::Stdout>::new(config, None);
     let report = session
-        .format(Input::File(filename.into()))
+        .format(Input::File(input_file_name.into()))
         .expect("Should not have had any execution errors");
     let errors_by_file = &report.internal.borrow().0;
-    assert!(errors_by_file.contains_key(&FileName::Real(PathBuf::from(
-        "tests/mod-resolver/issue-4874/bar/baz.rs",
-    ))));
-    assert!(errors_by_file.contains_key(&FileName::Real(PathBuf::from(
-        "tests/mod-resolver/issue-4874/foo/qux.rs",
-    ))));
+    for exp_file in exp_misformatted_files {
+        assert!(errors_by_file.contains_key(&FileName::Real(PathBuf::from(exp_file))));
+    }
+}
+
+#[test]
+fn nested_out_of_line_mods_loaded() {
+    // See also https://github.com/rust-lang/rustfmt/issues/4874
+    verify_mod_resolution(
+        "tests/mod-resolver/issue-4874/main.rs",
+        &[
+            "tests/mod-resolver/issue-4874/bar/baz.rs",
+            "tests/mod-resolver/issue-4874/foo/qux.rs",
+        ],
+    );
+}
+
+#[test]
+fn out_of_line_nested_inline_within_out_of_line() {
+    // See also https://github.com/rust-lang/rustfmt/issues/5063
+    verify_mod_resolution(
+        "tests/mod-resolver/issue-5063/main.rs",
+        &[
+            "tests/mod-resolver/issue-5063/foo/bar/baz.rs",
+            "tests/mod-resolver/issue-5063/foo.rs",
+        ],
+    );
 }
diff --git a/tests/mod-resolver/issue-5063/foo.rs b/tests/mod-resolver/issue-5063/foo.rs
new file mode 100644
index 00000000..d5697477
--- /dev/null
+++ b/tests/mod-resolver/issue-5063/foo.rs
@@ -0,0 +1,2 @@
+mod bar {
+        mod baz;}
\ No newline at end of file
diff --git a/tests/mod-resolver/issue-5063/foo/bar/baz.rs b/tests/mod-resolver/issue-5063/foo/bar/baz.rs
new file mode 100644
index 00000000..3519b0ee
--- /dev/null
+++ b/tests/mod-resolver/issue-5063/foo/bar/baz.rs
@@ -0,0 +1,1 @@
+fn    baz()    {       }
\ No newline at end of file
diff --git a/tests/mod-resolver/issue-5063/main.rs b/tests/mod-resolver/issue-5063/main.rs
new file mode 100644
index 00000000..41c81c7b
--- /dev/null
+++ b/tests/mod-resolver/issue-5063/main.rs
@@ -0,0 +1,5 @@
+fn main() {
+    println!("Hello, world!");
+}
+
+mod foo;
\ No newline at end of file
diff --git a/tests/source/empty-item-single-line-false.rs b/tests/source/empty-item-single-line-false.rs
new file mode 100644
index 00000000..20c5bc83
--- /dev/null
+++ b/tests/source/empty-item-single-line-false.rs
@@ -0,0 +1,46 @@
+// rustfmt-brace_style: AlwaysNextLine
+// rustfmt-empty_item_single_line: false
+
+fn function()
+{
+
+}
+
+struct Struct
+{
+
+}
+
+enum Enum
+{
+
+}
+
+trait Trait
+{
+
+}
+
+impl<T> Trait for T
+{
+
+}
+
+trait Trait2<T>
+where
+    T: Copy + Display + Write + Read + FromStr, {}
+
+trait Trait3<T>
+where
+    T: Something
+        + SomethingElse
+        + Sync
+        + Send
+        + Display
+        + Debug
+        + Copy
+        + Hash
+        + Debug
+        + Display
+        + Write
+        + Read, {}
diff --git a/tests/source/item-brace-style-always-next-line.rs b/tests/source/item-brace-style-always-next-line.rs
index 38094d67..0fb64051 100644
--- a/tests/source/item-brace-style-always-next-line.rs
+++ b/tests/source/item-brace-style-always-next-line.rs
@@ -27,3 +27,38 @@ mod M {
 
     struct D<T> where T: Copy {}
 }
+
+
+fn function()
+{
+
+}
+
+trait Trait
+{
+
+}
+
+impl<T> Trait for T
+{
+
+}
+
+trait Trait2<T>
+where
+    T: Copy + Display + Write + Read + FromStr, {}
+
+trait Trait3<T>
+where
+    T: Something
+        + SomethingElse
+        + Sync
+        + Send
+        + Display
+        + Debug
+        + Copy
+        + Hash
+        + Debug
+        + Display
+        + Write
+        + Read, {}
diff --git a/tests/target/empty-item-single-line-false.rs b/tests/target/empty-item-single-line-false.rs
new file mode 100644
index 00000000..bf7f70e7
--- /dev/null
+++ b/tests/target/empty-item-single-line-false.rs
@@ -0,0 +1,41 @@
+// rustfmt-brace_style: AlwaysNextLine
+// rustfmt-empty_item_single_line: false
+
+fn function()
+{
+}
+
+struct Struct {}
+
+enum Enum {}
+
+trait Trait
+{
+}
+
+impl<T> Trait for T
+{
+}
+
+trait Trait2<T>
+where
+    T: Copy + Display + Write + Read + FromStr,
+{
+}
+
+trait Trait3<T>
+where
+    T: Something
+        + SomethingElse
+        + Sync
+        + Send
+        + Display
+        + Debug
+        + Copy
+        + Hash
+        + Debug
+        + Display
+        + Write
+        + Read,
+{
+}
diff --git a/tests/target/item-brace-style-always-next-line.rs b/tests/target/item-brace-style-always-next-line.rs
index 531ac598..4935fac0 100644
--- a/tests/target/item-brace-style-always-next-line.rs
+++ b/tests/target/item-brace-style-always-next-line.rs
@@ -40,3 +40,32 @@ mod M
     where
         T: Copy, {}
 }
+
+fn function() {}
+
+trait Trait {}
+
+impl<T> Trait for T {}
+
+trait Trait2<T>
+where
+    T: Copy + Display + Write + Read + FromStr,
+{
+}
+
+trait Trait3<T>
+where
+    T: Something
+        + SomethingElse
+        + Sync
+        + Send
+        + Display
+        + Debug
+        + Copy
+        + Hash
+        + Debug
+        + Display
+        + Write
+        + Read,
+{
+}