Repository: rust-lang/rustfmt (rust)

Issue:
Rustfmt cannot resolve submodules of integration tests
## Overview

Rustfmt cannot find a submodule of an integration test located in a separate file.

Running `cargo fmt` on a project that contains a submodule `test1/sub1` implemented by the file `tests/test1/sub1.rs` produces the following error message:

```
Error writing files: failed to resolve mod `sub1`: $PACKAGE/tests/test1/test1 does not exist
```

Submodules in integration tests can be created as follows:

In `tests/test1.rs`:
```
mod test1 {
    mod sub1;
    mod sub2;
}
```

The submodules `sub1` and `sub2` can be implemented in the files `tests/test1/sub1.rs` and `tests/test1/sub2.rs`.

This structure works with `cargo build`, `cargo test`, and `cargo clippy`, but fails for `cargo fmt` with the error message given above.


## Steps to reproduce

Clone this repo containing a minimal example illustrating the problem: https://github.com/walkie/rustfmt-test-submodule-issue

From within the repo:

 * Run `cargo test` and observe that the package successfully compiles and the tests in both submodules run and pass.
 * Run `cargo fmt` and observe the following error message:

```
Error writing files: failed to resolve mod `sub1`: [...]/rustfmt-test-submodule-issue/tests/test1/test1 does not exist
```

The same error can also be produced by `rustfmt` directly with the command: `rustfmt tests/test1.rs`


## Expected behavior

 * No Rustfmt I/O errors. :-)


## Other information

 * Installed Rustfmt by `rustup component add rustfmt`
 * Tested with versions `1.51`, `1.57`, and `1.59.0-nightly (48a5999fc 2021-12-01)`
 * Tested on Fedora Workstation 34 and MacOS 11.6.1

The error occurs in all combinations of the above.

Pull request:
Improve out of line module resolution
Fixes #5119

When the directory structure was laid out as follows:

```
dir
 |---mod_a
 |    |---sub_mod_1.rs
 |    |---sub_mod_2.rs
 |---mod_a.rs
```

And ``mod_a.rs`` contains the following content:

```rust
mod mod_a {
    mod sub_mod_1;
    mod sub_mod_2;
}
```

rustfmt previously tried to find ``sub_mod_1.rs`` and ``sub_mod_2.rs``
in ``./mod_a/mod_a/``. This directory does not exist and this caused
rustfmt to fail with the error message:

    Error writing files: failed to resolve mod

Now, both ``sub_mod_1.rs`` and ``sub_mod_2.rs`` are resolved correctly
and found at ``mod_a/sub_mod_1.rs`` and ``mod_a/sub_mod_2.rs``.

Patch:
diff --git a/rust-toolchain b/rust-toolchain
index c97b5ec6..d4cdcec2 100644
--- a/rust-toolchain
+++ b/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-12-20"
+channel = "nightly-2021-12-29"
 components = ["rustc-dev"]
diff --git a/src/closures.rs b/src/closures.rs
index 34d73a77..e688db1c 100644
--- a/src/closures.rs
+++ b/src/closures.rs
@@ -236,21 +236,21 @@ fn rewrite_closure_fn_decl(
     context: &RewriteContext<'_>,
     shape: Shape,
 ) -> Option<(String, usize)> {
-    let is_async = if asyncness.is_async() { "async " } else { "" };
-    let mover = if capture == ast::CaptureBy::Value {
-        "move "
+    let immovable = if movability == ast::Movability::Static {
+        "static "
     } else {
         ""
     };
-    let immovable = if movability == ast::Movability::Static {
-        "static "
+    let is_async = if asyncness.is_async() { "async " } else { "" };
+    let mover = if capture == ast::CaptureBy::Value {
+        "move "
     } else {
         ""
     };
     // 4 = "|| {".len(), which is overconservative when the closure consists of
     // a single expression.
     let nested_shape = shape
-        .shrink_left(is_async.len() + mover.len() + immovable.len())?
+        .shrink_left(immovable.len() + is_async.len() + mover.len())?
         .sub_width(4)?;
 
     // 1 = |
@@ -288,7 +288,7 @@ fn rewrite_closure_fn_decl(
         .tactic(tactic)
         .preserve_newline(true);
     let list_str = write_list(&item_vec, &fmt)?;
-    let mut prefix = format!("{}{}{}|{}|", is_async, immovable, mover, list_str);
+    let mut prefix = format!("{}{}{}|{}|", immovable, is_async, mover, list_str);
 
     if !ret_str.is_empty() {
         if prefix.contains('\n') {
diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs
index 4b799780..7b498dc4 100644
--- a/src/config/file_lines.rs
+++ b/src/config/file_lines.rs
@@ -13,9 +13,9 @@ use thiserror::Error;
 
 /// A range of lines in a file, inclusive of both ends.
 pub struct LineRange {
-    pub file: Lrc<SourceFile>,
-    pub lo: usize,
-    pub hi: usize,
+    pub(crate) file: Lrc<SourceFile>,
+    pub(crate) lo: usize,
+    pub(crate) hi: usize,
 }
 
 /// Defines the name of an input - either a file or stdin.
@@ -75,7 +75,7 @@ impl Serialize for FileName {
 }
 
 impl LineRange {
-    pub fn file_name(&self) -> FileName {
+    pub(crate) fn file_name(&self) -> FileName {
         self.file.name.clone().into()
     }
 }
diff --git a/src/config/options.rs b/src/config/options.rs
index bce9e5d0..d857c29b 100644
--- a/src/config/options.rs
+++ b/src/config/options.rs
@@ -218,24 +218,24 @@ pub enum Verbosity {
 pub struct WidthHeuristics {
     // Maximum width of the args of a function call before falling back
     // to vertical formatting.
-    pub fn_call_width: usize,
+    pub(crate) fn_call_width: usize,
     // Maximum width of the args of a function-like attributes before falling
     // back to vertical formatting.
-    pub attr_fn_like_width: usize,
+    pub(crate) attr_fn_like_width: usize,
     // Maximum width in the body of a struct lit before falling back to
     // vertical formatting.
-    pub struct_lit_width: usize,
+    pub(crate) struct_lit_width: usize,
     // Maximum width in the body of a struct variant before falling back
     // to vertical formatting.
-    pub struct_variant_width: usize,
+    pub(crate) struct_variant_width: usize,
     // Maximum width of an array literal before falling back to vertical
     // formatting.
-    pub array_width: usize,
+    pub(crate) array_width: usize,
     // Maximum length of a chain to fit on a single line.
-    pub chain_width: usize,
+    pub(crate) chain_width: usize,
     // Maximum line length for single line if-else expressions. A value
     // of zero means always break if-else expressions.
-    pub single_line_if_else_max_width: usize,
+    pub(crate) single_line_if_else_max_width: usize,
 }
 
 impl fmt::Display for WidthHeuristics {
diff --git a/src/expr.rs b/src/expr.rs
index edd004ac..c9c8852c 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -108,9 +108,21 @@ pub(crate) fn format_expr(
         ast::ExprKind::Unary(op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape),
         ast::ExprKind::Struct(ref struct_expr) => {
             let ast::StructExpr {
-                fields, path, rest, ..
+                qself,
+                fields,
+                path,
+                rest,
             } = &**struct_expr;
-            rewrite_struct_lit(context, path, fields, rest, &expr.attrs, expr.span, shape)
+            rewrite_struct_lit(
+                context,
+                path,
+                qself.as_ref(),
+                fields,
+                rest,
+                &expr.attrs,
+                expr.span,
+                shape,
+            )
         }
         ast::ExprKind::Tup(ref items) => {
             rewrite_tuple(context, items.iter(), expr.span, shape, items.len() == 1)
@@ -1511,6 +1523,7 @@ fn struct_lit_can_be_aligned(fields: &[ast::ExprField], has_base: bool) -> bool
 fn rewrite_struct_lit<'a>(
     context: &RewriteContext<'_>,
     path: &ast::Path,
+    qself: Option<&ast::QSelf>,
     fields: &'a [ast::ExprField],
     struct_rest: &ast::StructRest,
     attrs: &[ast::Attribute],
@@ -1527,7 +1540,7 @@ fn rewrite_struct_lit<'a>(
 
     // 2 = " {".len()
     let path_shape = shape.sub_width(2)?;
-    let path_str = rewrite_path(context, PathContext::Expr, None, path, path_shape)?;
+    let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?;
 
     let has_base_or_rest = match struct_rest {
         ast::StructRest::None if fields.is_empty() => return Some(format!("{} {{}}", path_str)),
diff --git a/src/lib.rs b/src/lib.rs
index f59ebad9..ad23b16e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,6 +15,7 @@ extern crate log;
 // N.B. these crates are loaded from the sysroot, so they need extern crate.
 extern crate rustc_ast;
 extern crate rustc_ast_pretty;
+extern crate rustc_builtin_macros;
 extern crate rustc_data_structures;
 extern crate rustc_errors;
 extern crate rustc_expand;
diff --git a/src/matches.rs b/src/matches.rs
index 22d23fc1..85d9c5d2 100644
--- a/src/matches.rs
+++ b/src/matches.rs
@@ -322,7 +322,11 @@ fn flatten_arm_body<'a>(
     if let Some(block) = block_can_be_flattened(context, body) {
         if let ast::StmtKind::Expr(ref expr) = block.stmts[0].kind {
             if let ast::ExprKind::Block(..) = expr.kind {
-                flatten_arm_body(context, expr, None)
+                if expr.attrs.is_empty() {
+                    flatten_arm_body(context, expr, None)
+                } else {
+                    (true, body)
+                }
             } else {
                 let cond_becomes_muti_line = opt_shape
                     .and_then(|shape| rewrite_cond(context, expr, shape))
diff --git a/src/modules.rs b/src/modules.rs
index 9c964b27..70b937b0 100644
--- a/src/modules.rs
+++ b/src/modules.rs
@@ -458,6 +458,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
             self.directory.path.push(path.as_str());
             self.directory.ownership = DirectoryOwnership::Owned { relative: None };
         } else {
+            let id = id.as_str();
             // We have to push on the current module name in the case of relative
             // paths in order to ensure that any additional module paths from inline
             // `mod x { ... }` come after the relative extension.
@@ -468,9 +469,15 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
                 if let Some(ident) = relative.take() {
                     // remove the relative offset
                     self.directory.path.push(ident.as_str());
+
+                    // In the case where there is an x.rs and an ./x directory we want
+                    // to prevent adding x twice. For example, ./x/x
+                    if self.directory.path.exists() && !self.directory.path.join(id).exists() {
+                        return;
+                    }
                 }
             }
-            self.directory.path.push(id.as_str());
+            self.directory.path.push(id);
         }
     }
 
diff --git a/src/parse/macros/asm.rs b/src/parse/macros/asm.rs
new file mode 100644
index 00000000..cc9fb507
--- /dev/null
+++ b/src/parse/macros/asm.rs
@@ -0,0 +1,11 @@
+use rustc_ast::ast;
+use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs};
+
+use crate::rewrite::RewriteContext;
+
+#[allow(dead_code)]
+pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option<AsmArgs> {
+    let ts = mac.args.inner_tokens();
+    let mut parser = super::build_parser(context, ts);
+    parse_asm_args(&mut parser, context.parse_sess.inner(), mac.span(), false).ok()
+}
diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs
index 7e12f82a..2e9ce1d3 100644
--- a/src/parse/macros/mod.rs
+++ b/src/parse/macros/mod.rs
@@ -10,6 +10,7 @@ use rustc_span::Symbol;
 use crate::macros::MacroArg;
 use crate::rewrite::RewriteContext;
 
+pub(crate) mod asm;
 pub(crate) mod cfg_if;
 pub(crate) mod lazy_static;
 
diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs
index a394ce07..1724a0f8 100644
--- a/src/rustfmt_diff.rs
+++ b/src/rustfmt_diff.rs
@@ -6,20 +6,20 @@ use std::io::Write;
 use crate::config::{Color, Config, Verbosity};
 
 #[derive(Debug, PartialEq)]
-pub enum DiffLine {
+pub(crate) enum DiffLine {
     Context(String),
     Expected(String),
     Resulting(String),
 }
 
 #[derive(Debug, PartialEq)]
-pub struct Mismatch {
+pub(crate) struct Mismatch {
     /// The line number in the formatted version.
-    pub line_number: u32,
+    pub(crate) line_number: u32,
     /// The line number in the original version.
-    pub line_number_orig: u32,
+    pub(crate) line_number_orig: u32,
     /// The set of lines (context and old/new) in the mismatch.
-    pub lines: Vec<DiffLine>,
+    pub(crate) lines: Vec<DiffLine>,
 }
 
 impl Mismatch {


Test patch:
diff --git a/src/test/mod_resolver.rs b/src/test/mod_resolver.rs
index ec9ed0f0..fcff6d14 100644
--- a/src/test/mod_resolver.rs
+++ b/src/test/mod_resolver.rs
@@ -50,3 +50,17 @@ fn skip_out_of_line_nested_inline_within_out_of_line() {
         &["tests/mod-resolver/skip-files-issue-5065/one.rs"],
     );
 }
+
+#[test]
+fn fmt_out_of_line_test_modules() {
+    // See also https://github.com/rust-lang/rustfmt/issues/5119
+    verify_mod_resolution(
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs",
+        &[
+            "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs",
+            "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs",
+            "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs",
+            "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs",
+        ],
+    )
+}
diff --git a/tests/cargo-fmt/main.rs b/tests/cargo-fmt/main.rs
index 5493b09e..bf81f253 100644
--- a/tests/cargo-fmt/main.rs
+++ b/tests/cargo-fmt/main.rs
@@ -2,6 +2,7 @@
 
 use std::env;
 use std::process::Command;
+use std::path::Path;
 
 /// Run the cargo-fmt executable and return its output.
 fn cargo_fmt(args: &[&str]) -> (String, String) {
@@ -71,3 +72,26 @@ fn rustfmt_help() {
     assert_that!(&["--", "-h"], contains("Format Rust code"));
     assert_that!(&["--", "--help=config"], contains("Configuration Options:"));
 }
+
+#[test]
+fn cargo_fmt_out_of_line_test_modules() {
+    // See also https://github.com/rust-lang/rustfmt/issues/5119
+    let expected_modified_files = [
+        "tests/mod-resolver/test-submodule-issue-5119/src/lib.rs",
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs",
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs",
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs",
+        "tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs",
+    ];
+    let args = [
+        "-v",
+        "--check",
+        "--manifest-path",
+        "tests/mod-resolver/test-submodule-issue-5119/Cargo.toml",
+    ];
+    let (stdout, _) = cargo_fmt(&args);
+    for file in expected_modified_files {
+        let path = Path::new(file).canonicalize().unwrap();
+        assert!(stdout.contains(&format!("Diff in {}", path.display())))
+    }
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml
new file mode 100644
index 00000000..0993f127
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "rustfmt-test-submodule-issue"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs
new file mode 100644
index 00000000..3f7ddba8
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/src/lib.rs
@@ -0,0 +1,7 @@
+pub fn foo() -> i32 {
+3
+}
+
+pub fn bar() -> i32 {
+4
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs
new file mode 100644
index 00000000..da4e8616
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1.rs
@@ -0,0 +1,8 @@
+mod test1 {
+#[cfg(unix)]
+mod sub1;
+#[cfg(not(unix))]
+mod sub2;
+
+mod sub3;
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs
new file mode 100644
index 00000000..b760ba23
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub1.rs
@@ -0,0 +1,6 @@
+use rustfmt_test_submodule_issue::foo;
+
+#[test]
+fn test_foo() {
+assert_eq!(3, foo());
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs
new file mode 100644
index 00000000..4fd8286e
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub2.rs
@@ -0,0 +1,6 @@
+use rustfmt_test_submodule_issue::bar;
+
+#[test]
+fn test_bar() {
+assert_eq!(4, bar());
+}
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs
new file mode 100644
index 00000000..e029785b
--- /dev/null
+++ b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/mod.rs
@@ -0,0 +1,1 @@
+mod sub4;
diff --git a/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs b/tests/mod-resolver/test-submodule-issue-5119/tests/test1/sub3/sub4.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/source/async_block.rs b/tests/source/async_block.rs
index 3de51a08..18cb4fb5 100644
--- a/tests/source/async_block.rs
+++ b/tests/source/async_block.rs
@@ -32,4 +32,20 @@ fn baz() {
             Ok(())
         },
     );
+
+    spawn(
+        a,
+        static async || {
+            action();
+            Ok(())
+        },
+    );
+
+    spawn(
+        a,
+        static async move || {
+            action();
+            Ok(())
+        },
+    );
 }
diff --git a/tests/source/match.rs b/tests/source/match.rs
index da205172..b5dc9957 100644
--- a/tests/source/match.rs
+++ b/tests/source/match.rs
@@ -568,3 +568,22 @@ fn issue_3774() {
         }
     }
 }
+
+// #4109
+fn issue_4109() {
+    match () {
+        _ => {
+#[cfg(debug_assertions)]
+{
+println!("Foo");
+}
+}
+}
+
+match () {
+_ => {
+#[allow(unsafe_code)]
+unsafe {}
+}
+}
+}
diff --git a/tests/target/async_block.rs b/tests/target/async_block.rs
index 258dd937..137d849c 100644
--- a/tests/target/async_block.rs
+++ b/tests/target/async_block.rs
@@ -22,4 +22,14 @@ fn baz() {
         action();
         Ok(())
     });
+
+    spawn(a, static async || {
+        action();
+        Ok(())
+    });
+
+    spawn(a, static async move || {
+        action();
+        Ok(())
+    });
 }
diff --git a/tests/target/issue-5151/minimum_example.rs b/tests/target/issue-5151/minimum_example.rs
new file mode 100644
index 00000000..2ed3d936
--- /dev/null
+++ b/tests/target/issue-5151/minimum_example.rs
@@ -0,0 +1,16 @@
+#![feature(more_qualified_paths)]
+
+struct Struct {}
+
+trait Trait {
+    type Type;
+}
+
+impl Trait for Struct {
+    type Type = Self;
+}
+
+fn main() {
+    // keep the qualified path details
+    let _ = <Struct as Trait>::Type {};
+}
diff --git a/tests/target/match.rs b/tests/target/match.rs
index e2c522be..1bf3fb75 100644
--- a/tests/target/match.rs
+++ b/tests/target/match.rs
@@ -608,3 +608,22 @@ fn issue_3774() {
         }
     }
 }
+
+// #4109
+fn issue_4109() {
+    match () {
+        _ => {
+            #[cfg(debug_assertions)]
+            {
+                println!("Foo");
+            }
+        }
+    }
+
+    match () {
+        _ => {
+            #[allow(unsafe_code)]
+            unsafe {}
+        }
+    }
+}