Repository: rust-lang/rustfmt (rust)

Issue:
Rustfmt loses qself type inside struct expr
```rust
#![feature(more_qualified_paths)]

struct Struct {}

trait Trait {
    type Type;
}

impl Trait for Struct {
    type Type = Self;
}

fn main() {
    let _ = <Struct as Trait>::Type {};
}
```

As of current master (7b8303d47968a0e1fcad4b143e0ff4b5e512f37b), `cargo run --bin rustfmt -- repro.rs --emit=stdout` creates the following diff:

```diff
-     let _ = <Struct as Trait>::Type {};
+     let _ = Trait::Type {};
```

which fails to compile:

```console
error[E0223]: ambiguous associated type
  --> repro.rs:14:13
   |
14 |     let _ = Trait::Type {};
   |             ^^^^^^^^^^^ help: use fully-qualified syntax: `<Type as Trait>::Type`
```

Pull request description:
Retain qualified path when rewriting struct literal expressions
Fixes #5151

Details about the qualified path are now passed along so that rustfmt
can include them when formatting struct literal expressions

Patch:
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/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)),


Test patch:
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/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 {};
+}