about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorCaleb Cartwright <caleb.cartwright@outlook.com>2022-03-29 23:17:30 -0500
committerCaleb Cartwright <caleb.cartwright@outlook.com>2022-03-29 23:17:30 -0500
commit8211d64c71bd3ab050ef5840c825fd762f33abb3 (patch)
treeaee558c61c2564fd45129803ccc3875715f7453b /src
parent4f89c51a5b3f27f38a7701d5243a80d7c1c44dff (diff)
Merge commit '5ff7b632a95bac6955611d85040859128902c580' into sync-rustfmt-subtree
Diffstat (limited to 'src')
-rw-r--r--src/attr.rs6
-rw-r--r--src/bin/main.rs38
-rw-r--r--src/cargo-fmt/main.rs3
-rw-r--r--src/comment.rs64
-rw-r--r--src/config/file_lines.rs2
-rw-r--r--src/config/mod.rs7
-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/ignore_path.rs9
-rw-r--r--src/imports.rs56
-rw-r--r--src/items.rs26
-rw-r--r--src/lists.rs27
-rw-r--r--src/macros.rs16
-rw-r--r--src/modules.rs47
-rw-r--r--src/overflow.rs13
-rw-r--r--src/parse/session.rs34
-rw-r--r--src/spanned.rs15
-rw-r--r--src/string.rs5
-rw-r--r--src/test/configuration_snippet.rs30
-rw-r--r--src/test/mod.rs128
-rw-r--r--src/test/mod_resolver.rs30
-rw-r--r--src/types.rs11
-rw-r--r--src/utils.rs19
-rw-r--r--src/vertical.rs23
26 files changed, 606 insertions, 208 deletions
diff --git a/src/attr.rs b/src/attr.rs
index 3887a8051f2..befe12ae2c4 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -389,6 +389,10 @@ impl Rewrite for [ast::Attribute] {
         let mut attrs = self;
         let mut result = String::new();
 
+        // Determine if the source text is annotated with `#[rustfmt::skip::attributes(derive)]`
+        // or `#![rustfmt::skip::attributes(derive)]`
+        let skip_derives = context.skip_context.skip_attribute("derive");
+
         // This is not just a simple map because we need to handle doc comments
         // (where we take as many doc comment attributes as possible) and possibly
         // merging derives into a single attribute.
@@ -431,7 +435,7 @@ impl Rewrite for [ast::Attribute] {
             }
 
             // Handle derives if we will merge them.
-            if context.config.merge_derives() && is_derive(&attrs[0]) {
+            if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) {
                 let derives = take_while_with_pred(context, attrs, is_derive);
                 let derive_str = format_derive(derives, shape, context)?;
                 result.push_str(&derive_str);
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 4d845547cdf..ad10b9ede60 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -26,7 +26,7 @@ fn main() {
     let exit_code = match execute(&opts) {
         Ok(code) => code,
         Err(e) => {
-            eprintln!("{}", e);
+            eprintln!("{:#}", e);
             1
         }
     };
@@ -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
@@ -393,9 +394,8 @@ fn print_usage_to_stdout(opts: &Options, reason: &str) {
         format!("{}\n\n", reason)
     };
     let msg = format!(
-        "{}Format Rust code\n\nusage: {} [options] <file>...",
-        sep,
-        env::args_os().next().unwrap().to_string_lossy()
+        "{}Format Rust code\n\nusage: rustfmt [options] <file>...",
+        sep
     );
     println!("{}", opts.usage(&msg));
 }
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/comment.rs b/src/comment.rs
index 0f850b9b2f2..f9d8a0fa70c 100644
--- a/src/comment.rs
+++ b/src/comment.rs
@@ -432,16 +432,16 @@ impl CodeBlockAttribute {
 
 /// Block that is formatted as an item.
 ///
-/// An item starts with either a star `*` or a dash `-`. Different level of indentation are
-/// handled by shrinking the shape accordingly.
+/// An item starts with either a star `*` a dash `-` or a greater-than `>`.
+/// Different level of indentation are handled by shrinking the shape accordingly.
 struct ItemizedBlock {
     /// the lines that are identified as part of an itemized block
     lines: Vec<String>,
-    /// the number of whitespaces up to the item sigil
+    /// the number of characters (typically whitespaces) up to the item sigil
     indent: usize,
     /// the string that marks the start of an item
     opener: String,
-    /// sequence of whitespaces to prefix new lines that are part of the item
+    /// sequence of characters (typically whitespaces) to prefix new lines that are part of the item
     line_start: String,
 }
 
@@ -449,19 +449,32 @@ impl ItemizedBlock {
     /// Returns `true` if the line is formatted as an item
     fn is_itemized_line(line: &str) -> bool {
         let trimmed = line.trim_start();
-        trimmed.starts_with("* ") || trimmed.starts_with("- ")
+        trimmed.starts_with("* ") || trimmed.starts_with("- ") || trimmed.starts_with("> ")
     }
 
     /// Creates a new ItemizedBlock described with the given line.
     /// The `is_itemized_line` needs to be called first.
     fn new(line: &str) -> ItemizedBlock {
         let space_to_sigil = line.chars().take_while(|c| c.is_whitespace()).count();
-        let indent = space_to_sigil + 2;
+        // +2 = '* ', which will add the appropriate amount of whitespace to keep itemized
+        // content formatted correctly.
+        let mut indent = space_to_sigil + 2;
+        let mut line_start = " ".repeat(indent);
+
+        // Markdown blockquote start with a "> "
+        if line.trim_start().starts_with(">") {
+            // remove the original +2 indent because there might be multiple nested block quotes
+            // and it's easier to reason about the final indent by just taking the length
+            // of th new line_start. We update the indent because it effects the max width
+            // of each formatted line.
+            line_start = itemized_block_quote_start(line, line_start, 2);
+            indent = line_start.len();
+        }
         ItemizedBlock {
             lines: vec![line[indent..].to_string()],
             indent,
             opener: line[..indent].to_string(),
-            line_start: " ".repeat(indent),
+            line_start,
         }
     }
 
@@ -504,6 +517,25 @@ impl ItemizedBlock {
     }
 }
 
+/// Determine the line_start when formatting markdown block quotes.
+/// The original line_start likely contains indentation (whitespaces), which we'd like to
+/// replace with '> ' characters.
+fn itemized_block_quote_start(line: &str, mut line_start: String, remove_indent: usize) -> String {
+    let quote_level = line
+        .chars()
+        .take_while(|c| !c.is_alphanumeric())
+        .fold(0, |acc, c| if c == '>' { acc + 1 } else { acc });
+
+    for _ in 0..remove_indent {
+        line_start.pop();
+    }
+
+    for _ in 0..quote_level {
+        line_start.push_str("> ")
+    }
+    line_start
+}
+
 struct CommentRewrite<'a> {
     result: String,
     code_block_buffer: String,
@@ -651,6 +683,7 @@ impl<'a> CommentRewrite<'a> {
         i: usize,
         line: &'a str,
         has_leading_whitespace: bool,
+        is_doc_comment: bool,
     ) -> bool {
         let num_newlines = count_newlines(orig);
         let is_last = i == num_newlines;
@@ -757,10 +790,19 @@ impl<'a> CommentRewrite<'a> {
             }
         }
 
-        if self.fmt.config.wrap_comments()
+        let is_markdown_header_doc_comment = is_doc_comment && line.starts_with("#");
+
+        // We only want to wrap the comment if:
+        // 1) wrap_comments = true is configured
+        // 2) The comment is not the start of a markdown header doc comment
+        // 3) The comment width exceeds the shape's width
+        // 4) No URLS were found in the commnet
+        let should_wrap_comment = self.fmt.config.wrap_comments()
+            && !is_markdown_header_doc_comment
             && unicode_str_width(line) > self.fmt.shape.width
-            && !has_url(line)
-        {
+            && !has_url(line);
+
+        if should_wrap_comment {
             match rewrite_string(line, &self.fmt, self.max_width) {
                 Some(ref s) => {
                     self.is_prev_line_multi_line = s.contains('\n');
@@ -850,7 +892,7 @@ fn rewrite_comment_inner(
         });
 
     for (i, (line, has_leading_whitespace)) in lines.enumerate() {
-        if rewriter.handle_line(orig, i, line, has_leading_whitespace) {
+        if rewriter.handle_line(orig, i, line, has_leading_whitespace, is_doc_comment) {
             break;
         }
     }
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/config/mod.rs b/src/config/mod.rs
index cd90e0904b6..18e1854612b 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -106,6 +106,8 @@ create_config! {
     // Misc.
     remove_nested_parens: bool, true, true, "Remove nested parens";
     combine_control_expr: bool, true, false, "Combine control expressions with function calls";
+    short_array_element_width_threshold: usize, 10, true,
+        "Width threshold for an array element to be considered short";
     overflow_delimited_expr: bool, false, false,
         "Allow trailing bracket/brace delimited expressions to overflow";
     struct_field_align_threshold: usize, 0, false,
@@ -364,7 +366,9 @@ fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
             // find the project file yet, and continue searching.
             Err(e) => {
                 if e.kind() != ErrorKind::NotFound {
-                    return Err(e);
+                    let ctx = format!("Failed to get metadata for config file {:?}", &config_file);
+                    let err = anyhow::Error::new(e).context(ctx);
+                    return Err(Error::new(ErrorKind::Other, err));
                 }
             }
             _ => {}
@@ -589,6 +593,7 @@ spaces_around_ranges = false
 binop_separator = "Front"
 remove_nested_parens = true
 combine_control_expr = true
+short_array_element_width_threshold = 10
 overflow_delimited_expr = false
 struct_field_align_threshold = 0
 enum_discrim_align_threshold = 0
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/ignore_path.rs b/src/ignore_path.rs
index 7738eee0a76..d955949496a 100644
--- a/src/ignore_path.rs
+++ b/src/ignore_path.rs
@@ -32,16 +32,15 @@ impl IgnorePathSet {
 
 #[cfg(test)]
 mod test {
-    use std::path::{Path, PathBuf};
-
-    use crate::config::{Config, FileName};
-    use crate::ignore_path::IgnorePathSet;
-
     use rustfmt_config_proc_macro::nightly_only_test;
 
     #[nightly_only_test]
     #[test]
     fn test_ignore_path_set() {
+        use crate::config::{Config, FileName};
+        use crate::ignore_path::IgnorePathSet;
+        use std::path::{Path, PathBuf};
+
         let config =
             Config::from_toml(r#"ignore = ["foo.rs", "bar_dir/*"]"#, Path::new("")).unwrap();
         let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap();
diff --git a/src/imports.rs b/src/imports.rs
index 40e0d06f99d..02319809486 100644
--- a/src/imports.rs
+++ b/src/imports.rs
@@ -190,13 +190,17 @@ pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, merge_by: SharedPrefix) -
             continue;
         }
 
-        for flattened in use_tree.flatten() {
+        for mut flattened in use_tree.flatten() {
             if let Some(tree) = result
                 .iter_mut()
                 .find(|tree| tree.share_prefix(&flattened, merge_by))
             {
                 tree.merge(&flattened, merge_by);
             } else {
+                // If this is the first tree with this prefix, handle potential trailing ::self
+                if merge_by == SharedPrefix::Module {
+                    flattened = flattened.nest_trailing_self();
+                }
                 result.push(flattened);
             }
         }
@@ -208,17 +212,7 @@ pub(crate) fn flatten_use_trees(use_trees: Vec<UseTree>) -> Vec<UseTree> {
     use_trees
         .into_iter()
         .flat_map(UseTree::flatten)
-        .map(|mut tree| {
-            // If a path ends in `::self`, rewrite it to `::{self}`.
-            if let Some(UseSegment::Slf(..)) = tree.path.last() {
-                let self_segment = tree.path.pop().unwrap();
-                tree.path.push(UseSegment::List(vec![UseTree::from_path(
-                    vec![self_segment],
-                    DUMMY_SP,
-                )]));
-            }
-            tree
-        })
+        .map(UseTree::nest_trailing_self)
         .collect()
 }
 
@@ -238,7 +232,8 @@ impl fmt::Display for UseSegment {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
             UseSegment::Glob => write!(f, "*"),
-            UseSegment::Ident(ref s, _) => write!(f, "{}", s),
+            UseSegment::Ident(ref s, Some(ref alias)) => write!(f, "{} as {}", s, alias),
+            UseSegment::Ident(ref s, None) => write!(f, "{}", s),
             UseSegment::Slf(..) => write!(f, "self"),
             UseSegment::Super(..) => write!(f, "super"),
             UseSegment::Crate(..) => write!(f, "crate"),
@@ -622,7 +617,8 @@ impl UseTree {
     fn merge(&mut self, other: &UseTree, merge_by: SharedPrefix) {
         let mut prefix = 0;
         for (a, b) in self.path.iter().zip(other.path.iter()) {
-            if a.equal_except_alias(b) {
+            // only discard the alias at the root of the tree
+            if (prefix == 0 && a.equal_except_alias(b)) || a == b {
                 prefix += 1;
             } else {
                 break;
@@ -633,6 +629,18 @@ impl UseTree {
             self.span = self.span.to(other.span);
         }
     }
+
+    /// If this tree ends in `::self`, rewrite it to `::{self}`.
+    fn nest_trailing_self(mut self) -> UseTree {
+        if let Some(UseSegment::Slf(..)) = self.path.last() {
+            let self_segment = self.path.pop().unwrap();
+            self.path.push(UseSegment::List(vec![UseTree::from_path(
+                vec![self_segment],
+                DUMMY_SP,
+            )]));
+        }
+        self
+    }
 }
 
 fn merge_rest(
@@ -1309,4 +1317,24 @@ mod test {
                 < parse_use_tree("std::cmp::{b, e, g, f}").normalize()
         );
     }
+
+    #[test]
+    fn test_use_tree_nest_trailing_self() {
+        assert_eq!(
+            parse_use_tree("a::b::self").nest_trailing_self(),
+            parse_use_tree("a::b::{self}")
+        );
+        assert_eq!(
+            parse_use_tree("a::b::c").nest_trailing_self(),
+            parse_use_tree("a::b::c")
+        );
+        assert_eq!(
+            parse_use_tree("a::b::{c, d}").nest_trailing_self(),
+            parse_use_tree("a::b::{c, d}")
+        );
+        assert_eq!(
+            parse_use_tree("a::b::{self, c}").nest_trailing_self(),
+            parse_use_tree("a::b::{self, c}")
+        );
+    }
 }
diff --git a/src/items.rs b/src/items.rs
index 8498cb6adda..92f423bbb62 100644
--- a/src/items.rs
+++ b/src/items.rs
@@ -1273,7 +1273,13 @@ pub(crate) fn format_struct_struct(
     result.push_str(&header_str);
 
     let header_hi = struct_parts.ident.span.hi();
-    let body_lo = context.snippet_provider.span_after(span, "{");
+    let body_lo = if let Some(generics) = struct_parts.generics {
+        // Adjust the span to start at the end of the generic arguments before searching for the '{'
+        let span = span.with_lo(generics.span.hi());
+        context.snippet_provider.span_after(span, "{")
+    } else {
+        context.snippet_provider.span_after(span, "{")
+    };
 
     let generics_str = match struct_parts.generics {
         Some(g) => format_generics(
@@ -1377,17 +1383,21 @@ fn format_empty_struct_or_tuple(
         result.push_str(&offset.to_string_with_newline(context.config))
     }
     result.push_str(opener);
-    match rewrite_missing_comment(span, Shape::indented(offset, context.config), context) {
+
+    // indented shape for proper indenting of multi-line comments
+    let shape = Shape::indented(offset.block_indent(context.config), context.config);
+    match rewrite_missing_comment(span, shape, context) {
         Some(ref s) if s.is_empty() => (),
         Some(ref s) => {
-            if !is_single_line(s) || first_line_contains_single_line_comment(s) {
+            let is_multi_line = !is_single_line(s);
+            if is_multi_line || first_line_contains_single_line_comment(s) {
                 let nested_indent_str = offset
                     .block_indent(context.config)
                     .to_string_with_newline(context.config);
                 result.push_str(&nested_indent_str);
             }
             result.push_str(s);
-            if last_line_contains_single_line_comment(s) {
+            if is_multi_line || last_line_contains_single_line_comment(s) {
                 result.push_str(&offset.to_string_with_newline(context.config));
             }
         }
@@ -2046,9 +2056,15 @@ impl Rewrite for ast::Param {
                 {
                     result.push_str(&ty_str);
                 } else {
+                    let prev_str = if param_attrs_result.is_empty() {
+                        param_attrs_result
+                    } else {
+                        param_attrs_result + &shape.to_string_with_newline(context.config)
+                    };
+
                     result = combine_strs_with_missing_comments(
                         context,
-                        &(param_attrs_result + &shape.to_string_with_newline(context.config)),
+                        &prev_str,
                         param_name,
                         span,
                         shape,
diff --git a/src/lists.rs b/src/lists.rs
index 29d75585eb7..e8785050782 100644
--- a/src/lists.rs
+++ b/src/lists.rs
@@ -611,15 +611,30 @@ pub(crate) fn extract_post_comment(
     post_snippet: &str,
     comment_end: usize,
     separator: &str,
+    is_last: bool,
 ) -> Option<String> {
     let white_space: &[_] = &[' ', '\t'];
 
     // Cleanup post-comment: strip separators and whitespace.
     let post_snippet = post_snippet[..comment_end].trim();
+
+    let last_inline_comment_ends_with_separator = if is_last {
+        if let Some(line) = post_snippet.lines().last() {
+            line.ends_with(separator) && line.trim().starts_with("//")
+        } else {
+            false
+        }
+    } else {
+        false
+    };
+
     let post_snippet_trimmed = if post_snippet.starts_with(|c| c == ',' || c == ':') {
         post_snippet[1..].trim_matches(white_space)
     } else if let Some(stripped) = post_snippet.strip_prefix(separator) {
         stripped.trim_matches(white_space)
+    } else if last_inline_comment_ends_with_separator {
+        // since we're on the last item it's fine to keep any trailing separators in comments
+        post_snippet.trim_matches(white_space)
     }
     // not comment or over two lines
     else if post_snippet.ends_with(',')
@@ -748,14 +763,12 @@ where
                 .snippet_provider
                 .span_to_snippet(mk_sp((self.get_hi)(&item), next_start))
                 .unwrap_or("");
-            let comment_end = get_comment_end(
-                post_snippet,
-                self.separator,
-                self.terminator,
-                self.inner.peek().is_none(),
-            );
+            let is_last = self.inner.peek().is_none();
+            let comment_end =
+                get_comment_end(post_snippet, self.separator, self.terminator, is_last);
             let new_lines = has_extra_newline(post_snippet, comment_end);
-            let post_comment = extract_post_comment(post_snippet, comment_end, self.separator);
+            let post_comment =
+                extract_post_comment(post_snippet, comment_end, self.separator, is_last);
 
             self.prev_span_end = (self.get_hi)(&item) + BytePos(comment_end as u32);
 
diff --git a/src/macros.rs b/src/macros.rs
index f29552caf8d..664f152e8be 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(
@@ -152,7 +157,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 {
@@ -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(),
+            );
         }
     };
 
diff --git a/src/modules.rs b/src/modules.rs
index 64d96a5c6a6..a65dc66f797 100644
--- a/src/modules.rs
+++ b/src/modules.rs
@@ -81,6 +81,7 @@ pub struct ModuleResolutionError {
     pub(crate) kind: ModuleResolutionErrorKind,
 }
 
+/// Defines variants similar to those of [rustc_expand::module::ModError]
 #[derive(Debug, Error)]
 pub(crate) enum ModuleResolutionErrorKind {
     /// Find a file that cannot be parsed.
@@ -89,6 +90,12 @@ pub(crate) enum ModuleResolutionErrorKind {
     /// File cannot be found.
     #[error("{file} does not exist")]
     NotFound { file: PathBuf },
+    /// File a.rs and a/mod.rs both exist
+    #[error("file for module found at both {default_path:?} and {secondary_path:?}")]
+    MultipleCandidates {
+        default_path: PathBuf,
+        secondary_path: PathBuf,
+    },
 }
 
 #[derive(Clone)]
@@ -444,12 +451,31 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
                 }
                 Ok(Some(SubModKind::MultiExternal(mods_outside_ast)))
             }
-            Err(_) => Err(ModuleResolutionError {
-                module: mod_name.to_string(),
-                kind: ModuleResolutionErrorKind::NotFound {
-                    file: self.directory.path.clone(),
-                },
-            }),
+            Err(e) => match e {
+                ModError::FileNotFound(_, default_path, _secondary_path) => {
+                    Err(ModuleResolutionError {
+                        module: mod_name.to_string(),
+                        kind: ModuleResolutionErrorKind::NotFound { file: default_path },
+                    })
+                }
+                ModError::MultipleCandidates(_, default_path, secondary_path) => {
+                    Err(ModuleResolutionError {
+                        module: mod_name.to_string(),
+                        kind: ModuleResolutionErrorKind::MultipleCandidates {
+                            default_path,
+                            secondary_path,
+                        },
+                    })
+                }
+                ModError::ParserError(_)
+                | ModError::CircularInclusion(_)
+                | ModError::ModInBlock(_) => Err(ModuleResolutionError {
+                    module: mod_name.to_string(),
+                    kind: ModuleResolutionErrorKind::ParseError {
+                        file: self.directory.path.clone(),
+                    },
+                }),
+            },
         }
     }
 
@@ -458,6 +484,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 +495,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/overflow.rs b/src/overflow.rs
index 3475f5c378c..80aed998d73 100644
--- a/src/overflow.rs
+++ b/src/overflow.rs
@@ -26,8 +26,6 @@ use crate::spanned::Spanned;
 use crate::types::{can_be_overflowed_type, SegmentParam};
 use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
 
-const SHORT_ITEM_THRESHOLD: usize = 10;
-
 /// A list of `format!`-like macros, that take a long format string and a list of arguments to
 /// format.
 ///
@@ -572,7 +570,12 @@ impl<'a> Context<'a> {
                             if one_line {
                                 tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
                             };
-                        } else if is_every_expr_simple(&self.items) && no_long_items(list_items) {
+                        } else if is_every_expr_simple(&self.items)
+                            && no_long_items(
+                                list_items,
+                                self.context.config.short_array_element_width_threshold(),
+                            )
+                        {
                             tactic = DefinitiveListTactic::Mixed;
                         }
                     }
@@ -755,9 +758,9 @@ fn shape_from_indent_style(
     }
 }
 
-fn no_long_items(list: &[ListItem]) -> bool {
+fn no_long_items(list: &[ListItem], short_array_element_width_threshold: usize) -> bool {
     list.iter()
-        .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)
+        .all(|item| item.inner_as_ref().len() <= short_array_element_width_threshold)
 }
 
 /// In case special-case style is required, returns an offset from which we start horizontal layout.
diff --git a/src/parse/session.rs b/src/parse/session.rs
index 412f4434b9e..7571e6d078a 100644
--- a/src/parse/session.rs
+++ b/src/parse/session.rs
@@ -12,6 +12,7 @@ use rustc_span::{
 
 use crate::config::file_lines::LineRange;
 use crate::ignore_path::IgnorePathSet;
+use crate::parse::parser::{ModError, ModulePathSuccess};
 use crate::source_map::LineRangeUtils;
 use crate::utils::starts_with_newline;
 use crate::visitor::SnippetProvider;
@@ -145,13 +146,33 @@ impl ParseSess {
         })
     }
 
+    /// Determine the submodule path for the given module identifier.
+    ///
+    /// * `id` - The name of the module
+    /// * `relative` - If Some(symbol), the symbol name is a directory relative to the dir_path.
+    ///   If relative is Some, resolve the submodle at {dir_path}/{symbol}/{id}.rs
+    ///   or {dir_path}/{symbol}/{id}/mod.rs. if None, resolve the module at {dir_path}/{id}.rs.
+    /// *  `dir_path` - Module resolution will occur relative to this direcotry.
     pub(crate) fn default_submod_path(
         &self,
         id: symbol::Ident,
         relative: Option<symbol::Ident>,
         dir_path: &Path,
-    ) -> Result<rustc_expand::module::ModulePathSuccess, rustc_expand::module::ModError<'_>> {
-        rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path)
+    ) -> Result<ModulePathSuccess, ModError<'_>> {
+        rustc_expand::module::default_submod_path(&self.parse_sess, id, relative, dir_path).or_else(
+            |e| {
+                // If resloving a module relative to {dir_path}/{symbol} fails because a file
+                // could not be found, then try to resolve the module relative to {dir_path}.
+                // If we still can't find the module after searching for it in {dir_path},
+                // surface the original error.
+                if matches!(e, ModError::FileNotFound(..)) && relative.is_some() {
+                    rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path)
+                        .map_err(|_| e)
+                } else {
+                    Err(e)
+                }
+            },
+        )
     }
 
     pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
@@ -197,6 +218,15 @@ impl ParseSess {
         self.parse_sess.source_map().lookup_char_pos(pos).line
     }
 
+    // TODO(calebcartwright): Preemptive, currently unused addition
+    // that will be used to support formatting scenarios that take original
+    // positions into account
+    /// Determines whether two byte positions are in the same source line.
+    #[allow(dead_code)]
+    pub(crate) fn byte_pos_same_line(&self, a: BytePos, b: BytePos) -> bool {
+        self.line_of_byte_pos(a) == self.line_of_byte_pos(b)
+    }
+
     pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
         self.parse_sess.source_map().span_to_diagnostic_string(span)
     }
diff --git a/src/spanned.rs b/src/spanned.rs
index 8e6c75a3744..2136cfeae1a 100644
--- a/src/spanned.rs
+++ b/src/spanned.rs
@@ -113,17 +113,10 @@ impl Spanned for ast::Param {
 
 impl Spanned for ast::GenericParam {
     fn span(&self) -> Span {
-        let lo = if let ast::GenericParamKind::Const {
-            ty: _,
-            kw_span,
-            default: _,
-        } = self.kind
-        {
-            kw_span.lo()
-        } else if self.attrs.is_empty() {
-            self.ident.span.lo()
-        } else {
-            self.attrs[0].span.lo()
+        let lo = match self.kind {
+            _ if !self.attrs.is_empty() => self.attrs[0].span.lo(),
+            ast::GenericParamKind::Const { kw_span, .. } => kw_span.lo(),
+            _ => self.ident.span.lo(),
         };
         let hi = if self.bounds.is_empty() {
             self.ident.span.hi()
diff --git a/src/string.rs b/src/string.rs
index 64ae15672df..b65aa5b33b2 100644
--- a/src/string.rs
+++ b/src/string.rs
@@ -278,6 +278,9 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str]
         }
         cur_index
     };
+    if max_width_index_in_input == 0 {
+        return SnippetState::EndOfInput(input.concat());
+    }
 
     // Find the position in input for breaking the string
     if line_end.is_empty()
@@ -301,7 +304,7 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str]
         return if trim_end {
             SnippetState::LineEnd(input[..=url_index_end].concat(), index_plus_ws + 1)
         } else {
-            return SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1);
+            SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1)
         };
     }
 
diff --git a/src/test/configuration_snippet.rs b/src/test/configuration_snippet.rs
index 92949ab576a..c8fda7c8556 100644
--- a/src/test/configuration_snippet.rs
+++ b/src/test/configuration_snippet.rs
@@ -290,3 +290,33 @@ fn get_code_blocks() -> Vec<ConfigCodeBlock> {
 
     code_blocks
 }
+
+#[test]
+fn check_unstable_option_tracking_issue_numbers() {
+    // Ensure that tracking issue links point to the correct issue number
+    let tracking_issue =
+        regex::Regex::new(r"\(tracking issue: \[#(?P<number>\d+)\]\((?P<link>\S+)\)\)")
+            .expect("failed creating configuration pattern");
+
+    let lines = BufReader::new(
+        fs::File::open(Path::new(CONFIGURATIONS_FILE_NAME))
+            .unwrap_or_else(|_| panic!("couldn't read file {}", CONFIGURATIONS_FILE_NAME)),
+    )
+    .lines()
+    .map(Result::unwrap)
+    .enumerate();
+
+    for (idx, line) in lines {
+        if let Some(capture) = tracking_issue.captures(&line) {
+            let number = capture.name("number").unwrap().as_str();
+            let link = capture.name("link").unwrap().as_str();
+            assert!(
+                link.ends_with(number),
+                "{} on line {} does not point to issue #{}",
+                link,
+                idx + 1,
+                number,
+            );
+        }
+    }
+}
diff --git a/src/test/mod.rs b/src/test/mod.rs
index c50d18644b0..ab966d4a360 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();
@@ -502,7 +572,10 @@ fn stdin_generated_files_issue_5172() {
         assert!(session.has_no_errors());
     }
     // N.B. this should be changed once `format_generated_files` is supported with stdin
-    assert_eq!(buf, "stdin:\n\n//@generated\nfn main() {}\n".as_bytes());
+    assert_eq!(
+        String::from_utf8(buf).unwrap(),
+        "<stdin>:\n\n//@generated\nfn main() {}\n",
+    );
 }
 
 #[test]
@@ -914,3 +987,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..aacb2acc684 100644
--- a/src/test/mod_resolver.rs
+++ b/src/test/mod_resolver.rs
@@ -50,3 +50,33 @@ 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",
+        ],
+    )
+}
+
+#[test]
+fn fallback_and_try_to_resolve_external_submod_relative_to_current_dir_path() {
+    // See also https://github.com/rust-lang/rustfmt/issues/5198
+    verify_mod_resolution(
+        "tests/mod-resolver/issue-5198/lib.rs",
+        &[
+            "tests/mod-resolver/issue-5198/a.rs",
+            "tests/mod-resolver/issue-5198/lib/b.rs",
+            "tests/mod-resolver/issue-5198/lib/c/mod.rs",
+            "tests/mod-resolver/issue-5198/lib/c/e.rs",
+            "tests/mod-resolver/issue-5198/lib/c/d/f.rs",
+            "tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs",
+        ],
+    )
+}
diff --git a/src/types.rs b/src/types.rs
index a49d473a13f..64a201e45dd 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -575,7 +575,16 @@ impl Rewrite for ast::GenericParam {
         let mut result = String::with_capacity(128);
         // FIXME: If there are more than one attributes, this will force multiline.
         match self.attrs.rewrite(context, shape) {
-            Some(ref rw) if !rw.is_empty() => result.push_str(&format!("{} ", rw)),
+            Some(ref rw) if !rw.is_empty() => {
+                result.push_str(rw);
+                // When rewriting generic params, an extra newline should be put
+                // if the attributes end with a doc comment
+                if let Some(true) = self.attrs.last().map(|a| a.is_doc_comment()) {
+                    result.push_str(&shape.indent.to_string_with_newline(context.config));
+                } else {
+                    result.push(' ');
+                }
+            }
             _ => (),
         }
 
diff --git a/src/utils.rs b/src/utils.rs
index 2428d8cb0fd..35512e78fa6 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -646,9 +646,22 @@ pub(crate) fn trim_left_preserve_layout(
 }
 
 /// Based on the given line, determine if the next line can be indented or not.
-/// This allows to preserve the indentation of multi-line literals.
-pub(crate) fn indent_next_line(kind: FullCodeCharKind, _line: &str, config: &Config) -> bool {
-    !(kind.is_string() || (config.version() == Version::Two && kind.is_commented_string()))
+/// This allows to preserve the indentation of multi-line literals when
+/// re-inserted a code block that has been formatted separately from the rest
+/// of the code, such as code in macro defs or code blocks doc comments.
+pub(crate) fn indent_next_line(kind: FullCodeCharKind, line: &str, config: &Config) -> bool {
+    if kind.is_string() {
+        // If the string ends with '\', the string has been wrapped over
+        // multiple lines. If `format_strings = true`, then the indentation of
+        // strings wrapped over multiple lines will have been adjusted while
+        // formatting the code block, therefore the string's indentation needs
+        // to be adjusted for the code surrounding the code block.
+        config.format_strings() && line.ends_with('\\')
+    } else if config.version() == Version::Two {
+        !kind.is_commented_string()
+    } else {
+        true
+    }
 }
 
 pub(crate) fn is_empty_line(s: &str) -> bool {
diff --git a/src/vertical.rs b/src/vertical.rs
index c4208848c6c..a06bc995aa5 100644
--- a/src/vertical.rs
+++ b/src/vertical.rs
@@ -160,8 +160,18 @@ pub(crate) fn rewrite_with_alignment<T: AlignedItem>(
     };
     let init_span = mk_sp(span.lo(), init_last_pos);
     let one_line_width = if rest.is_empty() { one_line_width } else { 0 };
-    let result =
-        rewrite_aligned_items_inner(context, init, init_span, shape.indent, one_line_width)?;
+
+    // if another group follows, we must force a separator
+    let force_separator = !rest.is_empty();
+
+    let result = rewrite_aligned_items_inner(
+        context,
+        init,
+        init_span,
+        shape.indent,
+        one_line_width,
+        force_separator,
+    )?;
     if rest.is_empty() {
         Some(result + spaces)
     } else {
@@ -201,6 +211,7 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
     span: Span,
     offset: Indent,
     one_line_width: usize,
+    force_trailing_separator: bool,
 ) -> Option<String> {
     // 1 = ","
     let item_shape = Shape::indented(offset, context.config).sub_width(1)?;
@@ -246,9 +257,15 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
             });
     }
 
+    let separator_tactic = if force_trailing_separator {
+        SeparatorTactic::Always
+    } else {
+        context.config.trailing_comma()
+    };
+
     let fmt = ListFormatting::new(item_shape, context.config)
         .tactic(tactic)
-        .trailing_separator(context.config.trailing_comma())
+        .trailing_separator(separator_tactic)
         .preserve_newline(true);
     write_list(&items, &fmt)
 }