about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCaleb Cartwright <caleb.cartwright@outlook.com>2022-01-23 11:07:20 -0600
committerCaleb Cartwright <caleb.cartwright@outlook.com>2022-01-23 11:07:20 -0600
commit9b454a59906b472022855b7044a01f7bc89210cf (patch)
treeb5a3e2148509cd74f9882d01937e2a3e14cc934b
parent7913f130d3ee0682916586c7b43664b651435594 (diff)
parent5056f4cfb311a084420f1828cd58af94d143f5e0 (diff)
downloadrust-9b454a59906b472022855b7044a01f7bc89210cf.tar.gz
rust-9b454a59906b472022855b7044a01f7bc89210cf.zip
Merge remote-tracking branch 'upstream/master' into subtree-sync-2022-01-23
-rw-r--r--src/bin/main.rs31
-rw-r--r--src/cargo-fmt/main.rs3
-rw-r--r--src/config/file_lines.rs2
-rw-r--r--src/emitter/checkstyle.rs12
-rw-r--r--src/emitter/diff.rs5
-rw-r--r--src/emitter/json.rs187
-rw-r--r--src/format_report_formatter.rs1
-rw-r--r--src/macros.rs2
-rw-r--r--src/modules.rs9
-rw-r--r--src/test/mod.rs123
-rw-r--r--src/test/mod_resolver.rs14
-rw-r--r--tests/cargo-fmt/main.rs24
-rw-r--r--tests/mod-resolver/test-submodule-issue-5119/Cargo.toml8
-rw-r--r--tests/mod-resolver/test-submodule-issue-5119/src/lib.rs7
-rw-r--r--tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs8
-rw-r--r--tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs6
-rw-r--r--tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs6
-rw-r--r--tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs1
-rw-r--r--tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs0
-rw-r--r--tests/writemode/source/stdin.rs6
-rw-r--r--tests/writemode/target/output.json2
-rw-r--r--tests/writemode/target/stdin.json1
-rw-r--r--tests/writemode/target/stdin.xml2
23 files changed, 334 insertions, 126 deletions
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 4d845547cdf..6f5b09fc86a 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -74,14 +74,10 @@ pub enum OperationError {
     /// An io error during reading or writing.
     #[error("{0}")]
     IoError(IoError),
-    /// Attempt to use --check with stdin, which isn't currently
-    /// supported.
-    #[error("The `--check` option is not supported with standard input.")]
-    CheckWithStdin,
-    /// Attempt to use --emit=json with stdin, which isn't currently
-    /// supported.
-    #[error("Using `--emit` other than stdout is not supported with standard input.")]
-    EmitWithStdin,
+    /// Attempt to use --emit with a mode which is not currently
+    /// supported with stdandard input.
+    #[error("Emit mode {0} not supported with standard output.")]
+    StdinBadEmit(EmitMode),
 }
 
 impl From<IoError> for OperationError {
@@ -255,15 +251,20 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32> {
     let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?;
 
     if options.check {
-        return Err(OperationError::CheckWithStdin.into());
-    }
-    if let Some(emit_mode) = options.emit_mode {
-        if emit_mode != EmitMode::Stdout {
-            return Err(OperationError::EmitWithStdin.into());
+        config.set().emit_mode(EmitMode::Diff);
+    } else {
+        match options.emit_mode {
+            // Emit modes which work with standard input
+            // None means default, which is Stdout.
+            None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {}
+            Some(emit_mode) => {
+                return Err(OperationError::StdinBadEmit(emit_mode).into());
+            }
         }
+        config
+            .set()
+            .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
     }
-    // emit mode is always Stdout for Stdin.
-    config.set().emit_mode(EmitMode::Stdout);
     config.set().verbose(Verbosity::Quiet);
 
     // parse file_lines
diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs
index 759b21218c3..8cb7b4585ec 100644
--- a/src/cargo-fmt/main.rs
+++ b/src/cargo-fmt/main.rs
@@ -387,8 +387,7 @@ fn get_targets_root_only(
                         .unwrap_or_default()
                         == current_dir_manifest
             })
-            .map(|p| p.targets)
-            .flatten()
+            .flat_map(|p| p.targets)
             .collect(),
     };
 
diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs
index 7b498dc46b3..e4e51a3f3b4 100644
--- a/src/config/file_lines.rs
+++ b/src/config/file_lines.rs
@@ -39,7 +39,7 @@ impl fmt::Display for FileName {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()),
-            FileName::Stdin => write!(f, "stdin"),
+            FileName::Stdin => write!(f, "<stdin>"),
         }
     }
 }
diff --git a/src/emitter/checkstyle.rs b/src/emitter/checkstyle.rs
index 76f2527db3d..545b259979d 100644
--- a/src/emitter/checkstyle.rs
+++ b/src/emitter/checkstyle.rs
@@ -2,7 +2,6 @@ use self::xml::XmlEscaped;
 use super::*;
 use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
 use std::io::{self, Write};
-use std::path::Path;
 
 mod xml;
 
@@ -30,7 +29,6 @@ impl Emitter for CheckstyleEmitter {
         }: FormattedFile<'_>,
     ) -> Result<EmitterResult, io::Error> {
         const CONTEXT_SIZE: usize = 0;
-        let filename = ensure_real_path(filename);
         let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
         output_checkstyle_file(output, filename, diff)?;
         Ok(EmitterResult::default())
@@ -39,13 +37,13 @@ impl Emitter for CheckstyleEmitter {
 
 pub(crate) fn output_checkstyle_file<T>(
     mut writer: T,
-    filename: &Path,
+    filename: &FileName,
     diff: Vec<Mismatch>,
 ) -> Result<(), io::Error>
 where
     T: Write,
 {
-    write!(writer, r#"<file name="{}">"#, filename.display())?;
+    write!(writer, r#"<file name="{}">"#, filename)?;
     for mismatch in diff {
         let begin_line = mismatch.line_number;
         let mut current_line;
@@ -77,7 +75,11 @@ mod tests {
     fn emits_empty_record_on_file_with_no_mismatches() {
         let file_name = "src/well_formatted.rs";
         let mut writer = Vec::new();
-        let _ = output_checkstyle_file(&mut writer, &PathBuf::from(file_name), vec![]);
+        let _ = output_checkstyle_file(
+            &mut writer,
+            &FileName::Real(PathBuf::from(file_name)),
+            vec![],
+        );
         assert_eq!(
             &writer[..],
             format!(r#"<file name="{}"></file>"#, file_name).as_bytes()
diff --git a/src/emitter/diff.rs b/src/emitter/diff.rs
index 7264ad8bbf3..5e1f1344656 100644
--- a/src/emitter/diff.rs
+++ b/src/emitter/diff.rs
@@ -28,7 +28,7 @@ impl Emitter for DiffEmitter {
 
         if has_diff {
             if self.config.print_misformatted_file_names() {
-                writeln!(output, "{}", ensure_real_path(filename).display())?;
+                writeln!(output, "{}", filename)?;
             } else {
                 print_diff(
                     mismatch,
@@ -40,8 +40,7 @@ impl Emitter for DiffEmitter {
             // This occurs when the only difference between the original and formatted values
             // is the newline style. This happens because The make_diff function compares the
             // original and formatted values line by line, independent of line endings.
-            let file_path = ensure_real_path(filename);
-            writeln!(output, "Incorrect newline style in {}", file_path.display())?;
+            writeln!(output, "Incorrect newline style in {}", filename)?;
             return Ok(EmitterResult { has_diff: true });
         }
 
diff --git a/src/emitter/json.rs b/src/emitter/json.rs
index 269dd2d4daf..c7f68d4675a 100644
--- a/src/emitter/json.rs
+++ b/src/emitter/json.rs
@@ -3,14 +3,13 @@ use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
 use serde::Serialize;
 use serde_json::to_string as to_json_string;
 use std::io::{self, Write};
-use std::path::Path;
 
 #[derive(Debug, Default)]
 pub(crate) struct JsonEmitter {
-    num_files: u32,
+    mismatched_files: Vec<MismatchedFile>,
 }
 
-#[derive(Debug, Default, Serialize)]
+#[derive(Debug, Default, PartialEq, Serialize)]
 struct MismatchedBlock {
     original_begin_line: u32,
     original_end_line: u32,
@@ -20,26 +19,20 @@ struct MismatchedBlock {
     expected: String,
 }
 
-#[derive(Debug, Default, Serialize)]
+#[derive(Debug, Default, PartialEq, Serialize)]
 struct MismatchedFile {
     name: String,
     mismatches: Vec<MismatchedBlock>,
 }
 
 impl Emitter for JsonEmitter {
-    fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> {
-        write!(output, "[")?;
-        Ok(())
-    }
-
     fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> {
-        write!(output, "]")?;
-        Ok(())
+        writeln!(output, "{}", &to_json_string(&self.mismatched_files)?)
     }
 
     fn emit_formatted_file(
         &mut self,
-        output: &mut dyn Write,
+        _output: &mut dyn Write,
         FormattedFile {
             filename,
             original_text,
@@ -47,71 +40,67 @@ impl Emitter for JsonEmitter {
         }: FormattedFile<'_>,
     ) -> Result<EmitterResult, io::Error> {
         const CONTEXT_SIZE: usize = 0;
-        let filename = ensure_real_path(filename);
         let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
         let has_diff = !diff.is_empty();
 
         if has_diff {
-            output_json_file(output, filename, diff, self.num_files)?;
-            self.num_files += 1;
+            self.add_misformatted_file(filename, diff)?;
         }
 
         Ok(EmitterResult { has_diff })
     }
 }
 
-fn output_json_file<T>(
-    mut writer: T,
-    filename: &Path,
-    diff: Vec<Mismatch>,
-    num_emitted_files: u32,
-) -> Result<(), io::Error>
-where
-    T: Write,
-{
-    let mut mismatches = vec![];
-    for mismatch in diff {
-        let original_begin_line = mismatch.line_number_orig;
-        let expected_begin_line = mismatch.line_number;
-        let mut original_end_line = original_begin_line;
-        let mut expected_end_line = expected_begin_line;
-        let mut original_line_counter = 0;
-        let mut expected_line_counter = 0;
-        let mut original_lines = vec![];
-        let mut expected_lines = vec![];
+impl JsonEmitter {
+    fn add_misformatted_file(
+        &mut self,
+        filename: &FileName,
+        diff: Vec<Mismatch>,
+    ) -> Result<(), io::Error> {
+        let mut mismatches = vec![];
+        for mismatch in diff {
+            let original_begin_line = mismatch.line_number_orig;
+            let expected_begin_line = mismatch.line_number;
+            let mut original_end_line = original_begin_line;
+            let mut expected_end_line = expected_begin_line;
+            let mut original_line_counter = 0;
+            let mut expected_line_counter = 0;
+            let mut original = String::new();
+            let mut expected = String::new();
 
-        for line in mismatch.lines {
-            match line {
-                DiffLine::Expected(msg) => {
-                    expected_end_line = expected_begin_line + expected_line_counter;
-                    expected_line_counter += 1;
-                    expected_lines.push(msg)
-                }
-                DiffLine::Resulting(msg) => {
-                    original_end_line = original_begin_line + original_line_counter;
-                    original_line_counter += 1;
-                    original_lines.push(msg)
+            for line in mismatch.lines {
+                match line {
+                    DiffLine::Expected(msg) => {
+                        expected_end_line = expected_begin_line + expected_line_counter;
+                        expected_line_counter += 1;
+                        expected.push_str(&msg);
+                        expected.push('\n');
+                    }
+                    DiffLine::Resulting(msg) => {
+                        original_end_line = original_begin_line + original_line_counter;
+                        original_line_counter += 1;
+                        original.push_str(&msg);
+                        original.push('\n');
+                    }
+                    DiffLine::Context(_) => continue,
                 }
-                DiffLine::Context(_) => continue,
             }
-        }
 
-        mismatches.push(MismatchedBlock {
-            original_begin_line,
-            original_end_line,
-            expected_begin_line,
-            expected_end_line,
-            original: original_lines.join("\n"),
-            expected: expected_lines.join("\n"),
+            mismatches.push(MismatchedBlock {
+                original_begin_line,
+                original_end_line,
+                expected_begin_line,
+                expected_end_line,
+                original,
+                expected,
+            });
+        }
+        self.mismatched_files.push(MismatchedFile {
+            name: format!("{}", filename),
+            mismatches,
         });
+        Ok(())
     }
-    let json = to_json_string(&MismatchedFile {
-        name: String::from(filename.to_str().unwrap()),
-        mismatches,
-    })?;
-    let prefix = if num_emitted_files > 0 { "," } else { "" };
-    write!(writer, "{}{}", prefix, &json)?;
-    Ok(())
 }
 
 #[cfg(test)]
@@ -122,6 +111,9 @@ mod tests {
 
     #[test]
     fn expected_line_range_correct_when_single_line_split() {
+        let mut emitter = JsonEmitter {
+            mismatched_files: vec![],
+        };
         let file = "foo/bar.rs";
         let mismatched_file = MismatchedFile {
             name: String::from(file),
@@ -130,8 +122,8 @@ mod tests {
                 original_end_line: 79,
                 expected_begin_line: 79,
                 expected_end_line: 82,
-                original: String::from("fn Foo<T>() where T: Bar {"),
-                expected: String::from("fn Foo<T>()\nwhere\n    T: Bar,\n{"),
+                original: String::from("fn Foo<T>() where T: Bar {\n"),
+                expected: String::from("fn Foo<T>()\nwhere\n    T: Bar,\n{\n"),
             }],
         };
         let mismatch = Mismatch {
@@ -146,14 +138,19 @@ mod tests {
             ],
         };
 
-        let mut writer = Vec::new();
-        let exp_json = to_json_string(&mismatched_file).unwrap();
-        let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0);
-        assert_eq!(&writer[..], format!("{}", exp_json).as_bytes());
+        let _ = emitter
+            .add_misformatted_file(&FileName::Real(PathBuf::from(file)), vec![mismatch])
+            .unwrap();
+
+        assert_eq!(emitter.mismatched_files.len(), 1);
+        assert_eq!(emitter.mismatched_files[0], mismatched_file);
     }
 
     #[test]
     fn context_lines_ignored() {
+        let mut emitter = JsonEmitter {
+            mismatched_files: vec![],
+        };
         let file = "src/lib.rs";
         let mismatched_file = MismatchedFile {
             name: String::from(file),
@@ -163,10 +160,10 @@ mod tests {
                 expected_begin_line: 5,
                 expected_end_line: 5,
                 original: String::from(
-                    "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {",
+                    "fn foo(_x: &u64) -> Option<&(dyn::std::error::Error + 'static)> {\n",
                 ),
                 expected: String::from(
-                    "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {",
+                    "fn foo(_x: &u64) -> Option<&(dyn ::std::error::Error + 'static)> {\n",
                 ),
             }],
         };
@@ -186,10 +183,12 @@ mod tests {
             ],
         };
 
-        let mut writer = Vec::new();
-        let exp_json = to_json_string(&mismatched_file).unwrap();
-        let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0);
-        assert_eq!(&writer[..], format!("{}", exp_json).as_bytes());
+        let _ = emitter
+            .add_misformatted_file(&FileName::Real(PathBuf::from(file)), vec![mismatch])
+            .unwrap();
+
+        assert_eq!(emitter.mismatched_files.len(), 1);
+        assert_eq!(emitter.mismatched_files[0], mismatched_file);
     }
 
     #[test]
@@ -209,7 +208,7 @@ mod tests {
             .unwrap();
         let _ = emitter.emit_footer(&mut writer);
         assert_eq!(result.has_diff, false);
-        assert_eq!(&writer[..], "[]".as_bytes());
+        assert_eq!(&writer[..], "[]\n".as_bytes());
     }
 
     #[test]
@@ -255,7 +254,7 @@ mod tests {
             )
             .unwrap();
         let _ = emitter.emit_footer(&mut writer);
-        let exp_json = to_json_string(&MismatchedFile {
+        let exp_json = to_json_string(&vec![MismatchedFile {
             name: String::from(file_name),
             mismatches: vec![
                 MismatchedBlock {
@@ -263,8 +262,8 @@ mod tests {
                     original_end_line: 2,
                     expected_begin_line: 2,
                     expected_end_line: 2,
-                    original: String::from("println!(\"Hello, world!\");"),
-                    expected: String::from("    println!(\"Hello, world!\");"),
+                    original: String::from("println!(\"Hello, world!\");\n"),
+                    expected: String::from("    println!(\"Hello, world!\");\n"),
                 },
                 MismatchedBlock {
                     original_begin_line: 7,
@@ -272,17 +271,17 @@ mod tests {
                     expected_begin_line: 7,
                     expected_end_line: 10,
                     original: String::from(
-                        "#[test]\nfn it_works() {\n    assert_eq!(2 + 2, 4);\n}",
+                        "#[test]\nfn it_works() {\n    assert_eq!(2 + 2, 4);\n}\n",
                     ),
                     expected: String::from(
-                        "    #[test]\n    fn it_works() {\n        assert_eq!(2 + 2, 4);\n    }",
+                        "    #[test]\n    fn it_works() {\n        assert_eq!(2 + 2, 4);\n    }\n",
                     ),
                 },
             ],
-        })
+        }])
         .unwrap();
         assert_eq!(result.has_diff, true);
-        assert_eq!(&writer[..], format!("[{}]", exp_json).as_bytes());
+        assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes());
     }
 
     #[test]
@@ -317,33 +316,31 @@ mod tests {
             )
             .unwrap();
         let _ = emitter.emit_footer(&mut writer);
-        let exp_bin_json = to_json_string(&MismatchedFile {
+        let exp_bin = MismatchedFile {
             name: String::from(bin_file),
             mismatches: vec![MismatchedBlock {
                 original_begin_line: 2,
                 original_end_line: 2,
                 expected_begin_line: 2,
                 expected_end_line: 2,
-                original: String::from("println!(\"Hello, world!\");"),
-                expected: String::from("    println!(\"Hello, world!\");"),
+                original: String::from("println!(\"Hello, world!\");\n"),
+                expected: String::from("    println!(\"Hello, world!\");\n"),
             }],
-        })
-        .unwrap();
-        let exp_lib_json = to_json_string(&MismatchedFile {
+        };
+
+        let exp_lib = MismatchedFile {
             name: String::from(lib_file),
             mismatches: vec![MismatchedBlock {
                 original_begin_line: 2,
                 original_end_line: 2,
                 expected_begin_line: 2,
                 expected_end_line: 2,
-                original: String::from("println!(\"Greetings!\");"),
-                expected: String::from("    println!(\"Greetings!\");"),
+                original: String::from("println!(\"Greetings!\");\n"),
+                expected: String::from("    println!(\"Greetings!\");\n"),
             }],
-        })
-        .unwrap();
-        assert_eq!(
-            &writer[..],
-            format!("[{},{}]", exp_bin_json, exp_lib_json).as_bytes()
-        );
+        };
+
+        let exp_json = to_json_string(&vec![exp_bin, exp_lib]).unwrap();
+        assert_eq!(&writer[..], format!("{}\n", exp_json).as_bytes());
     }
 }
diff --git a/src/format_report_formatter.rs b/src/format_report_formatter.rs
index c820259256c..90406cdb95e 100644
--- a/src/format_report_formatter.rs
+++ b/src/format_report_formatter.rs
@@ -20,6 +20,7 @@ impl<'a> FormatReportFormatterBuilder<'a> {
     }
 
     /// Enables colors and formatting in the output.
+    #[must_use]
     pub fn enable_colors(self, enable_colors: bool) -> Self {
         Self {
             enable_colors,
diff --git a/src/macros.rs b/src/macros.rs
index f29552caf8d..fdbe3374615 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -152,7 +152,7 @@ pub(crate) fn rewrite_macro(
 ) -> Option<String> {
     let should_skip = context
         .skip_context
-        .skip_macro(&context.snippet(mac.path.span).to_owned());
+        .skip_macro(context.snippet(mac.path.span));
     if should_skip {
         None
     } else {
diff --git a/src/modules.rs b/src/modules.rs
index 9c964b274e0..70b937b0283 100644
--- a/src/modules.rs
+++ b/src/modules.rs
@@ -458,6 +458,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
             self.directory.path.push(path.as_str());
             self.directory.ownership = DirectoryOwnership::Owned { relative: None };
         } else {
+            let id = id.as_str();
             // We have to push on the current module name in the case of relative
             // paths in order to ensure that any additional module paths from inline
             // `mod x { ... }` come after the relative extension.
@@ -468,9 +469,15 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
                 if let Some(ident) = relative.take() {
                     // remove the relative offset
                     self.directory.path.push(ident.as_str());
+
+                    // In the case where there is an x.rs and an ./x directory we want
+                    // to prevent adding x twice. For example, ./x/x
+                    if self.directory.path.exists() && !self.directory.path.join(id).exists() {
+                        return;
+                    }
                 }
             }
-            self.directory.path.push(id.as_str());
+            self.directory.path.push(id);
         }
     }
 
diff --git a/src/test/mod.rs b/src/test/mod.rs
index c50d18644b0..c399512ba7e 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -307,6 +307,52 @@ fn assert_output(source: &Path, expected_filename: &Path) {
     }
 }
 
+// Helper function for comparing the results of rustfmt
+// to a known output generated by one of the write modes.
+fn assert_stdin_output(
+    source: &Path,
+    expected_filename: &Path,
+    emit_mode: EmitMode,
+    has_diff: bool,
+) {
+    let mut config = Config::default();
+    config.set().newline_style(NewlineStyle::Unix);
+    config.set().emit_mode(emit_mode);
+
+    let mut source_file = fs::File::open(&source).expect("couldn't open source");
+    let mut source_text = String::new();
+    source_file
+        .read_to_string(&mut source_text)
+        .expect("Failed reading target");
+    let input = Input::Text(source_text);
+
+    // Populate output by writing to a vec.
+    let mut buf: Vec<u8> = vec![];
+    {
+        let mut session = Session::new(config, Some(&mut buf));
+        session.format(input).unwrap();
+        let errors = ReportedErrors {
+            has_diff: has_diff,
+            ..Default::default()
+        };
+        assert_eq!(session.errors, errors);
+    }
+
+    let mut expected_file = fs::File::open(&expected_filename).expect("couldn't open target");
+    let mut expected_text = String::new();
+    expected_file
+        .read_to_string(&mut expected_text)
+        .expect("Failed reading target");
+
+    let output = String::from_utf8(buf).unwrap();
+    let compare = make_diff(&expected_text, &output, DIFF_CONTEXT_SIZE);
+    if !compare.is_empty() {
+        let mut failures = HashMap::new();
+        failures.insert(source.to_owned(), compare);
+        print_mismatches_default_message(failures);
+        panic!("Text does not match expected output");
+    }
+}
 // Idempotence tests. Files in tests/target are checked to be unaltered by
 // rustfmt.
 #[nightly_only_test]
@@ -420,9 +466,9 @@ fn stdin_formatting_smoke_test() {
     }
 
     #[cfg(not(windows))]
-    assert_eq!(buf, "stdin:\n\nfn main() {}\n".as_bytes());
+    assert_eq!(buf, "<stdin>:\n\nfn main() {}\n".as_bytes());
     #[cfg(windows)]
-    assert_eq!(buf, "stdin:\n\nfn main() {}\r\n".as_bytes());
+    assert_eq!(buf, "<stdin>:\n\nfn main() {}\r\n".as_bytes());
 }
 
 #[test]
@@ -463,6 +509,30 @@ fn stdin_works_with_modified_lines() {
     assert_eq!(buf, output.as_bytes());
 }
 
+/// Ensures that `EmitMode::Json` works with input from `stdin`.
+#[test]
+fn stdin_works_with_json() {
+    init_log();
+    assert_stdin_output(
+        Path::new("tests/writemode/source/stdin.rs"),
+        Path::new("tests/writemode/target/stdin.json"),
+        EmitMode::Json,
+        true,
+    );
+}
+
+/// Ensures that `EmitMode::Checkstyle` works with input from `stdin`.
+#[test]
+fn stdin_works_with_checkstyle() {
+    init_log();
+    assert_stdin_output(
+        Path::new("tests/writemode/source/stdin.rs"),
+        Path::new("tests/writemode/target/stdin.xml"),
+        EmitMode::Checkstyle,
+        false,
+    );
+}
+
 #[test]
 fn stdin_disable_all_formatting_test() {
     init_log();
@@ -914,3 +984,52 @@ fn verify_check_works() {
         .status()
         .expect("run with check option failed");
 }
+
+#[test]
+fn verify_check_works_with_stdin() {
+    init_log();
+
+    let mut child = Command::new(rustfmt().to_str().unwrap())
+        .arg("--check")
+        .stdin(Stdio::piped())
+        .stderr(Stdio::piped())
+        .spawn()
+        .expect("run with check option failed");
+
+    {
+        let stdin = child.stdin.as_mut().expect("Failed to open stdin");
+        stdin
+            .write_all("fn main() {}\n".as_bytes())
+            .expect("Failed to write to rustfmt --check");
+    }
+    let output = child
+        .wait_with_output()
+        .expect("Failed to wait on rustfmt child");
+    assert!(output.status.success());
+}
+
+#[test]
+fn verify_check_l_works_with_stdin() {
+    init_log();
+
+    let mut child = Command::new(rustfmt().to_str().unwrap())
+        .arg("--check")
+        .arg("-l")
+        .stdin(Stdio::piped())
+        .stdout(Stdio::piped())
+        .stderr(Stdio::piped())
+        .spawn()
+        .expect("run with check option failed");
+
+    {
+        let stdin = child.stdin.as_mut().expect("Failed to open stdin");
+        stdin
+            .write_all("fn main()\n{}\n".as_bytes())
+            .expect("Failed to write to rustfmt --check");
+    }
+    let output = child
+        .wait_with_output()
+        .expect("Failed to wait on rustfmt child");
+    assert!(output.status.success());
+    assert_eq!(std::str::from_utf8(&output.stdout).unwrap(), "<stdin>\n");
+}
diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs
index ec9ed0f0b8d..fcff6d14e6f 100644
--- a/src/test/mod_resolver.rs
+++ b/src/test/mod_resolver.rs
@@ -50,3 +50,17 @@ fn skip_out_of_line_nested_inline_within_out_of_line() {
         &["tests/mod-resolver/skip-files-issue-5065/one.rs"],
     );
 }
+
+#[test]
+fn fmt_out_of_line_test_modules() {
+    // See also https://github.com/rust-lang/rustfmt/issues/5119
+    verify_mod_resolution(
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs",
+        &[
+            "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs",
+            "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs",
+            "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs",
+            "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs",
+        ],
+    )
+}
diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs
index 5493b09e4aa..bf81f253f69 100644
--- a/tests/cargo-fmt/main.rs
+++ b/tests/cargo-fmt/main.rs
@@ -2,6 +2,7 @@
 
 use std::env;
 use std::process::Command;
+use std::path::Path;
 
 /// Run the cargo-fmt executable and return its output.
 fn cargo_fmt(args: &[&str]) -> (String, String) {
@@ -71,3 +72,26 @@ fn rustfmt_help() {
     assert_that!(&["--", "-h"], contains("Format Rust code"));
     assert_that!(&["--", "--help=config"], contains("Configuration Options:"));
 }
+
+#[test]
+fn cargo_fmt_out_of_line_test_modules() {
+    // See also https://github.com/rust-lang/rustfmt/issues/5119
+    let expected_modified_files = [
+        "tests/mod-resolver/test-submodule-issue-5119/src/lib.rs",
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs",
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs",
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs",
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs",
+    ];
+    let args = [
+        "-v",
+        "--check",
+        "--manifest-path",
+        "tests/mod-resolver/test-submodule-issue-5119/Cargo.toml",
+    ];
+    let (stdout, _) = cargo_fmt(&args);
+    for file in expected_modified_files {
+        let path = Path::new(file).canonicalize().unwrap();
+        assert!(stdout.contains(&format!("Diff in {}", path.display())))
+    }
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml
new file mode 100644
index 00000000000..0993f127959
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "rustfmt-test-submodule-issue"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs
new file mode 100644
index 00000000000..3f7ddba8a28
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs
@@ -0,0 +1,7 @@
+pub fn foo() -> i32 {
+3
+}
+
+pub fn bar() -> i32 {
+4
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs
new file mode 100644
index 00000000000..da4e86169ad
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs
@@ -0,0 +1,8 @@
+mod test1 {
+#[cfg(unix)]
+mod sub1;
+#[cfg(not(unix))]
+mod sub2;
+
+mod sub3;
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs
new file mode 100644
index 00000000000..b760ba23cd2
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs
@@ -0,0 +1,6 @@
+use rustfmt_test_submodule_issue::foo;
+
+#[test]
+fn test_foo() {
+assert_eq!(3, foo());
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs
new file mode 100644
index 00000000000..4fd8286eac4
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs
@@ -0,0 +1,6 @@
+use rustfmt_test_submodule_issue::bar;
+
+#[test]
+fn test_bar() {
+assert_eq!(4, bar());
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs
new file mode 100644
index 00000000000..e029785bc24
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs
@@ -0,0 +1 @@
+mod sub4;
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs
diff --git a/tests/writemode/source/stdin.rs b/tests/writemode/source/stdin.rs
new file mode 100644
index 00000000000..06f8a0c288d
--- /dev/null
+++ b/tests/writemode/source/stdin.rs
@@ -0,0 +1,6 @@
+
+fn
+ some( )
+{
+}
+fn main () {}
diff --git a/tests/writemode/target/output.json b/tests/writemode/target/output.json
index b5f327b0a1c..d8b5467ee91 100644
--- a/tests/writemode/target/output.json
+++ b/tests/writemode/target/output.json
@@ -1 +1 @@
-[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n    1\n}","expected":"fn foo_expr() { 1 }"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n    foo();\n}","expected":"fn foo_stmt() { foo(); }"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local()  {\n    let z = 5;\n   }","expected":"fn foo_decl_local() { let z = 5; }"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn    foo_decl_item(x: &mut i32) {\n    x = 3;\n}","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":"   fn empty()     {","expected":"fn empty() {}"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}","expected":"fn foo_return() -> String { \"yay\" }"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n    \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {","expected":"fn foo_where() -> T\nwhere\n    T: Sync,\n{"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space                      ()                                                           {\n                           1                 \n}","expected":"fn lots_of_space() { 1 }"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":"    fn dummy(&self) {\n    }","expected":"    fn dummy(&self) {}"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { ","expected":"trait CoolerTypes {\n    fn dummy(&self) {}"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo<T>() where T: Bar {","expected":"fn Foo<T>()\nwhere\n    T: Bar,\n{"}]}]
\ No newline at end of file
+[{"name":"tests/writemode/source/json.rs","mismatches":[{"original_begin_line":5,"original_end_line":7,"expected_begin_line":5,"expected_end_line":5,"original":"fn foo_expr() {\n    1\n}\n","expected":"fn foo_expr() { 1 }\n"},{"original_begin_line":9,"original_end_line":11,"expected_begin_line":7,"expected_end_line":7,"original":"fn foo_stmt() {\n    foo();\n}\n","expected":"fn foo_stmt() { foo(); }\n"},{"original_begin_line":13,"original_end_line":15,"expected_begin_line":9,"expected_end_line":9,"original":"fn foo_decl_local()  {\n    let z = 5;\n   }\n","expected":"fn foo_decl_local() { let z = 5; }\n"},{"original_begin_line":17,"original_end_line":19,"expected_begin_line":11,"expected_end_line":11,"original":"fn    foo_decl_item(x: &mut i32) {\n    x = 3;\n}\n","expected":"fn foo_decl_item(x: &mut i32) { x = 3; }\n"},{"original_begin_line":21,"original_end_line":21,"expected_begin_line":13,"expected_end_line":13,"original":"   fn empty()     {\n","expected":"fn empty() {}\n"},{"original_begin_line":23,"original_end_line":23,"expected_begin_line":15,"expected_end_line":15,"original":"}\n","expected":"fn foo_return() -> String { \"yay\" }\n"},{"original_begin_line":25,"original_end_line":29,"expected_begin_line":17,"expected_end_line":20,"original":"fn foo_return() -> String {\n    \"yay\"\n}\n\nfn foo_where() -> T where T: Sync {\n","expected":"fn foo_where() -> T\nwhere\n    T: Sync,\n{\n"},{"original_begin_line":64,"original_end_line":66,"expected_begin_line":55,"expected_end_line":55,"original":"fn lots_of_space                      ()                                                           {\n                           1                 \n}\n","expected":"fn lots_of_space() { 1 }\n"},{"original_begin_line":71,"original_end_line":72,"expected_begin_line":60,"expected_end_line":60,"original":"    fn dummy(&self) {\n    }\n","expected":"    fn dummy(&self) {}\n"},{"original_begin_line":75,"original_end_line":75,"expected_begin_line":63,"expected_end_line":64,"original":"trait CoolerTypes { fn dummy(&self) { \n","expected":"trait CoolerTypes {\n    fn dummy(&self) {}\n"},{"original_begin_line":77,"original_end_line":77,"expected_begin_line":66,"expected_end_line":66,"original":"}\n","expected":""},{"original_begin_line":79,"original_end_line":79,"expected_begin_line":67,"expected_end_line":70,"original":"fn Foo<T>() where T: Bar {\n","expected":"fn Foo<T>()\nwhere\n    T: Bar,\n{\n"}]}]
diff --git a/tests/writemode/target/stdin.json b/tests/writemode/target/stdin.json
new file mode 100644
index 00000000000..dbf2c486322
--- /dev/null
+++ b/tests/writemode/target/stdin.json
@@ -0,0 +1 @@
+[{"name":"<stdin>","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}\n","expected":"fn some() {}\nfn main() {}\n"}]}]
diff --git a/tests/writemode/target/stdin.xml b/tests/writemode/target/stdin.xml
new file mode 100644
index 00000000000..a7301bbc553
--- /dev/null
+++ b/tests/writemode/target/stdin.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>

+<checkstyle version="4.3"><file name="<stdin>"><error line="1" severity="warning" message="Should be `fn some() {}`" /><error line="2" severity="warning" message="Should be `fn main() {}`" /></file></checkstyle>