about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-10-26 22:10:17 +0000
committerbors <bors@rust-lang.org>2023-10-26 22:10:17 +0000
commitdab715641e96a61a534587fda9de1128b75b34dc (patch)
treef695c750bbed83b0a1bd2def4e1849dea196c32c
parentaa1a71e9e90f6eb3aed8cf79fc80bea304c17ecb (diff)
parentc0b1c1aedf1f472fc5713f1334936c0873fb06af (diff)
downloadrust-dab715641e96a61a534587fda9de1128b75b34dc.tar.gz
rust-dab715641e96a61a534587fda9de1128b75b34dc.zip
Auto merge of #117249 - matthiaskrgr:rollup-h4og5rv, r=matthiaskrgr
Rollup of 6 pull requests

Successful merges:

 - #116968 (Invalid `?` suggestion on mismatched `Ok(T)`)
 - #117032 (Enable cg_clif tests for riscv64gc)
 - #117106 (When expecting closure argument but finding block provide suggestion)
 - #117114 (Improve `stringify.rs` test)
 - #117188 (Avoid repeated interning of `env!("CFG_RELEASE")`)
 - #117243 (Explain implementation of mem::replace)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_ast/src/ast.rs7
-rw-r--r--compiler/rustc_attr/src/builtin.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs53
-rw-r--r--compiler/rustc_macros/src/lib.rs1
-rw-r--r--compiler/rustc_macros/src/symbols.rs158
-rw-r--r--compiler/rustc_macros/src/symbols/tests.rs2
-rw-r--r--compiler/rustc_passes/src/lib_features.rs4
-rw-r--r--compiler/rustc_passes/src/stability.rs6
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs53
-rw-r--r--library/core/src/mem/mod.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs5
-rw-r--r--tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs10
-rw-r--r--tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr27
-rw-r--r--tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs7
-rw-r--r--tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr25
-rw-r--r--tests/ui/macros/stringify.rs1034
-rw-r--r--tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs6
-rw-r--r--tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr15
19 files changed, 784 insertions, 638 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index e94e26a63bc..e09ba03d881 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -734,6 +734,8 @@ pub enum RangeSyntax {
 }
 
 /// All the different flavors of pattern that Rust recognizes.
+//
+// Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum PatKind {
     /// Represents a wildcard pattern (`_`).
@@ -967,6 +969,7 @@ impl Stmt {
     }
 }
 
+// Adding a new variant? Please update `test_stmt` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum StmtKind {
     /// A local (let) binding.
@@ -1345,6 +1348,7 @@ pub struct StructExpr {
     pub rest: StructRest,
 }
 
+// Adding a new variant? Please update `test_expr` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum ExprKind {
     /// An array (`[a, b, c, d]`)
@@ -2015,6 +2019,8 @@ pub struct BareFnTy {
 }
 
 /// The various kinds of type recognized by the compiler.
+//
+// Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum TyKind {
     /// A variable-length slice (`[T]`).
@@ -2880,6 +2886,7 @@ pub struct ConstItem {
     pub expr: Option<P<Expr>>,
 }
 
+// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum ItemKind {
     /// An `extern crate` item, with the optional *original* crate name if the crate was renamed.
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index bd85483885e..363ba0d4a81 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -26,10 +26,6 @@ pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
 
 pub const CURRENT_RUSTC_VERSION: &str = env!("CFG_RELEASE");
 
-pub fn rust_version_symbol() -> Symbol {
-    Symbol::intern(CURRENT_RUSTC_VERSION)
-}
-
 pub fn is_builtin_attr(attr: &Attribute) -> bool {
     attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
 }
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index f379e8477e3..abb83228030 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -962,38 +962,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
     ) -> bool {
-        let ty::Adt(e, args_e) = expected.kind() else {
-            return false;
-        };
-        let ty::Adt(f, args_f) = found.kind() else {
-            return false;
-        };
-        if e.did() != f.did() {
-            return false;
-        }
-        if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) {
-            return false;
-        }
         let map = self.tcx.hir();
-        if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id)
-            && let hir::ExprKind::Ret(_) = expr.kind
-        {
-            // `return foo;`
-        } else if map.get_return_block(expr.hir_id).is_some() {
-            // Function's tail expression.
-        } else {
-            return false;
-        }
-        let e = args_e.type_at(1);
-        let f = args_f.type_at(1);
-        if self
-            .infcx
-            .type_implements_trait(
-                self.tcx.get_diagnostic_item(sym::Into).unwrap(),
-                [f, e],
-                self.param_env,
-            )
-            .must_apply_modulo_regions()
+        let returned = matches!(
+            map.find_parent(expr.hir_id),
+            Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }))
+        ) || map.get_return_block(expr.hir_id).is_some();
+        if returned
+            && let ty::Adt(e, args_e) = expected.kind()
+            && let ty::Adt(f, args_f) = found.kind()
+            && e.did() == f.did()
+            && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
+            && let e_ok = args_e.type_at(0)
+            && let f_ok = args_f.type_at(0)
+            && self.infcx.can_eq(self.param_env, f_ok, e_ok)
+            && let e_err = args_e.type_at(1)
+            && let f_err = args_f.type_at(1)
+            && self
+                .infcx
+                .type_implements_trait(
+                    self.tcx.get_diagnostic_item(sym::Into).unwrap(),
+                    [f_err, e_err],
+                    self.param_env,
+                )
+                .must_apply_modulo_regions()
         {
             err.multipart_suggestion(
                 "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 85829906f4e..776ba8f9ca1 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(never_type)]
 #![feature(proc_macro_diagnostic)]
 #![feature(proc_macro_span)]
+#![feature(proc_macro_tracked_env)]
 #![allow(rustc::default_hash_types)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs
index 04facbf657d..4129712a6b2 100644
--- a/compiler/rustc_macros/src/symbols.rs
+++ b/compiler/rustc_macros/src/symbols.rs
@@ -26,7 +26,7 @@ use proc_macro2::{Span, TokenStream};
 use quote::quote;
 use std::collections::HashMap;
 use syn::parse::{Parse, ParseStream, Result};
-use syn::{braced, punctuated::Punctuated, Ident, LitStr, Token};
+use syn::{braced, punctuated::Punctuated, Expr, Ident, Lit, LitStr, Macro, Token};
 
 #[cfg(test)]
 mod tests;
@@ -53,21 +53,46 @@ impl Parse for Keyword {
 
 struct Symbol {
     name: Ident,
-    value: Option<LitStr>,
+    value: Value,
+}
+
+enum Value {
+    SameAsName,
+    String(LitStr),
+    Env(LitStr, Macro),
+    Unsupported(Expr),
 }
 
 impl Parse for Symbol {
     fn parse(input: ParseStream<'_>) -> Result<Self> {
         let name = input.parse()?;
-        let value = match input.parse::<Token![:]>() {
-            Ok(_) => Some(input.parse()?),
-            Err(_) => None,
-        };
+        let colon_token: Option<Token![:]> = input.parse()?;
+        let value = if colon_token.is_some() { input.parse()? } else { Value::SameAsName };
 
         Ok(Symbol { name, value })
     }
 }
 
+impl Parse for Value {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        let expr: Expr = input.parse()?;
+        match &expr {
+            Expr::Lit(expr) => {
+                if let Lit::Str(lit) = &expr.lit {
+                    return Ok(Value::String(lit.clone()));
+                }
+            }
+            Expr::Macro(expr) => {
+                if expr.mac.path.is_ident("env") && let Ok(lit) = expr.mac.parse_body() {
+                    return Ok(Value::Env(lit, expr.mac.clone()));
+                }
+            }
+            _ => {}
+        }
+        Ok(Value::Unsupported(expr))
+    }
+}
+
 struct Input {
     keywords: Punctuated<Keyword, Token![,]>,
     symbols: Punctuated<Symbol, Token![,]>,
@@ -111,6 +136,37 @@ pub fn symbols(input: TokenStream) -> TokenStream {
     output
 }
 
+struct Preinterned {
+    idx: u32,
+    span_of_name: Span,
+}
+
+struct Entries {
+    map: HashMap<String, Preinterned>,
+}
+
+impl Entries {
+    fn with_capacity(capacity: usize) -> Self {
+        Entries { map: HashMap::with_capacity(capacity) }
+    }
+
+    fn insert(&mut self, span: Span, str: &str, errors: &mut Errors) -> u32 {
+        if let Some(prev) = self.map.get(str) {
+            errors.error(span, format!("Symbol `{str}` is duplicated"));
+            errors.error(prev.span_of_name, "location of previous definition".to_string());
+            prev.idx
+        } else {
+            let idx = self.len();
+            self.map.insert(str.to_string(), Preinterned { idx, span_of_name: span });
+            idx
+        }
+    }
+
+    fn len(&self) -> u32 {
+        u32::try_from(self.map.len()).expect("way too many symbols")
+    }
+}
+
 fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
     let mut errors = Errors::default();
 
@@ -127,20 +183,9 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
     let mut keyword_stream = quote! {};
     let mut symbols_stream = quote! {};
     let mut prefill_stream = quote! {};
-    let mut counter = 0u32;
-    let mut keys =
-        HashMap::<String, Span>::with_capacity(input.keywords.len() + input.symbols.len() + 10);
+    let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10);
     let mut prev_key: Option<(Span, String)> = None;
 
-    let mut check_dup = |span: Span, str: &str, errors: &mut Errors| {
-        if let Some(prev_span) = keys.get(str) {
-            errors.error(span, format!("Symbol `{str}` is duplicated"));
-            errors.error(*prev_span, "location of previous definition".to_string());
-        } else {
-            keys.insert(str.to_string(), span);
-        }
-    };
-
     let mut check_order = |span: Span, str: &str, errors: &mut Errors| {
         if let Some((prev_span, ref prev_str)) = prev_key {
             if str < prev_str {
@@ -156,49 +201,98 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
         let name = &keyword.name;
         let value = &keyword.value;
         let value_string = value.value();
-        check_dup(keyword.name.span(), &value_string, &mut errors);
+        let idx = entries.insert(keyword.name.span(), &value_string, &mut errors);
         prefill_stream.extend(quote! {
             #value,
         });
         keyword_stream.extend(quote! {
-            pub const #name: Symbol = Symbol::new(#counter);
+            pub const #name: Symbol = Symbol::new(#idx);
         });
-        counter += 1;
     }
 
     // Generate the listed symbols.
     for symbol in input.symbols.iter() {
         let name = &symbol.name;
+        check_order(symbol.name.span(), &name.to_string(), &mut errors);
+
         let value = match &symbol.value {
-            Some(value) => value.value(),
-            None => name.to_string(),
+            Value::SameAsName => name.to_string(),
+            Value::String(lit) => lit.value(),
+            Value::Env(..) => continue, // in another loop below
+            Value::Unsupported(expr) => {
+                errors.list.push(syn::Error::new_spanned(
+                    expr,
+                    concat!(
+                        "unsupported expression for symbol value; implement support for this in ",
+                        file!(),
+                    ),
+                ));
+                continue;
+            }
         };
-        check_dup(symbol.name.span(), &value, &mut errors);
-        check_order(symbol.name.span(), &name.to_string(), &mut errors);
+        let idx = entries.insert(symbol.name.span(), &value, &mut errors);
 
         prefill_stream.extend(quote! {
             #value,
         });
         symbols_stream.extend(quote! {
-            pub const #name: Symbol = Symbol::new(#counter);
+            pub const #name: Symbol = Symbol::new(#idx);
         });
-        counter += 1;
     }
 
     // Generate symbols for the strings "0", "1", ..., "9".
-    let digits_base = counter;
-    counter += 10;
     for n in 0..10 {
         let n = n.to_string();
-        check_dup(Span::call_site(), &n, &mut errors);
+        entries.insert(Span::call_site(), &n, &mut errors);
         prefill_stream.extend(quote! {
             #n,
         });
     }
 
+    // Symbols whose value comes from an environment variable. It's allowed for
+    // these to have the same value as another symbol.
+    for symbol in &input.symbols {
+        let (env_var, expr) = match &symbol.value {
+            Value::Env(lit, expr) => (lit, expr),
+            Value::SameAsName | Value::String(_) | Value::Unsupported(_) => continue,
+        };
+
+        if !proc_macro::is_available() {
+            errors.error(
+                Span::call_site(),
+                "proc_macro::tracked_env is not available in unit test".to_owned(),
+            );
+            break;
+        }
+
+        let value = match proc_macro::tracked_env::var(env_var.value()) {
+            Ok(value) => value,
+            Err(err) => {
+                errors.list.push(syn::Error::new_spanned(expr, err));
+                continue;
+            }
+        };
+
+        let idx = if let Some(prev) = entries.map.get(&value) {
+            prev.idx
+        } else {
+            prefill_stream.extend(quote! {
+                #value,
+            });
+            entries.insert(symbol.name.span(), &value, &mut errors)
+        };
+
+        let name = &symbol.name;
+        symbols_stream.extend(quote! {
+            pub const #name: Symbol = Symbol::new(#idx);
+        });
+    }
+
+    let symbol_digits_base = entries.map["0"].idx;
+    let preinterned_symbols_count = entries.len();
     let output = quote! {
-        const SYMBOL_DIGITS_BASE: u32 = #digits_base;
-        const PREINTERNED_SYMBOLS_COUNT: u32 = #counter;
+        const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base;
+        const PREINTERNED_SYMBOLS_COUNT: u32 = #preinterned_symbols_count;
 
         #[doc(hidden)]
         #[allow(non_upper_case_globals)]
diff --git a/compiler/rustc_macros/src/symbols/tests.rs b/compiler/rustc_macros/src/symbols/tests.rs
index bd0c08a53c4..9c53453df5b 100644
--- a/compiler/rustc_macros/src/symbols/tests.rs
+++ b/compiler/rustc_macros/src/symbols/tests.rs
@@ -27,7 +27,7 @@ fn test_symbols() {
 
     let body_tokens = m.mac.tokens.clone();
 
-    test_symbols_macro(body_tokens, &[]);
+    test_symbols_macro(body_tokens, &["proc_macro::tracked_env is not available in unit test"]);
 }
 
 fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) {
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index ceb8f58cac0..0daa273db67 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -5,7 +5,7 @@
 //! collect them instead.
 
 use rustc_ast::Attribute;
-use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER};
+use rustc_attr::VERSION_PLACEHOLDER;
 use rustc_hir::intravisit::Visitor;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::lib_features::LibFeatures;
@@ -59,7 +59,7 @@ impl<'tcx> LibFeatureCollector<'tcx> {
                 if let Some(s) = since
                     && s.as_str() == VERSION_PLACEHOLDER
                 {
-                    since = Some(rust_version_symbol());
+                    since = Some(sym::env_CFG_RELEASE);
                 }
 
                 if let Some(feature) = feature {
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 41a240fa880..7bfb0742b8b 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -3,8 +3,8 @@
 
 use crate::errors;
 use rustc_attr::{
-    self as attr, rust_version_symbol, ConstStability, Since, Stability, StabilityLevel, Unstable,
-    UnstableReason, VERSION_PLACEHOLDER,
+    self as attr, ConstStability, Since, Stability, StabilityLevel, Unstable, UnstableReason,
+    VERSION_PLACEHOLDER,
 };
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_hir as hir;
@@ -1115,7 +1115,7 @@ fn unnecessary_stable_feature_lint(
     mut since: Symbol,
 ) {
     if since.as_str() == VERSION_PLACEHOLDER {
-        since = rust_version_symbol();
+        since = sym::env_CFG_RELEASE;
     }
     tcx.emit_spanned_lint(
         lint::builtin::STABLE_FEATURES,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index ff61143a12b..88d9dab2ba5 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -713,6 +713,7 @@ symbols! {
         encode,
         end,
         env,
+        env_CFG_RELEASE: env!("CFG_RELEASE"),
         eprint_macro,
         eprintln_macro,
         eq,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index b9a08056ad1..947ea3eece3 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -3465,11 +3465,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         if let Some(Node::Expr(expr)) = hir.find(arg_hir_id)
             && let Some(typeck_results) = &self.typeck_results
         {
-            if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
-                let expr = expr.peel_blocks();
-                let ty =
-                    typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx));
-                let span = expr.span;
+            if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
+                let inner_expr = expr.peel_blocks();
+                let ty = typeck_results.expr_ty_adjusted_opt(inner_expr)
+                    .unwrap_or(Ty::new_misc_error(tcx));
+                let span = inner_expr.span;
                 if Some(span) != err.span.primary_span() {
                     err.span_label(
                         span,
@@ -3480,6 +3480,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             format!("this tail expression is of type `{ty}`")
                         },
                     );
+                    if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder()
+                        && let ty::ClauseKind::Trait(pred) = clause
+                        && [
+                            tcx.lang_items().fn_once_trait(),
+                            tcx.lang_items().fn_mut_trait(),
+                            tcx.lang_items().fn_trait(),
+                        ].contains(&Some(pred.def_id()))
+                    {
+                        if let [stmt, ..] = block.stmts
+                            && let hir::StmtKind::Semi(value) = stmt.kind
+                            && let hir::ExprKind::Closure(hir::Closure {
+                                body,
+                                fn_decl_span,
+                                ..
+                            }) = value.kind
+                            && let body = hir.body(*body)
+                            && !matches!(body.value.kind, hir::ExprKind::Block(..))
+                        {
+                            // Check if the failed predicate was an expectation of a closure type
+                            // and if there might have been a `{ |args|` typo instead of `|args| {`.
+                            err.multipart_suggestion(
+                                "you might have meant to open the closure body instead of placing \
+                                 a closure within a block",
+                                vec![
+                                    (expr.span.with_hi(value.span.lo()), String::new()),
+                                    (fn_decl_span.shrink_to_hi(), " {".to_string()),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+                        } else {
+                            // Maybe the bare block was meant to be a closure.
+                            err.span_suggestion_verbose(
+                                expr.span.shrink_to_lo(),
+                                "you might have meant to create the closure instead of a block",
+                                format!(
+                                    "|{}| ",
+                                    (0..pred.trait_ref.args.len() - 1).map(|_| "_")
+                                        .collect::<Vec<_>>()
+                                        .join(", ")),
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
                 }
             }
 
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index a79a204e2c6..7ef84b0f5b5 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -909,6 +909,10 @@ pub fn take<T: Default>(dest: &mut T) -> T {
 #[rustc_const_unstable(feature = "const_replace", issue = "83164")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "mem_replace")]
 pub const fn replace<T>(dest: &mut T, src: T) -> T {
+    // It may be tempting to use `swap` to avoid `unsafe` here. Don't!
+    // The compiler optimizes the implementation below to two `memcpy`s
+    // while `swap` would require at least three. See PR#83022 for details.
+
     // SAFETY: We read from `dest` but directly write `src` into it afterwards,
     // such that the old value is not duplicated. Nothing is dropped and
     // nothing here can panic.
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 246d742a99f..27eac212b14 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -3000,7 +3000,10 @@ impl Step for CodegenCranelift {
 
         let triple = run.target.triple;
         let target_supported = if triple.contains("linux") {
-            triple.contains("x86_64") || triple.contains("aarch64") || triple.contains("s390x")
+            triple.contains("x86_64")
+                || triple.contains("aarch64")
+                || triple.contains("s390x")
+                || triple.contains("riscv64gc")
         } else if triple.contains("darwin") || triple.contains("windows") {
             triple.contains("x86_64")
         } else {
diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs
new file mode 100644
index 00000000000..9c685590b8b
--- /dev/null
+++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs
@@ -0,0 +1,10 @@
+fn main() {
+    let number = 2;
+    Some(true).filter({ //~ ERROR expected a `FnOnce<(&bool,)>` closure, found `bool`
+        if number % 2 == 0 {
+            number == 0
+        } else {
+            number != 0
+        }
+    });
+}
diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr
new file mode 100644
index 00000000000..b72360562f2
--- /dev/null
+++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr
@@ -0,0 +1,27 @@
+error[E0277]: expected a `FnOnce<(&bool,)>` closure, found `bool`
+  --> $DIR/block_instead_of_closure_in_arg.rs:3:23
+   |
+LL |        Some(true).filter({
+   |  _________________------_^
+   | |                 |
+   | |                 required by a bound introduced by this call
+LL | |/         if number % 2 == 0 {
+LL | ||             number == 0
+LL | ||         } else {
+LL | ||             number != 0
+LL | ||         }
+   | ||_________- this tail expression is of type `bool`
+LL | |      });
+   | |______^ expected an `FnOnce<(&bool,)>` closure, found `bool`
+   |
+   = help: the trait `for<'a> FnOnce<(&'a bool,)>` is not implemented for `bool`
+note: required by a bound in `Option::<T>::filter`
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+help: you might have meant to create the closure instead of a block
+   |
+LL |     Some(true).filter(|_| {
+   |                       +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs
new file mode 100644
index 00000000000..982b9fd00f6
--- /dev/null
+++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs
@@ -0,0 +1,7 @@
+const x: usize =42;
+fn main() {
+    let p = Some(45).and_then({|x| //~ ERROR expected a `FnOnce<({integer},)>` closure, found `Option<usize>`
+        1 + 1;
+        Some(x * 2)
+    });
+}
diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr
new file mode 100644
index 00000000000..32d34430b7c
--- /dev/null
+++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr
@@ -0,0 +1,25 @@
+error[E0277]: expected a `FnOnce<({integer},)>` closure, found `Option<usize>`
+  --> $DIR/ruby_style_closure_successful_parse.rs:3:31
+   |
+LL |       let p = Some(45).and_then({|x|
+   |  ______________________--------_^
+   | |                      |
+   | |                      required by a bound introduced by this call
+LL | |         1 + 1;
+LL | |         Some(x * 2)
+   | |         ----------- this tail expression is of type `Option<usize>`
+LL | |     });
+   | |_____^ expected an `FnOnce<({integer},)>` closure, found `Option<usize>`
+   |
+   = help: the trait `FnOnce<({integer},)>` is not implemented for `Option<usize>`
+note: required by a bound in `Option::<T>::and_then`
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+help: you might have meant to open the closure body instead of placing a closure within a block
+   |
+LL -     let p = Some(45).and_then({|x|
+LL +     let p = Some(45).and_then(|x| {
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs
index 1e9e7a89dde..f1bbd4a6210 100644
--- a/tests/ui/macros/stringify.rs
+++ b/tests/ui/macros/stringify.rs
@@ -2,496 +2,441 @@
 // edition:2021
 // compile-flags: --test
 
+#![allow(incomplete_features)]
 #![feature(async_closure)]
 #![feature(auto_traits)]
 #![feature(box_patterns)]
 #![feature(const_trait_impl)]
-#![feature(decl_macro)]
 #![feature(coroutines)]
+#![feature(decl_macro)]
+#![feature(explicit_tail_calls)]
 #![feature(more_qualified_paths)]
 #![feature(raw_ref_op)]
 #![feature(trait_alias)]
 #![feature(try_blocks)]
 #![feature(type_ascription)]
+#![feature(yeet_expr)]
 #![deny(unused_macros)]
 
-macro_rules! stringify_block {
-    ($block:block) => {
-        stringify!($block)
-    };
-}
-
-macro_rules! stringify_expr {
-    ($expr:expr) => {
-        stringify!($expr)
-    };
-}
-
-macro_rules! stringify_item {
-    ($item:item) => {
-        stringify!($item)
-    };
-}
-
-macro_rules! stringify_meta {
-    ($meta:meta) => {
-        stringify!($meta)
-    };
-}
-
-macro_rules! stringify_pat {
-    ($pat:pat) => {
-        stringify!($pat)
-    };
-}
-
-macro_rules! stringify_path {
-    ($path:path) => {
-        stringify!($path)
-    };
-}
-
-macro_rules! stringify_stmt {
-    ($stmt:stmt) => {
-        stringify!($stmt)
-    };
-}
-
-macro_rules! stringify_ty {
-    ($ty:ty) => {
-        stringify!($ty)
+// These macros force the use of AST pretty-printing by converting the input to
+// a particular fragment specifier.
+macro_rules! block { ($block:block) => { stringify!($block) }; }
+macro_rules! expr { ($expr:expr) => { stringify!($expr) }; }
+macro_rules! item { ($item:item) => { stringify!($item) }; }
+macro_rules! meta { ($meta:meta) => { stringify!($meta) }; }
+macro_rules! pat { ($pat:pat) => { stringify!($pat) }; }
+macro_rules! path { ($path:path) => { stringify!($path) }; }
+macro_rules! stmt { ($stmt:stmt) => { stringify!($stmt) }; }
+macro_rules! ty { ($ty:ty) => { stringify!($ty) }; }
+macro_rules! vis { ($vis:vis) => { stringify!($vis) }; }
+
+// Use this when AST pretty-printing and TokenStream pretty-printing give
+// the same result (which is preferable.)
+macro_rules! c1 {
+    ($frag:ident, [$($tt:tt)*], $s:literal) => {
+        assert_eq!($frag!($($tt)*), $s);
+        assert_eq!(stringify!($($tt)*), $s);
     };
 }
 
-macro_rules! stringify_vis {
-    ($vis:vis) => {
-        stringify!($vis)
+// Use this when AST pretty-printing and TokenStream pretty-printing give
+// different results.
+//
+// `c1` and `c2` could be in a single macro, but having them separate makes it
+// easy to find the cases where the two pretty-printing approaches give
+// different results.
+macro_rules! c2 {
+    ($frag:ident, [$($tt:tt)*], $s1:literal, $s2:literal) => {
+        assert_ne!($s1, $s2, "should use `c1!` instead");
+        assert_eq!($frag!($($tt)*), $s1);
+        assert_eq!(stringify!($($tt)*), $s2);
     };
 }
 
 #[test]
 fn test_block() {
-    assert_eq!(stringify_block!({}), "{}");
-    assert_eq!(stringify_block!({ true }), "{ true }");
-    assert_eq!(stringify_block!({ return }), "{ return }");
-    assert_eq!(
-        stringify_block!({
+    c1!(block, [ {} ], "{}");
+    c1!(block, [ { true } ], "{ true }");
+    c1!(block, [ { return } ], "{ return }");
+    c2!(block, [ {
             return;
-        }),
+        } ],
         "{ return; }",
+        "{ return ; }"
     );
-    assert_eq!(
-        stringify_block!({
+    c2!(block,
+        [ {
             let _;
             true
-        }),
+        } ],
         "{ let _; true }",
+        "{ let _ ; true }"
     );
 }
 
 #[test]
 fn test_expr() {
     // ExprKind::Array
-    assert_eq!(stringify_expr!([]), "[]");
-    assert_eq!(stringify_expr!([true]), "[true]");
-    assert_eq!(stringify_expr!([true,]), "[true]");
-    assert_eq!(stringify_expr!([true, true]), "[true, true]");
+    c1!(expr, [ [] ], "[]");
+    c1!(expr, [ [true] ], "[true]");
+    c2!(expr, [ [true,] ], "[true]", "[true,]");
+    c1!(expr, [ [true, true] ], "[true, true]");
+
+    // ExprKind::ConstBlock
+    // FIXME: todo
 
     // ExprKind::Call
-    assert_eq!(stringify_expr!(f()), "f()");
-    assert_eq!(stringify_expr!(f::<u8>()), "f::<u8>()");
-    assert_eq!(stringify_expr!(f::<1>()), "f::<1>()");
-    assert_eq!(stringify_expr!(f::<'a, u8, 1>()), "f::<'a, u8, 1>()");
-    assert_eq!(stringify_expr!(f(true)), "f(true)");
-    assert_eq!(stringify_expr!(f(true,)), "f(true)");
-    assert_eq!(stringify_expr!(()()), "()()");
+    c1!(expr, [ f() ], "f()");
+    c2!(expr, [ f::<u8>() ], "f::<u8>()", "f :: < u8 > ()");
+    c2!(expr, [ f::<1>() ], "f::<1>()", "f :: < 1 > ()");
+    c2!(expr, [ f::<'a, u8, 1>() ], "f::<'a, u8, 1>()", "f :: < 'a, u8, 1 > ()");
+    c1!(expr, [ f(true) ], "f(true)");
+    c2!(expr, [ f(true,) ], "f(true)", "f(true,)");
+    c2!(expr, [ ()() ], "()()", "() ()");
 
     // ExprKind::MethodCall
-    assert_eq!(stringify_expr!(x.f()), "x.f()");
-    assert_eq!(stringify_expr!(x.f::<u8>()), "x.f::<u8>()");
+    c1!(expr, [ x.f() ], "x.f()");
+    c2!(expr, [ x.f::<u8>() ], "x.f::<u8>()", "x.f :: < u8 > ()");
+    c2!(expr, [ x.collect::<Vec<_>>() ], "x.collect::<Vec<_>>()", "x.collect :: < Vec < _ >> ()");
 
     // ExprKind::Tup
-    assert_eq!(stringify_expr!(()), "()");
-    assert_eq!(stringify_expr!((true,)), "(true,)");
-    assert_eq!(stringify_expr!((true, false)), "(true, false)");
-    assert_eq!(stringify_expr!((true, false,)), "(true, false)");
+    c1!(expr, [ () ], "()");
+    c1!(expr, [ (true,) ], "(true,)");
+    c1!(expr, [ (true, false) ], "(true, false)");
+    c2!(expr, [ (true, false,) ], "(true, false)", "(true, false,)");
 
     // ExprKind::Binary
-    assert_eq!(stringify_expr!(true || false), "true || false");
-    assert_eq!(stringify_expr!(true || false && false), "true || false && false");
+    c1!(expr, [ true || false ], "true || false");
+    c1!(expr, [ true || false && false ], "true || false && false");
+    c1!(expr, [ a < 1 && 2 < b && c > 3 && 4 > d ], "a < 1 && 2 < b && c > 3 && 4 > d");
+    c2!(expr, [ a & b & !c ], "a & b & !c", "a & b &! c"); // FIXME
+    c2!(expr,
+        [ a + b * c - d + -1 * -2 - -3],
+        "a + b * c - d + -1 * -2 - -3",
+        "a + b * c - d + - 1 * - 2 - - 3"
+    );
 
     // ExprKind::Unary
-    assert_eq!(stringify_expr!(*expr), "*expr");
-    assert_eq!(stringify_expr!(!expr), "!expr");
-    assert_eq!(stringify_expr!(-expr), "-expr");
+    c2!(expr, [ *expr ], "*expr", "* expr");
+    c2!(expr, [ !expr ], "!expr", "! expr");
+    c2!(expr, [ -expr ], "-expr", "- expr");
 
     // ExprKind::Lit
-    assert_eq!(stringify_expr!('x'), "'x'");
-    assert_eq!(stringify_expr!(1_000_i8), "1_000_i8");
-    assert_eq!(stringify_expr!(1.00000000000000001), "1.00000000000000001");
+    c1!(expr, [ 'x' ], "'x'");
+    c1!(expr, [ 1_000_i8 ], "1_000_i8");
+    c1!(expr, [ 1.00000000000000001 ], "1.00000000000000001");
 
     // ExprKind::Cast
-    assert_eq!(stringify_expr!(expr as T), "expr as T");
-    assert_eq!(stringify_expr!(expr as T<u8>), "expr as T<u8>");
+    c1!(expr, [ expr as T ], "expr as T");
+    c2!(expr, [ expr as T<u8> ], "expr as T<u8>", "expr as T < u8 >");
 
-    // ExprKind::Type
-    // There is no syntax for type ascription.
+    // ExprKind::Type: there is no syntax for type ascription.
+
+    // ExprKind::Let
+    c1!(expr, [ if let Some(a) = b { c } else { d } ], "if let Some(a) = b { c } else { d }");
 
     // ExprKind::If
-    assert_eq!(stringify_expr!(if true {}), "if true {}");
-    assert_eq!(
-        stringify_expr!(if true {
-        } else {
-        }),
-        "if true {} else {}",
-    );
-    assert_eq!(
-        stringify_expr!(if let true = true {
-        } else {
-        }),
-        "if let true = true {} else {}",
-    );
-    assert_eq!(
-        stringify_expr!(if true {
+    c1!(expr, [ if true {} ], "if true {}");
+    c2!(expr,
+        [ if ::std::blah() { } else { } ],
+        "if ::std::blah() {} else {}",
+        "if :: std :: blah() {} else {}"
+    );
+    c1!(expr, [ if let true = true {} else {} ], "if let true = true {} else {}");
+    c1!(expr,
+        [ if true {
         } else if false {
-        }),
-        "if true {} else if false {}",
+        } ],
+        "if true {} else if false {}"
     );
-    assert_eq!(
-        stringify_expr!(if true {
+    c1!(expr,
+        [ if true {
         } else if false {
         } else {
-        }),
-        "if true {} else if false {} else {}",
+        } ],
+        "if true {} else if false {} else {}"
     );
-    assert_eq!(
-        stringify_expr!(if true {
+    c2!(expr,
+        [ if true {
             return;
         } else if false {
             0
         } else {
             0
-        }),
+        } ],
         "if true { return; } else if false { 0 } else { 0 }",
+        "if true { return ; } else if false { 0 } else { 0 }"
     );
 
     // ExprKind::While
-    assert_eq!(stringify_expr!(while true {}), "while true {}");
-    assert_eq!(stringify_expr!('a: while true {}), "'a: while true {}");
-    assert_eq!(stringify_expr!(while let true = true {}), "while let true = true {}");
+    c1!(expr, [ while true {} ], "while true {}");
+    c2!(expr, [ 'a: while true {} ], "'a: while true {}", "'a : while true {}");
+    c1!(expr, [ while let true = true {} ], "while let true = true {}");
 
     // ExprKind::ForLoop
-    assert_eq!(stringify_expr!(for _ in x {}), "for _ in x {}");
-    assert_eq!(stringify_expr!('a: for _ in x {}), "'a: for _ in x {}");
+    c1!(expr, [ for _ in x {} ], "for _ in x {}");
+    c2!(expr, [ 'a: for _ in x {} ], "'a: for _ in x {}", "'a : for _ in x {}");
 
     // ExprKind::Loop
-    assert_eq!(stringify_expr!(loop {}), "loop {}");
-    assert_eq!(stringify_expr!('a: loop {}), "'a: loop {}");
+    c1!(expr, [ loop {} ], "loop {}");
+    c2!(expr, [ 'a: loop {} ], "'a: loop {}", "'a : loop {}");
 
     // ExprKind::Match
-    assert_eq!(stringify_expr!(match self {}), "match self {}");
-    assert_eq!(
-        stringify_expr!(match self {
+    c1!(expr, [ match self {} ], "match self {}");
+    c1!(expr,
+        [ match self {
             Ok => 1,
-        }),
-        "match self { Ok => 1, }",
+        } ],
+        "match self { Ok => 1, }"
     );
-    assert_eq!(
-        stringify_expr!(match self {
+    c1!(expr,
+        [ match self {
             Ok => 1,
             Err => 0,
-        }),
-        "match self { Ok => 1, Err => 0, }",
+        } ],
+        "match self { Ok => 1, Err => 0, }"
     );
 
     // ExprKind::Closure
-    assert_eq!(stringify_expr!(|| {}), "|| {}");
-    assert_eq!(stringify_expr!(|x| {}), "|x| {}");
-    assert_eq!(stringify_expr!(|x: u8| {}), "|x: u8| {}");
-    assert_eq!(stringify_expr!(|| ()), "|| ()");
-    assert_eq!(stringify_expr!(move || self), "move || self");
-    assert_eq!(stringify_expr!(async || self), "async || self");
-    assert_eq!(stringify_expr!(async move || self), "async move || self");
-    assert_eq!(stringify_expr!(static || self), "static || self");
-    assert_eq!(stringify_expr!(static move || self), "static move || self");
-    #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5149
-    assert_eq!(
-        stringify_expr!(static async || self),
-        "static async || self",
-    );
-    #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5149
-    assert_eq!(
-        stringify_expr!(static async move || self),
-        "static async move || self",
-    );
-    assert_eq!(stringify_expr!(|| -> u8 { self }), "|| -> u8 { self }");
-    assert_eq!(stringify_expr!(1 + || {}), "1 + (|| {})"); // ??
+    c1!(expr, [ || {} ], "|| {}");
+    c2!(expr, [ |x| {} ], "|x| {}", "| x | {}");
+    c2!(expr, [ |x: u8| {} ], "|x: u8| {}", "| x : u8 | {}");
+    c1!(expr, [ || () ], "|| ()");
+    c1!(expr, [ move || self ], "move || self");
+    c1!(expr, [ async || self ], "async || self");
+    c1!(expr, [ async move || self ], "async move || self");
+    c1!(expr, [ static || self ], "static || self");
+    c1!(expr, [ static move || self ], "static move || self");
+    c1!(expr, [ static async || self ], "static async || self");
+    c1!(expr, [ static async move || self ], "static async move || self");
+    c1!(expr, [ || -> u8 { self } ], "|| -> u8 { self }");
+    c2!(expr, [ 1 + || {} ], "1 + (|| {})", "1 + || {}"); // AST??
 
     // ExprKind::Block
-    assert_eq!(stringify_expr!({}), "{}");
-    assert_eq!(stringify_expr!(unsafe {}), "unsafe {}");
-    assert_eq!(stringify_expr!('a: {}), "'a: {}");
-    assert_eq!(
-        stringify_expr!(
-            #[attr]
-            {}
-        ),
-        "#[attr] {}",
-    );
-    assert_eq!(
-        stringify_expr!(
+    c1!(expr, [ {} ], "{}");
+    c1!(expr, [ unsafe {} ], "unsafe {}");
+    c2!(expr, [ 'a: {} ], "'a: {}", "'a : {}");
+    c1!(expr, [ #[attr] {} ], "#[attr] {}");
+    c2!(expr,
+        [
             {
                 #![attr]
             }
-        ),
+        ],
         "{\n\
         \x20   #![attr]\n\
         }",
+        "{ #! [attr] }"
     );
 
     // ExprKind::Async
-    assert_eq!(stringify_expr!(async {}), "async {}");
-    assert_eq!(stringify_expr!(async move {}), "async move {}");
+    c1!(expr, [ async {} ], "async {}");
+    c1!(expr, [ async move {} ], "async move {}");
 
     // ExprKind::Await
-    assert_eq!(stringify_expr!(expr.await), "expr.await");
+    c1!(expr, [ expr.await ], "expr.await");
 
     // ExprKind::TryBlock
-    assert_eq!(stringify_expr!(try {}), "try {}");
+    c1!(expr, [ try {} ], "try {}");
 
     // ExprKind::Assign
-    assert_eq!(stringify_expr!(expr = true), "expr = true");
+    c1!(expr, [ expr = true ], "expr = true");
 
     // ExprKind::AssignOp
-    assert_eq!(stringify_expr!(expr += true), "expr += true");
+    c1!(expr, [ expr += true ], "expr += true");
 
     // ExprKind::Field
-    assert_eq!(stringify_expr!(expr.field), "expr.field");
-    assert_eq!(stringify_expr!(expr.0), "expr.0");
+    c1!(expr, [ expr.field ], "expr.field");
+    c1!(expr, [ expr.0 ], "expr.0");
 
     // ExprKind::Index
-    assert_eq!(stringify_expr!(expr[true]), "expr[true]");
+    c2!(expr, [ expr[true] ], "expr[true]", "expr [true]");
 
     // ExprKind::Range
-    assert_eq!(stringify_expr!(..), "..");
-    assert_eq!(stringify_expr!(..hi), "..hi");
-    assert_eq!(stringify_expr!(lo..), "lo..");
-    assert_eq!(stringify_expr!(lo..hi), "lo..hi");
-    assert_eq!(stringify_expr!(..=hi), "..=hi");
-    assert_eq!(stringify_expr!(lo..=hi), "lo..=hi");
-    assert_eq!(stringify_expr!(-2..=-1), "-2..=-1");
+    c1!(expr, [ .. ], "..");
+    c2!(expr, [ ..hi ], "..hi", ".. hi");
+    c2!(expr, [ lo.. ], "lo..", "lo ..");
+    c2!(expr, [ lo..hi ], "lo..hi", "lo .. hi");
+    c2!(expr, [ ..=hi ], "..=hi", "..= hi");
+    c2!(expr, [ lo..=hi ], "lo..=hi", "lo ..= hi");
+    c2!(expr, [ -2..=-1 ], "-2..=-1", "- 2 ..= - 1");
+
+    // ExprKind::Underscore
+    // FIXME: todo
 
     // ExprKind::Path
-    assert_eq!(stringify_expr!(thing), "thing");
-    assert_eq!(stringify_expr!(m::thing), "m::thing");
-    assert_eq!(stringify_expr!(self::thing), "self::thing");
-    assert_eq!(stringify_expr!(crate::thing), "crate::thing");
-    assert_eq!(stringify_expr!(Self::thing), "Self::thing");
-    assert_eq!(stringify_expr!(<Self as T>::thing), "<Self as T>::thing");
-    assert_eq!(stringify_expr!(Self::<'static>), "Self::<'static>");
+    c1!(expr, [ thing ], "thing");
+    c2!(expr, [ m::thing ], "m::thing", "m :: thing");
+    c2!(expr, [ self::thing ], "self::thing", "self :: thing");
+    c2!(expr, [ crate::thing ], "crate::thing", "crate :: thing");
+    c2!(expr, [ Self::thing ], "Self::thing", "Self :: thing");
+    c2!(expr, [ <Self as T>::thing ], "<Self as T>::thing", "< Self as T > :: thing");
+    c2!(expr, [ Self::<'static> ], "Self::<'static>", "Self :: < 'static >");
 
     // ExprKind::AddrOf
-    assert_eq!(stringify_expr!(&expr), "&expr");
-    assert_eq!(stringify_expr!(&mut expr), "&mut expr");
-    assert_eq!(stringify_expr!(&raw const expr), "&raw const expr");
-    assert_eq!(stringify_expr!(&raw mut expr), "&raw mut expr");
+    c2!(expr, [ &expr ], "&expr", "& expr");
+    c2!(expr, [ &mut expr ], "&mut expr", "& mut expr");
+    c2!(expr, [ &raw const expr ], "&raw const expr", "& raw const expr");
+    c2!(expr, [ &raw mut expr ], "&raw mut expr", "& raw mut expr");
 
     // ExprKind::Break
-    assert_eq!(stringify_expr!(break), "break");
-    assert_eq!(stringify_expr!(break 'a), "break 'a");
-    assert_eq!(stringify_expr!(break true), "break true");
-    assert_eq!(stringify_expr!(break 'a true), "break 'a true");
+    c1!(expr, [ break ], "break");
+    c1!(expr, [ break 'a ], "break 'a");
+    c1!(expr, [ break true ], "break true");
+    c1!(expr, [ break 'a true ], "break 'a true");
 
     // ExprKind::Continue
-    assert_eq!(stringify_expr!(continue), "continue");
-    assert_eq!(stringify_expr!(continue 'a), "continue 'a");
+    c1!(expr, [ continue ], "continue");
+    c1!(expr, [ continue 'a ], "continue 'a");
 
     // ExprKind::Ret
-    assert_eq!(stringify_expr!(return), "return");
-    assert_eq!(stringify_expr!(return true), "return true");
+    c1!(expr, [ return ], "return");
+    c1!(expr, [ return true ], "return true");
+
+    // ExprKind::InlineAsm: untestable because this test works pre-expansion.
+
+    // ExprKind::OffsetOf: untestable because this test works pre-expansion.
 
     // ExprKind::MacCall
-    assert_eq!(stringify_expr!(mac!(...)), "mac!(...)");
-    assert_eq!(stringify_expr!(mac![...]), "mac![...]");
-    assert_eq!(stringify_expr!(mac! { ... }), "mac! { ... }");
+    c2!(expr, [ mac!(...) ], "mac!(...)", "mac! (...)");
+    c2!(expr, [ mac![...] ], "mac![...]", "mac! [...]");
+    c1!(expr, [ mac! { ... } ], "mac! { ... }");
 
     // ExprKind::Struct
-    assert_eq!(stringify_expr!(Struct {}), "Struct {}");
-    #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5151
-    assert_eq!(stringify_expr!(<Struct as Trait>::Type {}), "<Struct as Trait>::Type {}");
-    assert_eq!(stringify_expr!(Struct { .. }), "Struct { .. }");
-    assert_eq!(stringify_expr!(Struct { ..base }), "Struct { ..base }");
-    assert_eq!(stringify_expr!(Struct { x }), "Struct { x }");
-    assert_eq!(stringify_expr!(Struct { x, .. }), "Struct { x, .. }");
-    assert_eq!(stringify_expr!(Struct { x, ..base }), "Struct { x, ..base }");
-    assert_eq!(stringify_expr!(Struct { x: true }), "Struct { x: true }");
-    assert_eq!(stringify_expr!(Struct { x: true, .. }), "Struct { x: true, .. }");
-    assert_eq!(stringify_expr!(Struct { x: true, ..base }), "Struct { x: true, ..base }");
+    c1!(expr, [ Struct {} ], "Struct {}");
+    c2!(expr,
+        [ <Struct as Trait>::Type {} ],
+        "<Struct as Trait>::Type {}",
+        "< Struct as Trait > :: Type {}"
+    );
+    c1!(expr, [ Struct { .. } ], "Struct { .. }");
+    c2!(expr, [ Struct { ..base } ], "Struct { ..base }", "Struct { .. base }");
+    c1!(expr, [ Struct { x } ], "Struct { x }");
+    c1!(expr, [ Struct { x, .. } ], "Struct { x, .. }");
+    c2!(expr, [ Struct { x, ..base } ], "Struct { x, ..base }", "Struct { x, .. base }");
+    c2!(expr, [ Struct { x: true } ], "Struct { x: true }", "Struct { x : true }");
+    c2!(expr, [ Struct { x: true, .. } ], "Struct { x: true, .. }", "Struct { x : true, .. }");
+    c2!(expr,
+        [ Struct { x: true, ..base } ],
+        "Struct { x: true, ..base }",
+        "Struct { x : true, .. base }"
+    );
 
     // ExprKind::Repeat
-    assert_eq!(stringify_expr!([(); 0]), "[(); 0]");
+    c2!(expr, [ [(); 0] ], "[(); 0]", "[() ; 0]");
 
     // ExprKind::Paren
-    assert_eq!(stringify_expr!((expr)), "(expr)");
+    c1!(expr, [ (expr) ], "(expr)");
 
     // ExprKind::Try
-    assert_eq!(stringify_expr!(expr?), "expr?");
+    c2!(expr, [ expr? ], "expr?", "expr ?");
 
     // ExprKind::Yield
-    assert_eq!(stringify_expr!(yield), "yield");
-    assert_eq!(stringify_expr!(yield true), "yield true");
+    c1!(expr, [ yield ], "yield");
+    c1!(expr, [ yield true ], "yield true");
+
+    // ExprKind::Yeet
+    c1!(expr, [ do yeet ], "do yeet");
+    c1!(expr, [ do yeet 0 ], "do yeet 0");
+
+    // ExprKind::Become
+    // FIXME: todo
+
+    // ExprKind::IncludedBytes
+    // FIXME: todo
+
+    // ExprKind::FormatArgs: untestable because this test works pre-expansion.
+
+    // ExprKind::Err: untestable.
 }
 
 #[test]
 fn test_item() {
     // ItemKind::ExternCrate
-    assert_eq!(
-        stringify_item!(
-            extern crate std;
-        ),
-        "extern crate std;",
-    );
-    assert_eq!(
-        stringify_item!(
-            pub extern crate self as std;
-        ),
+    c2!(item, [ extern crate std; ], "extern crate std;", "extern crate std ;");
+    c2!(item,
+        [ pub extern crate self as std; ],
         "pub extern crate self as std;",
+        "pub extern crate self as std ;"
     );
 
     // ItemKind::Use
-    assert_eq!(
-        stringify_item!(
-            pub use crate::{a, b::c};
-        ),
+    c2!(item,
+        [ pub use crate::{a, b::c}; ],
         "pub use crate::{a, b::c};",
+        "pub use crate :: { a, b :: c } ;"
     );
+    c2!(item, [ pub use A::*; ], "pub use A::*;", "pub use A :: * ;");
 
     // ItemKind::Static
-    assert_eq!(
-        stringify_item!(
-            pub static S: () = {};
-        ),
-        "pub static S: () = {};",
-    );
-    assert_eq!(
-        stringify_item!(
-            static mut S: () = {};
-        ),
-        "static mut S: () = {};",
-    );
-    assert_eq!(
-        stringify_item!(
-            static S: ();
-        ),
-        "static S: ();",
-    );
-    assert_eq!(
-        stringify_item!(
-            static mut S: ();
-        ),
-        "static mut S: ();",
-    );
+    c2!(item, [ pub static S: () = {}; ], "pub static S: () = {};", "pub static S : () = {} ;");
+    c2!(item, [ static mut S: () = {}; ], "static mut S: () = {};", "static mut S : () = {} ;");
+    c2!(item, [ static S: (); ], "static S: ();", "static S : () ;");
+    c2!(item, [ static mut S: (); ], "static mut S: ();", "static mut S : () ;");
 
     // ItemKind::Const
-    assert_eq!(
-        stringify_item!(
-            pub const S: () = {};
-        ),
-        "pub const S: () = {};",
-    );
-    assert_eq!(
-        stringify_item!(
-            const S: ();
-        ),
-        "const S: ();",
-    );
+    c2!(item, [ pub const S: () = {}; ], "pub const S: () = {};", "pub const S : () = {} ;");
+    c2!(item, [ const S: (); ], "const S: ();", "const S : () ;");
 
     // ItemKind::Fn
-    assert_eq!(
-        stringify_item!(
-            pub default const async unsafe extern "C" fn f() {}
-        ),
-        "pub default const async unsafe extern \"C\" fn f() {}",
+    c1!(item,
+        [ pub default const async unsafe extern "C" fn f() {} ],
+        "pub default const async unsafe extern \"C\" fn f() {}"
+    );
+    c2!(item,
+        [ fn g<T>(t: Vec<Vec<Vec<T>>>) {} ],
+        "fn g<T>(t: Vec<Vec<Vec<T>>>) {}",
+        "fn g < T > (t : Vec < Vec < Vec < T >> >) {}"
+    );
+    c2!(item,
+        [ fn h<'a>(t: &'a Vec<Cell<dyn D>>) {} ],
+        "fn h<'a>(t: &'a Vec<Cell<dyn D>>) {}",
+        "fn h < 'a > (t : & 'a Vec < Cell < dyn D >>) {}"
     );
 
     // ItemKind::Mod
-    assert_eq!(
-        stringify_item!(
-            pub mod m;
-        ),
-        "pub mod m;",
-    );
-    assert_eq!(
-        stringify_item!(
-            mod m {}
-        ),
-        "mod m {}",
-    );
-    assert_eq!(
-        stringify_item!(
-            unsafe mod m;
-        ),
-        "unsafe mod m;",
-    );
-    assert_eq!(
-        stringify_item!(
-            unsafe mod m {}
-        ),
-        "unsafe mod m {}",
-    );
+    c2!(item, [ pub mod m; ], "pub mod m;", "pub mod m ;");
+    c1!(item, [ mod m {} ], "mod m {}");
+    c2!(item, [ unsafe mod m; ], "unsafe mod m;", "unsafe mod m ;");
+    c1!(item, [ unsafe mod m {} ], "unsafe mod m {}");
 
     // ItemKind::ForeignMod
-    assert_eq!(
-        stringify_item!(
-            extern "C" {}
-        ),
-        "extern \"C\" {}",
-    );
-    #[rustfmt::skip]
-    assert_eq!(
-        stringify_item!(
-            pub extern "C" {}
-        ),
-        "extern \"C\" {}",
-    );
-    assert_eq!(
-        stringify_item!(
-            unsafe extern "C++" {}
-        ),
-        "unsafe extern \"C++\" {}",
+    c1!(item, [ extern "C" {} ], "extern \"C\" {}");
+    c2!(item,
+        [ pub extern "C" {} ],
+        "extern \"C\" {}", // ??
+        "pub extern \"C\" {}"
     );
+    c1!(item, [ unsafe extern "C++" {} ], "unsafe extern \"C++\" {}");
+
+    // ItemKind::GlobalAsm: untestable because this test works pre-expansion.
 
     // ItemKind::TyAlias
-    #[rustfmt::skip]
-    assert_eq!(
-        stringify_item!(
+    c2!(item,
+        [
             pub default type Type<'a>: Bound
             where
                 Self: 'a,
             = T;
-        ),
+        ],
         "pub default type Type<'a>: Bound where Self: 'a = T;",
+        "pub default type Type < 'a > : Bound where Self : 'a, = T ;"
     );
 
     // ItemKind::Enum
-    assert_eq!(
-        stringify_item!(
-            pub enum Void {}
-        ),
-        "pub enum Void {}",
-    );
-    assert_eq!(
-        stringify_item!(
+    c1!(item, [ pub enum Void {} ], "pub enum Void {}");
+    c1!(item,
+        [
             enum Empty {
                 Unit,
                 Tuple(),
                 Struct {},
             }
-        ),
-        "enum Empty { Unit, Tuple(), Struct {}, }",
+        ],
+        "enum Empty { Unit, Tuple(), Struct {}, }"
     );
-    assert_eq!(
-        stringify_item!(
+    c2!(item,
+        [
             enum Enum<T>
             where
                 T: 'a,
@@ -500,7 +445,7 @@ fn test_item() {
                 Tuple(T),
                 Struct { t: T },
             }
-        ),
+        ],
         "enum Enum<T> where T: 'a {\n\
         \x20   Unit,\n\
         \x20   Tuple(T),\n\
@@ -508,378 +453,349 @@ fn test_item() {
         \x20       t: T,\n\
         \x20   },\n\
         }",
+        "enum Enum < T > where T : 'a, { Unit, Tuple(T), Struct { t : T }, }"
     );
 
     // ItemKind::Struct
-    assert_eq!(
-        stringify_item!(
-            pub struct Unit;
-        ),
-        "pub struct Unit;",
-    );
-    assert_eq!(
-        stringify_item!(
-            struct Tuple();
-        ),
-        "struct Tuple();",
-    );
-    assert_eq!(
-        stringify_item!(
-            struct Tuple(T);
-        ),
-        "struct Tuple(T);",
-    );
-    assert_eq!(
-        stringify_item!(
-            struct Struct {}
-        ),
-        "struct Struct {}",
-    );
-    assert_eq!(
-        stringify_item!(
+    c2!(item, [ pub struct Unit; ], "pub struct Unit;", "pub struct Unit ;");
+    c2!(item, [ struct Tuple(); ], "struct Tuple();", "struct Tuple() ;");
+    c2!(item, [ struct Tuple(T); ], "struct Tuple(T);", "struct Tuple(T) ;");
+    c1!(item, [ struct Struct {} ], "struct Struct {}");
+    c2!(item,
+        [
             struct Struct<T>
             where
                 T: 'a,
             {
                 t: T,
             }
-        ),
+        ],
         "struct Struct<T> where T: 'a {\n\
         \x20   t: T,\n\
         }",
+        "struct Struct < T > where T : 'a, { t : T, }"
     );
 
     // ItemKind::Union
-    assert_eq!(
-        stringify_item!(
-            pub union Union {}
-        ),
-        "pub union Union {}",
-    );
-    assert_eq!(
-        stringify_item!(
+    c1!(item, [ pub union Union {} ], "pub union Union {}");
+    c2!(item,
+        [
             union Union<T> where T: 'a {
                 t: T,
             }
-        ),
+        ],
         "union Union<T> where T: 'a {\n\
         \x20   t: T,\n\
         }",
+        "union Union < T > where T : 'a { t : T, }"
     );
 
     // ItemKind::Trait
-    assert_eq!(
-        stringify_item!(
-            pub unsafe auto trait Send {}
-        ),
-        "pub unsafe auto trait Send {}",
-    );
-    assert_eq!(
-        stringify_item!(
+    c1!(item, [ pub unsafe auto trait Send {} ], "pub unsafe auto trait Send {}");
+    c2!(item,
+        [
             trait Trait<'a>: Sized
             where
                 Self: 'a,
             {
             }
-        ),
+        ],
         "trait Trait<'a>: Sized where Self: 'a {}",
+        "trait Trait < 'a > : Sized where Self : 'a, {}"
     );
 
     // ItemKind::TraitAlias
-    assert_eq!(
-        stringify_item!(
-            pub trait Trait<T> = Sized where T: 'a;
-        ),
+    c2!(item,
+        [ pub trait Trait<T> = Sized where T: 'a; ],
         "pub trait Trait<T> = Sized where T: 'a;",
+        "pub trait Trait < T > = Sized where T : 'a ;"
     );
 
     // ItemKind::Impl
-    assert_eq!(
-        stringify_item!(
-            pub impl Struct {}
-        ),
-        "pub impl Struct {}",
-    );
-    assert_eq!(
-        stringify_item!(
-            impl<T> Struct<T> {}
-        ),
-        "impl<T> Struct<T> {}",
-    );
-    assert_eq!(
-        stringify_item!(
-            pub impl Trait for Struct {}
-        ),
-        "pub impl Trait for Struct {}",
-    );
-    assert_eq!(
-        stringify_item!(
-            impl<T> const Trait for T {}
-        ),
+    c1!(item, [ pub impl Struct {} ], "pub impl Struct {}");
+    c2!(item, [ impl<T> Struct<T> {} ], "impl<T> Struct<T> {}", "impl < T > Struct < T > {}");
+    c1!(item, [ pub impl Trait for Struct {} ], "pub impl Trait for Struct {}");
+    c2!(item,
+        [ impl<T> const Trait for T {} ],
         "impl<T> const Trait for T {}",
+        "impl < T > const Trait for T {}"
     );
-    assert_eq!(
-        stringify_item!(
-            impl ~const Struct {}
-        ),
-        "impl ~const Struct {}",
-    );
+    c2!(item, [ impl ~const Struct {} ], "impl ~const Struct {}", "impl ~ const Struct {}");
 
     // ItemKind::MacCall
-    assert_eq!(stringify_item!(mac!(...);), "mac!(...);");
-    assert_eq!(stringify_item!(mac![...];), "mac![...];");
-    assert_eq!(stringify_item!(mac! { ... }), "mac! { ... }");
+    c2!(item, [ mac!(...); ], "mac!(...);", "mac! (...) ;");
+    c2!(item, [ mac![...]; ], "mac![...];", "mac! [...] ;");
+    c1!(item, [ mac! { ... } ], "mac! { ... }");
 
     // ItemKind::MacroDef
-    assert_eq!(
-        stringify_item!(
+    c1!(item,
+        [
             macro_rules! stringify {
                 () => {};
             }
-        ),
-        "macro_rules! stringify { () => {} ; }", // FIXME
+        ],
+        "macro_rules! stringify { () => {} ; }"
     );
-    assert_eq!(
-        stringify_item!(
-            pub macro stringify() {}
-        ),
-        "pub macro stringify { () => {} }",
+    c2!(item,
+        [ pub macro stringify() {} ],
+        "pub macro stringify { () => {} }", // ??
+        "pub macro stringify() {}"
     );
 }
 
 #[test]
 fn test_meta() {
-    assert_eq!(stringify_meta!(k), "k");
-    assert_eq!(stringify_meta!(k = "v"), "k = \"v\"");
-    assert_eq!(stringify_meta!(list(k1, k2 = "v")), "list(k1, k2 = \"v\")");
-    assert_eq!(stringify_meta!(serde::k), "serde::k");
+    c1!(meta, [ k ], "k");
+    c1!(meta, [ k = "v" ], "k = \"v\"");
+    c1!(meta, [ list(k1, k2 = "v") ], "list(k1, k2 = \"v\")");
+    c2!(meta, [ serde::k ], "serde::k", "serde :: k");
 }
 
 #[test]
 fn test_pat() {
     // PatKind::Wild
-    assert_eq!(stringify_pat!(_), "_");
+    c1!(pat, [ _ ], "_");
 
     // PatKind::Ident
-    assert_eq!(stringify_pat!(_x), "_x");
-    assert_eq!(stringify_pat!(ref _x), "ref _x");
-    assert_eq!(stringify_pat!(mut _x), "mut _x");
-    assert_eq!(stringify_pat!(ref mut _x), "ref mut _x");
-    assert_eq!(stringify_pat!(ref mut _x @ _), "ref mut _x @ _");
+    c1!(pat, [ _x ], "_x");
+    c1!(pat, [ ref _x ], "ref _x");
+    c1!(pat, [ mut _x ], "mut _x");
+    c1!(pat, [ ref mut _x ], "ref mut _x");
+    c1!(pat, [ ref mut _x @ _ ], "ref mut _x @ _");
 
     // PatKind::Struct
-    assert_eq!(stringify_pat!(Struct {}), "Struct {}");
-    assert_eq!(stringify_pat!(Struct::<u8> {}), "Struct::<u8> {}");
-    assert_eq!(stringify_pat!(Struct::<'static> {}), "Struct::<'static> {}");
-    assert_eq!(stringify_pat!(Struct { x }), "Struct { x }");
-    assert_eq!(stringify_pat!(Struct { x: _x }), "Struct { x: _x }");
-    assert_eq!(stringify_pat!(Struct { .. }), "Struct { .. }");
-    assert_eq!(stringify_pat!(Struct { x, .. }), "Struct { x, .. }");
-    assert_eq!(stringify_pat!(Struct { x: _x, .. }), "Struct { x: _x, .. }");
-    #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5151
-    assert_eq!(
-        stringify_pat!(<Struct as Trait>::Type {}),
+    c1!(pat, [ Struct {} ], "Struct {}");
+    c2!(pat, [ Struct::<u8> {} ], "Struct::<u8> {}", "Struct :: < u8 > {}");
+    c2!(pat, [ Struct::<'static> {} ], "Struct::<'static> {}", "Struct :: < 'static > {}");
+    c1!(pat, [ Struct { x } ], "Struct { x }");
+    c2!(pat, [ Struct { x: _x } ], "Struct { x: _x }", "Struct { x : _x }");
+    c1!(pat, [ Struct { .. } ], "Struct { .. }");
+    c1!(pat, [ Struct { x, .. } ], "Struct { x, .. }");
+    c2!(pat, [ Struct { x: _x, .. } ], "Struct { x: _x, .. }", "Struct { x : _x, .. }");
+    c2!(pat,
+        [ <Struct as Trait>::Type {} ],
         "<Struct as Trait>::Type {}",
+        "< Struct as Trait > :: Type {}"
     );
 
     // PatKind::TupleStruct
-    assert_eq!(stringify_pat!(Tuple()), "Tuple()");
-    assert_eq!(stringify_pat!(Tuple::<u8>()), "Tuple::<u8>()");
-    assert_eq!(stringify_pat!(Tuple::<'static>()), "Tuple::<'static>()");
-    assert_eq!(stringify_pat!(Tuple(x)), "Tuple(x)");
-    assert_eq!(stringify_pat!(Tuple(..)), "Tuple(..)");
-    assert_eq!(stringify_pat!(Tuple(x, ..)), "Tuple(x, ..)");
-    assert_eq!(stringify_pat!(<Struct as Trait>::Type()), "<Struct as Trait>::Type()");
+    c1!(pat, [ Tuple() ], "Tuple()");
+    c2!(pat, [ Tuple::<u8>() ], "Tuple::<u8>()", "Tuple :: < u8 > ()");
+    c2!(pat, [ Tuple::<'static>() ], "Tuple::<'static>()", "Tuple :: < 'static > ()");
+    c1!(pat, [ Tuple(x) ], "Tuple(x)");
+    c1!(pat, [ Tuple(..) ], "Tuple(..)");
+    c1!(pat, [ Tuple(x, ..) ], "Tuple(x, ..)");
+    c2!(pat,
+        [ <Struct as Trait>::Type() ],
+        "<Struct as Trait>::Type()",
+        "< Struct as Trait > :: Type()"
+    );
 
     // PatKind::Or
-    assert_eq!(stringify_pat!(true | false), "true | false");
-    assert_eq!(stringify_pat!(| true), "true");
-    assert_eq!(stringify_pat!(|true| false), "true | false");
+    c1!(pat, [ true | false ], "true | false");
+    c2!(pat, [ | true ], "true", "| true");
+    c2!(pat, [ |true| false ], "true | false", "| true | false");
 
     // PatKind::Path
-    assert_eq!(stringify_pat!(crate::Path), "crate::Path");
-    assert_eq!(stringify_pat!(Path::<u8>), "Path::<u8>");
-    assert_eq!(stringify_pat!(Path::<'static>), "Path::<'static>");
-    assert_eq!(stringify_pat!(<Struct as Trait>::Type), "<Struct as Trait>::Type");
+    c2!(pat, [ crate::Path ], "crate::Path", "crate :: Path");
+    c2!(pat, [ Path::<u8> ], "Path::<u8>", "Path :: < u8 >");
+    c2!(pat, [ Path::<'static> ], "Path::<'static>", "Path :: < 'static >");
+    c2!(pat, [ <Struct as Trait>::Type ], "<Struct as Trait>::Type", "< Struct as Trait > :: Type");
 
     // PatKind::Tuple
-    assert_eq!(stringify_pat!(()), "()");
-    assert_eq!(stringify_pat!((true,)), "(true,)");
-    assert_eq!(stringify_pat!((true, false)), "(true, false)");
+    c1!(pat, [ () ], "()");
+    c1!(pat, [ (true,) ], "(true,)");
+    c1!(pat, [ (true, false) ], "(true, false)");
 
     // PatKind::Box
-    assert_eq!(stringify_pat!(box pat), "box pat");
+    c1!(pat, [ box pat ], "box pat");
 
     // PatKind::Ref
-    assert_eq!(stringify_pat!(&pat), "&pat");
-    assert_eq!(stringify_pat!(&mut pat), "&mut pat");
+    c2!(pat, [ &pat ], "&pat", "& pat");
+    c2!(pat, [ &mut pat ], "&mut pat", "& mut pat");
 
     // PatKind::Lit
-    assert_eq!(stringify_pat!(1_000_i8), "1_000_i8");
+    c1!(pat, [ 1_000_i8 ], "1_000_i8");
 
     // PatKind::Range
-    assert_eq!(stringify_pat!(..1), "..1");
-    assert_eq!(stringify_pat!(0..), "0..");
-    assert_eq!(stringify_pat!(0..1), "0..1");
-    assert_eq!(stringify_pat!(0..=1), "0..=1");
-    assert_eq!(stringify_pat!(-2..=-1), "-2..=-1");
+    c2!(pat, [ ..1 ], "..1", ".. 1");
+    c2!(pat, [ 0.. ], "0..", "0 ..");
+    c2!(pat, [ 0..1 ], "0..1", "0 .. 1");
+    c2!(pat, [ 0..=1 ], "0..=1", "0 ..= 1");
+    c2!(pat, [ -2..=-1 ], "-2..=-1", "- 2 ..= - 1");
 
     // PatKind::Slice
-    assert_eq!(stringify_pat!([]), "[]");
-    assert_eq!(stringify_pat!([true]), "[true]");
-    assert_eq!(stringify_pat!([true,]), "[true]");
-    assert_eq!(stringify_pat!([true, false]), "[true, false]");
+    c1!(pat, [ [] ], "[]");
+    c1!(pat, [ [true] ], "[true]");
+    c2!(pat, [ [true,] ], "[true]", "[true,]");
+    c1!(pat, [ [true, false] ], "[true, false]");
 
     // PatKind::Rest
-    assert_eq!(stringify_pat!(..), "..");
+    c1!(pat, [ .. ], "..");
 
     // PatKind::Paren
-    assert_eq!(stringify_pat!((pat)), "(pat)");
+    c1!(pat, [ (pat) ], "(pat)");
 
     // PatKind::MacCall
-    assert_eq!(stringify_pat!(mac!(...)), "mac!(...)");
-    assert_eq!(stringify_pat!(mac![...]), "mac![...]");
-    assert_eq!(stringify_pat!(mac! { ... }), "mac! { ... }");
+    c2!(pat, [ mac!(...) ], "mac!(...)", "mac! (...)");
+    c2!(pat, [ mac![...] ], "mac![...]", "mac! [...]");
+    c1!(pat, [ mac! { ... } ], "mac! { ... }");
 }
 
 #[test]
 fn test_path() {
-    assert_eq!(stringify_path!(thing), "thing");
-    assert_eq!(stringify_path!(m::thing), "m::thing");
-    assert_eq!(stringify_path!(self::thing), "self::thing");
-    assert_eq!(stringify_path!(crate::thing), "crate::thing");
-    assert_eq!(stringify_path!(Self::thing), "Self::thing");
-    assert_eq!(stringify_path!(Self<'static>), "Self<'static>");
-    assert_eq!(stringify_path!(Self::<'static>), "Self<'static>");
-    assert_eq!(stringify_path!(Self()), "Self()");
-    assert_eq!(stringify_path!(Self() -> ()), "Self() -> ()");
+    c1!(path, [ thing ], "thing");
+    c2!(path, [ m::thing ], "m::thing", "m :: thing");
+    c2!(path, [ self::thing ], "self::thing", "self :: thing");
+    c2!(path, [ crate::thing ], "crate::thing", "crate :: thing");
+    c2!(path, [ Self::thing ], "Self::thing", "Self :: thing");
+    c2!(path, [ Self<'static> ], "Self<'static>", "Self < 'static >");
+    c2!(path, [ Self::<'static> ], "Self<'static>", "Self :: < 'static >");
+    c1!(path, [ Self() ], "Self()");
+    c1!(path, [ Self() -> () ], "Self() -> ()");
 }
 
 #[test]
 fn test_stmt() {
     // StmtKind::Local
-    assert_eq!(stringify_stmt!(let _), "let _;");
-    assert_eq!(stringify_stmt!(let x = true), "let x = true;");
-    assert_eq!(stringify_stmt!(let x: bool = true), "let x: bool = true;");
+    c2!(stmt, [ let _ ], "let _;", "let _");
+    c2!(stmt, [ let x = true ], "let x = true;", "let x = true");
+    c2!(stmt, [ let x: bool = true ], "let x: bool = true;", "let x : bool = true");
+    c2!(stmt, [ let (a, b) = (1, 2) ], "let (a, b) = (1, 2);", "let(a, b) = (1, 2)"); // FIXME
+    c2!(stmt,
+        [ let (a, b): (u32, u32) = (1, 2) ],
+        "let (a, b): (u32, u32) = (1, 2);",
+        "let(a, b) : (u32, u32) = (1, 2)"
+    );
 
     // StmtKind::Item
-    assert_eq!(
-        stringify_stmt!(
-            struct S;
-        ),
-        "struct S;",
-    );
+    c2!(stmt, [ struct S; ], "struct S;", "struct S ;");
+    c1!(stmt, [ struct S {} ], "struct S {}");
 
     // StmtKind::Expr
-    assert_eq!(stringify_stmt!(loop {}), "loop {}");
+    c1!(stmt, [ loop {} ], "loop {}");
 
     // StmtKind::Semi
-    assert_eq!(stringify_stmt!(1 + 1), "1 + 1;");
+    c2!(stmt, [ 1 + 1 ], "1 + 1;", "1 + 1");
 
     // StmtKind::Empty
-    assert_eq!(stringify_stmt!(;), ";");
+    c1!(stmt, [ ; ], ";");
 
     // StmtKind::MacCall
-    assert_eq!(stringify_stmt!(mac!(...)), "mac!(...)");
-    assert_eq!(stringify_stmt!(mac![...]), "mac![...]");
-    assert_eq!(stringify_stmt!(mac! { ... }), "mac! { ... }");
+    c2!(stmt, [ mac!(...) ], "mac!(...)", "mac! (...)");
+    c2!(stmt, [ mac![...] ], "mac![...]", "mac! [...]");
+    c1!(stmt, [ mac! { ... } ], "mac! { ... }");
 }
 
 #[test]
 fn test_ty() {
     // TyKind::Slice
-    assert_eq!(stringify_ty!([T]), "[T]");
+    c1!(ty, [ [T] ], "[T]");
 
     // TyKind::Array
-    assert_eq!(stringify_ty!([T; 0]), "[T; 0]");
+    c2!(ty, [ [T; 0] ], "[T; 0]", "[T ; 0]");
 
     // TyKind::Ptr
-    assert_eq!(stringify_ty!(*const T), "*const T");
-    assert_eq!(stringify_ty!(*mut T), "*mut T");
+    c2!(ty, [ *const T ], "*const T", "* const T");
+    c2!(ty, [ *mut T ], "*mut T", "* mut T");
 
     // TyKind::Ref
-    assert_eq!(stringify_ty!(&T), "&T");
-    assert_eq!(stringify_ty!(&mut T), "&mut T");
-    assert_eq!(stringify_ty!(&'a T), "&'a T");
-    assert_eq!(stringify_ty!(&'a mut T), "&'a mut T");
+    c2!(ty, [ &T ], "&T", "& T");
+    c2!(ty, [ &mut T ], "&mut T", "& mut T");
+    c2!(ty, [ &'a T ], "&'a T", "& 'a T");
+    c2!(ty, [ &'a mut [T] ], "&'a mut [T]", "& 'a mut [T]");
+    c2!(ty, [ &A<B<C<D<E>>>> ], "&A<B<C<D<E>>>>", "& A < B < C < D < E >> >>");
 
     // TyKind::BareFn
-    assert_eq!(stringify_ty!(fn()), "fn()");
-    assert_eq!(stringify_ty!(fn() -> ()), "fn() -> ()");
-    assert_eq!(stringify_ty!(fn(u8)), "fn(u8)");
-    assert_eq!(stringify_ty!(fn(x: u8)), "fn(x: u8)");
-    #[rustfmt::skip]
-    assert_eq!(stringify_ty!(for<> fn()), "fn()");
-    assert_eq!(stringify_ty!(for<'a> fn()), "for<'a> fn()");
+    c1!(ty, [ fn() ], "fn()");
+    c1!(ty, [ fn() -> () ], "fn() -> ()");
+    c1!(ty, [ fn(u8) ], "fn(u8)");
+    c2!(ty, [ fn(x: u8) ], "fn(x: u8)", "fn(x : u8)");
+    c2!(ty, [ for<> fn() ], "fn()", "for < > fn()");
+    c2!(ty, [ for<'a> fn() ], "for<'a> fn()", "for < 'a > fn()");
 
     // TyKind::Never
-    assert_eq!(stringify_ty!(!), "!");
+    c1!(ty, [ ! ], "!");
 
     // TyKind::Tup
-    assert_eq!(stringify_ty!(()), "()");
-    assert_eq!(stringify_ty!((T,)), "(T,)");
-    assert_eq!(stringify_ty!((T, U)), "(T, U)");
+    c1!(ty, [ () ], "()");
+    c1!(ty, [ (T,) ], "(T,)");
+    c1!(ty, [ (T, U) ], "(T, U)");
+
+    // TyKind::AnonStruct: untestable in isolation.
+
+    // TyKind::AnonUnion: untestable in isolation.
 
     // TyKind::Path
-    assert_eq!(stringify_ty!(T), "T");
-    assert_eq!(stringify_ty!(Ref<'a>), "Ref<'a>");
-    assert_eq!(stringify_ty!(PhantomData<T>), "PhantomData<T>");
-    assert_eq!(stringify_ty!(PhantomData::<T>), "PhantomData<T>");
-    assert_eq!(stringify_ty!(Fn() -> !), "Fn() -> !");
-    assert_eq!(stringify_ty!(Fn(u8) -> !), "Fn(u8) -> !");
-    assert_eq!(stringify_ty!(<Struct as Trait>::Type), "<Struct as Trait>::Type");
+    c1!(ty, [ T ], "T");
+    c2!(ty, [ Ref<'a> ], "Ref<'a>", "Ref < 'a >");
+    c2!(ty, [ PhantomData<T> ], "PhantomData<T>", "PhantomData < T >");
+    c2!(ty, [ PhantomData::<T> ], "PhantomData<T>", "PhantomData :: < T >");
+    c2!(ty, [ Fn() -> ! ], "Fn() -> !", "Fn() ->!");
+    c2!(ty, [ Fn(u8) -> ! ], "Fn(u8) -> !", "Fn(u8) ->!"); // FIXME
+    c2!(ty, [ <Struct as Trait>::Type ], "<Struct as Trait>::Type", "< Struct as Trait > :: Type");
 
     // TyKind::TraitObject
-    assert_eq!(stringify_ty!(dyn Send), "dyn Send");
-    assert_eq!(stringify_ty!(dyn Send + 'a), "dyn Send + 'a");
-    assert_eq!(stringify_ty!(dyn 'a + Send), "dyn 'a + Send");
-    assert_eq!(stringify_ty!(dyn ?Sized), "dyn ?Sized");
-    assert_eq!(stringify_ty!(dyn ~const Clone), "dyn ~const Clone");
-    assert_eq!(stringify_ty!(dyn for<'a> Send), "dyn for<'a> Send");
+    c1!(ty, [ dyn Send ], "dyn Send");
+    c1!(ty, [ dyn Send + 'a ], "dyn Send + 'a");
+    c1!(ty, [ dyn 'a + Send ], "dyn 'a + Send");
+    c2!(ty, [ dyn ?Sized ], "dyn ?Sized", "dyn ? Sized");
+    c2!(ty, [ dyn ~const Clone ], "dyn ~const Clone", "dyn ~ const Clone");
+    c2!(ty, [ dyn for<'a> Send ], "dyn for<'a> Send", "dyn for < 'a > Send");
 
     // TyKind::ImplTrait
-    assert_eq!(stringify_ty!(impl Send), "impl Send");
-    assert_eq!(stringify_ty!(impl Send + 'a), "impl Send + 'a");
-    assert_eq!(stringify_ty!(impl 'a + Send), "impl 'a + Send");
-    assert_eq!(stringify_ty!(impl ?Sized), "impl ?Sized");
-    assert_eq!(stringify_ty!(impl ~const Clone), "impl ~const Clone");
-    assert_eq!(stringify_ty!(impl for<'a> Send), "impl for<'a> Send");
+    c1!(ty, [ impl Send ], "impl Send");
+    c1!(ty, [ impl Send + 'a ], "impl Send + 'a");
+    c1!(ty, [ impl 'a + Send ], "impl 'a + Send");
+    c2!(ty, [ impl ?Sized ], "impl ?Sized", "impl ? Sized");
+    c2!(ty, [ impl ~const Clone ], "impl ~const Clone", "impl ~ const Clone");
+    c2!(ty, [ impl for<'a> Send ], "impl for<'a> Send", "impl for < 'a > Send");
 
     // TyKind::Paren
-    assert_eq!(stringify_ty!((T)), "(T)");
+    c1!(ty, [ (T) ], "(T)");
+
+    // TyKind::Typeof: unused for now.
 
     // TyKind::Infer
-    assert_eq!(stringify_ty!(_), "_");
+    c1!(ty, [ _ ], "_");
+
+    // TyKind::ImplicitSelf: there is no syntax for this.
 
     // TyKind::MacCall
-    assert_eq!(stringify_ty!(mac!(...)), "mac!(...)");
-    assert_eq!(stringify_ty!(mac![...]), "mac![...]");
-    assert_eq!(stringify_ty!(mac! { ... }), "mac! { ... }");
+    c2!(ty, [ mac!(...) ], "mac!(...)", "mac! (...)");
+    c2!(ty, [ mac![...] ], "mac![...]", "mac! [...]");
+    c1!(ty, [ mac! { ... } ], "mac! { ... }");
+
+    // TyKind::Err: untestable.
+
+    // TyKind::CVarArgs
+    // FIXME: todo
 }
 
 #[test]
 fn test_vis() {
     // VisibilityKind::Public
-    assert_eq!(stringify_vis!(pub), "pub ");
+    c2!(vis, [ pub ], "pub ", "pub");
 
     // VisibilityKind::Restricted
-    assert_eq!(stringify_vis!(pub(crate)), "pub(crate) ");
-    assert_eq!(stringify_vis!(pub(self)), "pub(self) ");
-    assert_eq!(stringify_vis!(pub(super)), "pub(super) ");
-    assert_eq!(stringify_vis!(pub(in crate)), "pub(in crate) ");
-    assert_eq!(stringify_vis!(pub(in self)), "pub(in self) ");
-    assert_eq!(stringify_vis!(pub(in super)), "pub(in super) ");
-    assert_eq!(stringify_vis!(pub(in path::to)), "pub(in path::to) ");
-    assert_eq!(stringify_vis!(pub(in ::path::to)), "pub(in ::path::to) ");
-    assert_eq!(stringify_vis!(pub(in self::path::to)), "pub(in self::path::to) ");
-    assert_eq!(stringify_vis!(pub(in super::path::to)), "pub(in super::path::to) ");
+    c2!(vis, [ pub(crate) ], "pub(crate) ", "pub(crate)");
+    c2!(vis, [ pub(self) ], "pub(self) ", "pub(self)");
+    c2!(vis, [ pub(super) ], "pub(super) ", "pub(super)");
+    c2!(vis, [ pub(in crate) ], "pub(in crate) ", "pub(in crate)");
+    c2!(vis, [ pub(in self) ], "pub(in self) ", "pub(in self)");
+    c2!(vis, [ pub(in super) ], "pub(in super) ", "pub(in super)");
+    c2!(vis, [ pub(in path::to) ], "pub(in path::to) ", "pub(in path :: to)");
+    c2!(vis, [ pub(in ::path::to) ], "pub(in ::path::to) ", "pub(in :: path :: to)");
+    c2!(vis, [ pub(in self::path::to) ], "pub(in self::path::to) ", "pub(in self :: path :: to)");
+    c2!(vis,
+        [ pub(in super::path::to) ],
+        "pub(in super::path::to) ",
+        "pub(in super :: path :: to)"
+    );
 
     // VisibilityKind::Inherited
-    // Directly calling `stringify_vis!()` does not work.
-    macro_rules! stringify_inherited_vis {
-        ($vis:vis struct) => {
-            stringify_vis!($vis)
-        };
-    }
-    assert_eq!(stringify_inherited_vis!(struct), "");
+    // This one is different because directly calling `vis!` does not work.
+    macro_rules! inherited_vis { ($vis:vis struct) => { vis!($vis) }; }
+    assert_eq!(inherited_vis!(struct), "");
+    assert_eq!(stringify!(), "");
 }
diff --git a/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs
new file mode 100644
index 00000000000..adf3049b4ba
--- /dev/null
+++ b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs
@@ -0,0 +1,6 @@
+fn foo() -> Result<String, ()> {
+    let out: Result<(), ()> = Ok(());
+    out //~ ERROR mismatched types
+}
+
+fn main() {}
diff --git a/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr
new file mode 100644
index 00000000000..447b22a152d
--- /dev/null
+++ b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr
@@ -0,0 +1,15 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-116967-cannot-coerce-returned-result.rs:3:5
+   |
+LL | fn foo() -> Result<String, ()> {
+   |             ------------------ expected `Result<String, ()>` because of return type
+LL |     let out: Result<(), ()> = Ok(());
+LL |     out
+   |     ^^^ expected `Result<String, ()>`, found `Result<(), ()>`
+   |
+   = note: expected enum `Result<String, _>`
+              found enum `Result<(), _>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.