Repository: rust-lang/rustfmt (rust)

Issue:
Rustfmt fails to resolve mod
Reproduction

```sh
git clone https://github.com/Skgland/scryer-prolog.git
pushd scryer-prolog
git checkout --track origin/rustfmt/issue1
cargo check # succeeds
cargo fmt --all # Error writing files: failed to resolve mod `macros`: /home/bennet/Git/tmp/scryer-prolog/src does not exist
```

#5167 has a similar error message but this one should not be ambiguous.

Pull request:
fallback to dir_path when relative external mod resolution fails
We only want to fall back if two conditions are met:

1) Initial module resolution is performed relative to some nested directory.
2) Module resolution fails because of a ``ModError::FileNotFound`` error.

When these conditions are met we can try to fallback to searching for the module's file relative to the ``dir_path`` instead of the nested relative directory.

Fixes #5198

As demonstrated by 5198, it's possible that a directory name conflicts with a rust file name. For example, ``src/lib/`` and ``src/lib.rs``.

If ``src/lib.rs`` references an external module like ``mod foo;``, then module resolution will try to resolve ``foo`` to ``src/lib/foo.rs`` or ``src/lib/foo/mod.rs``. Module resolution would fail with a file not found error if the ``foo`` module were defined at ``src/foo.rs``.

When encountering these kinds of module resolution issues we now fall back to the current directory and attempt to resolve the module again.

Given the current example, this means that if we can't find the module ``foo`` at ``src/lib/foo.rs`` or ``src/lib/foo/mod.rs``, we'll attempt to resolve the module to ``src/foo.rs``.

Patch:
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index db497941..45f63b83 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -40,6 +40,10 @@ jobs:
         rustc -Vv
         cargo -V
         cargo build
+      env:
+        RUSTFLAGS: '-D warnings'
 
     - name: test
       run: cargo test
+      env:
+        RUSTFLAGS: '-D warnings'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b59438dc..5b233608 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 ## [Unreleased]
 
+### Fixed
+
+- Fixes issue where wrapped strings would be incorrectly indented in macro defs when `format_strings` was enabled [#4036](https://github.com/rust-lang/rustfmt/issues/4036)
+
 ## [1.4.38] 2021-10-20
 
 ### Changed
@@ -57,6 +61,7 @@ Note this hit the rustup distributions prior to the v1.4.38 release as part of a
 
 - New `One` variant added to `imports_granularity` configuration option which can be used to reformat all imports into a single use statement [#4669](https://github.com/rust-lang/rustfmt/issues/4669)
 - rustfmt will now skip files that are annotated with `@generated` at the top of the file [#3958](https://github.com/rust-lang/rustfmt/issues/3958)
+  if `format_generated_files` option is set to `false` (by default `@generated` files are formatted)
 - New configuration option `hex_literal_case` that allows user to control the casing utilized for hex literals [PR #4903](https://github.com/rust-lang/rustfmt/pull/4903)
 
 See the section on the configuration site for more information
diff --git a/Configurations.md b/Configurations.md
index 4476f2a4..2e2b0f7c 100644
--- a/Configurations.md
+++ b/Configurations.md
@@ -930,6 +930,8 @@ fn add_one(x: i32) -> i32 {
 
 Format generated files. A file is considered generated
 if any of the first five lines contain a `@generated` comment marker.
+By default, generated files are reformatted, i. e. `@generated` marker is ignored.
+This option is currently ignored for stdin (`@generated` in stdin is ignored.)
 
 - **Default value**: `true`
 - **Possible values**: `true`, `false`
diff --git a/config_proc_macro/src/utils.rs b/config_proc_macro/src/utils.rs
index 5b68d274..f5cba87b 100644
--- a/config_proc_macro/src/utils.rs
+++ b/config_proc_macro/src/utils.rs
@@ -22,10 +22,10 @@ pub fn is_unit(v: &syn::Variant) -> bool {
 #[cfg(feature = "debug-with-rustfmt")]
 /// Pretty-print the output of proc macro using rustfmt.
 pub fn debug_with_rustfmt(input: &TokenStream) {
-    use std::io::Write;
-    use std::process::{Command, Stdio};
     use std::env;
     use std::ffi::OsStr;
+    use std::io::Write;
+    use std::process::{Command, Stdio};
 
     let rustfmt_var = env::var_os("RUSTFMT");
     let rustfmt = match &rustfmt_var {
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 6f5b09fc..ad10b9ed 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
         }
     };
@@ -394,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/comment.rs b/src/comment.rs
index 0f850b9b..96778c4e 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,
diff --git a/src/config/mod.rs b/src/config/mod.rs
index cd90e090..5041e1e3 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -364,7 +364,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));
                 }
             }
             _ => {}
diff --git a/src/ignore_path.rs b/src/ignore_path.rs
index 7738eee0..d9559494 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 40e0d06f..c60bec6d 100644
--- a/src/imports.rs
+++ b/src/imports.rs
@@ -238,7 +238,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 +623,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;
diff --git a/src/lib.rs b/src/lib.rs
index fae8080c..ad23b16e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,7 +3,6 @@
 #![warn(unreachable_pub)]
 #![recursion_limit = "256"]
 #![allow(clippy::match_like_matches_macro)]
-#![allow(unreachable_pub)]
 
 #[macro_use]
 extern crate derive_new;
diff --git a/src/lists.rs b/src/lists.rs
index 7aa0315f..97eea19f 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/parse/session.rs b/src/parse/session.rs
index 624fed0d..fb918215 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,30 @@ 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 matches!(e, ModError::FileNotFound(..)) && relative.is_some() {
+                    rustc_expand::module::default_submod_path(&self.parse_sess, id, None, dir_path)
+                } else {
+                    Err(e)
+                }
+            },
+        )
     }
 
     pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
diff --git a/src/spanned.rs b/src/spanned.rs
index 8e6c75a3..2136cfea 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 64ae1567..b65aa5b3 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/types.rs b/src/types.rs
index 5de30129..b13e75a9 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 2428d8cb..35512e78 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 {


Test patch:
diff --git a/src/test/mod.rs b/src/test/mod.rs
index ab966d4a..4191e3e9 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -375,43 +375,21 @@ fn idempotence_tests() {
     });
 }
 
-// Run rustfmt on itself. This operation must be idempotent. We also check that
-// no warnings are emitted.
-// Issue-3443: these tests require nightly
 #[nightly_only_test]
 #[test]
 fn self_tests() {
-    init_log();
-    let mut files = get_test_files(Path::new("tests"), false);
-    let bin_directories = vec!["cargo-fmt", "git-rustfmt", "bin", "format-diff"];
-    for dir in bin_directories {
-        let mut path = PathBuf::from("src");
-        path.push(dir);
-        path.push("main.rs");
-        files.push(path);
-    }
-    files.push(PathBuf::from("src/lib.rs"));
-
-    let (reports, count, fails) = check_files(files, &Some(PathBuf::from("rustfmt.toml")));
-    let mut warnings = 0;
-
-    // Display results.
-    println!("Ran {} self tests.", count);
-    assert_eq!(fails, 0, "{} self tests failed", fails);
-
-    for format_report in reports {
-        println!(
-            "{}",
-            FormatReportFormatterBuilder::new(&format_report).build()
-        );
-        warnings += format_report.warning_count();
-    }
-
-    assert_eq!(
-        warnings, 0,
-        "Rustfmt's code generated {} warnings",
-        warnings
-    );
+    let get_exe_path = |name| {
+        let mut path = env::current_exe().unwrap();
+        path.pop();
+        path.set_file_name(format!("{name}{}", env::consts::EXE_SUFFIX));
+        path
+    };
+    let status = Command::new(get_exe_path("cargo-fmt"))
+        .args(["--check", "--all"])
+        .env("RUSTFMT", get_exe_path("rustfmt"))
+        .status()
+        .unwrap();
+    assert!(status.success());
 }
 
 #[test]
diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs
index fcff6d14..aacb2acc 100644
--- a/src/test/mod_resolver.rs
+++ b/src/test/mod_resolver.rs
@@ -64,3 +64,19 @@ fn fmt_out_of_line_test_modules() {
         ],
     )
 }
+
+#[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/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs
index bf81f253..3713552c 100644
--- a/tests/cargo-fmt/main.rs
+++ b/tests/cargo-fmt/main.rs
@@ -1,8 +1,8 @@
 // Integration tests for cargo-fmt.
 
 use std::env;
-use std::process::Command;
 use std::path::Path;
+use std::process::Command;
 
 /// Run the cargo-fmt executable and return its output.
 fn cargo_fmt(args: &[&str]) -> (String, String) {
diff --git a/tests/mod-resolver/issue-5198/a.rs b/tests/mod-resolver/issue-5198/a.rs
new file mode 100644
index 00000000..cd686f56
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/a.rs
@@ -0,0 +1,1 @@
+fn main(        ) {   println!("Hello World!")                         }
diff --git a/tests/mod-resolver/issue-5198/lib.rs b/tests/mod-resolver/issue-5198/lib.rs
new file mode 100644
index 00000000..69683291
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/lib.rs
@@ -0,0 +1,3 @@
+mod a;
+mod b;
+mod c;
diff --git a/tests/mod-resolver/issue-5198/lib/b.rs b/tests/mod-resolver/issue-5198/lib/b.rs
new file mode 100644
index 00000000..cd686f56
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/lib/b.rs
@@ -0,0 +1,1 @@
+fn main(        ) {   println!("Hello World!")                         }
diff --git a/tests/mod-resolver/issue-5198/lib/c/d.rs b/tests/mod-resolver/issue-5198/lib/c/d.rs
new file mode 100644
index 00000000..d1604aa2
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/lib/c/d.rs
@@ -0,0 +1,3 @@
+mod e;
+mod f;
+mod g;
diff --git a/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt
new file mode 100644
index 00000000..92c9e302
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/lib/c/d/explanation.txt
@@ -0,0 +1,16 @@
+This file is contained in the './lib/c/d/' directory.
+
+The directory name './lib/c/d/' conflicts with the './lib/c/d.rs' file name.
+
+'./lib/c/d.rs' defines 3 external modules:
+
+    * mod e;
+    * mod f;
+    * mod g;
+
+Module resolution will fail if we look for './lib/c/d/e.rs' or './lib/c/d/e/mod.rs',
+so we should fall back to looking for './lib/c/e.rs', which correctly finds the modlue, that
+rustfmt should format.
+
+'./lib/c/d/f.rs' and './lib/c/d/g/mod.rs' exist at the default submodule paths so we should be able
+to resolve these modules with no problems.
\ No newline at end of file
diff --git a/tests/mod-resolver/issue-5198/lib/c/d/f.rs b/tests/mod-resolver/issue-5198/lib/c/d/f.rs
new file mode 100644
index 00000000..cd686f56
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/lib/c/d/f.rs
@@ -0,0 +1,1 @@
+fn main(        ) {   println!("Hello World!")                         }
diff --git a/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs b/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs
new file mode 100644
index 00000000..cd686f56
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/lib/c/d/g/mod.rs
@@ -0,0 +1,1 @@
+fn main(        ) {   println!("Hello World!")                         }
diff --git a/tests/mod-resolver/issue-5198/lib/c/e.rs b/tests/mod-resolver/issue-5198/lib/c/e.rs
new file mode 100644
index 00000000..cd686f56
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/lib/c/e.rs
@@ -0,0 +1,1 @@
+fn main(        ) {   println!("Hello World!")                         }
diff --git a/tests/mod-resolver/issue-5198/lib/c/mod.rs b/tests/mod-resolver/issue-5198/lib/c/mod.rs
new file mode 100644
index 00000000..81904619
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/lib/c/mod.rs
@@ -0,0 +1,3 @@
+mod d;
+
+fn main(        ) {   println!("Hello World!")                         }
diff --git a/tests/mod-resolver/issue-5198/lib/explanation.txt b/tests/mod-resolver/issue-5198/lib/explanation.txt
new file mode 100644
index 00000000..d436a807
--- /dev/null
+++ b/tests/mod-resolver/issue-5198/lib/explanation.txt
@@ -0,0 +1,16 @@
+This file is contained in the './lib' directory.
+
+The directory name './lib' conflicts with the './lib.rs' file name.
+
+'lib.rs' defines 3 external modules:
+
+    * mod a;
+    * mod b;
+    * mod c;
+
+Module resolution will fail if we look for './lib/a.rs' or './lib/a/mod.rs',
+so we should fall back to looking for './a.rs', which correctly finds the modlue that
+rustfmt should format.
+
+'./lib/b.rs' and './lib/c/mod.rs' exist at the default submodule paths so we should be able
+to resolve these modules with no problems.
diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs
index 8effb1c6..2262ae3a 100644
--- a/tests/rustfmt/main.rs
+++ b/tests/rustfmt/main.rs
@@ -106,3 +106,10 @@ fn inline_config() {
             && contains("format_strings = true")
     );
 }
+
+#[test]
+fn rustfmt_usage_text() {
+    let args = ["--help"];
+    let (stdout, _) = rustfmt(&args);
+    assert!(stdout.contains("Format Rust code\n\nusage: rustfmt [options] <file>..."));
+}
diff --git a/tests/source/5131_crate.rs b/tests/source/5131_crate.rs
new file mode 100644
index 00000000..96a31659
--- /dev/null
+++ b/tests/source/5131_crate.rs
@@ -0,0 +1,14 @@
+// rustfmt-imports_granularity: Crate
+
+use foo::a;
+use foo::a;
+use foo::b;
+use foo::b as b2;
+use foo::b::f;
+use foo::b::g;
+use foo::b::g as g2;
+use foo::c;
+use foo::d::e;
+use qux::h;
+use qux::h as h2;
+use qux::i;
diff --git a/tests/source/5131_module.rs b/tests/source/5131_module.rs
new file mode 100644
index 00000000..3e913917
--- /dev/null
+++ b/tests/source/5131_module.rs
@@ -0,0 +1,33 @@
+// rustfmt-imports_granularity: Module
+
+#![allow(dead_code)]
+
+mod a {
+    pub mod b {
+        pub struct Data {
+            pub a: i32,
+        }
+    }
+
+    use crate::a::b::Data;
+    use crate::a::b::Data as Data2;
+
+    pub fn data(a: i32) -> Data {
+        Data { a }
+    }
+
+    pub fn data2(a: i32) -> Data2 {
+        Data2 { a }
+    }
+
+    #[cfg(test)]
+    mod tests {
+        use super::*;
+
+        #[test]
+        pub fn test() {
+            data(1);
+            data2(1);
+        }
+    }
+}
diff --git a/tests/source/5131_one.rs b/tests/source/5131_one.rs
new file mode 100644
index 00000000..61ddf134
--- /dev/null
+++ b/tests/source/5131_one.rs
@@ -0,0 +1,15 @@
+// rustfmt-imports_granularity: One
+
+pub use foo::x;
+pub use foo::x as x2;
+pub use foo::y;
+use bar::a;
+use bar::b;
+use bar::b::f;
+use bar::b::f as f2;
+use bar::b::g;
+use bar::c;
+use bar::d::e;
+use bar::d::e as e2;
+use qux::h;
+use qux::i;
diff --git a/tests/source/issue-4036/one.rs b/tests/source/issue-4036/one.rs
new file mode 100644
index 00000000..9f9675f5
--- /dev/null
+++ b/tests/source/issue-4036/one.rs
@@ -0,0 +1,11 @@
+// rustfmt-format_strings: true
+
+macro_rules! test {
+    () => {
+        fn from() {
+            None.expect(
+                "We asserted that `buffer.len()` is exactly `$n` so we can expect `ApInt::from_iter` to be successful.",
+            )
+        }
+    };
+}
diff --git a/tests/source/issue-4036/three.rs b/tests/source/issue-4036/three.rs
new file mode 100644
index 00000000..e1865dd0
--- /dev/null
+++ b/tests/source/issue-4036/three.rs
@@ -0,0 +1,12 @@
+// rustfmt-format_strings: true
+// rustfmt-hard_tabs: true
+
+macro_rules! test {
+    () => {
+        fn from() {
+            None.expect(
+                "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+            )
+        }
+    };
+}
diff --git a/tests/source/issue-4036/two.rs b/tests/source/issue-4036/two.rs
new file mode 100644
index 00000000..fa54d2e3
--- /dev/null
+++ b/tests/source/issue-4036/two.rs
@@ -0,0 +1,11 @@
+// rustfmt-format_strings: true
+
+macro_rules! test {
+    () => {
+        fn from() {
+            None.expect(
+                "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+            )
+        }
+    };
+}
diff --git a/tests/source/issue-5023.rs b/tests/source/issue-5023.rs
new file mode 100644
index 00000000..ae1c723e
--- /dev/null
+++ b/tests/source/issue-5023.rs
@@ -0,0 +1,22 @@
+// rustfmt-wrap_comments: true
+
+/// A comment to test special unicode characters on boundaries
+/// 是，是，是，是，是，是，是，是，是，是，是，是  it should break right here this goes to the next line
+fn main() {
+    if xxx {
+        let xxx = xxx
+            .into_iter()
+            .filter(|(xxx, xxx)| {
+                if let Some(x) = Some(1) {
+                    // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx
+                    // xxxxxxxxxx. xxxxxxxxxxxxxxxx，xxxxxxxxxxxxxxxxx xxx xxxxxxx
+                    // 是sdfadsdfxxxxxxxxx，sdfaxxxxxx_xxxxx_masdfaonxxx，
+                    if false {
+                        return true;
+                    }
+                }
+                false
+            })
+            .collect();
+    }
+}
diff --git a/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs b/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs
new file mode 100644
index 00000000..5d171f32
--- /dev/null
+++ b/tests/source/issue-5042/multi-line_comment_with_trailing_comma.rs
@@ -0,0 +1,24 @@
+fn main() {
+    // 5042 deals with trailing commas, not the indentation issue of these comments
+    // When a future PR fixes the inentation issues these test can be updated
+    let _ = std::ops::Add::add(10, 20
+        // ...
+        // ...,
+        );
+
+    let _ = std::ops::Add::add(10, 20
+        /* ... */
+        // ...,
+        );
+
+    let _ = std::ops::Add::add(10, 20
+        // ...,
+        // ...,
+        );
+
+    let _ = std::ops::Add::add(10, 20
+        // ...,
+        /* ...
+        */,
+        );
+}
diff --git a/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs b/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs
new file mode 100644
index 00000000..b8a824b3
--- /dev/null
+++ b/tests/source/issue-5042/multi-line_comment_without_trailing_comma.rs
@@ -0,0 +1,24 @@
+fn main() {
+    // 5042 deals with trailing commas, not the indentation issue of these comments
+    // When a future PR fixes the inentation issues these test can be updated
+    let _ = std::ops::Add::add(10, 20
+        // ...
+        // ...
+        );
+
+    let _ = std::ops::Add::add(10, 20
+        /* ... */
+        // ...
+        );
+
+    let _ = std::ops::Add::add(10, 20
+        // ...
+        // ...
+        );
+
+    let _ = std::ops::Add::add(10, 20
+        // ...
+        /* ...
+        */
+        );
+}
diff --git a/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs b/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs
new file mode 100644
index 00000000..bd765b7b
--- /dev/null
+++ b/tests/source/issue-5042/single-line_comment_with_trailing_comma.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let _ = std::ops::Add::add(10, 20
+        // ...,
+        );
+
+    let _ = std::ops::Add::add(10, 20
+        /* ... */,
+        );
+}
diff --git a/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs b/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs
new file mode 100644
index 00000000..2ed8de87
--- /dev/null
+++ b/tests/source/issue-5042/single-line_comment_without_trailing_comma.rs
@@ -0,0 +1,10 @@
+fn main() {
+    let _ = std::ops::Add::add(10, 20
+        // ...
+        );
+
+    let _ = std::ops::Add::add(10, 20
+        /* ... */
+        );
+}
+
diff --git a/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs b/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs
new file mode 100644
index 00000000..5c1d79a7
--- /dev/null
+++ b/tests/source/issue-5157/indented_itemized_markdown_blockquote.rs
@@ -0,0 +1,4 @@
+// rustfmt-wrap_comments: true
+
+///        > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
+fn block_quote() {}
diff --git a/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs b/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs
new file mode 100644
index 00000000..cf200d04
--- /dev/null
+++ b/tests/source/issue-5157/nested_itemized_markdown_blockquote.rs
@@ -0,0 +1,10 @@
+// rustfmt-wrap_comments: true
+
+/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
+///
+/// > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
+///
+/// > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
+///
+/// > > > > > > > > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
+fn block_quote() {}
diff --git a/tests/source/issue-5157/support_itemized_markdown_blockquote.rs b/tests/source/issue-5157/support_itemized_markdown_blockquote.rs
new file mode 100644
index 00000000..eb436402
--- /dev/null
+++ b/tests/source/issue-5157/support_itemized_markdown_blockquote.rs
@@ -0,0 +1,4 @@
+// rustfmt-wrap_comments: true
+
+/// > For each sample received, the middleware internally maintains a sample_state relative to each DataReader. The sample_state can either be READ or NOT_READ.
+fn block_quote() {}
diff --git a/tests/target/5131_crate.rs b/tests/target/5131_crate.rs
new file mode 100644
index 00000000..557d6670
--- /dev/null
+++ b/tests/target/5131_crate.rs
@@ -0,0 +1,9 @@
+// rustfmt-imports_granularity: Crate
+
+use foo::{
+    a, b, b as b2,
+    b::{f, g, g as g2},
+    c,
+    d::e,
+};
+use qux::{h, h as h2, i};
diff --git a/tests/target/5131_module.rs b/tests/target/5131_module.rs
new file mode 100644
index 00000000..763024d6
--- /dev/null
+++ b/tests/target/5131_module.rs
@@ -0,0 +1,32 @@
+// rustfmt-imports_granularity: Module
+
+#![allow(dead_code)]
+
+mod a {
+    pub mod b {
+        pub struct Data {
+            pub a: i32,
+        }
+    }
+
+    use crate::a::b::{Data, Data as Data2};
+
+    pub fn data(a: i32) -> Data {
+        Data { a }
+    }
+
+    pub fn data2(a: i32) -> Data2 {
+        Data2 { a }
+    }
+
+    #[cfg(test)]
+    mod tests {
+        use super::*;
+
+        #[test]
+        pub fn test() {
+            data(1);
+            data2(1);
+        }
+    }
+}
diff --git a/tests/target/5131_one.rs b/tests/target/5131_one.rs
new file mode 100644
index 00000000..a086dae5
--- /dev/null
+++ b/tests/target/5131_one.rs
@@ -0,0 +1,12 @@
+// rustfmt-imports_granularity: One
+
+pub use foo::{x, x as x2, y};
+use {
+    bar::{
+        a,
+        b::{self, f, g},
+        c,
+        d::{e, e as e2},
+    },
+    qux::{h, i},
+};
diff --git a/tests/target/doc-of-generic-item.rs b/tests/target/doc-of-generic-item.rs
new file mode 100644
index 00000000..2efc5e09
--- /dev/null
+++ b/tests/target/doc-of-generic-item.rs
@@ -0,0 +1,14 @@
+// Non-doc pre-comment of Foo
+/// doc of Foo
+// Non-doc post-comment of Foo
+struct Foo<
+    // Non-doc pre-comment of 'a
+    /// doc of 'a
+    'a,
+    // Non-doc pre-comment of T
+    /// doc of T
+    T,
+    // Non-doc pre-comment of N
+    /// doc of N
+    const N: item,
+>;
diff --git a/tests/target/issue-4036/one.rs b/tests/target/issue-4036/one.rs
new file mode 100644
index 00000000..54e490b7
--- /dev/null
+++ b/tests/target/issue-4036/one.rs
@@ -0,0 +1,12 @@
+// rustfmt-format_strings: true
+
+macro_rules! test {
+    () => {
+        fn from() {
+            None.expect(
+                "We asserted that `buffer.len()` is exactly `$n` so we can expect \
+                 `ApInt::from_iter` to be successful.",
+            )
+        }
+    };
+}
diff --git a/tests/target/issue-4036/three.rs b/tests/target/issue-4036/three.rs
new file mode 100644
index 00000000..394dc863
--- /dev/null
+++ b/tests/target/issue-4036/three.rs
@@ -0,0 +1,17 @@
+// rustfmt-format_strings: true
+// rustfmt-hard_tabs: true
+
+macro_rules! test {
+	() => {
+		fn from() {
+			None.expect(
+				"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \
+				 incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \
+				 nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
+				 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \
+				 fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \
+				 culpa qui officia deserunt mollit anim id est laborum.",
+			)
+		}
+	};
+}
diff --git a/tests/target/issue-4036/two.rs b/tests/target/issue-4036/two.rs
new file mode 100644
index 00000000..01cafa76
--- /dev/null
+++ b/tests/target/issue-4036/two.rs
@@ -0,0 +1,16 @@
+// rustfmt-format_strings: true
+
+macro_rules! test {
+    () => {
+        fn from() {
+            None.expect(
+                "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \
+                 incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \
+                 nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
+                 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \
+                 fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in \
+                 culpa qui officia deserunt mollit anim id est laborum.",
+            )
+        }
+    };
+}
diff --git a/tests/target/issue-5023.rs b/tests/target/issue-5023.rs
new file mode 100644
index 00000000..4e84c7d9
--- /dev/null
+++ b/tests/target/issue-5023.rs
@@ -0,0 +1,23 @@
+// rustfmt-wrap_comments: true
+
+/// A comment to test special unicode characters on boundaries
+/// 是，是，是，是，是，是，是，是，是，是，是，是  it should break right here
+/// this goes to the next line
+fn main() {
+    if xxx {
+        let xxx = xxx
+            .into_iter()
+            .filter(|(xxx, xxx)| {
+                if let Some(x) = Some(1) {
+                    // xxxxxxxxxxxxxxxxxx, xxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxx xxx xxxxxxx, xxxxx xxx
+                    // xxxxxxxxxx. xxxxxxxxxxxxxxxx，xxxxxxxxxxxxxxxxx xxx xxxxxxx
+                    // 是sdfadsdfxxxxxxxxx，sdfaxxxxxx_xxxxx_masdfaonxxx，
+                    if false {
+                        return true;
+                    }
+                }
+                false
+            })
+            .collect();
+    }
+}
diff --git a/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs b/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs
new file mode 100644
index 00000000..1ae1212b
--- /dev/null
+++ b/tests/target/issue-5042/multi-line_comment_with_trailing_comma.rs
@@ -0,0 +1,24 @@
+fn main() {
+    // 5042 deals with trailing commas, not the indentation issue of these comments
+    // When a future PR fixes the inentation issues these test can be updated
+    let _ = std::ops::Add::add(
+        10, 20, // ...
+           // ...,
+    );
+
+    let _ = std::ops::Add::add(
+        10, 20, /* ... */
+           // ...,
+    );
+
+    let _ = std::ops::Add::add(
+        10, 20, // ...,
+           // ...,
+    );
+
+    let _ = std::ops::Add::add(
+        10, 20, // ...,
+           /* ...
+            */
+    );
+}
diff --git a/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs b/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs
new file mode 100644
index 00000000..30d17466
--- /dev/null
+++ b/tests/target/issue-5042/multi-line_comment_without_trailing_comma.rs
@@ -0,0 +1,24 @@
+fn main() {
+    // 5042 deals with trailing commas, not the indentation issue of these comments
+    // When a future PR fixes the inentation issues these test can be updated
+    let _ = std::ops::Add::add(
+        10, 20, // ...
+           // ...
+    );
+
+    let _ = std::ops::Add::add(
+        10, 20, /* ... */
+           // ...
+    );
+
+    let _ = std::ops::Add::add(
+        10, 20, // ...
+           // ...
+    );
+
+    let _ = std::ops::Add::add(
+        10, 20, // ...
+           /* ...
+            */
+    );
+}
diff --git a/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs b/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs
new file mode 100644
index 00000000..87b651dd
--- /dev/null
+++ b/tests/target/issue-5042/single-line_comment_with_trailing_comma.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let _ = std::ops::Add::add(
+        10, 20, // ...,
+    );
+
+    let _ = std::ops::Add::add(10, 20 /* ... */);
+}
diff --git a/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs b/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs
new file mode 100644
index 00000000..116df86a
--- /dev/null
+++ b/tests/target/issue-5042/single-line_comment_without_trailing_comma.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let _ = std::ops::Add::add(
+        10, 20, // ...
+    );
+
+    let _ = std::ops::Add::add(10, 20 /* ... */);
+}
diff --git a/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs b/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs
new file mode 100644
index 00000000..e47677f2
--- /dev/null
+++ b/tests/target/issue-5157/indented_itemized_markdown_blockquote.rs
@@ -0,0 +1,6 @@
+// rustfmt-wrap_comments: true
+
+///        > For each sample received, the middleware internally maintains a
+///        > sample_state relative to each DataReader. The sample_state can
+///        > either be READ or NOT_READ.
+fn block_quote() {}
diff --git a/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs b/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs
new file mode 100644
index 00000000..07951044
--- /dev/null
+++ b/tests/target/issue-5157/nested_itemized_markdown_blockquote.rs
@@ -0,0 +1,18 @@
+// rustfmt-wrap_comments: true
+
+/// > For each sample received, the middleware internally maintains a
+/// > sample_state relative to each DataReader. The sample_state can either be
+/// > READ or NOT_READ.
+///
+/// > > For each sample received, the middleware internally maintains a
+/// > > sample_state relative to each DataReader. The sample_state can either be
+/// > > READ or NOT_READ.
+///
+/// > > > For each sample received, the middleware internally maintains a
+/// > > > sample_state relative to each DataReader. The sample_state can either
+/// > > > be READ or NOT_READ.
+///
+/// > > > > > > > > For each sample received, the middleware internally
+/// > > > > > > > > maintains a sample_state relative to each DataReader. The
+/// > > > > > > > > sample_state can either be READ or NOT_READ.
+fn block_quote() {}
diff --git a/tests/target/issue-5157/support_itemized_markdown_blockquote.rs b/tests/target/issue-5157/support_itemized_markdown_blockquote.rs
new file mode 100644
index 00000000..029ee37d
--- /dev/null
+++ b/tests/target/issue-5157/support_itemized_markdown_blockquote.rs
@@ -0,0 +1,6 @@
+// rustfmt-wrap_comments: true
+
+/// > For each sample received, the middleware internally maintains a
+/// > sample_state relative to each DataReader. The sample_state can either be
+/// > READ or NOT_READ.
+fn block_quote() {}