about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--compiler/rustc_abi/src/layout.rs13
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs46
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs3
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs7
-rw-r--r--src/bootstrap/src/core/builder.rs1
-rw-r--r--src/librustdoc/doctest/rust.rs13
-rw-r--r--src/tools/compiletest/src/header.rs12
-rw-r--r--src/tools/compiletest/src/runtest.rs2141
-rw-r--r--src/tools/compiletest/src/runtest/assembly.rs19
-rw-r--r--src/tools/compiletest/src/runtest/codegen.rs22
-rw-r--r--src/tools/compiletest/src/runtest/codegen_units.rs191
-rw-r--r--src/tools/compiletest/src/runtest/coverage.rs4
-rw-r--r--src/tools/compiletest/src/runtest/crash.rs25
-rw-r--r--src/tools/compiletest/src/runtest/debuginfo.rs509
-rw-r--r--src/tools/compiletest/src/runtest/incremental.rs128
-rw-r--r--src/tools/compiletest/src/runtest/js_doc.rs32
-rw-r--r--src/tools/compiletest/src/runtest/mir_opt.rs155
-rw-r--r--src/tools/compiletest/src/runtest/pretty.rs104
-rw-r--r--src/tools/compiletest/src/runtest/run_make.rs518
-rw-r--r--src/tools/compiletest/src/runtest/rustdoc.rs34
-rw-r--r--src/tools/compiletest/src/runtest/rustdoc_json.rs48
-rw-r--r--src/tools/compiletest/src/runtest/ui.rs233
-rw-r--r--src/tools/compiletest/src/runtest/valgrind.rs34
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-enum.rs5
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-enum.stderr4
-rw-r--r--src/tools/rustfmt/.github/workflows/check_diff.yml2
-rw-r--r--src/tools/rustfmt/.github/workflows/integration.yml2
-rw-r--r--src/tools/rustfmt/.github/workflows/linux.yml2
-rw-r--r--src/tools/rustfmt/.github/workflows/mac.yml3
-rw-r--r--src/tools/rustfmt/.github/workflows/rustdoc_check.yml2
-rw-r--r--src/tools/rustfmt/.github/workflows/upload-assets.yml2
-rw-r--r--src/tools/rustfmt/.github/workflows/windows.yml2
-rw-r--r--src/tools/rustfmt/CHANGELOG.md64
-rw-r--r--src/tools/rustfmt/Cargo.lock14
-rw-r--r--src/tools/rustfmt/Cargo.toml4
-rw-r--r--src/tools/rustfmt/Configurations.md17
-rw-r--r--src/tools/rustfmt/Contributing.md21
-rw-r--r--src/tools/rustfmt/build.rs19
-rw-r--r--src/tools/rustfmt/check_diff/Cargo.lock290
-rw-r--r--src/tools/rustfmt/check_diff/Cargo.toml4
-rw-r--r--src/tools/rustfmt/check_diff/src/lib.rs58
-rw-r--r--src/tools/rustfmt/check_diff/src/main.rs8
-rw-r--r--src/tools/rustfmt/check_diff/tests/bash_commands.rs12
-rw-r--r--src/tools/rustfmt/check_diff/tests/git.rs16
-rwxr-xr-xsrc/tools/rustfmt/ci/build_and_test.bat8
-rwxr-xr-xsrc/tools/rustfmt/ci/build_and_test.sh8
-rw-r--r--src/tools/rustfmt/config_proc_macro/Cargo.toml4
-rw-r--r--src/tools/rustfmt/config_proc_macro/src/utils.rs2
-rw-r--r--src/tools/rustfmt/rust-toolchain2
-rw-r--r--src/tools/rustfmt/rustfmt.toml3
-rw-r--r--src/tools/rustfmt/src/attr.rs76
-rw-r--r--src/tools/rustfmt/src/bin/main.rs334
-rw-r--r--src/tools/rustfmt/src/chains.rs191
-rw-r--r--src/tools/rustfmt/src/closures.rs93
-rw-r--r--src/tools/rustfmt/src/comment.rs60
-rw-r--r--src/tools/rustfmt/src/config/config_type.rs149
-rw-r--r--src/tools/rustfmt/src/config/file_lines.rs6
-rw-r--r--src/tools/rustfmt/src/config/mod.rs580
-rw-r--r--src/tools/rustfmt/src/config/options.rs201
-rw-r--r--src/tools/rustfmt/src/config/style_edition.rs2
-rw-r--r--src/tools/rustfmt/src/emitter/checkstyle.rs2
-rw-r--r--src/tools/rustfmt/src/emitter/json.rs2
-rw-r--r--src/tools/rustfmt/src/emitter/modified_lines.rs2
-rw-r--r--src/tools/rustfmt/src/expr.rs476
-rw-r--r--src/tools/rustfmt/src/formatting.rs2
-rw-r--r--src/tools/rustfmt/src/git-rustfmt/main.rs13
-rw-r--r--src/tools/rustfmt/src/ignore_path.rs9
-rw-r--r--src/tools/rustfmt/src/imports.rs218
-rw-r--r--src/tools/rustfmt/src/items.rs433
-rw-r--r--src/tools/rustfmt/src/lib.rs8
-rw-r--r--src/tools/rustfmt/src/lists.rs31
-rw-r--r--src/tools/rustfmt/src/macros.rs265
-rw-r--r--src/tools/rustfmt/src/matches.rs143
-rw-r--r--src/tools/rustfmt/src/missed_spans.rs14
-rw-r--r--src/tools/rustfmt/src/modules.rs2
-rw-r--r--src/tools/rustfmt/src/overflow.rs58
-rw-r--r--src/tools/rustfmt/src/pairs.rs99
-rw-r--r--src/tools/rustfmt/src/parse/macros/asm.rs2
-rw-r--r--src/tools/rustfmt/src/parse/macros/cfg_if.rs2
-rw-r--r--src/tools/rustfmt/src/parse/macros/mod.rs4
-rw-r--r--src/tools/rustfmt/src/parse/parser.rs6
-rw-r--r--src/tools/rustfmt/src/parse/session.rs9
-rw-r--r--src/tools/rustfmt/src/patterns.rs143
-rw-r--r--src/tools/rustfmt/src/reorder.rs27
-rw-r--r--src/tools/rustfmt/src/rewrite.rs68
-rw-r--r--src/tools/rustfmt/src/rustfmt_diff.rs2
-rw-r--r--src/tools/rustfmt/src/sort.rs368
-rw-r--r--src/tools/rustfmt/src/source_file.rs2
-rw-r--r--src/tools/rustfmt/src/spanned.rs9
-rw-r--r--src/tools/rustfmt/src/stmt.rs41
-rw-r--r--src/tools/rustfmt/src/string.rs4
-rw-r--r--src/tools/rustfmt/src/test/configuration_snippet.rs4
-rw-r--r--src/tools/rustfmt/src/test/mod.rs76
-rw-r--r--src/tools/rustfmt/src/types.rs371
-rw-r--r--src/tools/rustfmt/src/utils.rs16
-rw-r--r--src/tools/rustfmt/src/vertical.rs47
-rw-r--r--src/tools/rustfmt/src/visitor.rs48
-rw-r--r--src/tools/rustfmt/tests/config/issue-5801-v2.toml2
-rw-r--r--src/tools/rustfmt/tests/config/issue-6302.toml1
-rw-r--r--src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml1
-rw-r--r--src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml1
-rw-r--r--src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml2
-rw-r--r--src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml2
-rw-r--r--src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml2
-rw-r--r--src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml3
-rw-r--r--src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml2
-rw-r--r--src/tools/rustfmt/tests/rustfmt/main.rs27
-rw-r--r--src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs2
-rw-r--r--src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs2
-rw-r--r--src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs155
-rw-r--r--src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs155
-rw-r--r--src/tools/rustfmt/tests/source/fn-single-line/version_one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/fn-single-line/version_two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-2179/one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-2179/two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3213/version_one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3213/version_two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3227/two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3270/one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3270/two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3272/v1.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3272/v2.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3278/version_one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3278/version_two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3295/two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3302.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3701/one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3701/two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3805.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs3
-rw-r--r--src/tools/rustfmt/tests/source/issue-4530.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-4689/one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-4689/two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-5586.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-5655/one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-5655/two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-5987/two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs2
-rw-r--r--src/tools/rustfmt/tests/source/issue_5027.rs2
-rw-r--r--src/tools/rustfmt/tests/source/let_else_v2.rs2
-rw-r--r--src/tools/rustfmt/tests/source/long-fn-1/version_one.rs2
-rw-r--r--src/tools/rustfmt/tests/source/long-fn-1/version_two.rs2
-rw-r--r--src/tools/rustfmt/tests/source/one_line_if_v1.rs2
-rw-r--r--src/tools/rustfmt/tests/source/one_line_if_v2.rs2
-rw-r--r--src/tools/rustfmt/tests/source/single-line-macro/v1.rs2
-rw-r--r--src/tools/rustfmt/tests/source/single-line-macro/v2.rs2
-rw-r--r--src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs2
-rw-r--r--src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs2
-rw-r--r--src/tools/rustfmt/tests/source/tuple_v2.rs2
-rw-r--r--src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs31
-rw-r--r--src/tools/rustfmt/tests/source/type-alias-where-clauses.rs20
-rw-r--r--src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs2
-rw-r--r--src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs2
-rw-r--r--src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs135
-rw-r--r--src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs120
-rw-r--r--src/tools/rustfmt/tests/target/configs/version/mapped.rs9
-rw-r--r--src/tools/rustfmt/tests/target/fn-single-line/version_one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/fn-single-line/version_two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-2179/one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-2179/two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3132.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3213/version_one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3213/version_two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3227/one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3227/two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3270/one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3270/two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3270/wrap.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3272/v1.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3272/v2.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3278/version_one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3278/version_two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3295/two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3302.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3614/version_one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3614/version_two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3701/one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3701/two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3805.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-3882.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs3
-rw-r--r--src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs3
-rw-r--r--src/tools/rustfmt/tests/target/issue-4530.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-4689/one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-4689/two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-5586.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-5655/one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-5655/two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-5987/one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-5987/two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-6109.rs7
-rw-r--r--src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs2
-rw-r--r--src/tools/rustfmt/tests/target/issue_5027.rs2
-rw-r--r--src/tools/rustfmt/tests/target/let_else_v2.rs2
-rw-r--r--src/tools/rustfmt/tests/target/long-fn-1/version_one.rs2
-rw-r--r--src/tools/rustfmt/tests/target/long-fn-1/version_two.rs2
-rw-r--r--src/tools/rustfmt/tests/target/one_line_if_v1.rs2
-rw-r--r--src/tools/rustfmt/tests/target/one_line_if_v2.rs2
-rw-r--r--src/tools/rustfmt/tests/target/single-line-macro/v1.rs2
-rw-r--r--src/tools/rustfmt/tests/target/single-line-macro/v2.rs2
-rw-r--r--src/tools/rustfmt/tests/target/style_edition/default.rs10
-rw-r--r--src/tools/rustfmt/tests/target/style_edition/follows_edition.rs14
-rw-r--r--src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs14
-rw-r--r--src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs2
-rw-r--r--src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs2
-rw-r--r--src/tools/rustfmt/tests/target/tuple_v2.rs2
-rw-r--r--src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs39
-rw-r--r--src/tools/rustfmt/tests/target/type-alias-where-clauses.rs20
-rw-r--r--tests/rustdoc-ui/doctest/doctest-output-include-fail.md7
-rw-r--r--tests/rustdoc-ui/doctest/doctest-output-include-fail.rs7
-rw-r--r--tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout24
-rw-r--r--tests/rustdoc-ui/doctest/doctest-output.stdout2
-rw-r--r--tests/ui/macros/auxiliary/metavar_2018.rs14
-rw-r--r--tests/ui/macros/metavar_cross_edition_recursive_macros.rs38
-rw-r--r--tests/ui/structs-enums/type-sizes.rs20
-rw-r--r--tests/ui/traits/sized-coniductive.rs14
230 files changed, 7048 insertions, 3645 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1497d2d463a..e973d7f9a2a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4630,7 +4630,7 @@ dependencies = [
 
 [[package]]
 name = "rustfmt-nightly"
-version = "1.7.1"
+version = "1.8.0"
 dependencies = [
  "annotate-snippets 0.9.2",
  "anyhow",
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index f4de4e06d1b..01593d34c97 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -527,15 +527,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
             let count =
                 (niche_variants.end().index() as u128 - niche_variants.start().index() as u128) + 1;
 
-            // Find the field with the largest niche
-            let (field_index, niche, (niche_start, niche_scalar)) = variants[largest_variant_index]
-                .iter()
-                .enumerate()
-                .filter_map(|(j, field)| Some((j, field.largest_niche?)))
-                .max_by_key(|(_, niche)| niche.available(dl))
-                .and_then(|(j, niche)| Some((j, niche, niche.reserve(dl, count)?)))?;
-            let niche_offset =
-                niche.offset + variant_layouts[largest_variant_index].fields.offset(field_index);
+            // Use the largest niche in the largest variant.
+            let niche = variant_layouts[largest_variant_index].largest_niche?;
+            let (niche_start, niche_scalar) = niche.reserve(dl, count)?;
+            let niche_offset = niche.offset;
             let niche_size = niche.value.size(dl);
             let size = variant_layouts[largest_variant_index].size.align_to(align.abi);
 
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index ba35a37c32c..79de9bbb351 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -330,8 +330,12 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
             ty::Float(FloatTy::F128) => "C4f128",
             ty::Never => "z",
 
-            // Placeholders (should be demangled as `_`).
-            ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => "p",
+            // Should only be encountered with polymorphization,
+            // or within the identity-substituted impl header of an
+            // item nested within an impl item.
+            ty::Param(_) => "p",
+
+            ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => bug!(),
 
             _ => "",
         };
@@ -416,12 +420,18 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
             // Mangle all nominal types as paths.
             ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did: def_id, .. }, _)), args)
             | ty::FnDef(def_id, args)
-            | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. })
             | ty::Closure(def_id, args)
             | ty::CoroutineClosure(def_id, args)
             | ty::Coroutine(def_id, args) => {
                 self.print_def_path(def_id, args)?;
             }
+
+            // We may still encounter projections here due to the printing
+            // logic sometimes passing identity-substituted impl headers.
+            ty::Alias(ty::Projection, ty::AliasTy { def_id, args, .. }) => {
+                self.print_def_path(def_id, args)?;
+            }
+
             ty::Foreign(def_id) => {
                 self.print_def_path(def_id, &[])?;
             }
@@ -467,8 +477,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
                 r.print(self)?;
             }
 
-            ty::Alias(ty::Inherent, _) => bug!("symbol_names: unexpected inherent projection"),
-            ty::Alias(ty::Weak, _) => bug!("symbol_names: unexpected weak projection"),
+            ty::Alias(..) => bug!("symbol_names: unexpected alias"),
             ty::CoroutineWitness(..) => bug!("symbol_names: unexpected `CoroutineWitness`"),
         }
 
@@ -550,21 +559,26 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
         let (ct_ty, valtree) = match ct.kind() {
             ty::ConstKind::Value(ty, val) => (ty, val),
 
-            // Placeholders (should be demangled as `_`).
-            // NOTE(eddyb) despite `Unevaluated` having a `DefId` (and therefore
-            // a path), even for it we still need to encode a placeholder, as
-            // the path could refer back to e.g. an `impl` using the constant.
-            ty::ConstKind::Unevaluated(_)
-            | ty::ConstKind::Expr(_)
-            | ty::ConstKind::Param(_)
-            | ty::ConstKind::Infer(_)
-            | ty::ConstKind::Bound(..)
-            | ty::ConstKind::Placeholder(_)
-            | ty::ConstKind::Error(_) => {
+            // Should only be encountered with polymorphization,
+            // or within the identity-substituted impl header of an
+            // item nested within an impl item.
+            ty::ConstKind::Param(_) => {
                 // Never cached (single-character).
                 self.push("p");
                 return Ok(());
             }
+
+            // We may still encounter unevaluated consts due to the printing
+            // logic sometimes passing identity-substituted impl headers.
+            ty::Unevaluated(ty::UnevaluatedConst { def, args, .. }) => {
+                return self.print_def_path(def, args);
+            }
+
+            ty::ConstKind::Expr(_)
+            | ty::ConstKind::Infer(_)
+            | ty::ConstKind::Bound(..)
+            | ty::ConstKind::Placeholder(_)
+            | ty::ConstKind::Error(_) => bug!(),
         };
 
         if let Some(&i) = self.consts.get(&ct) {
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index c82eaa5143d..538e23f4449 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -409,6 +409,9 @@ pub fn normalize_param_env_or_error<'tcx>(
     debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
 
     let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal());
+    if !normalize::needs_normalization(&elaborated_env, unnormalized_env.reveal()) {
+        return elaborated_env;
+    }
 
     // HACK: we are trying to normalize the param-env inside *itself*. The problem is that
     // normalization expects its param-env to be already normalized, which means we have
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 3755f4a33cb..979671cfa9d 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -1058,6 +1058,13 @@ tool_doc!(
     is_library = true,
     crates = ["run_make_support"]
 );
+tool_doc!(
+    Compiletest,
+    "src/tools/compiletest",
+    rustc_tool = false,
+    is_library = true,
+    crates = ["compiletest"]
+);
 
 #[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)]
 pub struct ErrorIndex {
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 003b11ec7cf..2124890b94f 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -945,6 +945,7 @@ impl<'a> Builder<'a> {
                 doc::Releases,
                 doc::RunMakeSupport,
                 doc::BuildHelper,
+                doc::Compiletest,
             ),
             Kind::Dist => describe!(
                 dist::Docs,
diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs
index 5c0898f28fc..71a0e3d72de 100644
--- a/src/librustdoc/doctest/rust.rs
+++ b/src/librustdoc/doctest/rust.rs
@@ -122,23 +122,14 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> {
         // anything else, this will combine them for us.
         let attrs = Attributes::from_ast(ast_attrs);
         if let Some(doc) = attrs.opt_doc_value() {
-            // Use the outermost invocation, so that doctest names come from where the docs were written.
-            let span = ast_attrs
-                .iter()
-                .find(|attr| attr.doc_str().is_some())
-                .map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span))
-                .unwrap_or(DUMMY_SP);
+            let span = span_of_fragments(&attrs.doc_strings).unwrap_or(sp);
             self.collector.position = span;
             markdown::find_testable_code(
                 &doc,
                 &mut self.collector,
                 self.codes,
                 self.enable_per_target_ignores,
-                Some(&crate::html::markdown::ExtraInfo::new(
-                    self.tcx,
-                    def_id,
-                    span_of_fragments(&attrs.doc_strings).unwrap_or(sp),
-                )),
+                Some(&crate::html::markdown::ExtraInfo::new(self.tcx, def_id, span)),
             );
         }
 
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 933913eb47c..93df6aa7255 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -125,7 +125,7 @@ pub struct TestProps {
     // Build documentation for all specified aux-builds as well
     pub build_aux_docs: bool,
     /// Build the documentation for each crate in a unique output directory.
-    /// Uses <root output directory>/docs/<test name>/doc
+    /// Uses `<root output directory>/docs/<test name>/doc`.
     pub unique_doc_out_dir: bool,
     // Flag to force a crate to be built with the host architecture
     pub force_host: bool,
@@ -1304,12 +1304,12 @@ pub fn llvm_has_libzstd(config: &Config) -> bool {
     false
 }
 
-/// Takes a directive of the form "<version1> [- <version2>]",
-/// returns the numeric representation of <version1> and <version2> as
-/// tuple: (<version1> as u32, <version2> as u32)
+/// Takes a directive of the form `"<version1> [- <version2>]"`,
+/// returns the numeric representation of `<version1>` and `<version2>` as
+/// tuple: `(<version1> as u32, <version2> as u32)`.
 ///
-/// If the <version2> part is omitted, the second component of the tuple
-/// is the same as <version1>.
+/// If the `<version2>` part is omitted, the second component of the tuple
+/// is the same as `<version1>`.
 fn extract_version_range<F>(line: &str, parse: F) -> Option<(u32, u32)>
 where
     F: Fn(&str) -> Option<u32>,
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index c18f569e528..7b23aa34639 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2,8 +2,8 @@
 
 use std::borrow::Cow;
 use std::collections::{HashMap, HashSet};
-use std::ffi::{OsStr, OsString};
-use std::fs::{self, create_dir_all, File, OpenOptions};
+use std::ffi::OsString;
+use std::fs::{self, create_dir_all, File};
 use std::hash::{DefaultHasher, Hash, Hasher};
 use std::io::prelude::*;
 use std::io::{self, BufReader};
@@ -14,10 +14,7 @@ use std::{env, iter, str};
 
 use anyhow::Context;
 use colored::Colorize;
-use glob::glob;
-use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile};
 use regex::{Captures, Regex};
-use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
 use tracing::*;
 
 use crate::common::{
@@ -31,12 +28,29 @@ use crate::compute_diff::{write_diff, write_filtered_diff};
 use crate::errors::{self, Error, ErrorKind};
 use crate::header::TestProps;
 use crate::read2::{read2_abbreviated, Truncated};
-use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, static_regex, PathBufExt};
-use crate::{extract_gdb_version, is_android_gdb_target, json, ColorConfig};
+use crate::util::{add_dylib_path, logv, static_regex, PathBufExt};
+use crate::{json, ColorConfig};
 
-mod coverage;
 mod debugger;
-use debugger::DebuggerCommands;
+
+// Helper modules that implement test running logic for each test suite.
+// tidy-alphabet-start
+mod assembly;
+mod codegen;
+mod codegen_units;
+mod coverage;
+mod crash;
+mod debuginfo;
+mod incremental;
+mod js_doc;
+mod mir_opt;
+mod pretty;
+mod run_make;
+mod rustdoc;
+mod rustdoc_json;
+mod ui;
+mod valgrind;
+// tidy-alphabet-end
 
 #[cfg(test)]
 mod tests;
@@ -325,81 +339,6 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn run_cfail_test(&self) {
-        let pm = self.pass_mode();
-        let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
-        self.check_if_test_should_compile(&proc_res, pm);
-        self.check_no_compiler_crash(&proc_res, self.props.should_ice);
-
-        let output_to_check = self.get_output(&proc_res);
-        let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
-        if !expected_errors.is_empty() {
-            if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty()
-            {
-                self.fatal("both error pattern and expected errors specified");
-            }
-            self.check_expected_errors(expected_errors, &proc_res);
-        } else {
-            self.check_all_error_patterns(&output_to_check, &proc_res, pm);
-        }
-        if self.props.should_ice {
-            match proc_res.status.code() {
-                Some(101) => (),
-                _ => self.fatal("expected ICE"),
-            }
-        }
-
-        self.check_forbid_output(&output_to_check, &proc_res);
-    }
-
-    fn run_crash_test(&self) {
-        let pm = self.pass_mode();
-        let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
-
-        if std::env::var("COMPILETEST_VERBOSE_CRASHES").is_ok() {
-            eprintln!("{}", proc_res.status);
-            eprintln!("{}", proc_res.stdout);
-            eprintln!("{}", proc_res.stderr);
-            eprintln!("{}", proc_res.cmdline);
-        }
-
-        // if a test does not crash, consider it an error
-        if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) {
-            self.fatal(&format!(
-                "crashtest no longer crashes/triggers ICE, horray! Please give it a meaningful name, \
-            add a doc-comment to the start of the test explaining why it exists and \
-            move it to tests/ui or wherever you see fit. Adding 'Fixes #<issueNr>' to your PR description \
-            ensures that the corresponding ticket is auto-closed upon merge."
-            ));
-        }
-    }
-
-    fn run_rfail_test(&self) {
-        let pm = self.pass_mode();
-        let should_run = self.run_if_enabled();
-        let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm));
-
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-
-        if let WillExecute::Disabled = should_run {
-            return;
-        }
-
-        let proc_res = self.exec_compiled_test();
-
-        // The value our Makefile configures valgrind to return on failure
-        const VALGRIND_ERR: i32 = 100;
-        if proc_res.status.code() == Some(VALGRIND_ERR) {
-            self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
-        }
-
-        let output_to_check = self.get_output(&proc_res);
-        self.check_correct_failure_status(&proc_res);
-        self.check_all_error_patterns(&output_to_check, &proc_res, pm);
-    }
-
     fn get_output(&self, proc_res: &ProcRes) -> String {
         if self.props.check_stdout {
             format!("{}{}", proc_res.stdout, proc_res.stderr)
@@ -423,73 +362,6 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn run_cpass_test(&self) {
-        let emit_metadata = self.should_emit_metadata(self.pass_mode());
-        let proc_res = self.compile_test(WillExecute::No, emit_metadata);
-
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-
-        // FIXME(#41968): Move this check to tidy?
-        if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
-            self.fatal("compile-pass tests with expected warnings should be moved to ui/");
-        }
-    }
-
-    fn run_rpass_test(&self) {
-        let emit_metadata = self.should_emit_metadata(self.pass_mode());
-        let should_run = self.run_if_enabled();
-        let proc_res = self.compile_test(should_run, emit_metadata);
-
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-
-        // FIXME(#41968): Move this check to tidy?
-        if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
-            self.fatal("run-pass tests with expected warnings should be moved to ui/");
-        }
-
-        if let WillExecute::Disabled = should_run {
-            return;
-        }
-
-        let proc_res = self.exec_compiled_test();
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("test run failed!", &proc_res);
-        }
-    }
-
-    fn run_valgrind_test(&self) {
-        assert!(self.revision.is_none(), "revisions not relevant here");
-
-        if self.config.valgrind_path.is_none() {
-            assert!(!self.config.force_valgrind);
-            return self.run_rpass_test();
-        }
-
-        let should_run = self.run_if_enabled();
-        let mut proc_res = self.compile_test(should_run, Emit::None);
-
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-
-        if let WillExecute::Disabled = should_run {
-            return;
-        }
-
-        let mut new_config = self.config.clone();
-        new_config.runner = new_config.valgrind_path.clone();
-        let new_cx = TestCx { config: &new_config, ..*self };
-        proc_res = new_cx.exec_compiled_test();
-
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("test run failed!", &proc_res);
-        }
-    }
-
     /// Runs a [`Command`] and waits for it to finish, then converts its exit
     /// status and output streams into a [`ProcRes`].
     ///
@@ -517,104 +389,6 @@ impl<'test> TestCx<'test> {
         proc_res
     }
 
-    fn run_pretty_test(&self) {
-        if self.props.pp_exact.is_some() {
-            logv(self.config, "testing for exact pretty-printing".to_owned());
-        } else {
-            logv(self.config, "testing for converging pretty-printing".to_owned());
-        }
-
-        let rounds = match self.props.pp_exact {
-            Some(_) => 1,
-            None => 2,
-        };
-
-        let src = fs::read_to_string(&self.testpaths.file).unwrap();
-        let mut srcs = vec![src];
-
-        let mut round = 0;
-        while round < rounds {
-            logv(
-                self.config,
-                format!("pretty-printing round {} revision {:?}", round, self.revision),
-            );
-            let read_from =
-                if round == 0 { ReadFrom::Path } else { ReadFrom::Stdin(srcs[round].to_owned()) };
-
-            let proc_res = self.print_source(read_from, &self.props.pretty_mode);
-            if !proc_res.status.success() {
-                self.fatal_proc_rec(
-                    &format!(
-                        "pretty-printing failed in round {} revision {:?}",
-                        round, self.revision
-                    ),
-                    &proc_res,
-                );
-            }
-
-            let ProcRes { stdout, .. } = proc_res;
-            srcs.push(stdout);
-            round += 1;
-        }
-
-        let mut expected = match self.props.pp_exact {
-            Some(ref file) => {
-                let filepath = self.testpaths.file.parent().unwrap().join(file);
-                fs::read_to_string(&filepath).unwrap()
-            }
-            None => srcs[srcs.len() - 2].clone(),
-        };
-        let mut actual = srcs[srcs.len() - 1].clone();
-
-        if self.props.pp_exact.is_some() {
-            // Now we have to care about line endings
-            let cr = "\r".to_owned();
-            actual = actual.replace(&cr, "");
-            expected = expected.replace(&cr, "");
-        }
-
-        if !self.config.bless {
-            self.compare_source(&expected, &actual);
-        } else if expected != actual {
-            let filepath_buf;
-            let filepath = match &self.props.pp_exact {
-                Some(file) => {
-                    filepath_buf = self.testpaths.file.parent().unwrap().join(file);
-                    &filepath_buf
-                }
-                None => &self.testpaths.file,
-            };
-            fs::write(filepath, &actual).unwrap();
-        }
-
-        // If we're only making sure that the output matches then just stop here
-        if self.props.pretty_compare_only {
-            return;
-        }
-
-        // Finally, let's make sure it actually appears to remain valid code
-        let proc_res = self.typecheck_source(actual);
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
-        }
-
-        if !self.props.pretty_expanded {
-            return;
-        }
-
-        // additionally, run `-Zunpretty=expanded` and try to build it.
-        let proc_res = self.print_source(ReadFrom::Path, "expanded");
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res);
-        }
-
-        let ProcRes { stdout: expanded_src, .. } = proc_res;
-        let proc_res = self.typecheck_source(expanded_src);
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("pretty-printed source (expanded) does not typecheck", &proc_res);
-        }
-    }
-
     fn print_source(&self, read_from: ReadFrom, pretty_type: &str) -> ProcRes {
         let aux_dir = self.aux_output_dir_name();
         let input: &str = match read_from {
@@ -727,500 +501,6 @@ impl<'test> TestCx<'test> {
         self.compose_and_run_compiler(rustc, Some(src), self.testpaths)
     }
 
-    fn run_debuginfo_test(&self) {
-        match self.config.debugger.unwrap() {
-            Debugger::Cdb => self.run_debuginfo_cdb_test(),
-            Debugger::Gdb => self.run_debuginfo_gdb_test(),
-            Debugger::Lldb => self.run_debuginfo_lldb_test(),
-        }
-    }
-
-    fn run_debuginfo_cdb_test(&self) {
-        let config = Config {
-            target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
-            host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
-            ..self.config.clone()
-        };
-
-        let test_cx = TestCx { config: &config, ..*self };
-
-        test_cx.run_debuginfo_cdb_test_no_opt();
-    }
-
-    fn run_debuginfo_cdb_test_no_opt(&self) {
-        let exe_file = self.make_exe_name();
-
-        // Existing PDB files are update in-place. When changing the debuginfo
-        // the compiler generates for something, this can lead to the situation
-        // where both the old and the new version of the debuginfo for the same
-        // type is present in the PDB, which is very confusing.
-        // Therefore we delete any existing PDB file before compiling the test
-        // case.
-        // FIXME: If can reliably detect that MSVC's link.exe is used, then
-        //        passing `/INCREMENTAL:NO` might be a cleaner way to do this.
-        let pdb_file = exe_file.with_extension(".pdb");
-        if pdb_file.exists() {
-            std::fs::remove_file(pdb_file).unwrap();
-        }
-
-        // compile test file (it should have 'compile-flags:-g' in the header)
-        let should_run = self.run_if_enabled();
-        let compile_result = self.compile_test(should_run, Emit::None);
-        if !compile_result.status.success() {
-            self.fatal_proc_rec("compilation failed!", &compile_result);
-        }
-        if let WillExecute::Disabled = should_run {
-            return;
-        }
-
-        let prefixes = {
-            static PREFIXES: &[&str] = &["cdb", "cdbg"];
-            // No "native rust support" variation for CDB yet.
-            PREFIXES
-        };
-
-        // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(
-            &self.testpaths.file,
-            self.config,
-            prefixes,
-            self.revision,
-        )
-        .unwrap_or_else(|e| self.fatal(&e));
-
-        // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
-        let mut script_str = String::with_capacity(2048);
-        script_str.push_str("version\n"); // List CDB (and more) version info in test output
-        script_str.push_str(".nvlist\n"); // List loaded `*.natvis` files, bulk of custom MSVC debug
-
-        // If a .js file exists next to the source file being tested, then this is a JavaScript
-        // debugging extension that needs to be loaded.
-        let mut js_extension = self.testpaths.file.clone();
-        js_extension.set_extension("cdb.js");
-        if js_extension.exists() {
-            script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension.to_string_lossy()));
-        }
-
-        // Set breakpoints on every line that contains the string "#break"
-        let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
-        for line in &dbg_cmds.breakpoint_lines {
-            script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line));
-        }
-
-        // Append the other `cdb-command:`s
-        for line in &dbg_cmds.commands {
-            script_str.push_str(line);
-            script_str.push('\n');
-        }
-
-        script_str.push_str("qq\n"); // Quit the debugger (including remote debugger, if any)
-
-        // Write the script into a file
-        debug!("script_str = {}", script_str);
-        self.dump_output_file(&script_str, "debugger.script");
-        let debugger_script = self.make_out_name("debugger.script");
-
-        let cdb_path = &self.config.cdb.as_ref().unwrap();
-        let mut cdb = Command::new(cdb_path);
-        cdb.arg("-lines") // Enable source line debugging.
-            .arg("-cf")
-            .arg(&debugger_script)
-            .arg(&exe_file);
-
-        let debugger_run_result = self.compose_and_run(
-            cdb,
-            self.config.run_lib_path.to_str().unwrap(),
-            None, // aux_path
-            None, // input
-        );
-
-        if !debugger_run_result.status.success() {
-            self.fatal_proc_rec("Error while running CDB", &debugger_run_result);
-        }
-
-        if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
-            self.fatal_proc_rec(&e, &debugger_run_result);
-        }
-    }
-
-    fn run_debuginfo_gdb_test(&self) {
-        let config = Config {
-            target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
-            host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
-            ..self.config.clone()
-        };
-
-        let test_cx = TestCx { config: &config, ..*self };
-
-        test_cx.run_debuginfo_gdb_test_no_opt();
-    }
-
-    fn run_debuginfo_gdb_test_no_opt(&self) {
-        let dbg_cmds = DebuggerCommands::parse_from(
-            &self.testpaths.file,
-            self.config,
-            &["gdb"],
-            self.revision,
-        )
-        .unwrap_or_else(|e| self.fatal(&e));
-        let mut cmds = dbg_cmds.commands.join("\n");
-
-        // compile test file (it should have 'compile-flags:-g' in the header)
-        let should_run = self.run_if_enabled();
-        let compiler_run_result = self.compile_test(should_run, Emit::None);
-        if !compiler_run_result.status.success() {
-            self.fatal_proc_rec("compilation failed!", &compiler_run_result);
-        }
-        if let WillExecute::Disabled = should_run {
-            return;
-        }
-
-        let exe_file = self.make_exe_name();
-
-        let debugger_run_result;
-        if is_android_gdb_target(&self.config.target) {
-            cmds = cmds.replace("run", "continue");
-
-            let tool_path = match self.config.android_cross_path.to_str() {
-                Some(x) => x.to_owned(),
-                None => self.fatal("cannot find android cross path"),
-            };
-
-            // write debugger script
-            let mut script_str = String::with_capacity(2048);
-            script_str.push_str(&format!("set charset {}\n", Self::charset()));
-            script_str.push_str(&format!("set sysroot {}\n", tool_path));
-            script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
-            script_str.push_str("target remote :5039\n");
-            script_str.push_str(&format!(
-                "set solib-search-path \
-                 ./{}/stage2/lib/rustlib/{}/lib/\n",
-                self.config.host, self.config.target
-            ));
-            for line in &dbg_cmds.breakpoint_lines {
-                script_str.push_str(
-                    format!(
-                        "break {:?}:{}\n",
-                        self.testpaths.file.file_name().unwrap().to_string_lossy(),
-                        *line
-                    )
-                    .as_str(),
-                );
-            }
-            script_str.push_str(&cmds);
-            script_str.push_str("\nquit\n");
-
-            debug!("script_str = {}", script_str);
-            self.dump_output_file(&script_str, "debugger.script");
-
-            let adb_path = &self.config.adb_path;
-
-            Command::new(adb_path)
-                .arg("push")
-                .arg(&exe_file)
-                .arg(&self.config.adb_test_dir)
-                .status()
-                .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
-
-            Command::new(adb_path)
-                .args(&["forward", "tcp:5039", "tcp:5039"])
-                .status()
-                .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
-
-            let adb_arg = format!(
-                "export LD_LIBRARY_PATH={}; \
-                 gdbserver{} :5039 {}/{}",
-                self.config.adb_test_dir.clone(),
-                if self.config.target.contains("aarch64") { "64" } else { "" },
-                self.config.adb_test_dir.clone(),
-                exe_file.file_name().unwrap().to_str().unwrap()
-            );
-
-            debug!("adb arg: {}", adb_arg);
-            let mut adb = Command::new(adb_path)
-                .args(&["shell", &adb_arg])
-                .stdout(Stdio::piped())
-                .stderr(Stdio::inherit())
-                .spawn()
-                .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
-
-            // Wait for the gdbserver to print out "Listening on port ..."
-            // at which point we know that it's started and then we can
-            // execute the debugger below.
-            let mut stdout = BufReader::new(adb.stdout.take().unwrap());
-            let mut line = String::new();
-            loop {
-                line.truncate(0);
-                stdout.read_line(&mut line).unwrap();
-                if line.starts_with("Listening on port 5039") {
-                    break;
-                }
-            }
-            drop(stdout);
-
-            let mut debugger_script = OsString::from("-command=");
-            debugger_script.push(self.make_out_name("debugger.script"));
-            let debugger_opts: &[&OsStr] =
-                &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
-
-            let gdb_path = self.config.gdb.as_ref().unwrap();
-            let Output { status, stdout, stderr } = Command::new(&gdb_path)
-                .args(debugger_opts)
-                .output()
-                .unwrap_or_else(|e| panic!("failed to exec `{gdb_path:?}`: {e:?}"));
-            let cmdline = {
-                let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
-                gdb.args(debugger_opts);
-                let cmdline = self.make_cmdline(&gdb, "");
-                logv(self.config, format!("executing {}", cmdline));
-                cmdline
-            };
-
-            debugger_run_result = ProcRes {
-                status,
-                stdout: String::from_utf8(stdout).unwrap(),
-                stderr: String::from_utf8(stderr).unwrap(),
-                truncated: Truncated::No,
-                cmdline,
-            };
-            if adb.kill().is_err() {
-                println!("Adb process is already finished.");
-            }
-        } else {
-            let rust_src_root =
-                self.config.find_rust_src_root().expect("Could not find Rust source root");
-            let rust_pp_module_rel_path = Path::new("./src/etc");
-            let rust_pp_module_abs_path =
-                rust_src_root.join(rust_pp_module_rel_path).to_str().unwrap().to_owned();
-            // write debugger script
-            let mut script_str = String::with_capacity(2048);
-            script_str.push_str(&format!("set charset {}\n", Self::charset()));
-            script_str.push_str("show version\n");
-
-            match self.config.gdb_version {
-                Some(version) => {
-                    println!("NOTE: compiletest thinks it is using GDB version {}", version);
-
-                    if version > extract_gdb_version("7.4").unwrap() {
-                        // Add the directory containing the pretty printers to
-                        // GDB's script auto loading safe path
-                        script_str.push_str(&format!(
-                            "add-auto-load-safe-path {}\n",
-                            rust_pp_module_abs_path.replace(r"\", r"\\")
-                        ));
-
-                        let output_base_dir = self.output_base_dir().to_str().unwrap().to_owned();
-
-                        // Add the directory containing the output binary to
-                        // include embedded pretty printers to GDB's script
-                        // auto loading safe path
-                        script_str.push_str(&format!(
-                            "add-auto-load-safe-path {}\n",
-                            output_base_dir.replace(r"\", r"\\")
-                        ));
-                    }
-                }
-                _ => {
-                    println!(
-                        "NOTE: compiletest does not know which version of \
-                         GDB it is using"
-                    );
-                }
-            }
-
-            // The following line actually doesn't have to do anything with
-            // pretty printing, it just tells GDB to print values on one line:
-            script_str.push_str("set print pretty off\n");
-
-            // Add the pretty printer directory to GDB's source-file search path
-            script_str
-                .push_str(&format!("directory {}\n", rust_pp_module_abs_path.replace(r"\", r"\\")));
-
-            // Load the target executable
-            script_str
-                .push_str(&format!("file {}\n", exe_file.to_str().unwrap().replace(r"\", r"\\")));
-
-            // Force GDB to print values in the Rust format.
-            script_str.push_str("set language rust\n");
-
-            // Add line breakpoints
-            for line in &dbg_cmds.breakpoint_lines {
-                script_str.push_str(&format!(
-                    "break '{}':{}\n",
-                    self.testpaths.file.file_name().unwrap().to_string_lossy(),
-                    *line
-                ));
-            }
-
-            script_str.push_str(&cmds);
-            script_str.push_str("\nquit\n");
-
-            debug!("script_str = {}", script_str);
-            self.dump_output_file(&script_str, "debugger.script");
-
-            let mut debugger_script = OsString::from("-command=");
-            debugger_script.push(self.make_out_name("debugger.script"));
-
-            let debugger_opts: &[&OsStr] =
-                &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
-
-            let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
-            let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
-                format!("{pp}:{rust_pp_module_abs_path}")
-            } else {
-                rust_pp_module_abs_path
-            };
-            gdb.args(debugger_opts).env("PYTHONPATH", pythonpath);
-
-            debugger_run_result =
-                self.compose_and_run(gdb, self.config.run_lib_path.to_str().unwrap(), None, None);
-        }
-
-        if !debugger_run_result.status.success() {
-            self.fatal_proc_rec("gdb failed to execute", &debugger_run_result);
-        }
-
-        if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
-            self.fatal_proc_rec(&e, &debugger_run_result);
-        }
-    }
-
-    fn run_debuginfo_lldb_test(&self) {
-        if self.config.lldb_python_dir.is_none() {
-            self.fatal("Can't run LLDB test because LLDB's python path is not set.");
-        }
-
-        let config = Config {
-            target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
-            host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
-            ..self.config.clone()
-        };
-
-        let test_cx = TestCx { config: &config, ..*self };
-
-        test_cx.run_debuginfo_lldb_test_no_opt();
-    }
-
-    fn run_debuginfo_lldb_test_no_opt(&self) {
-        // compile test file (it should have 'compile-flags:-g' in the header)
-        let should_run = self.run_if_enabled();
-        let compile_result = self.compile_test(should_run, Emit::None);
-        if !compile_result.status.success() {
-            self.fatal_proc_rec("compilation failed!", &compile_result);
-        }
-        if let WillExecute::Disabled = should_run {
-            return;
-        }
-
-        let exe_file = self.make_exe_name();
-
-        match self.config.lldb_version {
-            Some(ref version) => {
-                println!("NOTE: compiletest thinks it is using LLDB version {}", version);
-            }
-            _ => {
-                println!(
-                    "NOTE: compiletest does not know which version of \
-                     LLDB it is using"
-                );
-            }
-        }
-
-        // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(
-            &self.testpaths.file,
-            self.config,
-            &["lldb"],
-            self.revision,
-        )
-        .unwrap_or_else(|e| self.fatal(&e));
-
-        // Write debugger script:
-        // We don't want to hang when calling `quit` while the process is still running
-        let mut script_str = String::from("settings set auto-confirm true\n");
-
-        // Make LLDB emit its version, so we have it documented in the test output
-        script_str.push_str("version\n");
-
-        // Switch LLDB into "Rust mode"
-        let rust_src_root =
-            self.config.find_rust_src_root().expect("Could not find Rust source root");
-        let rust_pp_module_rel_path = Path::new("./src/etc");
-        let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path);
-
-        script_str.push_str(&format!(
-            "command script import {}/lldb_lookup.py\n",
-            rust_pp_module_abs_path.to_str().unwrap()
-        ));
-        File::open(rust_pp_module_abs_path.join("lldb_commands"))
-            .and_then(|mut file| file.read_to_string(&mut script_str))
-            .expect("Failed to read lldb_commands");
-
-        // Set breakpoints on every line that contains the string "#break"
-        let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
-        for line in &dbg_cmds.breakpoint_lines {
-            script_str.push_str(&format!(
-                "breakpoint set --file '{}' --line {}\n",
-                source_file_name, line
-            ));
-        }
-
-        // Append the other commands
-        for line in &dbg_cmds.commands {
-            script_str.push_str(line);
-            script_str.push('\n');
-        }
-
-        // Finally, quit the debugger
-        script_str.push_str("\nquit\n");
-
-        // Write the script into a file
-        debug!("script_str = {}", script_str);
-        self.dump_output_file(&script_str, "debugger.script");
-        let debugger_script = self.make_out_name("debugger.script");
-
-        // Let LLDB execute the script via lldb_batchmode.py
-        let debugger_run_result = self.run_lldb(&exe_file, &debugger_script, &rust_src_root);
-
-        if !debugger_run_result.status.success() {
-            self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
-        }
-
-        if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
-            self.fatal_proc_rec(&e, &debugger_run_result);
-        }
-    }
-
-    fn run_lldb(
-        &self,
-        test_executable: &Path,
-        debugger_script: &Path,
-        rust_src_root: &Path,
-    ) -> ProcRes {
-        // Prepare the lldb_batchmode which executes the debugger script
-        let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
-        let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
-            format!("{pp}:{}", self.config.lldb_python_dir.as_ref().unwrap())
-        } else {
-            self.config.lldb_python_dir.as_ref().unwrap().to_string()
-        };
-        self.run_command_to_procres(
-            Command::new(&self.config.python)
-                .arg(&lldb_script_path)
-                .arg(test_executable)
-                .arg(debugger_script)
-                .env("PYTHONUNBUFFERED", "1") // Help debugging #78665
-                .env("PYTHONPATH", pythonpath),
-        )
-    }
-
-    fn cleanup_debug_info_options(&self, options: &Vec<String>) -> Vec<String> {
-        // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
-        let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()];
-
-        options.iter().filter(|x| !options_to_remove.contains(x)).cloned().collect()
-    }
-
     fn maybe_add_external_args(&self, cmd: &mut Command, args: &Vec<String>) {
         // Filter out the arguments that should not be added by runtest here.
         //
@@ -2016,6 +1296,14 @@ impl<'test> TestCx<'test> {
             || self.config.src_base.ends_with("rustdoc-json")
     }
 
+    fn get_mir_dump_dir(&self) -> PathBuf {
+        let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
+        debug!("input_file: {:?}", self.testpaths.file);
+        mir_dump_dir.push(&self.testpaths.relative_dir);
+        mir_dump_dir.push(self.testpaths.file.file_stem().unwrap());
+        mir_dump_dir
+    }
+
     fn make_compile_args(
         &self,
         input_file: &Path,
@@ -2626,75 +1914,11 @@ impl<'test> TestCx<'test> {
         self.compose_and_run(filecheck, "", None, None)
     }
 
-    fn run_codegen_test(&self) {
-        if self.config.llvm_filecheck.is_none() {
-            self.fatal("missing --llvm-filecheck");
-        }
-
-        let (proc_res, output_path) = self.compile_test_and_save_ir();
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-
-        if let Some(PassMode::Build) = self.pass_mode() {
-            return;
-        }
-        let proc_res = self.verify_with_filecheck(&output_path);
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
-        }
-    }
-
-    fn run_assembly_test(&self) {
-        if self.config.llvm_filecheck.is_none() {
-            self.fatal("missing --llvm-filecheck");
-        }
-
-        let (proc_res, output_path) = self.compile_test_and_save_assembly();
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-
-        let proc_res = self.verify_with_filecheck(&output_path);
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
-        }
-    }
-
     fn charset() -> &'static str {
         // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
         if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" }
     }
 
-    fn run_rustdoc_test(&self) {
-        assert!(self.revision.is_none(), "revisions not relevant here");
-
-        let out_dir = self.output_base_dir();
-        remove_and_create_dir_all(&out_dir);
-
-        let proc_res = self.document(&out_dir, &self.testpaths);
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("rustdoc failed!", &proc_res);
-        }
-
-        if self.props.check_test_line_numbers_match {
-            self.check_rustdoc_test_option(proc_res);
-        } else {
-            let root = self.config.find_rust_src_root().unwrap();
-            let mut cmd = Command::new(&self.config.python);
-            cmd.arg(root.join("src/etc/htmldocck.py")).arg(&out_dir).arg(&self.testpaths.file);
-            if self.config.bless {
-                cmd.arg("--bless");
-            }
-            let res = self.run_command_to_procres(&mut cmd);
-            if !res.status.success() {
-                self.fatal_proc_rec_with_ctx("htmldocck failed!", &res, |mut this| {
-                    this.compare_to_default_rustdoc(&out_dir)
-                });
-            }
-        }
-    }
-
     fn compare_to_default_rustdoc(&mut self, out_dir: &Path) {
         if !self.config.has_tidy {
             return;
@@ -2847,49 +2071,6 @@ impl<'test> TestCx<'test> {
         };
     }
 
-    fn run_rustdoc_json_test(&self) {
-        //FIXME: Add bless option.
-
-        assert!(self.revision.is_none(), "revisions not relevant here");
-
-        let out_dir = self.output_base_dir();
-        remove_and_create_dir_all(&out_dir);
-
-        let proc_res = self.document(&out_dir, &self.testpaths);
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("rustdoc failed!", &proc_res);
-        }
-
-        let root = self.config.find_rust_src_root().unwrap();
-        let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
-        json_out.set_extension("json");
-        let res = self.run_command_to_procres(
-            Command::new(self.config.jsondocck_path.as_ref().unwrap())
-                .arg("--doc-dir")
-                .arg(root.join(&out_dir))
-                .arg("--template")
-                .arg(&self.testpaths.file),
-        );
-
-        if !res.status.success() {
-            self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| {
-                println!("Rustdoc Output:");
-                proc_res.print_info();
-            })
-        }
-
-        let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
-        json_out.set_extension("json");
-
-        let res = self.run_command_to_procres(
-            Command::new(self.config.jsondoclint_path.as_ref().unwrap()).arg(&json_out),
-        );
-
-        if !res.status.success() {
-            self.fatal_proc_rec("jsondoclint failed!", &res);
-        }
-    }
-
     fn get_lines<P: AsRef<Path>>(
         &self,
         path: &P,
@@ -2990,824 +2171,6 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn run_codegen_units_test(&self) {
-        assert!(self.revision.is_none(), "revisions not relevant here");
-
-        let proc_res = self.compile_test(WillExecute::No, Emit::None);
-
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-
-        self.check_no_compiler_crash(&proc_res, self.props.should_ice);
-
-        const PREFIX: &str = "MONO_ITEM ";
-        const CGU_MARKER: &str = "@@";
-
-        // Some MonoItems can contain {closure@/path/to/checkout/tests/codgen-units/test.rs}
-        // To prevent the current dir from leaking, we just replace the entire path to the test
-        // file with TEST_PATH.
-        let actual: Vec<MonoItem> = proc_res
-            .stdout
-            .lines()
-            .filter(|line| line.starts_with(PREFIX))
-            .map(|line| {
-                line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string()
-            })
-            .map(|line| str_to_mono_item(&line, true))
-            .collect();
-
-        let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None)
-            .iter()
-            .map(|e| str_to_mono_item(&e.msg[..], false))
-            .collect();
-
-        let mut missing = Vec::new();
-        let mut wrong_cgus = Vec::new();
-
-        for expected_item in &expected {
-            let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name);
-
-            if let Some(actual_item) = actual_item_with_same_name {
-                if !expected_item.codegen_units.is_empty() &&
-                   // Also check for codegen units
-                   expected_item.codegen_units != actual_item.codegen_units
-                {
-                    wrong_cgus.push((expected_item.clone(), actual_item.clone()));
-                }
-            } else {
-                missing.push(expected_item.string.clone());
-            }
-        }
-
-        let unexpected: Vec<_> = actual
-            .iter()
-            .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
-            .map(|acgu| acgu.string.clone())
-            .collect();
-
-        if !missing.is_empty() {
-            missing.sort();
-
-            println!("\nThese items should have been contained but were not:\n");
-
-            for item in &missing {
-                println!("{}", item);
-            }
-
-            println!("\n");
-        }
-
-        if !unexpected.is_empty() {
-            let sorted = {
-                let mut sorted = unexpected.clone();
-                sorted.sort();
-                sorted
-            };
-
-            println!("\nThese items were contained but should not have been:\n");
-
-            for item in sorted {
-                println!("{}", item);
-            }
-
-            println!("\n");
-        }
-
-        if !wrong_cgus.is_empty() {
-            wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
-            println!("\nThe following items were assigned to wrong codegen units:\n");
-
-            for &(ref expected_item, ref actual_item) in &wrong_cgus {
-                println!("{}", expected_item.name);
-                println!("  expected: {}", codegen_units_to_str(&expected_item.codegen_units));
-                println!("  actual:   {}", codegen_units_to_str(&actual_item.codegen_units));
-                println!();
-            }
-        }
-
-        if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) {
-            panic!();
-        }
-
-        #[derive(Clone, Eq, PartialEq)]
-        struct MonoItem {
-            name: String,
-            codegen_units: HashSet<String>,
-            string: String,
-        }
-
-        // [MONO_ITEM] name [@@ (cgu)+]
-        fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem {
-            let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() };
-
-            let full_string = format!("{}{}", PREFIX, s);
-
-            let parts: Vec<&str> =
-                s.split(CGU_MARKER).map(str::trim).filter(|s| !s.is_empty()).collect();
-
-            let name = parts[0].trim();
-
-            let cgus = if parts.len() > 1 {
-                let cgus_str = parts[1];
-
-                cgus_str
-                    .split(' ')
-                    .map(str::trim)
-                    .filter(|s| !s.is_empty())
-                    .map(|s| {
-                        if cgu_has_crate_disambiguator {
-                            remove_crate_disambiguators_from_set_of_cgu_names(s)
-                        } else {
-                            s.to_string()
-                        }
-                    })
-                    .collect()
-            } else {
-                HashSet::new()
-            };
-
-            MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string }
-        }
-
-        fn codegen_units_to_str(cgus: &HashSet<String>) -> String {
-            let mut cgus: Vec<_> = cgus.iter().collect();
-            cgus.sort();
-
-            let mut string = String::new();
-            for cgu in cgus {
-                string.push_str(&cgu[..]);
-                string.push(' ');
-            }
-
-            string
-        }
-
-        // Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or
-        // the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>,
-        // remove all crate-disambiguators.
-        fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String {
-            let Some(captures) =
-                static_regex!(r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?")
-                    .captures(cgu)
-            else {
-                panic!("invalid cgu name encountered: {cgu}");
-            };
-
-            let mut new_name = cgu.to_owned();
-
-            if let Some(d2) = captures.name("d2") {
-                new_name.replace_range(d2.start()..d2.end(), "");
-            }
-
-            let d1 = captures.name("d1").unwrap();
-            new_name.replace_range(d1.start()..d1.end(), "");
-
-            new_name
-        }
-
-        // The name of merged CGUs is constructed as the names of the original
-        // CGUs joined with "--". This function splits such composite CGU names
-        // and handles each component individually.
-        fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String {
-            cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::<Vec<_>>().join("--")
-        }
-    }
-
-    fn init_incremental_test(&self) {
-        // (See `run_incremental_test` for an overview of how incremental tests work.)
-
-        // Before any of the revisions have executed, create the
-        // incremental workproduct directory.  Delete any old
-        // incremental work products that may be there from prior
-        // runs.
-        let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
-        if incremental_dir.exists() {
-            // Canonicalizing the path will convert it to the //?/ format
-            // on Windows, which enables paths longer than 260 character
-            let canonicalized = incremental_dir.canonicalize().unwrap();
-            fs::remove_dir_all(canonicalized).unwrap();
-        }
-        fs::create_dir_all(&incremental_dir).unwrap();
-
-        if self.config.verbose {
-            println!("init_incremental_test: incremental_dir={}", incremental_dir.display());
-        }
-    }
-
-    fn run_incremental_test(&self) {
-        // Basic plan for a test incremental/foo/bar.rs:
-        // - load list of revisions rpass1, cfail2, rpass3
-        //   - each should begin with `cpass`, `rpass`, `cfail`, or `rfail`
-        //   - if `cpass`, expect compilation to succeed, don't execute
-        //   - if `rpass`, expect compilation and execution to succeed
-        //   - if `cfail`, expect compilation to fail
-        //   - if `rfail`, expect compilation to succeed and execution to fail
-        // - create a directory build/foo/bar.incremental
-        // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass1
-        //   - because name of revision starts with "rpass", expect success
-        // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C cfail2
-        //   - because name of revision starts with "cfail", expect an error
-        //   - load expected errors as usual, but filter for those that end in `[rfail2]`
-        // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass3
-        //   - because name of revision starts with "rpass", expect success
-        // - execute build/foo/bar.exe and save output
-        //
-        // FIXME -- use non-incremental mode as an oracle? That doesn't apply
-        // to #[rustc_dirty] and clean tests I guess
-
-        let revision = self.revision.expect("incremental tests require a list of revisions");
-
-        // Incremental workproduct directory should have already been created.
-        let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
-        assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir");
-
-        if self.config.verbose {
-            print!("revision={:?} props={:#?}", revision, self.props);
-        }
-
-        if revision.starts_with("cpass") {
-            if self.props.should_ice {
-                self.fatal("can only use should-ice in cfail tests");
-            }
-            self.run_cpass_test();
-        } else if revision.starts_with("rpass") {
-            if self.props.should_ice {
-                self.fatal("can only use should-ice in cfail tests");
-            }
-            self.run_rpass_test();
-        } else if revision.starts_with("rfail") {
-            if self.props.should_ice {
-                self.fatal("can only use should-ice in cfail tests");
-            }
-            self.run_rfail_test();
-        } else if revision.starts_with("cfail") {
-            self.run_cfail_test();
-        } else {
-            self.fatal("revision name must begin with cpass, rpass, rfail, or cfail");
-        }
-    }
-
-    fn run_rmake_test(&self) {
-        let test_dir = &self.testpaths.file;
-        if test_dir.join("rmake.rs").exists() {
-            self.run_rmake_v2_test();
-        } else if test_dir.join("Makefile").exists() {
-            self.run_rmake_legacy_test();
-        } else {
-            self.fatal("failed to find either `rmake.rs` or `Makefile`")
-        }
-    }
-
-    fn run_rmake_legacy_test(&self) {
-        let cwd = env::current_dir().unwrap();
-        let src_root = self.config.src_base.parent().unwrap().parent().unwrap();
-        let src_root = cwd.join(&src_root);
-
-        let tmpdir = cwd.join(self.output_base_name());
-        if tmpdir.exists() {
-            self.aggressive_rm_rf(&tmpdir).unwrap();
-        }
-        create_dir_all(&tmpdir).unwrap();
-
-        let host = &self.config.host;
-        let make = if host.contains("dragonfly")
-            || host.contains("freebsd")
-            || host.contains("netbsd")
-            || host.contains("openbsd")
-            || host.contains("aix")
-        {
-            "gmake"
-        } else {
-            "make"
-        };
-
-        let mut cmd = Command::new(make);
-        cmd.current_dir(&self.testpaths.file)
-            .stdout(Stdio::piped())
-            .stderr(Stdio::piped())
-            .env("TARGET", &self.config.target)
-            .env("PYTHON", &self.config.python)
-            .env("S", src_root)
-            .env("RUST_BUILD_STAGE", &self.config.stage_id)
-            .env("RUSTC", cwd.join(&self.config.rustc_path))
-            .env("TMPDIR", &tmpdir)
-            .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
-            .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
-            .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
-            .env("LLVM_COMPONENTS", &self.config.llvm_components)
-            // We for sure don't want these tests to run in parallel, so make
-            // sure they don't have access to these vars if we run via `make`
-            // at the top level
-            .env_remove("MAKEFLAGS")
-            .env_remove("MFLAGS")
-            .env_remove("CARGO_MAKEFLAGS");
-
-        if let Some(ref rustdoc) = self.config.rustdoc_path {
-            cmd.env("RUSTDOC", cwd.join(rustdoc));
-        }
-
-        if let Some(ref node) = self.config.nodejs {
-            cmd.env("NODE", node);
-        }
-
-        if let Some(ref linker) = self.config.target_linker {
-            cmd.env("RUSTC_LINKER", linker);
-        }
-
-        if let Some(ref clang) = self.config.run_clang_based_tests_with {
-            cmd.env("CLANG", clang);
-        }
-
-        if let Some(ref filecheck) = self.config.llvm_filecheck {
-            cmd.env("LLVM_FILECHECK", filecheck);
-        }
-
-        if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir {
-            cmd.env("LLVM_BIN_DIR", llvm_bin_dir);
-        }
-
-        if let Some(ref remote_test_client) = self.config.remote_test_client {
-            cmd.env("REMOTE_TEST_CLIENT", remote_test_client);
-        }
-
-        // We don't want RUSTFLAGS set from the outside to interfere with
-        // compiler flags set in the test cases:
-        cmd.env_remove("RUSTFLAGS");
-
-        // Use dynamic musl for tests because static doesn't allow creating dylibs
-        if self.config.host.contains("musl") {
-            cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1");
-        }
-
-        if self.config.bless {
-            cmd.env("RUSTC_BLESS_TEST", "--bless");
-            // Assume this option is active if the environment variable is "defined", with _any_ value.
-            // As an example, a `Makefile` can use this option by:
-            //
-            //   ifdef RUSTC_BLESS_TEST
-            //       cp "$(TMPDIR)"/actual_something.ext expected_something.ext
-            //   else
-            //       $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext
-            //   endif
-        }
-
-        if self.config.target.contains("msvc") && !self.config.cc.is_empty() {
-            // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
-            // and that `lib.exe` lives next to it.
-            let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
-
-            // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
-            // a path and instead passes `C:\msys64\foo`, so convert all
-            // `/`-arguments to MSVC here to `-` arguments.
-            let cflags = self
-                .config
-                .cflags
-                .split(' ')
-                .map(|s| s.replace("/", "-"))
-                .collect::<Vec<_>>()
-                .join(" ");
-            let cxxflags = self
-                .config
-                .cxxflags
-                .split(' ')
-                .map(|s| s.replace("/", "-"))
-                .collect::<Vec<_>>()
-                .join(" ");
-
-            cmd.env("IS_MSVC", "1")
-                .env("IS_WINDOWS", "1")
-                .env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
-                .env("MSVC_LIB_PATH", format!("{}", lib.display()))
-                .env("CC", format!("'{}' {}", self.config.cc, cflags))
-                .env("CXX", format!("'{}' {}", &self.config.cxx, cxxflags));
-        } else {
-            cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
-                .env("CXX", format!("{} {}", self.config.cxx, self.config.cxxflags))
-                .env("AR", &self.config.ar);
-
-            if self.config.target.contains("windows") {
-                cmd.env("IS_WINDOWS", "1");
-            }
-        }
-
-        let (output, truncated) =
-            self.read2_abbreviated(cmd.spawn().expect("failed to spawn `make`"));
-        if !output.status.success() {
-            let res = ProcRes {
-                status: output.status,
-                stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
-                stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
-                truncated,
-                cmdline: format!("{:?}", cmd),
-            };
-            self.fatal_proc_rec("make failed", &res);
-        }
-    }
-
-    fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
-        for e in path.read_dir()? {
-            let entry = e?;
-            let path = entry.path();
-            if entry.file_type()?.is_dir() {
-                self.aggressive_rm_rf(&path)?;
-            } else {
-                // Remove readonly files as well on windows (by default we can't)
-                fs::remove_file(&path).or_else(|e| {
-                    if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied {
-                        let mut meta = entry.metadata()?.permissions();
-                        meta.set_readonly(false);
-                        fs::set_permissions(&path, meta)?;
-                        fs::remove_file(&path)
-                    } else {
-                        Err(e)
-                    }
-                })?;
-            }
-        }
-        fs::remove_dir(path)
-    }
-
-    fn run_rmake_v2_test(&self) {
-        // For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe
-        // (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust
-        // library and is available under `build/$TARGET/stageN-tools-bin/librun_make_support.rlib`.
-        //
-        // 1. We need to build the recipe `rmake.rs` as a binary and link in the `run_make_support`
-        //    library.
-        // 2. We need to run the recipe binary.
-
-        // So we assume the rust-lang/rust project setup looks like the following (our `.` is the
-        // top-level directory, irrelevant entries to our purposes omitted):
-        //
-        // ```
-        // .                               // <- `source_root`
-        // ├── build/                      // <- `build_root`
-        // ├── compiler/
-        // ├── library/
-        // ├── src/
-        // │  └── tools/
-        // │     └── run_make_support/
-        // └── tests
-        //    └── run-make/
-        // ```
-
-        // `source_root` is the top-level directory containing the rust-lang/rust checkout.
-        let source_root =
-            self.config.find_rust_src_root().expect("could not determine rust source root");
-        // `self.config.build_base` is actually the build base folder + "test" + test suite name, it
-        // looks like `build/<host_triple>/test/run-make`. But we want `build/<host_triple>/`. Note
-        // that the `build` directory does not need to be called `build`, nor does it need to be
-        // under `source_root`, so we must compute it based off of `self.config.build_base`.
-        let build_root =
-            self.config.build_base.parent().and_then(Path::parent).unwrap().to_path_buf();
-
-        // We construct the following directory tree for each rmake.rs test:
-        // ```
-        // <base_dir>/
-        //     rmake.exe
-        //     rmake_out/
-        // ```
-        // having the recipe executable separate from the output artifacts directory allows the
-        // recipes to `remove_dir_all($TMPDIR)` without running into issues related trying to remove
-        // a currently running executable because the recipe executable is not under the
-        // `rmake_out/` directory.
-        //
-        // This setup intentionally diverges from legacy Makefile run-make tests.
-        let base_dir = self.output_base_name();
-        if base_dir.exists() {
-            self.aggressive_rm_rf(&base_dir).unwrap();
-        }
-        let rmake_out_dir = base_dir.join("rmake_out");
-        create_dir_all(&rmake_out_dir).unwrap();
-
-        // Copy all input files (apart from rmake.rs) to the temporary directory,
-        // so that the input directory structure from `tests/run-make/<test>` is mirrored
-        // to the `rmake_out` directory.
-        for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) {
-            let path = path.unwrap().path().to_path_buf();
-            if path.file_name().is_some_and(|s| s != "rmake.rs") {
-                let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap());
-                if path.is_dir() {
-                    copy_dir_all(&path, target).unwrap();
-                } else {
-                    fs::copy(&path, target).unwrap();
-                }
-            }
-        }
-
-        // `self.config.stage_id` looks like `stage1-<target_triple>`, but we only want
-        // the `stage1` part as that is what the output directories of bootstrap are prefixed with.
-        // Note that this *assumes* build layout from bootstrap is produced as:
-        //
-        // ```
-        // build/<target_triple>/          // <- this is `build_root`
-        // ├── stage0
-        // ├── stage0-bootstrap-tools
-        // ├── stage0-codegen
-        // ├── stage0-rustc
-        // ├── stage0-std
-        // ├── stage0-sysroot
-        // ├── stage0-tools
-        // ├── stage0-tools-bin
-        // ├── stage1
-        // ├── stage1-std
-        // ├── stage1-tools
-        // ├── stage1-tools-bin
-        // └── test
-        // ```
-        // FIXME(jieyouxu): improve the communication between bootstrap and compiletest here so
-        // we don't have to hack out a `stageN`.
-        let stage = self.config.stage_id.split('-').next().unwrap();
-
-        // In order to link in the support library as a rlib when compiling recipes, we need three
-        // paths:
-        // 1. Path of the built support library rlib itself.
-        // 2. Path of the built support library's dependencies directory.
-        // 3. Path of the built support library's dependencies' dependencies directory.
-        //
-        // The paths look like
-        //
-        // ```
-        // build/<target_triple>/
-        // ├── stageN-tools-bin/
-        // │   └── librun_make_support.rlib       // <- support rlib itself
-        // ├── stageN-tools/
-        // │   ├── release/deps/                  // <- deps of deps
-        // │   └── <host_triple>/release/deps/    // <- deps
-        // ```
-        //
-        // FIXME(jieyouxu): there almost certainly is a better way to do this (specifically how the
-        // support lib and its deps are organized, can't we copy them to the tools-bin dir as
-        // well?), but this seems to work for now.
-
-        let stage_tools_bin = build_root.join(format!("{stage}-tools-bin"));
-        let support_lib_path = stage_tools_bin.join("librun_make_support.rlib");
-
-        let stage_tools = build_root.join(format!("{stage}-tools"));
-        let support_lib_deps = stage_tools.join(&self.config.host).join("release").join("deps");
-        let support_lib_deps_deps = stage_tools.join("release").join("deps");
-
-        // To compile the recipe with rustc, we need to provide suitable dynamic library search
-        // paths to rustc. This includes both:
-        // 1. The "base" dylib search paths that was provided to compiletest, e.g. `LD_LIBRARY_PATH`
-        //    on some linux distros.
-        // 2. Specific library paths in `self.config.compile_lib_path` needed for running rustc.
-
-        let base_dylib_search_paths =
-            Vec::from_iter(env::split_paths(&env::var(dylib_env_var()).unwrap()));
-
-        let host_dylib_search_paths = {
-            let mut paths = vec![self.config.compile_lib_path.clone()];
-            paths.extend(base_dylib_search_paths.iter().cloned());
-            paths
-        };
-
-        // Calculate the paths of the recipe binary. As previously discussed, this is placed at
-        // `<base_dir>/<bin_name>` with `bin_name` being `rmake` or `rmake.exe` depending on
-        // platform.
-        let recipe_bin = {
-            let mut p = base_dir.join("rmake");
-            p.set_extension(env::consts::EXE_EXTENSION);
-            p
-        };
-
-        let mut rustc = Command::new(&self.config.rustc_path);
-        rustc
-            .arg("-o")
-            .arg(&recipe_bin)
-            // Specify library search paths for `run_make_support`.
-            .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap().to_string_lossy()))
-            .arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy()))
-            .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy()))
-            // Provide `run_make_support` as extern prelude, so test writers don't need to write
-            // `extern run_make_support;`.
-            .arg("--extern")
-            .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy()))
-            .arg("--edition=2021")
-            .arg(&self.testpaths.file.join("rmake.rs"))
-            // Provide necessary library search paths for rustc.
-            .env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap());
-
-        // In test code we want to be very pedantic about values being silently discarded that are
-        // annotated with `#[must_use]`.
-        rustc.arg("-Dunused_must_use");
-
-        // > `cg_clif` uses `COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0` for running the rustc
-        // > test suite. With the introduction of rmake.rs this broke. `librun_make_support.rlib` is
-        // > compiled using the bootstrap rustc wrapper which sets `--sysroot
-        // > build/aarch64-unknown-linux-gnu/stage0-sysroot`, but then compiletest will compile
-        // > `rmake.rs` using the sysroot of the bootstrap compiler causing it to not find the
-        // > `libstd.rlib` against which `librun_make_support.rlib` is compiled.
-        //
-        // The gist here is that we have to pass the proper stage0 sysroot if we want
-        //
-        // ```
-        // $ COMPILETEST_FORCE_STAGE0=1 ./x test run-make --stage 0
-        // ```
-        //
-        // to work correctly.
-        //
-        // See <https://github.com/rust-lang/rust/pull/122248> for more background.
-        if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() {
-            let stage0_sysroot = build_root.join("stage0-sysroot");
-            rustc.arg("--sysroot").arg(&stage0_sysroot);
-        }
-
-        // Now run rustc to build the recipe.
-        let res = self.run_command_to_procres(&mut rustc);
-        if !res.status.success() {
-            self.fatal_proc_rec("run-make test failed: could not build `rmake.rs` recipe", &res);
-        }
-
-        // To actually run the recipe, we have to provide the recipe with a bunch of information
-        // provided through env vars.
-
-        // Compute stage-specific standard library paths.
-        let stage_std_path = build_root.join(&stage).join("lib");
-
-        // Compute dynamic library search paths for recipes.
-        let recipe_dylib_search_paths = {
-            let mut paths = base_dylib_search_paths.clone();
-            paths.push(support_lib_path.parent().unwrap().to_path_buf());
-            paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib"));
-            paths
-        };
-
-        // Compute runtime library search paths for recipes. This is target-specific.
-        let target_runtime_dylib_search_paths = {
-            let mut paths = vec![rmake_out_dir.clone()];
-            paths.extend(base_dylib_search_paths.iter().cloned());
-            paths
-        };
-
-        // FIXME(jieyouxu): please rename `TARGET_RPATH_ENV`, `HOST_RPATH_DIR` and
-        // `TARGET_RPATH_DIR`, it is **extremely** confusing!
-        let mut cmd = Command::new(&recipe_bin);
-        cmd.current_dir(&rmake_out_dir)
-            .stdout(Stdio::piped())
-            .stderr(Stdio::piped())
-            // Provide the target-specific env var that is used to record dylib search paths. For
-            // example, this could be `LD_LIBRARY_PATH` on some linux distros but `PATH` on Windows.
-            .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
-            // Provide the dylib search paths.
-            .env(dylib_env_var(), &env::join_paths(recipe_dylib_search_paths).unwrap())
-            // Provide runtime dylib search paths.
-            .env("TARGET_RPATH_ENV", &env::join_paths(target_runtime_dylib_search_paths).unwrap())
-            // Provide the target.
-            .env("TARGET", &self.config.target)
-            // Some tests unfortunately still need Python, so provide path to a Python interpreter.
-            .env("PYTHON", &self.config.python)
-            // Provide path to checkout root. This is the top-level directory containing
-            // rust-lang/rust checkout.
-            .env("SOURCE_ROOT", &source_root)
-            // Provide path to stage-corresponding rustc.
-            .env("RUSTC", &self.config.rustc_path)
-            // Provide the directory to libraries that are needed to run the *compiler*. This is not
-            // to be confused with `TARGET_RPATH_ENV` or `TARGET_RPATH_DIR`. This is needed if the
-            // recipe wants to invoke rustc.
-            .env("HOST_RPATH_DIR", &self.config.compile_lib_path)
-            // Provide the directory to libraries that might be needed to run compiled binaries
-            // (further compiled by the recipe!).
-            .env("TARGET_RPATH_DIR", &self.config.run_lib_path)
-            // Provide which LLVM components are available (e.g. which LLVM components are provided
-            // through a specific CI runner).
-            .env("LLVM_COMPONENTS", &self.config.llvm_components);
-
-        if let Some(ref rustdoc) = self.config.rustdoc_path {
-            cmd.env("RUSTDOC", source_root.join(rustdoc));
-        }
-
-        if let Some(ref node) = self.config.nodejs {
-            cmd.env("NODE", node);
-        }
-
-        if let Some(ref linker) = self.config.target_linker {
-            cmd.env("RUSTC_LINKER", linker);
-        }
-
-        if let Some(ref clang) = self.config.run_clang_based_tests_with {
-            cmd.env("CLANG", clang);
-        }
-
-        if let Some(ref filecheck) = self.config.llvm_filecheck {
-            cmd.env("LLVM_FILECHECK", filecheck);
-        }
-
-        if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir {
-            cmd.env("LLVM_BIN_DIR", llvm_bin_dir);
-        }
-
-        if let Some(ref remote_test_client) = self.config.remote_test_client {
-            cmd.env("REMOTE_TEST_CLIENT", remote_test_client);
-        }
-
-        // We don't want RUSTFLAGS set from the outside to interfere with
-        // compiler flags set in the test cases:
-        cmd.env_remove("RUSTFLAGS");
-
-        // Use dynamic musl for tests because static doesn't allow creating dylibs
-        if self.config.host.contains("musl") {
-            cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1");
-        }
-
-        if self.config.bless {
-            // If we're running in `--bless` mode, set an environment variable to tell
-            // `run_make_support` to bless snapshot files instead of checking them.
-            //
-            // The value is this test's source directory, because the support code
-            // will need that path in order to bless the _original_ snapshot files,
-            // not the copies in `rmake_out`.
-            // (See <https://github.com/rust-lang/rust/issues/129038>.)
-            cmd.env("RUSTC_BLESS_TEST", &self.testpaths.file);
-        }
-
-        if self.config.target.contains("msvc") && !self.config.cc.is_empty() {
-            // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
-            // and that `lib.exe` lives next to it.
-            let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
-
-            // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
-            // a path and instead passes `C:\msys64\foo`, so convert all
-            // `/`-arguments to MSVC here to `-` arguments.
-            let cflags = self
-                .config
-                .cflags
-                .split(' ')
-                .map(|s| s.replace("/", "-"))
-                .collect::<Vec<_>>()
-                .join(" ");
-            let cxxflags = self
-                .config
-                .cxxflags
-                .split(' ')
-                .map(|s| s.replace("/", "-"))
-                .collect::<Vec<_>>()
-                .join(" ");
-
-            cmd.env("IS_MSVC", "1")
-                .env("IS_WINDOWS", "1")
-                .env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
-                .env("MSVC_LIB_PATH", format!("{}", lib.display()))
-                // Note: we diverge from legacy run_make and don't lump `CC` the compiler and
-                // default flags together.
-                .env("CC_DEFAULT_FLAGS", &cflags)
-                .env("CC", &self.config.cc)
-                .env("CXX_DEFAULT_FLAGS", &cxxflags)
-                .env("CXX", &self.config.cxx);
-        } else {
-            cmd.env("CC_DEFAULT_FLAGS", &self.config.cflags)
-                .env("CC", &self.config.cc)
-                .env("CXX_DEFAULT_FLAGS", &self.config.cxxflags)
-                .env("CXX", &self.config.cxx)
-                .env("AR", &self.config.ar);
-
-            if self.config.target.contains("windows") {
-                cmd.env("IS_WINDOWS", "1");
-            }
-        }
-
-        let (Output { stdout, stderr, status }, truncated) =
-            self.read2_abbreviated(cmd.spawn().expect("failed to spawn `rmake`"));
-        if !status.success() {
-            let res = ProcRes {
-                status,
-                stdout: String::from_utf8_lossy(&stdout).into_owned(),
-                stderr: String::from_utf8_lossy(&stderr).into_owned(),
-                truncated,
-                cmdline: format!("{:?}", cmd),
-            };
-            self.fatal_proc_rec("rmake recipe failed to complete", &res);
-        }
-    }
-
-    fn run_js_doc_test(&self) {
-        if let Some(nodejs) = &self.config.nodejs {
-            let out_dir = self.output_base_dir();
-
-            self.document(&out_dir, &self.testpaths);
-
-            let root = self.config.find_rust_src_root().unwrap();
-            let file_stem =
-                self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem");
-            let res = self.run_command_to_procres(
-                Command::new(&nodejs)
-                    .arg(root.join("src/tools/rustdoc-js/tester.js"))
-                    .arg("--doc-folder")
-                    .arg(out_dir)
-                    .arg("--crate-name")
-                    .arg(file_stem.replace("-", "_"))
-                    .arg("--test-file")
-                    .arg(self.testpaths.file.with_extension("js")),
-            );
-            if !res.status.success() {
-                self.fatal_proc_rec("rustdoc-js test failed!", &res);
-            }
-        } else {
-            self.fatal("no nodeJS");
-        }
-    }
-
     fn force_color_svg(&self) -> bool {
         self.props.compile_flags.iter().any(|s| s.contains("--color=always"))
     }
@@ -3895,377 +2258,6 @@ impl<'test> TestCx<'test> {
         errors
     }
 
-    fn run_ui_test(&self) {
-        if let Some(FailMode::Build) = self.props.fail_mode {
-            // Make sure a build-fail test cannot fail due to failing analysis (e.g. typeck).
-            let pm = Some(PassMode::Check);
-            let proc_res =
-                self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new());
-            self.check_if_test_should_compile(&proc_res, pm);
-        }
-
-        let pm = self.pass_mode();
-        let should_run = self.should_run(pm);
-        let emit_metadata = self.should_emit_metadata(pm);
-        let proc_res = self.compile_test(should_run, emit_metadata);
-        self.check_if_test_should_compile(&proc_res, pm);
-        if matches!(proc_res.truncated, Truncated::Yes)
-            && !self.props.dont_check_compiler_stdout
-            && !self.props.dont_check_compiler_stderr
-        {
-            self.fatal_proc_rec(
-                "compiler output got truncated, cannot compare with reference file",
-                &proc_res,
-            );
-        }
-
-        // if the user specified a format in the ui test
-        // print the output to the stderr file, otherwise extract
-        // the rendered error messages from json and print them
-        let explicit = self.props.compile_flags.iter().any(|s| s.contains("--error-format"));
-
-        let expected_fixed = self.load_expected_output(UI_FIXED);
-
-        self.check_and_prune_duplicate_outputs(&proc_res, &[], &[]);
-
-        let mut errors = self.load_compare_outputs(&proc_res, TestOutput::Compile, explicit);
-        let rustfix_input = json::rustfix_diagnostics_only(&proc_res.stderr);
-
-        if self.config.compare_mode.is_some() {
-            // don't test rustfix with nll right now
-        } else if self.config.rustfix_coverage {
-            // Find out which tests have `MachineApplicable` suggestions but are missing
-            // `run-rustfix` or `run-rustfix-only-machine-applicable` headers.
-            //
-            // This will return an empty `Vec` in case the executed test file has a
-            // `compile-flags: --error-format=xxxx` header with a value other than `json`.
-            let suggestions = get_suggestions_from_json(
-                &rustfix_input,
-                &HashSet::new(),
-                Filter::MachineApplicableOnly,
-            )
-            .unwrap_or_default();
-            if !suggestions.is_empty()
-                && !self.props.run_rustfix
-                && !self.props.rustfix_only_machine_applicable
-            {
-                let mut coverage_file_path = self.config.build_base.clone();
-                coverage_file_path.push("rustfix_missing_coverage.txt");
-                debug!("coverage_file_path: {}", coverage_file_path.display());
-
-                let mut file = OpenOptions::new()
-                    .create(true)
-                    .append(true)
-                    .open(coverage_file_path.as_path())
-                    .expect("could not create or open file");
-
-                if let Err(e) = writeln!(file, "{}", self.testpaths.file.display()) {
-                    panic!("couldn't write to {}: {e:?}", coverage_file_path.display());
-                }
-            }
-        } else if self.props.run_rustfix {
-            // Apply suggestions from rustc to the code itself
-            let unfixed_code = self.load_expected_output_from_path(&self.testpaths.file).unwrap();
-            let suggestions = get_suggestions_from_json(
-                &rustfix_input,
-                &HashSet::new(),
-                if self.props.rustfix_only_machine_applicable {
-                    Filter::MachineApplicableOnly
-                } else {
-                    Filter::Everything
-                },
-            )
-            .unwrap();
-            let fixed_code = apply_suggestions(&unfixed_code, &suggestions).unwrap_or_else(|e| {
-                panic!(
-                    "failed to apply suggestions for {:?} with rustfix: {}",
-                    self.testpaths.file, e
-                )
-            });
-
-            errors += self.compare_output("fixed", &fixed_code, &expected_fixed);
-        } else if !expected_fixed.is_empty() {
-            panic!(
-                "the `//@ run-rustfix` directive wasn't found but a `*.fixed` \
-                 file was found"
-            );
-        }
-
-        if errors > 0 {
-            println!("To update references, rerun the tests and pass the `--bless` flag");
-            let relative_path_to_file =
-                self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
-            println!(
-                "To only update this specific test, also pass `--test-args {}`",
-                relative_path_to_file.display(),
-            );
-            self.fatal_proc_rec(
-                &format!("{} errors occurred comparing output.", errors),
-                &proc_res,
-            );
-        }
-
-        let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
-
-        if let WillExecute::Yes = should_run {
-            let proc_res = self.exec_compiled_test();
-            let run_output_errors = if self.props.check_run_results {
-                self.load_compare_outputs(&proc_res, TestOutput::Run, explicit)
-            } else {
-                0
-            };
-            if run_output_errors > 0 {
-                self.fatal_proc_rec(
-                    &format!("{} errors occurred comparing run output.", run_output_errors),
-                    &proc_res,
-                );
-            }
-            if self.should_run_successfully(pm) {
-                if !proc_res.status.success() {
-                    self.fatal_proc_rec("test run failed!", &proc_res);
-                }
-            } else if proc_res.status.success() {
-                self.fatal_proc_rec("test run succeeded!", &proc_res);
-            }
-
-            if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty()
-            {
-                // "// error-pattern" comments
-                let output_to_check = self.get_output(&proc_res);
-                self.check_all_error_patterns(&output_to_check, &proc_res, pm);
-            }
-        }
-
-        debug!(
-            "run_ui_test: explicit={:?} config.compare_mode={:?} expected_errors={:?} \
-               proc_res.status={:?} props.error_patterns={:?}",
-            explicit,
-            self.config.compare_mode,
-            expected_errors,
-            proc_res.status,
-            self.props.error_patterns
-        );
-
-        let check_patterns = should_run == WillExecute::No
-            && (!self.props.error_patterns.is_empty()
-                || !self.props.regex_error_patterns.is_empty());
-        if !explicit && self.config.compare_mode.is_none() {
-            let check_annotations = !check_patterns || !expected_errors.is_empty();
-
-            if check_annotations {
-                // "//~ERROR comments"
-                self.check_expected_errors(expected_errors, &proc_res);
-            }
-        } else if explicit && !expected_errors.is_empty() {
-            let msg = format!(
-                "line {}: cannot combine `--error-format` with {} annotations; use `error-pattern` instead",
-                expected_errors[0].line_num,
-                expected_errors[0].kind.unwrap_or(ErrorKind::Error),
-            );
-            self.fatal(&msg);
-        }
-        if check_patterns {
-            // "// error-pattern" comments
-            let output_to_check = self.get_output(&proc_res);
-            self.check_all_error_patterns(&output_to_check, &proc_res, pm);
-        }
-
-        if self.props.run_rustfix && self.config.compare_mode.is_none() {
-            // And finally, compile the fixed code and make sure it both
-            // succeeds and has no diagnostics.
-            let mut rustc = self.make_compile_args(
-                &self.expected_output_path(UI_FIXED),
-                TargetLocation::ThisFile(self.make_exe_name()),
-                emit_metadata,
-                AllowUnused::No,
-                LinkToAux::Yes,
-                Vec::new(),
-            );
-
-            // If a test is revisioned, it's fixed source file can be named "a.foo.fixed", which,
-            // well, "a.foo" isn't a valid crate name. So we explicitly mangle the test name
-            // (including the revision) here to avoid the test writer having to manually specify a
-            // `#![crate_name = "..."]` as a workaround. This is okay since we're only checking if
-            // the fixed code is compilable.
-            if self.revision.is_some() {
-                let crate_name =
-                    self.testpaths.file.file_stem().expect("test must have a file stem");
-                // crate name must be alphanumeric or `_`.
-                let crate_name =
-                    crate_name.to_str().expect("crate name implies file name must be valid UTF-8");
-                // replace `a.foo` -> `a__foo` for crate name purposes.
-                // replace `revision-name-with-dashes` -> `revision_name_with_underscore`
-                let crate_name = crate_name.replace('.', "__");
-                let crate_name = crate_name.replace('-', "_");
-                rustc.arg("--crate-name");
-                rustc.arg(crate_name);
-            }
-
-            let res = self.compose_and_run_compiler(rustc, None, self.testpaths);
-            if !res.status.success() {
-                self.fatal_proc_rec("failed to compile fixed code", &res);
-            }
-            if !res.stderr.is_empty()
-                && !self.props.rustfix_only_machine_applicable
-                && !json::rustfix_diagnostics_only(&res.stderr).is_empty()
-            {
-                self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
-            }
-        }
-    }
-
-    fn run_mir_opt_test(&self) {
-        let pm = self.pass_mode();
-        let should_run = self.should_run(pm);
-
-        let mut test_info = files_for_miropt_test(
-            &self.testpaths.file,
-            self.config.get_pointer_width(),
-            self.config.target_cfg().panic.for_miropt_test_tools(),
-        );
-
-        let passes = std::mem::take(&mut test_info.passes);
-
-        let proc_res = self.compile_test_with_passes(should_run, Emit::Mir, passes);
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-        self.check_mir_dump(test_info);
-
-        if let WillExecute::Yes = should_run {
-            let proc_res = self.exec_compiled_test();
-
-            if !proc_res.status.success() {
-                self.fatal_proc_rec("test run failed!", &proc_res);
-            }
-        }
-    }
-
-    fn check_mir_dump(&self, test_info: MiroptTest) {
-        let test_dir = self.testpaths.file.parent().unwrap();
-        let test_crate =
-            self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_");
-
-        let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info;
-
-        if self.config.bless {
-            for e in
-                glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, suffix)).unwrap()
-            {
-                std::fs::remove_file(e.unwrap()).unwrap();
-            }
-            for e in
-                glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, suffix)).unwrap()
-            {
-                std::fs::remove_file(e.unwrap()).unwrap();
-            }
-        }
-
-        for MiroptTestFile { from_file, to_file, expected_file } in files {
-            let dumped_string = if let Some(after) = to_file {
-                self.diff_mir_files(from_file.into(), after.into())
-            } else {
-                let mut output_file = PathBuf::new();
-                output_file.push(self.get_mir_dump_dir());
-                output_file.push(&from_file);
-                debug!(
-                    "comparing the contents of: {} with {}",
-                    output_file.display(),
-                    expected_file.display()
-                );
-                if !output_file.exists() {
-                    panic!(
-                        "Output file `{}` from test does not exist, available files are in `{}`",
-                        output_file.display(),
-                        output_file.parent().unwrap().display()
-                    );
-                }
-                self.check_mir_test_timestamp(&from_file, &output_file);
-                let dumped_string = fs::read_to_string(&output_file).unwrap();
-                self.normalize_output(&dumped_string, &[])
-            };
-
-            if self.config.bless {
-                let _ = std::fs::remove_file(&expected_file);
-                std::fs::write(expected_file, dumped_string.as_bytes()).unwrap();
-            } else {
-                if !expected_file.exists() {
-                    panic!("Output file `{}` from test does not exist", expected_file.display());
-                }
-                let expected_string = fs::read_to_string(&expected_file).unwrap();
-                if dumped_string != expected_string {
-                    print!("{}", write_diff(&expected_string, &dumped_string, 3));
-                    panic!(
-                        "Actual MIR output differs from expected MIR output {}",
-                        expected_file.display()
-                    );
-                }
-            }
-        }
-
-        if run_filecheck {
-            let output_path = self.output_base_name().with_extension("mir");
-            let proc_res = self.verify_with_filecheck(&output_path);
-            if !proc_res.status.success() {
-                self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
-            }
-        }
-    }
-
-    fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String {
-        let to_full_path = |path: PathBuf| {
-            let full = self.get_mir_dump_dir().join(&path);
-            if !full.exists() {
-                panic!(
-                    "the mir dump file for {} does not exist (requested in {})",
-                    path.display(),
-                    self.testpaths.file.display(),
-                );
-            }
-            full
-        };
-        let before = to_full_path(before);
-        let after = to_full_path(after);
-        debug!("comparing the contents of: {} with {}", before.display(), after.display());
-        let before = fs::read_to_string(before).unwrap();
-        let after = fs::read_to_string(after).unwrap();
-        let before = self.normalize_output(&before, &[]);
-        let after = self.normalize_output(&after, &[]);
-        let mut dumped_string = String::new();
-        for result in diff::lines(&before, &after) {
-            use std::fmt::Write;
-            match result {
-                diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(),
-                diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(),
-                diff::Result::Both(s, _) => writeln!(dumped_string, "  {}", s).unwrap(),
-            }
-        }
-        dumped_string
-    }
-
-    fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) {
-        let t = |file| fs::metadata(file).unwrap().modified().unwrap();
-        let source_file = &self.testpaths.file;
-        let output_time = t(output_file);
-        let source_time = t(source_file);
-        if source_time > output_time {
-            debug!("source file time: {:?} output file time: {:?}", source_time, output_time);
-            panic!(
-                "test source file `{}` is newer than potentially stale output file `{}`.",
-                source_file.display(),
-                test_name
-            );
-        }
-    }
-
-    fn get_mir_dump_dir(&self) -> PathBuf {
-        let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
-        debug!("input_file: {:?}", self.testpaths.file);
-        mir_dump_dir.push(&self.testpaths.relative_dir);
-        mir_dump_dir.push(self.testpaths.file.file_stem().unwrap());
-        mir_dump_dir
-    }
-
     fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String {
         // Crude heuristic to detect when the output should have JSON-specific
         // normalization steps applied.
@@ -4634,6 +2626,77 @@ impl<'test> TestCx<'test> {
         let stamp = crate::stamp(&self.config, self.testpaths, self.revision);
         fs::write(&stamp, compute_stamp_hash(&self.config)).unwrap();
     }
+
+    fn init_incremental_test(&self) {
+        // (See `run_incremental_test` for an overview of how incremental tests work.)
+
+        // Before any of the revisions have executed, create the
+        // incremental workproduct directory.  Delete any old
+        // incremental work products that may be there from prior
+        // runs.
+        let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
+        if incremental_dir.exists() {
+            // Canonicalizing the path will convert it to the //?/ format
+            // on Windows, which enables paths longer than 260 character
+            let canonicalized = incremental_dir.canonicalize().unwrap();
+            fs::remove_dir_all(canonicalized).unwrap();
+        }
+        fs::create_dir_all(&incremental_dir).unwrap();
+
+        if self.config.verbose {
+            println!("init_incremental_test: incremental_dir={}", incremental_dir.display());
+        }
+    }
+
+    // FIXME(jieyouxu): `run_rpass_test` is hoisted out here and not in incremental because
+    // apparently valgrind test falls back to `run_rpass_test` if valgrind isn't available, which
+    // seems highly questionable to me.
+    fn run_rpass_test(&self) {
+        let emit_metadata = self.should_emit_metadata(self.pass_mode());
+        let should_run = self.run_if_enabled();
+        let proc_res = self.compile_test(should_run, emit_metadata);
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        // FIXME(#41968): Move this check to tidy?
+        if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
+            self.fatal("run-pass tests with expected warnings should be moved to ui/");
+        }
+
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
+        let proc_res = self.exec_compiled_test();
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("test run failed!", &proc_res);
+        }
+    }
+
+    fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
+        for e in path.read_dir()? {
+            let entry = e?;
+            let path = entry.path();
+            if entry.file_type()?.is_dir() {
+                self.aggressive_rm_rf(&path)?;
+            } else {
+                // Remove readonly files as well on windows (by default we can't)
+                fs::remove_file(&path).or_else(|e| {
+                    if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied {
+                        let mut meta = entry.metadata()?.permissions();
+                        meta.set_readonly(false);
+                        fs::set_permissions(&path, meta)?;
+                        fs::remove_file(&path)
+                    } else {
+                        Err(e)
+                    }
+                })?;
+            }
+        }
+        fs::remove_dir(path)
+    }
 }
 
 struct ProcArgs {
diff --git a/src/tools/compiletest/src/runtest/assembly.rs b/src/tools/compiletest/src/runtest/assembly.rs
new file mode 100644
index 00000000000..430a5534da1
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/assembly.rs
@@ -0,0 +1,19 @@
+use super::TestCx;
+
+impl TestCx<'_> {
+    pub(super) fn run_assembly_test(&self) {
+        if self.config.llvm_filecheck.is_none() {
+            self.fatal("missing --llvm-filecheck");
+        }
+
+        let (proc_res, output_path) = self.compile_test_and_save_assembly();
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        let proc_res = self.verify_with_filecheck(&output_path);
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/codegen.rs b/src/tools/compiletest/src/runtest/codegen.rs
new file mode 100644
index 00000000000..6e61ab5e46d
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/codegen.rs
@@ -0,0 +1,22 @@
+use super::{PassMode, TestCx};
+
+impl TestCx<'_> {
+    pub(super) fn run_codegen_test(&self) {
+        if self.config.llvm_filecheck.is_none() {
+            self.fatal("missing --llvm-filecheck");
+        }
+
+        let (proc_res, output_path) = self.compile_test_and_save_ir();
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        if let Some(PassMode::Build) = self.pass_mode() {
+            return;
+        }
+        let proc_res = self.verify_with_filecheck(&output_path);
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/codegen_units.rs b/src/tools/compiletest/src/runtest/codegen_units.rs
new file mode 100644
index 00000000000..6c866cbef21
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/codegen_units.rs
@@ -0,0 +1,191 @@
+use std::collections::HashSet;
+
+use super::{Emit, TestCx, WillExecute};
+use crate::errors;
+use crate::util::static_regex;
+
+impl TestCx<'_> {
+    pub(super) fn run_codegen_units_test(&self) {
+        assert!(self.revision.is_none(), "revisions not relevant here");
+
+        let proc_res = self.compile_test(WillExecute::No, Emit::None);
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        self.check_no_compiler_crash(&proc_res, self.props.should_ice);
+
+        const PREFIX: &str = "MONO_ITEM ";
+        const CGU_MARKER: &str = "@@";
+
+        // Some MonoItems can contain {closure@/path/to/checkout/tests/codgen-units/test.rs}
+        // To prevent the current dir from leaking, we just replace the entire path to the test
+        // file with TEST_PATH.
+        let actual: Vec<MonoItem> = proc_res
+            .stdout
+            .lines()
+            .filter(|line| line.starts_with(PREFIX))
+            .map(|line| {
+                line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string()
+            })
+            .map(|line| str_to_mono_item(&line, true))
+            .collect();
+
+        let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None)
+            .iter()
+            .map(|e| str_to_mono_item(&e.msg[..], false))
+            .collect();
+
+        let mut missing = Vec::new();
+        let mut wrong_cgus = Vec::new();
+
+        for expected_item in &expected {
+            let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name);
+
+            if let Some(actual_item) = actual_item_with_same_name {
+                if !expected_item.codegen_units.is_empty() &&
+                   // Also check for codegen units
+                   expected_item.codegen_units != actual_item.codegen_units
+                {
+                    wrong_cgus.push((expected_item.clone(), actual_item.clone()));
+                }
+            } else {
+                missing.push(expected_item.string.clone());
+            }
+        }
+
+        let unexpected: Vec<_> = actual
+            .iter()
+            .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
+            .map(|acgu| acgu.string.clone())
+            .collect();
+
+        if !missing.is_empty() {
+            missing.sort();
+
+            println!("\nThese items should have been contained but were not:\n");
+
+            for item in &missing {
+                println!("{}", item);
+            }
+
+            println!("\n");
+        }
+
+        if !unexpected.is_empty() {
+            let sorted = {
+                let mut sorted = unexpected.clone();
+                sorted.sort();
+                sorted
+            };
+
+            println!("\nThese items were contained but should not have been:\n");
+
+            for item in sorted {
+                println!("{}", item);
+            }
+
+            println!("\n");
+        }
+
+        if !wrong_cgus.is_empty() {
+            wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
+            println!("\nThe following items were assigned to wrong codegen units:\n");
+
+            for &(ref expected_item, ref actual_item) in &wrong_cgus {
+                println!("{}", expected_item.name);
+                println!("  expected: {}", codegen_units_to_str(&expected_item.codegen_units));
+                println!("  actual:   {}", codegen_units_to_str(&actual_item.codegen_units));
+                println!();
+            }
+        }
+
+        if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) {
+            panic!();
+        }
+
+        #[derive(Clone, Eq, PartialEq)]
+        struct MonoItem {
+            name: String,
+            codegen_units: HashSet<String>,
+            string: String,
+        }
+
+        // [MONO_ITEM] name [@@ (cgu)+]
+        fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem {
+            let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() };
+
+            let full_string = format!("{}{}", PREFIX, s);
+
+            let parts: Vec<&str> =
+                s.split(CGU_MARKER).map(str::trim).filter(|s| !s.is_empty()).collect();
+
+            let name = parts[0].trim();
+
+            let cgus = if parts.len() > 1 {
+                let cgus_str = parts[1];
+
+                cgus_str
+                    .split(' ')
+                    .map(str::trim)
+                    .filter(|s| !s.is_empty())
+                    .map(|s| {
+                        if cgu_has_crate_disambiguator {
+                            remove_crate_disambiguators_from_set_of_cgu_names(s)
+                        } else {
+                            s.to_string()
+                        }
+                    })
+                    .collect()
+            } else {
+                HashSet::new()
+            };
+
+            MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string }
+        }
+
+        fn codegen_units_to_str(cgus: &HashSet<String>) -> String {
+            let mut cgus: Vec<_> = cgus.iter().collect();
+            cgus.sort();
+
+            let mut string = String::new();
+            for cgu in cgus {
+                string.push_str(&cgu[..]);
+                string.push(' ');
+            }
+
+            string
+        }
+
+        // Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or
+        // the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>,
+        // remove all crate-disambiguators.
+        fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String {
+            let Some(captures) =
+                static_regex!(r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?")
+                    .captures(cgu)
+            else {
+                panic!("invalid cgu name encountered: {cgu}");
+            };
+
+            let mut new_name = cgu.to_owned();
+
+            if let Some(d2) = captures.name("d2") {
+                new_name.replace_range(d2.start()..d2.end(), "");
+            }
+
+            let d1 = captures.name("d1").unwrap();
+            new_name.replace_range(d1.start()..d1.end(), "");
+
+            new_name
+        }
+
+        // The name of merged CGUs is constructed as the names of the original
+        // CGUs joined with "--". This function splits such composite CGU names
+        // and handles each component individually.
+        fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String {
+            cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::<Vec<_>>().join("--")
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs
index 05191a15980..961a1602986 100644
--- a/src/tools/compiletest/src/runtest/coverage.rs
+++ b/src/tools/compiletest/src/runtest/coverage.rs
@@ -18,7 +18,7 @@ impl<'test> TestCx<'test> {
             .unwrap_or_else(|| self.fatal("missing --coverage-dump"))
     }
 
-    pub(crate) fn run_coverage_map_test(&self) {
+    pub(super) fn run_coverage_map_test(&self) {
         let coverage_dump_path = self.coverage_dump_path();
 
         let (proc_res, llvm_ir_path) = self.compile_test_and_save_ir();
@@ -50,7 +50,7 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    pub(crate) fn run_coverage_run_test(&self) {
+    pub(super) fn run_coverage_run_test(&self) {
         let should_run = self.run_if_enabled();
         let proc_res = self.compile_test(should_run, Emit::None);
 
diff --git a/src/tools/compiletest/src/runtest/crash.rs b/src/tools/compiletest/src/runtest/crash.rs
new file mode 100644
index 00000000000..7f2bec4949b
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/crash.rs
@@ -0,0 +1,25 @@
+use super::{TestCx, WillExecute};
+
+impl TestCx<'_> {
+    pub(super) fn run_crash_test(&self) {
+        let pm = self.pass_mode();
+        let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
+
+        if std::env::var("COMPILETEST_VERBOSE_CRASHES").is_ok() {
+            eprintln!("{}", proc_res.status);
+            eprintln!("{}", proc_res.stdout);
+            eprintln!("{}", proc_res.stderr);
+            eprintln!("{}", proc_res.cmdline);
+        }
+
+        // if a test does not crash, consider it an error
+        if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) {
+            self.fatal(&format!(
+                "crashtest no longer crashes/triggers ICE, horray! Please give it a meaningful name, \
+            add a doc-comment to the start of the test explaining why it exists and \
+            move it to tests/ui or wherever you see fit. Adding 'Fixes #<issueNr>' to your PR description \
+            ensures that the corresponding ticket is auto-closed upon merge."
+            ));
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
new file mode 100644
index 00000000000..36127414ab1
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -0,0 +1,509 @@
+use std::ffi::{OsStr, OsString};
+use std::fs::File;
+use std::io::{BufRead, BufReader, Read};
+use std::path::Path;
+use std::process::{Command, Output, Stdio};
+
+use tracing::debug;
+
+use super::debugger::DebuggerCommands;
+use super::{Debugger, Emit, ProcRes, TestCx, Truncated, WillExecute};
+use crate::common::Config;
+use crate::util::logv;
+use crate::{extract_gdb_version, is_android_gdb_target};
+
+impl TestCx<'_> {
+    pub(super) fn run_debuginfo_test(&self) {
+        match self.config.debugger.unwrap() {
+            Debugger::Cdb => self.run_debuginfo_cdb_test(),
+            Debugger::Gdb => self.run_debuginfo_gdb_test(),
+            Debugger::Lldb => self.run_debuginfo_lldb_test(),
+        }
+    }
+
+    fn run_debuginfo_cdb_test(&self) {
+        let config = Config {
+            target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
+            host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
+            ..self.config.clone()
+        };
+
+        let test_cx = TestCx { config: &config, ..*self };
+
+        test_cx.run_debuginfo_cdb_test_no_opt();
+    }
+
+    fn run_debuginfo_cdb_test_no_opt(&self) {
+        let exe_file = self.make_exe_name();
+
+        // Existing PDB files are update in-place. When changing the debuginfo
+        // the compiler generates for something, this can lead to the situation
+        // where both the old and the new version of the debuginfo for the same
+        // type is present in the PDB, which is very confusing.
+        // Therefore we delete any existing PDB file before compiling the test
+        // case.
+        // FIXME: If can reliably detect that MSVC's link.exe is used, then
+        //        passing `/INCREMENTAL:NO` might be a cleaner way to do this.
+        let pdb_file = exe_file.with_extension(".pdb");
+        if pdb_file.exists() {
+            std::fs::remove_file(pdb_file).unwrap();
+        }
+
+        // compile test file (it should have 'compile-flags:-g' in the header)
+        let should_run = self.run_if_enabled();
+        let compile_result = self.compile_test(should_run, Emit::None);
+        if !compile_result.status.success() {
+            self.fatal_proc_rec("compilation failed!", &compile_result);
+        }
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
+        let prefixes = {
+            static PREFIXES: &[&str] = &["cdb", "cdbg"];
+            // No "native rust support" variation for CDB yet.
+            PREFIXES
+        };
+
+        // Parse debugger commands etc from test files
+        let dbg_cmds = DebuggerCommands::parse_from(
+            &self.testpaths.file,
+            self.config,
+            prefixes,
+            self.revision,
+        )
+        .unwrap_or_else(|e| self.fatal(&e));
+
+        // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
+        let mut script_str = String::with_capacity(2048);
+        script_str.push_str("version\n"); // List CDB (and more) version info in test output
+        script_str.push_str(".nvlist\n"); // List loaded `*.natvis` files, bulk of custom MSVC debug
+
+        // If a .js file exists next to the source file being tested, then this is a JavaScript
+        // debugging extension that needs to be loaded.
+        let mut js_extension = self.testpaths.file.clone();
+        js_extension.set_extension("cdb.js");
+        if js_extension.exists() {
+            script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension.to_string_lossy()));
+        }
+
+        // Set breakpoints on every line that contains the string "#break"
+        let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
+        for line in &dbg_cmds.breakpoint_lines {
+            script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line));
+        }
+
+        // Append the other `cdb-command:`s
+        for line in &dbg_cmds.commands {
+            script_str.push_str(line);
+            script_str.push('\n');
+        }
+
+        script_str.push_str("qq\n"); // Quit the debugger (including remote debugger, if any)
+
+        // Write the script into a file
+        debug!("script_str = {}", script_str);
+        self.dump_output_file(&script_str, "debugger.script");
+        let debugger_script = self.make_out_name("debugger.script");
+
+        let cdb_path = &self.config.cdb.as_ref().unwrap();
+        let mut cdb = Command::new(cdb_path);
+        cdb.arg("-lines") // Enable source line debugging.
+            .arg("-cf")
+            .arg(&debugger_script)
+            .arg(&exe_file);
+
+        let debugger_run_result = self.compose_and_run(
+            cdb,
+            self.config.run_lib_path.to_str().unwrap(),
+            None, // aux_path
+            None, // input
+        );
+
+        if !debugger_run_result.status.success() {
+            self.fatal_proc_rec("Error while running CDB", &debugger_run_result);
+        }
+
+        if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
+            self.fatal_proc_rec(&e, &debugger_run_result);
+        }
+    }
+
+    fn run_debuginfo_gdb_test(&self) {
+        let config = Config {
+            target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
+            host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
+            ..self.config.clone()
+        };
+
+        let test_cx = TestCx { config: &config, ..*self };
+
+        test_cx.run_debuginfo_gdb_test_no_opt();
+    }
+
+    fn run_debuginfo_gdb_test_no_opt(&self) {
+        let dbg_cmds = DebuggerCommands::parse_from(
+            &self.testpaths.file,
+            self.config,
+            &["gdb"],
+            self.revision,
+        )
+        .unwrap_or_else(|e| self.fatal(&e));
+        let mut cmds = dbg_cmds.commands.join("\n");
+
+        // compile test file (it should have 'compile-flags:-g' in the header)
+        let should_run = self.run_if_enabled();
+        let compiler_run_result = self.compile_test(should_run, Emit::None);
+        if !compiler_run_result.status.success() {
+            self.fatal_proc_rec("compilation failed!", &compiler_run_result);
+        }
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
+        let exe_file = self.make_exe_name();
+
+        let debugger_run_result;
+        if is_android_gdb_target(&self.config.target) {
+            cmds = cmds.replace("run", "continue");
+
+            let tool_path = match self.config.android_cross_path.to_str() {
+                Some(x) => x.to_owned(),
+                None => self.fatal("cannot find android cross path"),
+            };
+
+            // write debugger script
+            let mut script_str = String::with_capacity(2048);
+            script_str.push_str(&format!("set charset {}\n", Self::charset()));
+            script_str.push_str(&format!("set sysroot {}\n", tool_path));
+            script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
+            script_str.push_str("target remote :5039\n");
+            script_str.push_str(&format!(
+                "set solib-search-path \
+                 ./{}/stage2/lib/rustlib/{}/lib/\n",
+                self.config.host, self.config.target
+            ));
+            for line in &dbg_cmds.breakpoint_lines {
+                script_str.push_str(
+                    format!(
+                        "break {:?}:{}\n",
+                        self.testpaths.file.file_name().unwrap().to_string_lossy(),
+                        *line
+                    )
+                    .as_str(),
+                );
+            }
+            script_str.push_str(&cmds);
+            script_str.push_str("\nquit\n");
+
+            debug!("script_str = {}", script_str);
+            self.dump_output_file(&script_str, "debugger.script");
+
+            let adb_path = &self.config.adb_path;
+
+            Command::new(adb_path)
+                .arg("push")
+                .arg(&exe_file)
+                .arg(&self.config.adb_test_dir)
+                .status()
+                .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
+
+            Command::new(adb_path)
+                .args(&["forward", "tcp:5039", "tcp:5039"])
+                .status()
+                .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
+
+            let adb_arg = format!(
+                "export LD_LIBRARY_PATH={}; \
+                 gdbserver{} :5039 {}/{}",
+                self.config.adb_test_dir.clone(),
+                if self.config.target.contains("aarch64") { "64" } else { "" },
+                self.config.adb_test_dir.clone(),
+                exe_file.file_name().unwrap().to_str().unwrap()
+            );
+
+            debug!("adb arg: {}", adb_arg);
+            let mut adb = Command::new(adb_path)
+                .args(&["shell", &adb_arg])
+                .stdout(Stdio::piped())
+                .stderr(Stdio::inherit())
+                .spawn()
+                .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
+
+            // Wait for the gdbserver to print out "Listening on port ..."
+            // at which point we know that it's started and then we can
+            // execute the debugger below.
+            let mut stdout = BufReader::new(adb.stdout.take().unwrap());
+            let mut line = String::new();
+            loop {
+                line.truncate(0);
+                stdout.read_line(&mut line).unwrap();
+                if line.starts_with("Listening on port 5039") {
+                    break;
+                }
+            }
+            drop(stdout);
+
+            let mut debugger_script = OsString::from("-command=");
+            debugger_script.push(self.make_out_name("debugger.script"));
+            let debugger_opts: &[&OsStr] =
+                &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
+
+            let gdb_path = self.config.gdb.as_ref().unwrap();
+            let Output { status, stdout, stderr } = Command::new(&gdb_path)
+                .args(debugger_opts)
+                .output()
+                .unwrap_or_else(|e| panic!("failed to exec `{gdb_path:?}`: {e:?}"));
+            let cmdline = {
+                let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
+                gdb.args(debugger_opts);
+                let cmdline = self.make_cmdline(&gdb, "");
+                logv(self.config, format!("executing {}", cmdline));
+                cmdline
+            };
+
+            debugger_run_result = ProcRes {
+                status,
+                stdout: String::from_utf8(stdout).unwrap(),
+                stderr: String::from_utf8(stderr).unwrap(),
+                truncated: Truncated::No,
+                cmdline,
+            };
+            if adb.kill().is_err() {
+                println!("Adb process is already finished.");
+            }
+        } else {
+            let rust_src_root =
+                self.config.find_rust_src_root().expect("Could not find Rust source root");
+            let rust_pp_module_rel_path = Path::new("./src/etc");
+            let rust_pp_module_abs_path =
+                rust_src_root.join(rust_pp_module_rel_path).to_str().unwrap().to_owned();
+            // write debugger script
+            let mut script_str = String::with_capacity(2048);
+            script_str.push_str(&format!("set charset {}\n", Self::charset()));
+            script_str.push_str("show version\n");
+
+            match self.config.gdb_version {
+                Some(version) => {
+                    println!("NOTE: compiletest thinks it is using GDB version {}", version);
+
+                    if version > extract_gdb_version("7.4").unwrap() {
+                        // Add the directory containing the pretty printers to
+                        // GDB's script auto loading safe path
+                        script_str.push_str(&format!(
+                            "add-auto-load-safe-path {}\n",
+                            rust_pp_module_abs_path.replace(r"\", r"\\")
+                        ));
+
+                        let output_base_dir = self.output_base_dir().to_str().unwrap().to_owned();
+
+                        // Add the directory containing the output binary to
+                        // include embedded pretty printers to GDB's script
+                        // auto loading safe path
+                        script_str.push_str(&format!(
+                            "add-auto-load-safe-path {}\n",
+                            output_base_dir.replace(r"\", r"\\")
+                        ));
+                    }
+                }
+                _ => {
+                    println!(
+                        "NOTE: compiletest does not know which version of \
+                         GDB it is using"
+                    );
+                }
+            }
+
+            // The following line actually doesn't have to do anything with
+            // pretty printing, it just tells GDB to print values on one line:
+            script_str.push_str("set print pretty off\n");
+
+            // Add the pretty printer directory to GDB's source-file search path
+            script_str
+                .push_str(&format!("directory {}\n", rust_pp_module_abs_path.replace(r"\", r"\\")));
+
+            // Load the target executable
+            script_str
+                .push_str(&format!("file {}\n", exe_file.to_str().unwrap().replace(r"\", r"\\")));
+
+            // Force GDB to print values in the Rust format.
+            script_str.push_str("set language rust\n");
+
+            // Add line breakpoints
+            for line in &dbg_cmds.breakpoint_lines {
+                script_str.push_str(&format!(
+                    "break '{}':{}\n",
+                    self.testpaths.file.file_name().unwrap().to_string_lossy(),
+                    *line
+                ));
+            }
+
+            script_str.push_str(&cmds);
+            script_str.push_str("\nquit\n");
+
+            debug!("script_str = {}", script_str);
+            self.dump_output_file(&script_str, "debugger.script");
+
+            let mut debugger_script = OsString::from("-command=");
+            debugger_script.push(self.make_out_name("debugger.script"));
+
+            let debugger_opts: &[&OsStr] =
+                &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
+
+            let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
+            let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
+                format!("{pp}:{rust_pp_module_abs_path}")
+            } else {
+                rust_pp_module_abs_path
+            };
+            gdb.args(debugger_opts).env("PYTHONPATH", pythonpath);
+
+            debugger_run_result =
+                self.compose_and_run(gdb, self.config.run_lib_path.to_str().unwrap(), None, None);
+        }
+
+        if !debugger_run_result.status.success() {
+            self.fatal_proc_rec("gdb failed to execute", &debugger_run_result);
+        }
+
+        if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
+            self.fatal_proc_rec(&e, &debugger_run_result);
+        }
+    }
+
+    fn run_debuginfo_lldb_test(&self) {
+        if self.config.lldb_python_dir.is_none() {
+            self.fatal("Can't run LLDB test because LLDB's python path is not set.");
+        }
+
+        let config = Config {
+            target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
+            host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
+            ..self.config.clone()
+        };
+
+        let test_cx = TestCx { config: &config, ..*self };
+
+        test_cx.run_debuginfo_lldb_test_no_opt();
+    }
+
+    fn run_debuginfo_lldb_test_no_opt(&self) {
+        // compile test file (it should have 'compile-flags:-g' in the header)
+        let should_run = self.run_if_enabled();
+        let compile_result = self.compile_test(should_run, Emit::None);
+        if !compile_result.status.success() {
+            self.fatal_proc_rec("compilation failed!", &compile_result);
+        }
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
+        let exe_file = self.make_exe_name();
+
+        match self.config.lldb_version {
+            Some(ref version) => {
+                println!("NOTE: compiletest thinks it is using LLDB version {}", version);
+            }
+            _ => {
+                println!(
+                    "NOTE: compiletest does not know which version of \
+                     LLDB it is using"
+                );
+            }
+        }
+
+        // Parse debugger commands etc from test files
+        let dbg_cmds = DebuggerCommands::parse_from(
+            &self.testpaths.file,
+            self.config,
+            &["lldb"],
+            self.revision,
+        )
+        .unwrap_or_else(|e| self.fatal(&e));
+
+        // Write debugger script:
+        // We don't want to hang when calling `quit` while the process is still running
+        let mut script_str = String::from("settings set auto-confirm true\n");
+
+        // Make LLDB emit its version, so we have it documented in the test output
+        script_str.push_str("version\n");
+
+        // Switch LLDB into "Rust mode"
+        let rust_src_root =
+            self.config.find_rust_src_root().expect("Could not find Rust source root");
+        let rust_pp_module_rel_path = Path::new("./src/etc");
+        let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path);
+
+        script_str.push_str(&format!(
+            "command script import {}/lldb_lookup.py\n",
+            rust_pp_module_abs_path.to_str().unwrap()
+        ));
+        File::open(rust_pp_module_abs_path.join("lldb_commands"))
+            .and_then(|mut file| file.read_to_string(&mut script_str))
+            .expect("Failed to read lldb_commands");
+
+        // Set breakpoints on every line that contains the string "#break"
+        let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
+        for line in &dbg_cmds.breakpoint_lines {
+            script_str.push_str(&format!(
+                "breakpoint set --file '{}' --line {}\n",
+                source_file_name, line
+            ));
+        }
+
+        // Append the other commands
+        for line in &dbg_cmds.commands {
+            script_str.push_str(line);
+            script_str.push('\n');
+        }
+
+        // Finally, quit the debugger
+        script_str.push_str("\nquit\n");
+
+        // Write the script into a file
+        debug!("script_str = {}", script_str);
+        self.dump_output_file(&script_str, "debugger.script");
+        let debugger_script = self.make_out_name("debugger.script");
+
+        // Let LLDB execute the script via lldb_batchmode.py
+        let debugger_run_result = self.run_lldb(&exe_file, &debugger_script, &rust_src_root);
+
+        if !debugger_run_result.status.success() {
+            self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
+        }
+
+        if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
+            self.fatal_proc_rec(&e, &debugger_run_result);
+        }
+    }
+
+    fn run_lldb(
+        &self,
+        test_executable: &Path,
+        debugger_script: &Path,
+        rust_src_root: &Path,
+    ) -> ProcRes {
+        // Prepare the lldb_batchmode which executes the debugger script
+        let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
+        let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
+            format!("{pp}:{}", self.config.lldb_python_dir.as_ref().unwrap())
+        } else {
+            self.config.lldb_python_dir.as_ref().unwrap().to_string()
+        };
+        self.run_command_to_procres(
+            Command::new(&self.config.python)
+                .arg(&lldb_script_path)
+                .arg(test_executable)
+                .arg(debugger_script)
+                .env("PYTHONUNBUFFERED", "1") // Help debugging #78665
+                .env("PYTHONPATH", pythonpath),
+        )
+    }
+
+    fn cleanup_debug_info_options(&self, options: &Vec<String>) -> Vec<String> {
+        // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
+        let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()];
+
+        options.iter().filter(|x| !options_to_remove.contains(x)).cloned().collect()
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/incremental.rs b/src/tools/compiletest/src/runtest/incremental.rs
new file mode 100644
index 00000000000..81b006292e4
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/incremental.rs
@@ -0,0 +1,128 @@
+use super::{TestCx, WillExecute};
+use crate::errors;
+
+// FIXME(jieyouxu): `run_rpass_test` got hoisted out of this because apparently valgrind falls back
+// to `run_rpass_test` if valgrind isn't available, which is questionable, but keeping it for
+// refactoring changes to preserve current behavior.
+
+impl TestCx<'_> {
+    pub(super) fn run_incremental_test(&self) {
+        // Basic plan for a test incremental/foo/bar.rs:
+        // - load list of revisions rpass1, cfail2, rpass3
+        //   - each should begin with `cpass`, `rpass`, `cfail`, or `rfail`
+        //   - if `cpass`, expect compilation to succeed, don't execute
+        //   - if `rpass`, expect compilation and execution to succeed
+        //   - if `cfail`, expect compilation to fail
+        //   - if `rfail`, expect compilation to succeed and execution to fail
+        // - create a directory build/foo/bar.incremental
+        // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass1
+        //   - because name of revision starts with "rpass", expect success
+        // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C cfail2
+        //   - because name of revision starts with "cfail", expect an error
+        //   - load expected errors as usual, but filter for those that end in `[rfail2]`
+        // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass3
+        //   - because name of revision starts with "rpass", expect success
+        // - execute build/foo/bar.exe and save output
+        //
+        // FIXME -- use non-incremental mode as an oracle? That doesn't apply
+        // to #[rustc_dirty] and clean tests I guess
+
+        let revision = self.revision.expect("incremental tests require a list of revisions");
+
+        // Incremental workproduct directory should have already been created.
+        let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
+        assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir");
+
+        if self.config.verbose {
+            print!("revision={:?} props={:#?}", revision, self.props);
+        }
+
+        if revision.starts_with("cpass") {
+            if self.props.should_ice {
+                self.fatal("can only use should-ice in cfail tests");
+            }
+            self.run_cpass_test();
+        } else if revision.starts_with("rpass") {
+            if self.props.should_ice {
+                self.fatal("can only use should-ice in cfail tests");
+            }
+            self.run_rpass_test();
+        } else if revision.starts_with("rfail") {
+            if self.props.should_ice {
+                self.fatal("can only use should-ice in cfail tests");
+            }
+            self.run_rfail_test();
+        } else if revision.starts_with("cfail") {
+            self.run_cfail_test();
+        } else {
+            self.fatal("revision name must begin with cpass, rpass, rfail, or cfail");
+        }
+    }
+
+    fn run_cpass_test(&self) {
+        let emit_metadata = self.should_emit_metadata(self.pass_mode());
+        let proc_res = self.compile_test(WillExecute::No, emit_metadata);
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        // FIXME(#41968): Move this check to tidy?
+        if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
+            self.fatal("compile-pass tests with expected warnings should be moved to ui/");
+        }
+    }
+
+    fn run_cfail_test(&self) {
+        let pm = self.pass_mode();
+        let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
+        self.check_if_test_should_compile(&proc_res, pm);
+        self.check_no_compiler_crash(&proc_res, self.props.should_ice);
+
+        let output_to_check = self.get_output(&proc_res);
+        let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
+        if !expected_errors.is_empty() {
+            if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty()
+            {
+                self.fatal("both error pattern and expected errors specified");
+            }
+            self.check_expected_errors(expected_errors, &proc_res);
+        } else {
+            self.check_all_error_patterns(&output_to_check, &proc_res, pm);
+        }
+        if self.props.should_ice {
+            match proc_res.status.code() {
+                Some(101) => (),
+                _ => self.fatal("expected ICE"),
+            }
+        }
+
+        self.check_forbid_output(&output_to_check, &proc_res);
+    }
+
+    fn run_rfail_test(&self) {
+        let pm = self.pass_mode();
+        let should_run = self.run_if_enabled();
+        let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm));
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
+        let proc_res = self.exec_compiled_test();
+
+        // The value our Makefile configures valgrind to return on failure
+        const VALGRIND_ERR: i32 = 100;
+        if proc_res.status.code() == Some(VALGRIND_ERR) {
+            self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
+        }
+
+        let output_to_check = self.get_output(&proc_res);
+        self.check_correct_failure_status(&proc_res);
+        self.check_all_error_patterns(&output_to_check, &proc_res, pm);
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/js_doc.rs b/src/tools/compiletest/src/runtest/js_doc.rs
new file mode 100644
index 00000000000..68c74cd155c
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/js_doc.rs
@@ -0,0 +1,32 @@
+use std::process::Command;
+
+use super::TestCx;
+
+impl TestCx<'_> {
+    pub(super) fn run_js_doc_test(&self) {
+        if let Some(nodejs) = &self.config.nodejs {
+            let out_dir = self.output_base_dir();
+
+            self.document(&out_dir, &self.testpaths);
+
+            let root = self.config.find_rust_src_root().unwrap();
+            let file_stem =
+                self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem");
+            let res = self.run_command_to_procres(
+                Command::new(&nodejs)
+                    .arg(root.join("src/tools/rustdoc-js/tester.js"))
+                    .arg("--doc-folder")
+                    .arg(out_dir)
+                    .arg("--crate-name")
+                    .arg(file_stem.replace("-", "_"))
+                    .arg("--test-file")
+                    .arg(self.testpaths.file.with_extension("js")),
+            );
+            if !res.status.success() {
+                self.fatal_proc_rec("rustdoc-js test failed!", &res);
+            }
+        } else {
+            self.fatal("no nodeJS");
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/mir_opt.rs b/src/tools/compiletest/src/runtest/mir_opt.rs
new file mode 100644
index 00000000000..02289a8df1e
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/mir_opt.rs
@@ -0,0 +1,155 @@
+use std::fs;
+use std::path::{Path, PathBuf};
+
+use glob::glob;
+use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile};
+use tracing::debug;
+
+use super::{Emit, TestCx, WillExecute};
+use crate::compute_diff::write_diff;
+
+impl TestCx<'_> {
+    pub(super) fn run_mir_opt_test(&self) {
+        let pm = self.pass_mode();
+        let should_run = self.should_run(pm);
+
+        let mut test_info = files_for_miropt_test(
+            &self.testpaths.file,
+            self.config.get_pointer_width(),
+            self.config.target_cfg().panic.for_miropt_test_tools(),
+        );
+
+        let passes = std::mem::take(&mut test_info.passes);
+
+        let proc_res = self.compile_test_with_passes(should_run, Emit::Mir, passes);
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+        self.check_mir_dump(test_info);
+
+        if let WillExecute::Yes = should_run {
+            let proc_res = self.exec_compiled_test();
+
+            if !proc_res.status.success() {
+                self.fatal_proc_rec("test run failed!", &proc_res);
+            }
+        }
+    }
+
+    fn check_mir_dump(&self, test_info: MiroptTest) {
+        let test_dir = self.testpaths.file.parent().unwrap();
+        let test_crate =
+            self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_");
+
+        let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info;
+
+        if self.config.bless {
+            for e in
+                glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, suffix)).unwrap()
+            {
+                fs::remove_file(e.unwrap()).unwrap();
+            }
+            for e in
+                glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, suffix)).unwrap()
+            {
+                fs::remove_file(e.unwrap()).unwrap();
+            }
+        }
+
+        for MiroptTestFile { from_file, to_file, expected_file } in files {
+            let dumped_string = if let Some(after) = to_file {
+                self.diff_mir_files(from_file.into(), after.into())
+            } else {
+                let mut output_file = PathBuf::new();
+                output_file.push(self.get_mir_dump_dir());
+                output_file.push(&from_file);
+                debug!(
+                    "comparing the contents of: {} with {}",
+                    output_file.display(),
+                    expected_file.display()
+                );
+                if !output_file.exists() {
+                    panic!(
+                        "Output file `{}` from test does not exist, available files are in `{}`",
+                        output_file.display(),
+                        output_file.parent().unwrap().display()
+                    );
+                }
+                self.check_mir_test_timestamp(&from_file, &output_file);
+                let dumped_string = fs::read_to_string(&output_file).unwrap();
+                self.normalize_output(&dumped_string, &[])
+            };
+
+            if self.config.bless {
+                let _ = fs::remove_file(&expected_file);
+                fs::write(expected_file, dumped_string.as_bytes()).unwrap();
+            } else {
+                if !expected_file.exists() {
+                    panic!("Output file `{}` from test does not exist", expected_file.display());
+                }
+                let expected_string = fs::read_to_string(&expected_file).unwrap();
+                if dumped_string != expected_string {
+                    print!("{}", write_diff(&expected_string, &dumped_string, 3));
+                    panic!(
+                        "Actual MIR output differs from expected MIR output {}",
+                        expected_file.display()
+                    );
+                }
+            }
+        }
+
+        if run_filecheck {
+            let output_path = self.output_base_name().with_extension("mir");
+            let proc_res = self.verify_with_filecheck(&output_path);
+            if !proc_res.status.success() {
+                self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
+            }
+        }
+    }
+
+    fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String {
+        let to_full_path = |path: PathBuf| {
+            let full = self.get_mir_dump_dir().join(&path);
+            if !full.exists() {
+                panic!(
+                    "the mir dump file for {} does not exist (requested in {})",
+                    path.display(),
+                    self.testpaths.file.display(),
+                );
+            }
+            full
+        };
+        let before = to_full_path(before);
+        let after = to_full_path(after);
+        debug!("comparing the contents of: {} with {}", before.display(), after.display());
+        let before = fs::read_to_string(before).unwrap();
+        let after = fs::read_to_string(after).unwrap();
+        let before = self.normalize_output(&before, &[]);
+        let after = self.normalize_output(&after, &[]);
+        let mut dumped_string = String::new();
+        for result in diff::lines(&before, &after) {
+            use std::fmt::Write;
+            match result {
+                diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(),
+                diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(),
+                diff::Result::Both(s, _) => writeln!(dumped_string, "  {}", s).unwrap(),
+            }
+        }
+        dumped_string
+    }
+
+    fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) {
+        let t = |file| fs::metadata(file).unwrap().modified().unwrap();
+        let source_file = &self.testpaths.file;
+        let output_time = t(output_file);
+        let source_time = t(source_file);
+        if source_time > output_time {
+            debug!("source file time: {:?} output file time: {:?}", source_time, output_time);
+            panic!(
+                "test source file `{}` is newer than potentially stale output file `{}`.",
+                source_file.display(),
+                test_name
+            );
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/pretty.rs b/src/tools/compiletest/src/runtest/pretty.rs
new file mode 100644
index 00000000000..40e767e84ef
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/pretty.rs
@@ -0,0 +1,104 @@
+use std::fs;
+
+use super::{ProcRes, ReadFrom, TestCx};
+use crate::util::logv;
+
+impl TestCx<'_> {
+    pub(super) fn run_pretty_test(&self) {
+        if self.props.pp_exact.is_some() {
+            logv(self.config, "testing for exact pretty-printing".to_owned());
+        } else {
+            logv(self.config, "testing for converging pretty-printing".to_owned());
+        }
+
+        let rounds = match self.props.pp_exact {
+            Some(_) => 1,
+            None => 2,
+        };
+
+        let src = fs::read_to_string(&self.testpaths.file).unwrap();
+        let mut srcs = vec![src];
+
+        let mut round = 0;
+        while round < rounds {
+            logv(
+                self.config,
+                format!("pretty-printing round {} revision {:?}", round, self.revision),
+            );
+            let read_from =
+                if round == 0 { ReadFrom::Path } else { ReadFrom::Stdin(srcs[round].to_owned()) };
+
+            let proc_res = self.print_source(read_from, &self.props.pretty_mode);
+            if !proc_res.status.success() {
+                self.fatal_proc_rec(
+                    &format!(
+                        "pretty-printing failed in round {} revision {:?}",
+                        round, self.revision
+                    ),
+                    &proc_res,
+                );
+            }
+
+            let ProcRes { stdout, .. } = proc_res;
+            srcs.push(stdout);
+            round += 1;
+        }
+
+        let mut expected = match self.props.pp_exact {
+            Some(ref file) => {
+                let filepath = self.testpaths.file.parent().unwrap().join(file);
+                fs::read_to_string(&filepath).unwrap()
+            }
+            None => srcs[srcs.len() - 2].clone(),
+        };
+        let mut actual = srcs[srcs.len() - 1].clone();
+
+        if self.props.pp_exact.is_some() {
+            // Now we have to care about line endings
+            let cr = "\r".to_owned();
+            actual = actual.replace(&cr, "");
+            expected = expected.replace(&cr, "");
+        }
+
+        if !self.config.bless {
+            self.compare_source(&expected, &actual);
+        } else if expected != actual {
+            let filepath_buf;
+            let filepath = match &self.props.pp_exact {
+                Some(file) => {
+                    filepath_buf = self.testpaths.file.parent().unwrap().join(file);
+                    &filepath_buf
+                }
+                None => &self.testpaths.file,
+            };
+            fs::write(filepath, &actual).unwrap();
+        }
+
+        // If we're only making sure that the output matches then just stop here
+        if self.props.pretty_compare_only {
+            return;
+        }
+
+        // Finally, let's make sure it actually appears to remain valid code
+        let proc_res = self.typecheck_source(actual);
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
+        }
+
+        if !self.props.pretty_expanded {
+            return;
+        }
+
+        // additionally, run `-Zunpretty=expanded` and try to build it.
+        let proc_res = self.print_source(ReadFrom::Path, "expanded");
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res);
+        }
+
+        let ProcRes { stdout: expanded_src, .. } = proc_res;
+        let proc_res = self.typecheck_source(expanded_src);
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("pretty-printed source (expanded) does not typecheck", &proc_res);
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
new file mode 100644
index 00000000000..852568ae925
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -0,0 +1,518 @@
+use std::path::Path;
+use std::process::{Command, Output, Stdio};
+use std::{env, fs};
+
+use super::{ProcRes, TestCx};
+use crate::util::{copy_dir_all, dylib_env_var};
+
+impl TestCx<'_> {
+    pub(super) fn run_rmake_test(&self) {
+        let test_dir = &self.testpaths.file;
+        if test_dir.join("rmake.rs").exists() {
+            self.run_rmake_v2_test();
+        } else if test_dir.join("Makefile").exists() {
+            self.run_rmake_legacy_test();
+        } else {
+            self.fatal("failed to find either `rmake.rs` or `Makefile`")
+        }
+    }
+
+    fn run_rmake_legacy_test(&self) {
+        let cwd = env::current_dir().unwrap();
+        let src_root = self.config.src_base.parent().unwrap().parent().unwrap();
+        let src_root = cwd.join(&src_root);
+
+        let tmpdir = cwd.join(self.output_base_name());
+        if tmpdir.exists() {
+            self.aggressive_rm_rf(&tmpdir).unwrap();
+        }
+        fs::create_dir_all(&tmpdir).unwrap();
+
+        let host = &self.config.host;
+        let make = if host.contains("dragonfly")
+            || host.contains("freebsd")
+            || host.contains("netbsd")
+            || host.contains("openbsd")
+            || host.contains("aix")
+        {
+            "gmake"
+        } else {
+            "make"
+        };
+
+        let mut cmd = Command::new(make);
+        cmd.current_dir(&self.testpaths.file)
+            .stdout(Stdio::piped())
+            .stderr(Stdio::piped())
+            .env("TARGET", &self.config.target)
+            .env("PYTHON", &self.config.python)
+            .env("S", src_root)
+            .env("RUST_BUILD_STAGE", &self.config.stage_id)
+            .env("RUSTC", cwd.join(&self.config.rustc_path))
+            .env("TMPDIR", &tmpdir)
+            .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
+            .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
+            .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
+            .env("LLVM_COMPONENTS", &self.config.llvm_components)
+            // We for sure don't want these tests to run in parallel, so make
+            // sure they don't have access to these vars if we run via `make`
+            // at the top level
+            .env_remove("MAKEFLAGS")
+            .env_remove("MFLAGS")
+            .env_remove("CARGO_MAKEFLAGS");
+
+        if let Some(ref rustdoc) = self.config.rustdoc_path {
+            cmd.env("RUSTDOC", cwd.join(rustdoc));
+        }
+
+        if let Some(ref node) = self.config.nodejs {
+            cmd.env("NODE", node);
+        }
+
+        if let Some(ref linker) = self.config.target_linker {
+            cmd.env("RUSTC_LINKER", linker);
+        }
+
+        if let Some(ref clang) = self.config.run_clang_based_tests_with {
+            cmd.env("CLANG", clang);
+        }
+
+        if let Some(ref filecheck) = self.config.llvm_filecheck {
+            cmd.env("LLVM_FILECHECK", filecheck);
+        }
+
+        if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir {
+            cmd.env("LLVM_BIN_DIR", llvm_bin_dir);
+        }
+
+        if let Some(ref remote_test_client) = self.config.remote_test_client {
+            cmd.env("REMOTE_TEST_CLIENT", remote_test_client);
+        }
+
+        // We don't want RUSTFLAGS set from the outside to interfere with
+        // compiler flags set in the test cases:
+        cmd.env_remove("RUSTFLAGS");
+
+        // Use dynamic musl for tests because static doesn't allow creating dylibs
+        if self.config.host.contains("musl") {
+            cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1");
+        }
+
+        if self.config.bless {
+            cmd.env("RUSTC_BLESS_TEST", "--bless");
+            // Assume this option is active if the environment variable is "defined", with _any_ value.
+            // As an example, a `Makefile` can use this option by:
+            //
+            //   ifdef RUSTC_BLESS_TEST
+            //       cp "$(TMPDIR)"/actual_something.ext expected_something.ext
+            //   else
+            //       $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext
+            //   endif
+        }
+
+        if self.config.target.contains("msvc") && !self.config.cc.is_empty() {
+            // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
+            // and that `lib.exe` lives next to it.
+            let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
+
+            // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
+            // a path and instead passes `C:\msys64\foo`, so convert all
+            // `/`-arguments to MSVC here to `-` arguments.
+            let cflags = self
+                .config
+                .cflags
+                .split(' ')
+                .map(|s| s.replace("/", "-"))
+                .collect::<Vec<_>>()
+                .join(" ");
+            let cxxflags = self
+                .config
+                .cxxflags
+                .split(' ')
+                .map(|s| s.replace("/", "-"))
+                .collect::<Vec<_>>()
+                .join(" ");
+
+            cmd.env("IS_MSVC", "1")
+                .env("IS_WINDOWS", "1")
+                .env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
+                .env("MSVC_LIB_PATH", format!("{}", lib.display()))
+                .env("CC", format!("'{}' {}", self.config.cc, cflags))
+                .env("CXX", format!("'{}' {}", &self.config.cxx, cxxflags));
+        } else {
+            cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
+                .env("CXX", format!("{} {}", self.config.cxx, self.config.cxxflags))
+                .env("AR", &self.config.ar);
+
+            if self.config.target.contains("windows") {
+                cmd.env("IS_WINDOWS", "1");
+            }
+        }
+
+        let (output, truncated) =
+            self.read2_abbreviated(cmd.spawn().expect("failed to spawn `make`"));
+        if !output.status.success() {
+            let res = ProcRes {
+                status: output.status,
+                stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
+                stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
+                truncated,
+                cmdline: format!("{:?}", cmd),
+            };
+            self.fatal_proc_rec("make failed", &res);
+        }
+    }
+
+    fn run_rmake_v2_test(&self) {
+        // For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe
+        // (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust
+        // library and is available under `build/$TARGET/stageN-tools-bin/librun_make_support.rlib`.
+        //
+        // 1. We need to build the recipe `rmake.rs` as a binary and link in the `run_make_support`
+        //    library.
+        // 2. We need to run the recipe binary.
+
+        // So we assume the rust-lang/rust project setup looks like the following (our `.` is the
+        // top-level directory, irrelevant entries to our purposes omitted):
+        //
+        // ```
+        // .                               // <- `source_root`
+        // ├── build/                      // <- `build_root`
+        // ├── compiler/
+        // ├── library/
+        // ├── src/
+        // │  └── tools/
+        // │     └── run_make_support/
+        // └── tests
+        //    └── run-make/
+        // ```
+
+        // `source_root` is the top-level directory containing the rust-lang/rust checkout.
+        let source_root =
+            self.config.find_rust_src_root().expect("could not determine rust source root");
+        // `self.config.build_base` is actually the build base folder + "test" + test suite name, it
+        // looks like `build/<host_triple>/test/run-make`. But we want `build/<host_triple>/`. Note
+        // that the `build` directory does not need to be called `build`, nor does it need to be
+        // under `source_root`, so we must compute it based off of `self.config.build_base`.
+        let build_root =
+            self.config.build_base.parent().and_then(Path::parent).unwrap().to_path_buf();
+
+        // We construct the following directory tree for each rmake.rs test:
+        // ```
+        // <base_dir>/
+        //     rmake.exe
+        //     rmake_out/
+        // ```
+        // having the recipe executable separate from the output artifacts directory allows the
+        // recipes to `remove_dir_all($TMPDIR)` without running into issues related trying to remove
+        // a currently running executable because the recipe executable is not under the
+        // `rmake_out/` directory.
+        //
+        // This setup intentionally diverges from legacy Makefile run-make tests.
+        let base_dir = self.output_base_name();
+        if base_dir.exists() {
+            self.aggressive_rm_rf(&base_dir).unwrap();
+        }
+        let rmake_out_dir = base_dir.join("rmake_out");
+        fs::create_dir_all(&rmake_out_dir).unwrap();
+
+        // Copy all input files (apart from rmake.rs) to the temporary directory,
+        // so that the input directory structure from `tests/run-make/<test>` is mirrored
+        // to the `rmake_out` directory.
+        for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) {
+            let path = path.unwrap().path().to_path_buf();
+            if path.file_name().is_some_and(|s| s != "rmake.rs") {
+                let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap());
+                if path.is_dir() {
+                    copy_dir_all(&path, target).unwrap();
+                } else {
+                    fs::copy(&path, target).unwrap();
+                }
+            }
+        }
+
+        // `self.config.stage_id` looks like `stage1-<target_triple>`, but we only want
+        // the `stage1` part as that is what the output directories of bootstrap are prefixed with.
+        // Note that this *assumes* build layout from bootstrap is produced as:
+        //
+        // ```
+        // build/<target_triple>/          // <- this is `build_root`
+        // ├── stage0
+        // ├── stage0-bootstrap-tools
+        // ├── stage0-codegen
+        // ├── stage0-rustc
+        // ├── stage0-std
+        // ├── stage0-sysroot
+        // ├── stage0-tools
+        // ├── stage0-tools-bin
+        // ├── stage1
+        // ├── stage1-std
+        // ├── stage1-tools
+        // ├── stage1-tools-bin
+        // └── test
+        // ```
+        // FIXME(jieyouxu): improve the communication between bootstrap and compiletest here so
+        // we don't have to hack out a `stageN`.
+        let stage = self.config.stage_id.split('-').next().unwrap();
+
+        // In order to link in the support library as a rlib when compiling recipes, we need three
+        // paths:
+        // 1. Path of the built support library rlib itself.
+        // 2. Path of the built support library's dependencies directory.
+        // 3. Path of the built support library's dependencies' dependencies directory.
+        //
+        // The paths look like
+        //
+        // ```
+        // build/<target_triple>/
+        // ├── stageN-tools-bin/
+        // │   └── librun_make_support.rlib       // <- support rlib itself
+        // ├── stageN-tools/
+        // │   ├── release/deps/                  // <- deps of deps
+        // │   └── <host_triple>/release/deps/    // <- deps
+        // ```
+        //
+        // FIXME(jieyouxu): there almost certainly is a better way to do this (specifically how the
+        // support lib and its deps are organized, can't we copy them to the tools-bin dir as
+        // well?), but this seems to work for now.
+
+        let stage_tools_bin = build_root.join(format!("{stage}-tools-bin"));
+        let support_lib_path = stage_tools_bin.join("librun_make_support.rlib");
+
+        let stage_tools = build_root.join(format!("{stage}-tools"));
+        let support_lib_deps = stage_tools.join(&self.config.host).join("release").join("deps");
+        let support_lib_deps_deps = stage_tools.join("release").join("deps");
+
+        // To compile the recipe with rustc, we need to provide suitable dynamic library search
+        // paths to rustc. This includes both:
+        // 1. The "base" dylib search paths that was provided to compiletest, e.g. `LD_LIBRARY_PATH`
+        //    on some linux distros.
+        // 2. Specific library paths in `self.config.compile_lib_path` needed for running rustc.
+
+        let base_dylib_search_paths =
+            Vec::from_iter(env::split_paths(&env::var(dylib_env_var()).unwrap()));
+
+        let host_dylib_search_paths = {
+            let mut paths = vec![self.config.compile_lib_path.clone()];
+            paths.extend(base_dylib_search_paths.iter().cloned());
+            paths
+        };
+
+        // Calculate the paths of the recipe binary. As previously discussed, this is placed at
+        // `<base_dir>/<bin_name>` with `bin_name` being `rmake` or `rmake.exe` depending on
+        // platform.
+        let recipe_bin = {
+            let mut p = base_dir.join("rmake");
+            p.set_extension(env::consts::EXE_EXTENSION);
+            p
+        };
+
+        let mut rustc = Command::new(&self.config.rustc_path);
+        rustc
+            .arg("-o")
+            .arg(&recipe_bin)
+            // Specify library search paths for `run_make_support`.
+            .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap().to_string_lossy()))
+            .arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy()))
+            .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy()))
+            // Provide `run_make_support` as extern prelude, so test writers don't need to write
+            // `extern run_make_support;`.
+            .arg("--extern")
+            .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy()))
+            .arg("--edition=2021")
+            .arg(&self.testpaths.file.join("rmake.rs"))
+            // Provide necessary library search paths for rustc.
+            .env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap());
+
+        // In test code we want to be very pedantic about values being silently discarded that are
+        // annotated with `#[must_use]`.
+        rustc.arg("-Dunused_must_use");
+
+        // > `cg_clif` uses `COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0` for running the rustc
+        // > test suite. With the introduction of rmake.rs this broke. `librun_make_support.rlib` is
+        // > compiled using the bootstrap rustc wrapper which sets `--sysroot
+        // > build/aarch64-unknown-linux-gnu/stage0-sysroot`, but then compiletest will compile
+        // > `rmake.rs` using the sysroot of the bootstrap compiler causing it to not find the
+        // > `libstd.rlib` against which `librun_make_support.rlib` is compiled.
+        //
+        // The gist here is that we have to pass the proper stage0 sysroot if we want
+        //
+        // ```
+        // $ COMPILETEST_FORCE_STAGE0=1 ./x test run-make --stage 0
+        // ```
+        //
+        // to work correctly.
+        //
+        // See <https://github.com/rust-lang/rust/pull/122248> for more background.
+        if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() {
+            let stage0_sysroot = build_root.join("stage0-sysroot");
+            rustc.arg("--sysroot").arg(&stage0_sysroot);
+        }
+
+        // Now run rustc to build the recipe.
+        let res = self.run_command_to_procres(&mut rustc);
+        if !res.status.success() {
+            self.fatal_proc_rec("run-make test failed: could not build `rmake.rs` recipe", &res);
+        }
+
+        // To actually run the recipe, we have to provide the recipe with a bunch of information
+        // provided through env vars.
+
+        // Compute stage-specific standard library paths.
+        let stage_std_path = build_root.join(&stage).join("lib");
+
+        // Compute dynamic library search paths for recipes.
+        let recipe_dylib_search_paths = {
+            let mut paths = base_dylib_search_paths.clone();
+            paths.push(support_lib_path.parent().unwrap().to_path_buf());
+            paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib"));
+            paths
+        };
+
+        // Compute runtime library search paths for recipes. This is target-specific.
+        let target_runtime_dylib_search_paths = {
+            let mut paths = vec![rmake_out_dir.clone()];
+            paths.extend(base_dylib_search_paths.iter().cloned());
+            paths
+        };
+
+        // FIXME(jieyouxu): please rename `TARGET_RPATH_ENV`, `HOST_RPATH_DIR` and
+        // `TARGET_RPATH_DIR`, it is **extremely** confusing!
+        let mut cmd = Command::new(&recipe_bin);
+        cmd.current_dir(&rmake_out_dir)
+            .stdout(Stdio::piped())
+            .stderr(Stdio::piped())
+            // Provide the target-specific env var that is used to record dylib search paths. For
+            // example, this could be `LD_LIBRARY_PATH` on some linux distros but `PATH` on Windows.
+            .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
+            // Provide the dylib search paths.
+            .env(dylib_env_var(), &env::join_paths(recipe_dylib_search_paths).unwrap())
+            // Provide runtime dylib search paths.
+            .env("TARGET_RPATH_ENV", &env::join_paths(target_runtime_dylib_search_paths).unwrap())
+            // Provide the target.
+            .env("TARGET", &self.config.target)
+            // Some tests unfortunately still need Python, so provide path to a Python interpreter.
+            .env("PYTHON", &self.config.python)
+            // Provide path to checkout root. This is the top-level directory containing
+            // rust-lang/rust checkout.
+            .env("SOURCE_ROOT", &source_root)
+            // Provide path to stage-corresponding rustc.
+            .env("RUSTC", &self.config.rustc_path)
+            // Provide the directory to libraries that are needed to run the *compiler*. This is not
+            // to be confused with `TARGET_RPATH_ENV` or `TARGET_RPATH_DIR`. This is needed if the
+            // recipe wants to invoke rustc.
+            .env("HOST_RPATH_DIR", &self.config.compile_lib_path)
+            // Provide the directory to libraries that might be needed to run compiled binaries
+            // (further compiled by the recipe!).
+            .env("TARGET_RPATH_DIR", &self.config.run_lib_path)
+            // Provide which LLVM components are available (e.g. which LLVM components are provided
+            // through a specific CI runner).
+            .env("LLVM_COMPONENTS", &self.config.llvm_components);
+
+        if let Some(ref rustdoc) = self.config.rustdoc_path {
+            cmd.env("RUSTDOC", source_root.join(rustdoc));
+        }
+
+        if let Some(ref node) = self.config.nodejs {
+            cmd.env("NODE", node);
+        }
+
+        if let Some(ref linker) = self.config.target_linker {
+            cmd.env("RUSTC_LINKER", linker);
+        }
+
+        if let Some(ref clang) = self.config.run_clang_based_tests_with {
+            cmd.env("CLANG", clang);
+        }
+
+        if let Some(ref filecheck) = self.config.llvm_filecheck {
+            cmd.env("LLVM_FILECHECK", filecheck);
+        }
+
+        if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir {
+            cmd.env("LLVM_BIN_DIR", llvm_bin_dir);
+        }
+
+        if let Some(ref remote_test_client) = self.config.remote_test_client {
+            cmd.env("REMOTE_TEST_CLIENT", remote_test_client);
+        }
+
+        // We don't want RUSTFLAGS set from the outside to interfere with
+        // compiler flags set in the test cases:
+        cmd.env_remove("RUSTFLAGS");
+
+        // Use dynamic musl for tests because static doesn't allow creating dylibs
+        if self.config.host.contains("musl") {
+            cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1");
+        }
+
+        if self.config.bless {
+            // If we're running in `--bless` mode, set an environment variable to tell
+            // `run_make_support` to bless snapshot files instead of checking them.
+            //
+            // The value is this test's source directory, because the support code
+            // will need that path in order to bless the _original_ snapshot files,
+            // not the copies in `rmake_out`.
+            // (See <https://github.com/rust-lang/rust/issues/129038>.)
+            cmd.env("RUSTC_BLESS_TEST", &self.testpaths.file);
+        }
+
+        if self.config.target.contains("msvc") && !self.config.cc.is_empty() {
+            // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
+            // and that `lib.exe` lives next to it.
+            let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
+
+            // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
+            // a path and instead passes `C:\msys64\foo`, so convert all
+            // `/`-arguments to MSVC here to `-` arguments.
+            let cflags = self
+                .config
+                .cflags
+                .split(' ')
+                .map(|s| s.replace("/", "-"))
+                .collect::<Vec<_>>()
+                .join(" ");
+            let cxxflags = self
+                .config
+                .cxxflags
+                .split(' ')
+                .map(|s| s.replace("/", "-"))
+                .collect::<Vec<_>>()
+                .join(" ");
+
+            cmd.env("IS_MSVC", "1")
+                .env("IS_WINDOWS", "1")
+                .env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
+                .env("MSVC_LIB_PATH", format!("{}", lib.display()))
+                // Note: we diverge from legacy run_make and don't lump `CC` the compiler and
+                // default flags together.
+                .env("CC_DEFAULT_FLAGS", &cflags)
+                .env("CC", &self.config.cc)
+                .env("CXX_DEFAULT_FLAGS", &cxxflags)
+                .env("CXX", &self.config.cxx);
+        } else {
+            cmd.env("CC_DEFAULT_FLAGS", &self.config.cflags)
+                .env("CC", &self.config.cc)
+                .env("CXX_DEFAULT_FLAGS", &self.config.cxxflags)
+                .env("CXX", &self.config.cxx)
+                .env("AR", &self.config.ar);
+
+            if self.config.target.contains("windows") {
+                cmd.env("IS_WINDOWS", "1");
+            }
+        }
+
+        let (Output { stdout, stderr, status }, truncated) =
+            self.read2_abbreviated(cmd.spawn().expect("failed to spawn `rmake`"));
+        if !status.success() {
+            let res = ProcRes {
+                status,
+                stdout: String::from_utf8_lossy(&stdout).into_owned(),
+                stderr: String::from_utf8_lossy(&stderr).into_owned(),
+                truncated,
+                cmdline: format!("{:?}", cmd),
+            };
+            self.fatal_proc_rec("rmake recipe failed to complete", &res);
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/rustdoc.rs b/src/tools/compiletest/src/runtest/rustdoc.rs
new file mode 100644
index 00000000000..6a31888527e
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/rustdoc.rs
@@ -0,0 +1,34 @@
+use std::process::Command;
+
+use super::{remove_and_create_dir_all, TestCx};
+
+impl TestCx<'_> {
+    pub(super) fn run_rustdoc_test(&self) {
+        assert!(self.revision.is_none(), "revisions not relevant here");
+
+        let out_dir = self.output_base_dir();
+        remove_and_create_dir_all(&out_dir);
+
+        let proc_res = self.document(&out_dir, &self.testpaths);
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("rustdoc failed!", &proc_res);
+        }
+
+        if self.props.check_test_line_numbers_match {
+            self.check_rustdoc_test_option(proc_res);
+        } else {
+            let root = self.config.find_rust_src_root().unwrap();
+            let mut cmd = Command::new(&self.config.python);
+            cmd.arg(root.join("src/etc/htmldocck.py")).arg(&out_dir).arg(&self.testpaths.file);
+            if self.config.bless {
+                cmd.arg("--bless");
+            }
+            let res = self.run_command_to_procres(&mut cmd);
+            if !res.status.success() {
+                self.fatal_proc_rec_with_ctx("htmldocck failed!", &res, |mut this| {
+                    this.compare_to_default_rustdoc(&out_dir)
+                });
+            }
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs
new file mode 100644
index 00000000000..a39887ccd02
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs
@@ -0,0 +1,48 @@
+use std::process::Command;
+
+use super::{remove_and_create_dir_all, TestCx};
+
+impl TestCx<'_> {
+    pub(super) fn run_rustdoc_json_test(&self) {
+        //FIXME: Add bless option.
+
+        assert!(self.revision.is_none(), "revisions not relevant here");
+
+        let out_dir = self.output_base_dir();
+        remove_and_create_dir_all(&out_dir);
+
+        let proc_res = self.document(&out_dir, &self.testpaths);
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("rustdoc failed!", &proc_res);
+        }
+
+        let root = self.config.find_rust_src_root().unwrap();
+        let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
+        json_out.set_extension("json");
+        let res = self.run_command_to_procres(
+            Command::new(self.config.jsondocck_path.as_ref().unwrap())
+                .arg("--doc-dir")
+                .arg(root.join(&out_dir))
+                .arg("--template")
+                .arg(&self.testpaths.file),
+        );
+
+        if !res.status.success() {
+            self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| {
+                println!("Rustdoc Output:");
+                proc_res.print_info();
+            })
+        }
+
+        let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
+        json_out.set_extension("json");
+
+        let res = self.run_command_to_procres(
+            Command::new(self.config.jsondoclint_path.as_ref().unwrap()).arg(&json_out),
+        );
+
+        if !res.status.success() {
+            self.fatal_proc_rec("jsondoclint failed!", &res);
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
new file mode 100644
index 00000000000..88a0ec3aa3b
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -0,0 +1,233 @@
+use std::collections::HashSet;
+use std::fs::OpenOptions;
+use std::io::Write;
+
+use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
+use tracing::debug;
+
+use super::{
+    AllowUnused, Emit, ErrorKind, FailMode, LinkToAux, PassMode, TargetLocation, TestCx,
+    TestOutput, Truncated, WillExecute, UI_FIXED,
+};
+use crate::{errors, json};
+
+impl TestCx<'_> {
+    pub(super) fn run_ui_test(&self) {
+        if let Some(FailMode::Build) = self.props.fail_mode {
+            // Make sure a build-fail test cannot fail due to failing analysis (e.g. typeck).
+            let pm = Some(PassMode::Check);
+            let proc_res =
+                self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new());
+            self.check_if_test_should_compile(&proc_res, pm);
+        }
+
+        let pm = self.pass_mode();
+        let should_run = self.should_run(pm);
+        let emit_metadata = self.should_emit_metadata(pm);
+        let proc_res = self.compile_test(should_run, emit_metadata);
+        self.check_if_test_should_compile(&proc_res, pm);
+        if matches!(proc_res.truncated, Truncated::Yes)
+            && !self.props.dont_check_compiler_stdout
+            && !self.props.dont_check_compiler_stderr
+        {
+            self.fatal_proc_rec(
+                "compiler output got truncated, cannot compare with reference file",
+                &proc_res,
+            );
+        }
+
+        // if the user specified a format in the ui test
+        // print the output to the stderr file, otherwise extract
+        // the rendered error messages from json and print them
+        let explicit = self.props.compile_flags.iter().any(|s| s.contains("--error-format"));
+
+        let expected_fixed = self.load_expected_output(UI_FIXED);
+
+        self.check_and_prune_duplicate_outputs(&proc_res, &[], &[]);
+
+        let mut errors = self.load_compare_outputs(&proc_res, TestOutput::Compile, explicit);
+        let rustfix_input = json::rustfix_diagnostics_only(&proc_res.stderr);
+
+        if self.config.compare_mode.is_some() {
+            // don't test rustfix with nll right now
+        } else if self.config.rustfix_coverage {
+            // Find out which tests have `MachineApplicable` suggestions but are missing
+            // `run-rustfix` or `run-rustfix-only-machine-applicable` headers.
+            //
+            // This will return an empty `Vec` in case the executed test file has a
+            // `compile-flags: --error-format=xxxx` header with a value other than `json`.
+            let suggestions = get_suggestions_from_json(
+                &rustfix_input,
+                &HashSet::new(),
+                Filter::MachineApplicableOnly,
+            )
+            .unwrap_or_default();
+            if !suggestions.is_empty()
+                && !self.props.run_rustfix
+                && !self.props.rustfix_only_machine_applicable
+            {
+                let mut coverage_file_path = self.config.build_base.clone();
+                coverage_file_path.push("rustfix_missing_coverage.txt");
+                debug!("coverage_file_path: {}", coverage_file_path.display());
+
+                let mut file = OpenOptions::new()
+                    .create(true)
+                    .append(true)
+                    .open(coverage_file_path.as_path())
+                    .expect("could not create or open file");
+
+                if let Err(e) = writeln!(file, "{}", self.testpaths.file.display()) {
+                    panic!("couldn't write to {}: {e:?}", coverage_file_path.display());
+                }
+            }
+        } else if self.props.run_rustfix {
+            // Apply suggestions from rustc to the code itself
+            let unfixed_code = self.load_expected_output_from_path(&self.testpaths.file).unwrap();
+            let suggestions = get_suggestions_from_json(
+                &rustfix_input,
+                &HashSet::new(),
+                if self.props.rustfix_only_machine_applicable {
+                    Filter::MachineApplicableOnly
+                } else {
+                    Filter::Everything
+                },
+            )
+            .unwrap();
+            let fixed_code = apply_suggestions(&unfixed_code, &suggestions).unwrap_or_else(|e| {
+                panic!(
+                    "failed to apply suggestions for {:?} with rustfix: {}",
+                    self.testpaths.file, e
+                )
+            });
+
+            errors += self.compare_output("fixed", &fixed_code, &expected_fixed);
+        } else if !expected_fixed.is_empty() {
+            panic!(
+                "the `//@ run-rustfix` directive wasn't found but a `*.fixed` \
+                 file was found"
+            );
+        }
+
+        if errors > 0 {
+            println!("To update references, rerun the tests and pass the `--bless` flag");
+            let relative_path_to_file =
+                self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
+            println!(
+                "To only update this specific test, also pass `--test-args {}`",
+                relative_path_to_file.display(),
+            );
+            self.fatal_proc_rec(
+                &format!("{} errors occurred comparing output.", errors),
+                &proc_res,
+            );
+        }
+
+        let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
+
+        if let WillExecute::Yes = should_run {
+            let proc_res = self.exec_compiled_test();
+            let run_output_errors = if self.props.check_run_results {
+                self.load_compare_outputs(&proc_res, TestOutput::Run, explicit)
+            } else {
+                0
+            };
+            if run_output_errors > 0 {
+                self.fatal_proc_rec(
+                    &format!("{} errors occurred comparing run output.", run_output_errors),
+                    &proc_res,
+                );
+            }
+            if self.should_run_successfully(pm) {
+                if !proc_res.status.success() {
+                    self.fatal_proc_rec("test run failed!", &proc_res);
+                }
+            } else if proc_res.status.success() {
+                self.fatal_proc_rec("test run succeeded!", &proc_res);
+            }
+
+            if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty()
+            {
+                // "// error-pattern" comments
+                let output_to_check = self.get_output(&proc_res);
+                self.check_all_error_patterns(&output_to_check, &proc_res, pm);
+            }
+        }
+
+        debug!(
+            "run_ui_test: explicit={:?} config.compare_mode={:?} expected_errors={:?} \
+               proc_res.status={:?} props.error_patterns={:?}",
+            explicit,
+            self.config.compare_mode,
+            expected_errors,
+            proc_res.status,
+            self.props.error_patterns
+        );
+
+        let check_patterns = should_run == WillExecute::No
+            && (!self.props.error_patterns.is_empty()
+                || !self.props.regex_error_patterns.is_empty());
+        if !explicit && self.config.compare_mode.is_none() {
+            let check_annotations = !check_patterns || !expected_errors.is_empty();
+
+            if check_annotations {
+                // "//~ERROR comments"
+                self.check_expected_errors(expected_errors, &proc_res);
+            }
+        } else if explicit && !expected_errors.is_empty() {
+            let msg = format!(
+                "line {}: cannot combine `--error-format` with {} annotations; use `error-pattern` instead",
+                expected_errors[0].line_num,
+                expected_errors[0].kind.unwrap_or(ErrorKind::Error),
+            );
+            self.fatal(&msg);
+        }
+        if check_patterns {
+            // "// error-pattern" comments
+            let output_to_check = self.get_output(&proc_res);
+            self.check_all_error_patterns(&output_to_check, &proc_res, pm);
+        }
+
+        if self.props.run_rustfix && self.config.compare_mode.is_none() {
+            // And finally, compile the fixed code and make sure it both
+            // succeeds and has no diagnostics.
+            let mut rustc = self.make_compile_args(
+                &self.expected_output_path(UI_FIXED),
+                TargetLocation::ThisFile(self.make_exe_name()),
+                emit_metadata,
+                AllowUnused::No,
+                LinkToAux::Yes,
+                Vec::new(),
+            );
+
+            // If a test is revisioned, it's fixed source file can be named "a.foo.fixed", which,
+            // well, "a.foo" isn't a valid crate name. So we explicitly mangle the test name
+            // (including the revision) here to avoid the test writer having to manually specify a
+            // `#![crate_name = "..."]` as a workaround. This is okay since we're only checking if
+            // the fixed code is compilable.
+            if self.revision.is_some() {
+                let crate_name =
+                    self.testpaths.file.file_stem().expect("test must have a file stem");
+                // crate name must be alphanumeric or `_`.
+                let crate_name =
+                    crate_name.to_str().expect("crate name implies file name must be valid UTF-8");
+                // replace `a.foo` -> `a__foo` for crate name purposes.
+                // replace `revision-name-with-dashes` -> `revision_name_with_underscore`
+                let crate_name = crate_name.replace('.', "__");
+                let crate_name = crate_name.replace('-', "_");
+                rustc.arg("--crate-name");
+                rustc.arg(crate_name);
+            }
+
+            let res = self.compose_and_run_compiler(rustc, None, self.testpaths);
+            if !res.status.success() {
+                self.fatal_proc_rec("failed to compile fixed code", &res);
+            }
+            if !res.stderr.is_empty()
+                && !self.props.rustfix_only_machine_applicable
+                && !json::rustfix_diagnostics_only(&res.stderr).is_empty()
+            {
+                self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
+            }
+        }
+    }
+}
diff --git a/src/tools/compiletest/src/runtest/valgrind.rs b/src/tools/compiletest/src/runtest/valgrind.rs
new file mode 100644
index 00000000000..8d72c4be9ff
--- /dev/null
+++ b/src/tools/compiletest/src/runtest/valgrind.rs
@@ -0,0 +1,34 @@
+use super::{Emit, TestCx, WillExecute};
+
+impl TestCx<'_> {
+    pub(super) fn run_valgrind_test(&self) {
+        assert!(self.revision.is_none(), "revisions not relevant here");
+
+        // FIXME(jieyouxu): does this really make any sense? If a valgrind test isn't testing
+        // valgrind, what is it even testing?
+        if self.config.valgrind_path.is_none() {
+            assert!(!self.config.force_valgrind);
+            return self.run_rpass_test();
+        }
+
+        let should_run = self.run_if_enabled();
+        let mut proc_res = self.compile_test(should_run, Emit::None);
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
+        let mut new_config = self.config.clone();
+        new_config.runner = new_config.valgrind_path.clone();
+        let new_cx = TestCx { config: &new_config, ..*self };
+        proc_res = new_cx.exec_compiled_test();
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("test run failed!", &proc_res);
+        }
+    }
+}
diff --git a/src/tools/miri/tests/fail/uninit/padding-enum.rs b/src/tools/miri/tests/fail/uninit/padding-enum.rs
index a9628799b7d..e1a16bea23c 100644
--- a/src/tools/miri/tests/fail/uninit/padding-enum.rs
+++ b/src/tools/miri/tests/fail/uninit/padding-enum.rs
@@ -17,10 +17,11 @@ fn main() {
         assert!(matches!(*p.as_ptr(), E::None));
 
         // Turns out the discriminant is (currently) stored
-        // in the 2nd pointer, so the first half is padding.
+        // in the 1st pointer, so the second half is padding.
         let c = &p as *const _ as *const u8;
+        let padding_offset = mem::size_of::<&'static ()>();
         // Read a padding byte.
-        let _val = *c.add(0);
+        let _val = *c.add(padding_offset);
         //~^ERROR: uninitialized
     }
 }
diff --git a/src/tools/miri/tests/fail/uninit/padding-enum.stderr b/src/tools/miri/tests/fail/uninit/padding-enum.stderr
index 66d3092c9ba..765e7cc4e63 100644
--- a/src/tools/miri/tests/fail/uninit/padding-enum.stderr
+++ b/src/tools/miri/tests/fail/uninit/padding-enum.stderr
@@ -1,8 +1,8 @@
 error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
   --> tests/fail/uninit/padding-enum.rs:LL:CC
    |
-LL |         let _val = *c.add(0);
-   |                    ^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
+LL |         let _val = *c.add(padding_offset);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml
index 2f2beb76915..99daa0addf5 100644
--- a/src/tools/rustfmt/.github/workflows/check_diff.yml
+++ b/src/tools/rustfmt/.github/workflows/check_diff.yml
@@ -21,7 +21,7 @@ jobs:
 
     steps:
     - name: checkout
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: install rustup
       run: |
diff --git a/src/tools/rustfmt/.github/workflows/integration.yml b/src/tools/rustfmt/.github/workflows/integration.yml
index f0dd0cf73bb..bda374562bc 100644
--- a/src/tools/rustfmt/.github/workflows/integration.yml
+++ b/src/tools/rustfmt/.github/workflows/integration.yml
@@ -64,7 +64,7 @@ jobs:
 
     steps:
     - name: checkout
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
       # Run build
     - name: install rustup
diff --git a/src/tools/rustfmt/.github/workflows/linux.yml b/src/tools/rustfmt/.github/workflows/linux.yml
index bce9b0c8d5a..3a5e6ab5404 100644
--- a/src/tools/rustfmt/.github/workflows/linux.yml
+++ b/src/tools/rustfmt/.github/workflows/linux.yml
@@ -26,7 +26,7 @@ jobs:
 
     steps:
     - name: checkout
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
       # Run build
     - name: install rustup
diff --git a/src/tools/rustfmt/.github/workflows/mac.yml b/src/tools/rustfmt/.github/workflows/mac.yml
index 89a980c42c5..2c766d0573b 100644
--- a/src/tools/rustfmt/.github/workflows/mac.yml
+++ b/src/tools/rustfmt/.github/workflows/mac.yml
@@ -8,7 +8,6 @@ on:
 jobs:
   test:
     # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources
-    # macOS Catalina 10.15
     runs-on: macos-latest
     name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }})
     env:
@@ -23,7 +22,7 @@ jobs:
 
     steps:
     - name: checkout
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
       # Run build
     - name: install rustup
diff --git a/src/tools/rustfmt/.github/workflows/rustdoc_check.yml b/src/tools/rustfmt/.github/workflows/rustdoc_check.yml
index cd0c3218971..6e8a7ecd7ad 100644
--- a/src/tools/rustfmt/.github/workflows/rustdoc_check.yml
+++ b/src/tools/rustfmt/.github/workflows/rustdoc_check.yml
@@ -11,7 +11,7 @@ jobs:
     name: rustdoc check
     steps:
     - name: checkout
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: install rustup
       run: |
diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml
index 7dfaa4b9204..7a639b469e8 100644
--- a/src/tools/rustfmt/.github/workflows/upload-assets.yml
+++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml
@@ -31,7 +31,7 @@ jobs:
             target: x86_64-pc-windows-msvc
     runs-on: ${{ matrix.os }}
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
         # Run build
       - name: install rustup
diff --git a/src/tools/rustfmt/.github/workflows/windows.yml b/src/tools/rustfmt/.github/workflows/windows.yml
index ec37c714b08..728f1b90b13 100644
--- a/src/tools/rustfmt/.github/workflows/windows.yml
+++ b/src/tools/rustfmt/.github/workflows/windows.yml
@@ -33,7 +33,7 @@ jobs:
     - name: disable git eol translation
       run: git config --global core.autocrlf false
     - name: checkout
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
       # Run build
     - name: Install Rustup using win.rustup.rs
diff --git a/src/tools/rustfmt/CHANGELOG.md b/src/tools/rustfmt/CHANGELOG.md
index 89e90fb17dd..8af60f60dc6 100644
--- a/src/tools/rustfmt/CHANGELOG.md
+++ b/src/tools/rustfmt/CHANGELOG.md
@@ -1,6 +1,66 @@
 # Changelog
 
-## [Unreleased]
+## [1.8.0] 2024-09-20
+
+### Fixed
+- Fix issue where rustfmt would crash on Windows when using the `ignore` option [#6178](https://github.com/rust-lang/rustfmt/issues/6178)
+
+### Changed
+- `rustfmt --version` now prints a commit hash that is 10 characters long [#6258](https://github.com/rust-lang/rustfmt/pull/6258)
+- `rustfmt --version` will no longer print empty git information when git information isn't available at build time.
+  For example, git information is not available when building rustfmt from a source tarball [#6266](https://github.com/rust-lang/rustfmt/pull/6266)
+- `version` has been soft deprecated and replaced by `style_edition`.
+  `style_edition=2024` is equivalent to `version=Two` and `style_edition={2015|2018|2021}`
+  are equivalent to `version=One` [#6247](https://github.com/rust-lang/rustfmt/pull/6247)
+- When `style_edition=2024` is configured `overflow_delimited_expr` will default to `true` [#6260](https://github.com/rust-lang/rustfmt/pull/6260).
+  ```rust
+  // with style_edition=2015
+  do_thing(
+      x,
+      Bar {
+          x: value,
+          y: value2,
+      },
+  );
+
+  // with style_edition=2024
+  do_thing(x, Bar {
+      x: value,
+      y: value2,
+  });
+  ```
+- When `style_edition=2024` is configured rustfmt will apply the [style guide's version sorting algorithm]
+  when sorting imports [#6284](https://github.com/rust-lang/rustfmt/pull/6284)
+  ```rust
+  // with style_edition=2015
+  use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8};
+
+  // with style_edition=2024
+  use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64};
+  ```
+  [style guide's version sorting algorithm]: https://doc.rust-lang.org/nightly/style-guide/#sorting
+- When parsing rustfmt configurations fails, rustfmt will now include the path to the toml file in the erorr message [#6302](https://github.com/rust-lang/rustfmt/issues/6302)
+
+### Added
+- rustfmt now formats trailing where clauses in type aliases [#5887](https://github.com/rust-lang/rustfmt/pull/5887)
+  ```rust
+  type Foo
+      = Bar
+  where
+      A: B,
+      C: D;
+  ```
+- Users can now configure which `style_edition` rustfmt uses when formatting their code as specified
+  in [RFC 3338](https://rust-lang.github.io/rfcs/3338-style-evolution.html). Users are encouraged to configure `style_edition`
+  in their `rustfmt.toml` files, but the value can also be specified via the cli with `--unstable-features --style-edition={style_edition}`.
+  When `style_edition` is not explicitly configured it will be inferred from the `edition` configuration.
+  When neither `style_edition` nor `edition` are configured `style_edition` defaults to `2015` [#6247](https://github.com/rust-lang/rustfmt/pull/6247)
+
+### Misc
+- Removed `tracing-attributes` dependency [#6208](https://github.com/rust-lang/rustfmt/pull/6208)
+- Reduced syn's features in the internal `config_proc_macro` crate [#6237](https://github.com/rust-lang/rustfmt/pull/6237)
+
+## [1.7.1] 2024-06-24
 
 ### Fixed
 
@@ -238,7 +298,7 @@
 
 ### Added
 
-- New configuration option (`skip_macro_invocations`)[https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations] [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726)
+- New configuration option [`skip_macro_invocations`](https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations) [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726)
 
 ### Misc
 
diff --git a/src/tools/rustfmt/Cargo.lock b/src/tools/rustfmt/Cargo.lock
index 2a1fffa50fe..e2ceb668ebd 100644
--- a/src/tools/rustfmt/Cargo.lock
+++ b/src/tools/rustfmt/Cargo.lock
@@ -499,7 +499,7 @@ dependencies = [
 
 [[package]]
 name = "rustfmt-nightly"
-version = "1.7.1"
+version = "1.8.0"
 dependencies = [
  "annotate-snippets",
  "anyhow",
@@ -710,22 +710,10 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
 dependencies = [
  "cfg-if",
  "pin-project-lite",
- "tracing-attributes",
  "tracing-core",
 ]
 
 [[package]]
-name = "tracing-attributes"
-version = "0.1.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "tracing-core"
 version = "0.1.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/rustfmt/Cargo.toml b/src/tools/rustfmt/Cargo.toml
index a16620ed99b..e497b792342 100644
--- a/src/tools/rustfmt/Cargo.toml
+++ b/src/tools/rustfmt/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 
 name = "rustfmt-nightly"
-version = "1.7.1"
+version = "1.8.0"
 description = "Tool to find and fix Rust formatting issues"
 repository = "https://github.com/rust-lang/rustfmt"
 readme = "README.md"
@@ -50,7 +50,7 @@ serde_json = "1.0"
 term = "0.7"
 thiserror = "1.0.40"
 toml = "0.7.4"
-tracing = "0.1.37"
+tracing = { version = "0.1.37", default-features = false, features = ["std"] }
 tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
 unicode-segmentation = "1.9"
 unicode-width = "0.1"
diff --git a/src/tools/rustfmt/Configurations.md b/src/tools/rustfmt/Configurations.md
index f52c2573154..b1f54060392 100644
--- a/src/tools/rustfmt/Configurations.md
+++ b/src/tools/rustfmt/Configurations.md
@@ -534,7 +534,7 @@ Note that this option may be soft-deprecated in the future once the [ignore](#ig
 Specifies which edition is used by the parser.
 
 - **Default value**: `"2015"`
-- **Possible values**: `"2015"`, `"2018"`, `"2021"`
+- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"`
 - **Stable**: Yes
 
 Rustfmt is able to pick up the edition used by reading the `Cargo.toml` file if executed
@@ -2692,6 +2692,17 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi
 
 See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
 
+## `style_edition`
+
+Controls the edition of the [Rust Style Guide] to use for formatting ([RFC 3338])
+
+- **Default value**: `"2015"`
+- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"` (unstable variant)
+- **Stable**: No
+
+[Rust Style Guide]: https://doc.rust-lang.org/nightly/style-guide/
+[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html
+
 ## `tab_spaces`
 
 Number of spaces per tab
@@ -3051,9 +3062,7 @@ fn main() {
 
 ## `version`
 
-Which version of the formatting rules to use. `Version::One` is backwards-compatible
-with Rustfmt 1.0. Other versions are only backwards compatible within a major
-version number.
+This option is deprecated and has been replaced by [`style_edition`](#style_edition)
 
 - **Default value**: `One`
 - **Possible values**: `One`, `Two`
diff --git a/src/tools/rustfmt/Contributing.md b/src/tools/rustfmt/Contributing.md
index 2f2ccfb175a..85754a8658a 100644
--- a/src/tools/rustfmt/Contributing.md
+++ b/src/tools/rustfmt/Contributing.md
@@ -109,17 +109,17 @@ If you want to test modified `cargo-fmt`, or run `rustfmt` on the whole project
 RUSTFMT="./target/debug/rustfmt" cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml
 ```
 
-### Version-gate formatting changes
+### Gate formatting changes
 
-A change that introduces a different code-formatting should be gated on the
-`version` configuration. This is to ensure the formatting of the current major
-release is preserved, while allowing fixes to be implemented for the next
-release.
+A change that introduces a different code-formatting must be gated on the
+`style_edition` configuration. This is to ensure rustfmt upholds its formatting
+stability guarantees and adheres to the Style Edition process set in [RFC 3338]
 
-This is done by conditionally guarding the change like so:
+This can be done by conditionally guarding the formatting change, e.g.:
 
 ```rust
-if config.version() == Version::One { // if the current major release is 1.x
+// if the current stable Style Edition is Edition 2024
+if config.style_edition() <= StyleEdition::Edition2024 {
     // current formatting
 } else {
     // new formatting
@@ -129,13 +129,14 @@ if config.version() == Version::One { // if the current major release is 1.x
 This allows the user to apply the next formatting explicitly via the
 configuration, while being stable by default.
 
-When the next major release is done, the code block of the previous formatting
-can be deleted, e.g., the first block in the example above when going from `1.x`
-to `2.x`.
+This can then be enhanced as needed if and when there are
+new Style Editions with differing formatting prescriptions.
 
 | Note: Only formatting changes with default options need to be gated. |
 | --- |
 
+[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html
+
 ### A quick tour of Rustfmt
 
 Rustfmt is basically a pretty printer - that is, its mode of operation is to
diff --git a/src/tools/rustfmt/build.rs b/src/tools/rustfmt/build.rs
index 9a8bb77a8ed..696c713d726 100644
--- a/src/tools/rustfmt/build.rs
+++ b/src/tools/rustfmt/build.rs
@@ -25,7 +25,7 @@ fn main() {
 // (git not installed or if this is not a git repository) just return an empty string.
 fn commit_info() -> String {
     match (channel(), commit_hash(), commit_date()) {
-        (channel, Some(hash), Some(date)) => format!("{} ({} {})", channel, hash.trim_end(), date),
+        (channel, Some(hash), Some(date)) => format!("{} ({} {})", channel, hash, date),
         _ => String::new(),
     }
 }
@@ -39,17 +39,20 @@ fn channel() -> String {
 }
 
 fn commit_hash() -> Option<String> {
-    Command::new("git")
-        .args(["rev-parse", "--short", "HEAD"])
+    let output = Command::new("git")
+        .args(["rev-parse", "HEAD"])
         .output()
-        .ok()
-        .and_then(|r| String::from_utf8(r.stdout).ok())
+        .ok()?;
+    let mut stdout = output.status.success().then_some(output.stdout)?;
+    stdout.truncate(10);
+    String::from_utf8(stdout).ok()
 }
 
 fn commit_date() -> Option<String> {
-    Command::new("git")
+    let output = Command::new("git")
         .args(["log", "-1", "--date=short", "--pretty=format:%cd"])
         .output()
-        .ok()
-        .and_then(|r| String::from_utf8(r.stdout).ok())
+        .ok()?;
+    let stdout = output.status.success().then_some(output.stdout)?;
+    String::from_utf8(stdout).ok()
 }
diff --git a/src/tools/rustfmt/check_diff/Cargo.lock b/src/tools/rustfmt/check_diff/Cargo.lock
index 6716ccdf9a0..2abf5af2f98 100644
--- a/src/tools/rustfmt/check_diff/Cargo.lock
+++ b/src/tools/rustfmt/check_diff/Cargo.lock
@@ -3,6 +3,15 @@
 version = 3
 
 [[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "anstream"
 version = "0.6.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -52,10 +61,25 @@ dependencies = [
 ]
 
 [[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
 name = "check_diff"
 version = "0.1.0"
 dependencies = [
  "clap",
+ "tempfile",
+ "tracing",
+ "tracing-subscriber",
 ]
 
 [[package]]
@@ -105,6 +129,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
 
 [[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+
+[[package]]
 name = "heck"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -117,6 +157,73 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
 
 [[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
 name = "proc-macro2"
 version = "1.0.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -135,6 +242,78 @@ dependencies = [
 ]
 
 [[package]]
+name = "regex"
+version = "1.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.7",
+ "regex-syntax 0.8.4",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.4",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
 name = "strsim"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -152,6 +331,89 @@ dependencies = [
 ]
 
 [[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
 name = "unicode-ident"
 version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -164,6 +426,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
 [[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
 name = "windows-sys"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/rustfmt/check_diff/Cargo.toml b/src/tools/rustfmt/check_diff/Cargo.toml
index a1ed154481a..4ae8a5f1f3a 100644
--- a/src/tools/rustfmt/check_diff/Cargo.toml
+++ b/src/tools/rustfmt/check_diff/Cargo.toml
@@ -7,3 +7,7 @@ edition = "2021"
 
 [dependencies]
 clap = { version = "4.4.2", features = ["derive"] }
+tracing = "0.1.37"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+[dev-dependencies]
+tempfile = "3"
diff --git a/src/tools/rustfmt/check_diff/src/lib.rs b/src/tools/rustfmt/check_diff/src/lib.rs
new file mode 100644
index 00000000000..b83d67c8b6e
--- /dev/null
+++ b/src/tools/rustfmt/check_diff/src/lib.rs
@@ -0,0 +1,58 @@
+use std::env;
+use std::io;
+use std::path::Path;
+use std::process::Command;
+use tracing::info;
+
+pub enum GitError {
+    FailedClone { stdout: Vec<u8>, stderr: Vec<u8> },
+    IO(std::io::Error),
+}
+
+impl From<io::Error> for GitError {
+    fn from(error: io::Error) -> Self {
+        GitError::IO(error)
+    }
+}
+
+/// Clone a git repository
+///
+/// Parameters:
+/// url: git clone url
+/// dest: directory where the repo should be cloned
+pub fn clone_git_repo(url: &str, dest: &Path) -> Result<(), GitError> {
+    let git_cmd = Command::new("git")
+        .env("GIT_TERMINAL_PROMPT", "0")
+        .args([
+            "clone",
+            "--quiet",
+            url,
+            "--depth",
+            "1",
+            dest.to_str().unwrap(),
+        ])
+        .output()?;
+
+    // if the git command does not return successfully,
+    // any command on the repo will fail. So fail fast.
+    if !git_cmd.status.success() {
+        let error = GitError::FailedClone {
+            stdout: git_cmd.stdout,
+            stderr: git_cmd.stderr,
+        };
+        return Err(error);
+    }
+
+    info!("Successfully clone repository.");
+    return Ok(());
+}
+
+pub fn change_directory_to_path(dest: &Path) -> io::Result<()> {
+    let dest_path = Path::new(&dest);
+    env::set_current_dir(&dest_path)?;
+    info!(
+        "Current directory: {}",
+        env::current_dir().unwrap().display()
+    );
+    return Ok(());
+}
diff --git a/src/tools/rustfmt/check_diff/src/main.rs b/src/tools/rustfmt/check_diff/src/main.rs
index 6d07c1b0df6..01c5926c490 100644
--- a/src/tools/rustfmt/check_diff/src/main.rs
+++ b/src/tools/rustfmt/check_diff/src/main.rs
@@ -1,4 +1,5 @@
 use clap::Parser;
+
 /// Inputs for the check_diff script
 #[derive(Parser)]
 struct CliInputs {
@@ -16,10 +17,5 @@ struct CliInputs {
 }
 
 fn main() {
-    let args = CliInputs::parse();
-    println!(
-        "remote_repo_url: {:?}, feature_branch: {:?},
-        optional_commit_hash: {:?}, optional_rustfmt_config: {:?}",
-        args.remote_repo_url, args.feature_branch, args.commit_hash, args.rustfmt_config
-    );
+    let _args = CliInputs::parse();
 }
diff --git a/src/tools/rustfmt/check_diff/tests/bash_commands.rs b/src/tools/rustfmt/check_diff/tests/bash_commands.rs
new file mode 100644
index 00000000000..38ee34ef503
--- /dev/null
+++ b/src/tools/rustfmt/check_diff/tests/bash_commands.rs
@@ -0,0 +1,12 @@
+use check_diff::change_directory_to_path;
+use std::env;
+use tempfile::Builder;
+
+#[test]
+fn cd_test() {
+    // Creates an empty directory in the current working directory
+    let dir = Builder::new().tempdir_in("").unwrap();
+    let dest_path = dir.path();
+    change_directory_to_path(dest_path).unwrap();
+    assert_eq!(env::current_dir().unwrap(), dest_path);
+}
diff --git a/src/tools/rustfmt/check_diff/tests/git.rs b/src/tools/rustfmt/check_diff/tests/git.rs
new file mode 100644
index 00000000000..677c3840e1e
--- /dev/null
+++ b/src/tools/rustfmt/check_diff/tests/git.rs
@@ -0,0 +1,16 @@
+use check_diff::clone_git_repo;
+
+use tempfile::Builder;
+
+#[test]
+fn clone_repo_test() {
+    // Creates an empty directory in the current working directory
+    let dir = Builder::new().tempdir_in("").unwrap();
+    let sample_repo = "https://github.com/rust-lang/rustfmt.git";
+    let dest_path = dir.path();
+    let result = clone_git_repo(sample_repo, dest_path);
+    assert!(result.is_ok());
+    // check whether a .git folder exists after cloning the repo
+    let git_repo = dest_path.join(".git");
+    assert!(git_repo.exists());
+}
diff --git a/src/tools/rustfmt/ci/build_and_test.bat b/src/tools/rustfmt/ci/build_and_test.bat
index 16608a4aaa7..b6b5ca21364 100755
--- a/src/tools/rustfmt/ci/build_and_test.bat
+++ b/src/tools/rustfmt/ci/build_and_test.bat
@@ -13,7 +13,13 @@ if "%CFG_RELEASE_CHANNEL%"=="nightly" (
 )
 cargo test || exit /b 1
 
-:: Build and test other crates
+:: Build and test config_proc_macro
 cd config_proc_macro || exit /b 1
 cargo build --locked || exit /b 1
 cargo test || exit /b 1
+
+:: Build and test check_diff
+cd ..
+cd check_diff || exit /b 1
+cargo build --locked || exit /b 1
+cargo test || exit /b 1
diff --git a/src/tools/rustfmt/ci/build_and_test.sh b/src/tools/rustfmt/ci/build_and_test.sh
index 207da362fd6..dd9a0c0fd9b 100755
--- a/src/tools/rustfmt/ci/build_and_test.sh
+++ b/src/tools/rustfmt/ci/build_and_test.sh
@@ -17,7 +17,13 @@ else
 fi
 cargo test
 
-# Build and test other crates
+# Build and test config_proc_macro
 cd config_proc_macro
 cargo build --locked
 cargo test
+
+# Build and test check_diff
+cd ..
+cd check_diff
+cargo build --locked
+cargo test
diff --git a/src/tools/rustfmt/config_proc_macro/Cargo.toml b/src/tools/rustfmt/config_proc_macro/Cargo.toml
index eda8a7fce81..ec0db49d71c 100644
--- a/src/tools/rustfmt/config_proc_macro/Cargo.toml
+++ b/src/tools/rustfmt/config_proc_macro/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "rustfmt-config_proc_macro"
 version = "0.3.0"
-edition = "2018"
+edition = "2021"
 description = "A collection of procedural macros for rustfmt"
 license = "Apache-2.0 OR MIT"
 categories = ["development-tools::procedural-macro-helpers"]
@@ -13,7 +13,7 @@ proc-macro = true
 [dependencies]
 proc-macro2 = "1.0"
 quote = "1.0"
-syn = { version = "2.0", features = ["full", "visit"] }
+syn = { version = "2.0", default-features = false, features = ["full", "parsing", "proc-macro", "printing"] }
 
 [dev-dependencies]
 serde = { version = "1.0.160", features = ["derive"] }
diff --git a/src/tools/rustfmt/config_proc_macro/src/utils.rs b/src/tools/rustfmt/config_proc_macro/src/utils.rs
index f5cba87b07b..1f5b5cdb604 100644
--- a/src/tools/rustfmt/config_proc_macro/src/utils.rs
+++ b/src/tools/rustfmt/config_proc_macro/src/utils.rs
@@ -1,5 +1,5 @@
 use proc_macro2::TokenStream;
-use quote::{quote, ToTokens};
+use quote::{ToTokens, quote};
 
 pub fn fold_quote<F, I, T>(input: impl Iterator<Item = I>, f: F) -> TokenStream
 where
diff --git a/src/tools/rustfmt/rust-toolchain b/src/tools/rustfmt/rust-toolchain
index 25e3961d32a..80723123274 100644
--- a/src/tools/rustfmt/rust-toolchain
+++ b/src/tools/rustfmt/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2024-06-13"
+channel = "nightly-2024-09-10"
 components = ["llvm-tools", "rustc-dev"]
diff --git a/src/tools/rustfmt/rustfmt.toml b/src/tools/rustfmt/rustfmt.toml
index eccd5f9bd19..86447eac2e6 100644
--- a/src/tools/rustfmt/rustfmt.toml
+++ b/src/tools/rustfmt/rustfmt.toml
@@ -1,3 +1,4 @@
 error_on_line_overflow = true
 error_on_unformatted = true
-version = "Two"
+style_edition = "2024"
+overflow_delimited_expr = false
diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs
index a2c0a28d66e..5c2068b6a22 100644
--- a/src/tools/rustfmt/src/attr.rs
+++ b/src/tools/rustfmt/src/attr.rs
@@ -1,21 +1,21 @@
 //! Format attributes and meta items.
 
-use rustc_ast::ast;
 use rustc_ast::HasAttrs;
-use rustc_span::{symbol::sym, Span};
+use rustc_ast::ast;
+use rustc_span::{Span, symbol::sym};
 use tracing::debug;
 
 use self::doc_comment::DocCommentFormatter;
-use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle};
-use crate::config::lists::*;
+use crate::comment::{CommentStyle, contains_comment, rewrite_doc_comment};
 use crate::config::IndentStyle;
+use crate::config::lists::*;
 use crate::expr::rewrite_literal;
-use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
+use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list};
 use crate::overflow;
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
 use crate::source_map::SpanUtils;
-use crate::types::{rewrite_path, PathContext};
+use crate::types::{PathContext, rewrite_path};
 use crate::utils::{count_newlines, mk_sp};
 
 mod doc_comment;
@@ -100,7 +100,7 @@ fn format_derive(
                 ",",
                 |span| span.lo(),
                 |span| span.hi(),
-                |span| Some(context.snippet(*span).to_owned()),
+                |span| Ok(context.snippet(*span).to_owned()),
                 // We update derive attribute spans to start after the opening '('
                 // This helps us focus parsing to just what's inside #[derive(...)]
                 context.snippet_provider.span_after(attr.span, "("),
@@ -148,7 +148,7 @@ fn format_derive(
         .tactic(tactic)
         .trailing_separator(trailing_separator)
         .ends_with_newline(false);
-    let item_str = write_list(&all_items, &fmt)?;
+    let item_str = write_list(&all_items, &fmt).ok()?;
 
     debug!("item_str: '{}'", item_str);
 
@@ -218,9 +218,9 @@ fn rewrite_initial_doc_comments(
     context: &RewriteContext<'_>,
     attrs: &[ast::Attribute],
     shape: Shape,
-) -> Option<(usize, Option<String>)> {
+) -> Result<(usize, Option<String>), RewriteError> {
     if attrs.is_empty() {
-        return Some((0, None));
+        return Ok((0, None));
     }
     // Rewrite doc comments
     let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_doc_comment());
@@ -230,7 +230,7 @@ fn rewrite_initial_doc_comments(
             .map(|a| context.snippet(a.span))
             .collect::<Vec<_>>()
             .join("\n");
-        return Some((
+        return Ok((
             sugared_docs.len(),
             Some(rewrite_doc_comment(
                 &snippet,
@@ -240,13 +240,19 @@ fn rewrite_initial_doc_comments(
         ));
     }
 
-    Some((0, None))
+    Ok((0, None))
 }
 
 impl Rewrite for ast::NestedMetaItem {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match self {
-            ast::NestedMetaItem::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
+            ast::NestedMetaItem::MetaItem(ref meta_item) => {
+                meta_item.rewrite_result(context, shape)
+            }
             ast::NestedMetaItem::Lit(ref l) => {
                 rewrite_literal(context, l.as_token_lit(), l.span, shape)
             }
@@ -275,7 +281,11 @@ fn has_newlines_before_after_comment(comment: &str) -> (&str, &str) {
 
 impl Rewrite for ast::MetaItem {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
-        Some(match self.kind {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        Ok(match self.kind {
             ast::MetaItemKind::Word => {
                 rewrite_path(context, PathContext::Type, &None, &self.path, shape)?
             }
@@ -287,7 +297,7 @@ impl Rewrite for ast::MetaItem {
                     &path,
                     list.iter(),
                     // 1 = "]"
-                    shape.sub_width(1)?,
+                    shape.sub_width(1).max_width_error(shape.width, self.span)?,
                     self.span,
                     context.config.attr_fn_like_width(),
                     Some(if has_trailing_comma {
@@ -300,7 +310,9 @@ impl Rewrite for ast::MetaItem {
             ast::MetaItemKind::NameValue(ref lit) => {
                 let path = rewrite_path(context, PathContext::Type, &None, &self.path, shape)?;
                 // 3 = ` = `
-                let lit_shape = shape.shrink_left(path.len() + 3)?;
+                let lit_shape = shape
+                    .shrink_left(path.len() + 3)
+                    .max_width_error(shape.width, self.span)?;
                 // `rewrite_literal` returns `None` when `lit` exceeds max
                 // width. Since a literal is basically unformattable unless it
                 // is a string literal (and only if `format_strings` is set),
@@ -308,7 +320,7 @@ impl Rewrite for ast::MetaItem {
                 // is longer than the max width and continue on formatting.
                 // See #2479 for example.
                 let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape)
-                    .unwrap_or_else(|| context.snippet(lit.span).to_owned());
+                    .unwrap_or_else(|_| context.snippet(lit.span).to_owned());
                 format!("{path} = {value}")
             }
         })
@@ -317,6 +329,10 @@ impl Rewrite for ast::MetaItem {
 
 impl Rewrite for ast::Attribute {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         let snippet = context.snippet(self.span);
         if self.is_doc_comment() {
             rewrite_doc_comment(snippet, shape.comment(context.config), context.config)
@@ -328,7 +344,7 @@ impl Rewrite for ast::Attribute {
             let prefix = attr_prefix(self);
 
             if should_skip || contains_comment(snippet) {
-                return Some(snippet.to_owned());
+                return Ok(snippet.to_owned());
             }
 
             if let Some(ref meta) = self.meta() {
@@ -353,9 +369,11 @@ impl Rewrite for ast::Attribute {
                 }
 
                 // 1 = `[`
-                let shape = shape.offset_left(prefix.len() + 1)?;
-                Some(meta.rewrite(context, shape).map_or_else(
-                    || snippet.to_owned(),
+                let shape = shape
+                    .offset_left(prefix.len() + 1)
+                    .max_width_error(shape.width, self.span)?;
+                Ok(meta.rewrite_result(context, shape).map_or_else(
+                    |_| snippet.to_owned(),
                     |rw| match &self.kind {
                         ast::AttrKind::Normal(normal_attr) => match normal_attr.item.unsafety {
                             // For #![feature(unsafe_attributes)]
@@ -367,7 +385,7 @@ impl Rewrite for ast::Attribute {
                     },
                 ))
             } else {
-                Some(snippet.to_owned())
+                Ok(snippet.to_owned())
             }
         }
     }
@@ -375,8 +393,12 @@ impl Rewrite for ast::Attribute {
 
 impl Rewrite for [ast::Attribute] {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         if self.is_empty() {
-            return Some(String::new());
+            return Ok(String::new());
         }
 
         // The current remaining attributes.
@@ -392,7 +414,7 @@ impl Rewrite for [ast::Attribute] {
         // merging derives into a single attribute.
         loop {
             if attrs.is_empty() {
-                return Some(result);
+                return Ok(result);
             }
 
             // Handle doc comments.
@@ -431,7 +453,7 @@ impl Rewrite for [ast::Attribute] {
             // Handle derives if we will merge them.
             if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) {
                 let derives = take_while_with_pred(context, attrs, is_derive);
-                let derive_str = format_derive(derives, shape, context)?;
+                let derive_str = format_derive(derives, shape, context).unknown_error()?;
                 result.push_str(&derive_str);
 
                 let missing_span = attrs
@@ -464,7 +486,7 @@ impl Rewrite for [ast::Attribute] {
             // If we get here, then we have a regular attribute, just handle one
             // at a time.
 
-            let formatted_attr = attrs[0].rewrite(context, shape)?;
+            let formatted_attr = attrs[0].rewrite_result(context, shape)?;
             result.push_str(&formatted_attr);
 
             let missing_span = attrs
diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs
index 88281d296be..c7d3a060d54 100644
--- a/src/tools/rustfmt/src/bin/main.rs
+++ b/src/tools/rustfmt/src/bin/main.rs
@@ -1,6 +1,6 @@
 #![feature(rustc_private)]
 
-use anyhow::{format_err, Result};
+use anyhow::{Result, format_err};
 
 use io::Error as IoError;
 use thiserror::Error;
@@ -11,15 +11,15 @@ use tracing_subscriber::EnvFilter;
 use std::collections::HashMap;
 use std::env;
 use std::fs::File;
-use std::io::{self, stdout, Read, Write};
+use std::io::{self, Read, Write, stdout};
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
 use getopts::{Matches, Options};
 
 use crate::rustfmt::{
-    load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName,
-    FormatReportFormatterBuilder, Input, Session, Verbosity,
+    CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName,
+    FormatReportFormatterBuilder, Input, Session, StyleEdition, Verbosity, Version, load_config,
 };
 
 const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rustfmt/issues/new?labels=bug";
@@ -129,7 +129,12 @@ fn make_opts() -> Options {
          found reverts to the input file path",
         "[Path for the configuration file]",
     );
-    opts.optopt("", "edition", "Rust edition to use", "[2015|2018|2021]");
+    opts.optopt(
+        "",
+        "edition",
+        "Rust edition to use",
+        "[2015|2018|2021|2024]",
+    );
     opts.optopt(
         "",
         "color",
@@ -181,6 +186,12 @@ fn make_opts() -> Options {
             "skip-children",
             "Don't reformat child modules (unstable).",
         );
+        opts.optopt(
+            "",
+            "style-edition",
+            "The edition of the Style Guide (unstable).",
+            "[2015|2018|2021|2024]",
+        );
     }
 
     opts.optflag("v", "verbose", "Print verbose output");
@@ -263,24 +274,35 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32> {
     let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?;
 
     if options.check {
-        config.set().emit_mode(EmitMode::Diff);
+        config.set_cli().emit_mode(EmitMode::Diff);
     } else {
         match options.emit_mode {
             // Emit modes which work with standard input
             // None means default, which is Stdout.
-            None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {}
+            None => {
+                config
+                    .set()
+                    .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
+            }
+            Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {
+                config
+                    .set_cli()
+                    .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
+            }
             Some(emit_mode) => {
                 return Err(OperationError::StdinBadEmit(emit_mode).into());
             }
         }
-        config
-            .set()
-            .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
     }
     config.set().verbose(Verbosity::Quiet);
 
     // parse file_lines
-    config.set().file_lines(options.file_lines);
+    if options.file_lines.is_all() {
+        config.set().file_lines(options.file_lines);
+    } else {
+        config.set_cli().file_lines(options.file_lines);
+    }
+
     for f in config.file_lines().files() {
         match *f {
             FileName::Stdin => {}
@@ -319,10 +341,10 @@ fn format(
 
     for file in files {
         if !file.exists() {
-            eprintln!("Error: file `{}` does not exist", file.to_str().unwrap());
+            eprintln!("Error: file `{}` does not exist", file.display());
             session.add_operational_error();
         } else if file.is_dir() {
-            eprintln!("Error: `{}` is a directory", file.to_str().unwrap());
+            eprintln!("Error: `{}` is a directory", file.display());
             session.add_operational_error();
         } else {
             // Check the file directory if the config-path could not be read or not provided
@@ -428,27 +450,27 @@ are included as out of line modules from `src/lib.rs`."
 }
 
 fn print_version() {
-    let version_info = format!(
-        "{}-{}",
-        option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
-        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
-    );
+    let version_number = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
+    let commit_info = include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"));
 
-    println!("rustfmt {version_info}");
+    if commit_info.is_empty() {
+        println!("rustfmt {version_number}");
+    } else {
+        println!("rustfmt {version_number}-{commit_info}");
+    }
 }
 
 fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> {
     if matches.opt_present("h") {
-        let topic = matches.opt_str("h");
-        if topic.is_none() {
+        let Some(topic) = matches.opt_str("h") else {
             return Ok(Operation::Help(HelpOp::None));
-        } else if topic == Some("config".to_owned()) {
-            return Ok(Operation::Help(HelpOp::Config));
-        } else if topic == Some("file-lines".to_owned()) && is_nightly() {
-            return Ok(Operation::Help(HelpOp::FileLines));
-        } else {
-            return Err(OperationError::UnknownHelpTopic(topic.unwrap()));
-        }
+        };
+
+        return match topic.as_str() {
+            "config" => Ok(Operation::Help(HelpOp::Config)),
+            "file-lines" if is_nightly() => Ok(Operation::Help(HelpOp::FileLines)),
+            _ => Err(OperationError::UnknownHelpTopic(topic)),
+        };
     }
     let mut free_matches = matches.free.iter();
 
@@ -514,6 +536,7 @@ struct GetOptsOptions {
     backup: bool,
     check: bool,
     edition: Option<Edition>,
+    style_edition: Option<StyleEdition>,
     color: Option<Color>,
     file_lines: FileLines, // Default is all lines in all files.
     unstable_features: bool,
@@ -545,6 +568,10 @@ impl GetOptsOptions {
                 if let Some(ref file_lines) = matches.opt_str("file-lines") {
                     options.file_lines = file_lines.parse()?;
                 }
+                if let Some(ref edition_str) = matches.opt_str("style-edition") {
+                    options.style_edition =
+                        Some(style_edition_from_style_edition_str(edition_str)?);
+                }
             } else {
                 let mut unstable_options = vec![];
                 if matches.opt_present("skip-children") {
@@ -556,6 +583,9 @@ impl GetOptsOptions {
                 if matches.opt_present("file-lines") {
                     unstable_options.push("`--file-lines`");
                 }
+                if matches.opt_present("style-edition") {
+                    unstable_options.push("`--style-edition`");
+                }
                 if !unstable_options.is_empty() {
                     let s = if unstable_options.len() == 1 { "" } else { "s" };
                     return Err(format_err!(
@@ -650,36 +680,49 @@ impl GetOptsOptions {
 impl CliOptions for GetOptsOptions {
     fn apply_to(self, config: &mut Config) {
         if self.verbose {
-            config.set().verbose(Verbosity::Verbose);
+            config.set_cli().verbose(Verbosity::Verbose);
         } else if self.quiet {
-            config.set().verbose(Verbosity::Quiet);
+            config.set_cli().verbose(Verbosity::Quiet);
         } else {
             config.set().verbose(Verbosity::Normal);
         }
-        config.set().file_lines(self.file_lines);
-        config.set().unstable_features(self.unstable_features);
+
+        if self.file_lines.is_all() {
+            config.set().file_lines(self.file_lines);
+        } else {
+            config.set_cli().file_lines(self.file_lines);
+        }
+
+        if self.unstable_features {
+            config.set_cli().unstable_features(self.unstable_features);
+        } else {
+            config.set().unstable_features(self.unstable_features);
+        }
         if let Some(skip_children) = self.skip_children {
-            config.set().skip_children(skip_children);
+            config.set_cli().skip_children(skip_children);
         }
         if let Some(error_on_unformatted) = self.error_on_unformatted {
-            config.set().error_on_unformatted(error_on_unformatted);
+            config.set_cli().error_on_unformatted(error_on_unformatted);
         }
         if let Some(edition) = self.edition {
-            config.set().edition(edition);
+            config.set_cli().edition(edition);
+        }
+        if let Some(edition) = self.style_edition {
+            config.set_cli().style_edition(edition);
         }
         if self.check {
-            config.set().emit_mode(EmitMode::Diff);
+            config.set_cli().emit_mode(EmitMode::Diff);
         } else if let Some(emit_mode) = self.emit_mode {
-            config.set().emit_mode(emit_mode);
+            config.set_cli().emit_mode(emit_mode);
         }
         if self.backup {
-            config.set().make_backup(true);
+            config.set_cli().make_backup(true);
         }
         if let Some(color) = self.color {
-            config.set().color(color);
+            config.set_cli().color(color);
         }
         if self.print_misformatted_file_names {
-            config.set().print_misformatted_file_names(true);
+            config.set_cli().print_misformatted_file_names(true);
         }
 
         for (key, val) in self.inline_config {
@@ -690,6 +733,25 @@ impl CliOptions for GetOptsOptions {
     fn config_path(&self) -> Option<&Path> {
         self.config_path.as_deref()
     }
+
+    fn edition(&self) -> Option<Edition> {
+        self.inline_config
+            .get("edition")
+            .map_or(self.edition, |e| Edition::from_str(e).ok())
+    }
+
+    fn style_edition(&self) -> Option<StyleEdition> {
+        self.inline_config
+            .get("style_edition")
+            .map_or(self.style_edition, |se| StyleEdition::from_str(se).ok())
+    }
+
+    fn version(&self) -> Option<Version> {
+        self.inline_config
+            .get("version")
+            .map(|version| Version::from_str(version).ok())
+            .flatten()
+    }
 }
 
 fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
@@ -702,6 +764,16 @@ fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
     }
 }
 
+fn style_edition_from_style_edition_str(edition_str: &str) -> Result<StyleEdition> {
+    match edition_str {
+        "2015" => Ok(StyleEdition::Edition2015),
+        "2018" => Ok(StyleEdition::Edition2018),
+        "2021" => Ok(StyleEdition::Edition2021),
+        "2024" => Ok(StyleEdition::Edition2024),
+        _ => Err(format_err!("Invalid value for `--style-edition`")),
+    }
+}
+
 fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> {
     match emit_str {
         "files" => Ok(EmitMode::Files),
@@ -712,3 +784,185 @@ fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> {
         _ => Err(format_err!("Invalid value for `--emit`")),
     }
 }
+
+#[cfg(test)]
+#[allow(dead_code)]
+mod test {
+    use super::*;
+    use rustfmt_config_proc_macro::nightly_only_test;
+
+    fn get_config<O: CliOptions>(path: Option<&Path>, options: Option<O>) -> Config {
+        load_config(path, options).unwrap().0
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn flag_sets_style_edition_override_correctly() {
+        let mut options = GetOptsOptions::default();
+        options.style_edition = Some(StyleEdition::Edition2024);
+        let config = get_config(None, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn edition_sets_style_edition_override_correctly() {
+        let mut options = GetOptsOptions::default();
+        options.edition = Some(Edition::Edition2024);
+        let config = get_config(None, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn version_sets_style_edition_override_correctly() {
+        let mut options = GetOptsOptions::default();
+        options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
+        let config = get_config(None, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+        assert_eq!(config.overflow_delimited_expr(), true);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn version_config_file_sets_style_edition_override_correctly() {
+        let options = GetOptsOptions::default();
+        let config_file = Some(Path::new("tests/config/style-edition/just-version"));
+        let config = get_config(config_file, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+        assert_eq!(config.overflow_delimited_expr(), true);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_flag_has_correct_precedence_over_edition() {
+        let mut options = GetOptsOptions::default();
+        options.style_edition = Some(StyleEdition::Edition2021);
+        options.edition = Some(Edition::Edition2024);
+        let config = get_config(None, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2021);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_flag_has_correct_precedence_over_version() {
+        let mut options = GetOptsOptions::default();
+        options.style_edition = Some(StyleEdition::Edition2018);
+        options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
+        let config = get_config(None, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2018);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_flag_has_correct_precedence_over_edition_version() {
+        let mut options = GetOptsOptions::default();
+        options.style_edition = Some(StyleEdition::Edition2021);
+        options.edition = Some(Edition::Edition2018);
+        options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
+        let config = get_config(None, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2021);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_inline_has_correct_precedence_over_edition_version() {
+        let mut options = GetOptsOptions::default();
+        options.edition = Some(Edition::Edition2018);
+        options.inline_config = HashMap::from([
+            ("version".to_owned(), "One".to_owned()),
+            ("style_edition".to_owned(), "2024".to_owned()),
+        ]);
+        let config = get_config(None, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+        assert_eq!(config.overflow_delimited_expr(), true);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_config_file_trumps_edition_flag_version_inline() {
+        let mut options = GetOptsOptions::default();
+        let config_file = Some(Path::new("tests/config/style-edition/just-style-edition"));
+        options.edition = Some(Edition::Edition2018);
+        options.inline_config = HashMap::from([("version".to_owned(), "One".to_owned())]);
+        let config = get_config(config_file, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_config_file_trumps_edition_config_and_version_inline() {
+        let mut options = GetOptsOptions::default();
+        let config_file = Some(Path::new(
+            "tests/config/style-edition/style-edition-and-edition",
+        ));
+        options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
+        let config = get_config(config_file, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2021);
+        assert_eq!(config.edition(), Edition::Edition2024);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn version_config_trumps_edition_config_and_flag() {
+        let mut options = GetOptsOptions::default();
+        let config_file = Some(Path::new("tests/config/style-edition/version-edition"));
+        options.edition = Some(Edition::Edition2018);
+        let config = get_config(config_file, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_config_file_trumps_version_config() {
+        let options = GetOptsOptions::default();
+        let config_file = Some(Path::new(
+            "tests/config/style-edition/version-style-edition",
+        ));
+        let config = get_config(config_file, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2021);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_config_file_trumps_edition_version_config() {
+        let options = GetOptsOptions::default();
+        let config_file = Some(Path::new(
+            "tests/config/style-edition/version-style-edition-and-edition",
+        ));
+        let config = get_config(config_file, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2021);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn correct_defaults_for_style_edition_loaded() {
+        let mut options = GetOptsOptions::default();
+        options.style_edition = Some(StyleEdition::Edition2024);
+        let config = get_config(None, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+        assert_eq!(config.overflow_delimited_expr(), true);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_defaults_overridden_from_config() {
+        let options = GetOptsOptions::default();
+        let config_file = Some(Path::new("tests/config/style-edition/overrides"));
+        let config = get_config(config_file, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+        assert_eq!(config.overflow_delimited_expr(), false);
+    }
+
+    #[nightly_only_test]
+    #[test]
+    fn style_edition_defaults_overridden_from_cli() {
+        let mut options = GetOptsOptions::default();
+        let config_file = Some(Path::new("tests/config/style-edition/just-style-edition"));
+        options.inline_config =
+            HashMap::from([("overflow_delimited_expr".to_owned(), "false".to_owned())]);
+        let config = get_config(config_file, Some(options));
+        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+        assert_eq!(config.overflow_delimited_expr(), false);
+    }
+}
diff --git a/src/tools/rustfmt/src/chains.rs b/src/tools/rustfmt/src/chains.rs
index 96fbe7a963a..fd2ef9cb1db 100644
--- a/src/tools/rustfmt/src/chains.rs
+++ b/src/tools/rustfmt/src/chains.rs
@@ -59,15 +59,15 @@ use std::borrow::Cow;
 use std::cmp::min;
 
 use rustc_ast::{ast, ptr};
-use rustc_span::{symbol, BytePos, Span};
+use rustc_span::{BytePos, Span, symbol};
 use tracing::debug;
 
-use crate::comment::{rewrite_comment, CharClasses, FullCodeCharKind, RichChar};
-use crate::config::{IndentStyle, Version};
+use crate::comment::{CharClasses, FullCodeCharKind, RichChar, rewrite_comment};
+use crate::config::{IndentStyle, StyleEdition};
 use crate::expr::rewrite_call;
 use crate::lists::extract_pre_comment;
 use crate::macros::convert_try_mac;
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
 use crate::source_map::SpanUtils;
 use crate::utils::{
@@ -80,6 +80,9 @@ use thin_vec::ThinVec;
 /// Provides the original input contents from the span
 /// of a chain element with trailing spaces trimmed.
 fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option<String> {
+    // TODO(ding-young): Currently returning None when the given span is out of the range
+    // covered by the snippet provider. If this is a common cause for internal
+    // rewrite failure, add a new enum variant and return RewriteError instead of None
     context.snippet_provider.span_to_snippet(span).map(|s| {
         s.lines()
             .map(|l| l.trim_end())
@@ -93,12 +96,16 @@ fn format_chain_item(
     context: &RewriteContext<'_>,
     rewrite_shape: Shape,
     allow_overflow: bool,
-) -> Option<String> {
+) -> RewriteResult {
     if allow_overflow {
-        item.rewrite(context, rewrite_shape)
-            .or_else(|| format_overflow_style(item.span, context))
+        // TODO(ding-young): Consider calling format_overflow_style()
+        // only when item.rewrite_result() returns RewriteError::ExceedsMaxWidth.
+        // It may be inappropriate to call format_overflow_style on other RewriteError
+        // since the current approach retries formatting if allow_overflow is true
+        item.rewrite_result(context, rewrite_shape)
+            .or_else(|_| format_overflow_style(item.span, context).unknown_error())
     } else {
-        item.rewrite(context, rewrite_shape)
+        item.rewrite_result(context, rewrite_shape)
     }
 }
 
@@ -135,17 +142,17 @@ pub(crate) fn rewrite_chain(
     expr: &ast::Expr,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let chain = Chain::from_ast(expr, context);
     debug!("rewrite_chain {:?} {:?}", chain, shape);
 
     // If this is just an expression with some `?`s, then format it trivially and
     // return early.
     if chain.children.is_empty() {
-        return chain.parent.rewrite(context, shape);
+        return chain.parent.rewrite_result(context, shape);
     }
 
-    chain.rewrite(context, shape)
+    chain.rewrite_result(context, shape)
 }
 
 #[derive(Debug)]
@@ -203,7 +210,7 @@ impl ChainItemKind {
     fn is_tup_field_access(expr: &ast::Expr) -> bool {
         match expr.kind {
             ast::ExprKind::Field(_, ref field) => {
-                field.name.to_string().chars().all(|c| c.is_digit(10))
+                field.name.as_str().chars().all(|c| c.is_digit(10))
             }
             _ => false,
         }
@@ -269,7 +276,13 @@ impl ChainItemKind {
 
 impl Rewrite for ChainItem {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
-        let shape = shape.sub_width(self.tries)?;
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        let shape = shape
+            .sub_width(self.tries)
+            .max_width_error(shape.width, self.span)?;
         let rewrite = match self.kind {
             ChainItemKind::Parent {
                 ref expr,
@@ -278,14 +291,14 @@ impl Rewrite for ChainItem {
             ChainItemKind::Parent {
                 ref expr,
                 parens: false,
-            } => expr.rewrite(context, shape)?,
+            } => expr.rewrite_result(context, shape)?,
             ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => {
                 Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)?
             }
             ChainItemKind::StructField(ident) => format!(".{}", rewrite_ident(context, ident)),
             ChainItemKind::TupleField(ident, nested) => format!(
                 "{}.{}",
-                if nested && context.config.version() == Version::One {
+                if nested && context.config.style_edition() <= StyleEdition::Edition2021 {
                     " "
                 } else {
                     ""
@@ -297,7 +310,7 @@ impl Rewrite for ChainItem {
                 rewrite_comment(comment, false, shape, context.config)?
             }
         };
-        Some(format!("{rewrite}{}", "?".repeat(self.tries)))
+        Ok(format!("{rewrite}{}", "?".repeat(self.tries)))
     }
 }
 
@@ -327,14 +340,14 @@ impl ChainItem {
         span: Span,
         context: &RewriteContext<'_>,
         shape: Shape,
-    ) -> Option<String> {
+    ) -> RewriteResult {
         let type_str = if types.is_empty() {
             String::new()
         } else {
             let type_list = types
                 .iter()
-                .map(|ty| ty.rewrite(context, shape))
-                .collect::<Option<Vec<_>>>()?;
+                .map(|ty| ty.rewrite_result(context, shape))
+                .collect::<Result<Vec<_>, RewriteError>>()?;
 
             format!("::<{}>", type_list.join(", "))
         };
@@ -519,6 +532,10 @@ impl Chain {
 
 impl Rewrite for Chain {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         debug!("rewrite chain {:?} {:?}", self, shape);
 
         let mut formatter = match context.config.indent_style() {
@@ -532,17 +549,25 @@ impl Rewrite for Chain {
 
         formatter.format_root(&self.parent, context, shape)?;
         if let Some(result) = formatter.pure_root() {
-            return wrap_str(result, context.config.max_width(), shape);
+            return wrap_str(result, context.config.max_width(), shape)
+                .max_width_error(shape.width, self.parent.span);
         }
 
+        let first = self.children.first().unwrap_or(&self.parent);
+        let last = self.children.last().unwrap_or(&self.parent);
+        let children_span = mk_sp(first.span.lo(), last.span.hi());
+        let full_span = self.parent.span.with_hi(children_span.hi());
+
         // Decide how to layout the rest of the chain.
-        let child_shape = formatter.child_shape(context, shape)?;
+        let child_shape = formatter
+            .child_shape(context, shape)
+            .max_width_error(shape.width, children_span)?;
 
         formatter.format_children(context, child_shape)?;
         formatter.format_last_child(context, shape, child_shape)?;
 
         let result = formatter.join_rewrites(context, child_shape)?;
-        wrap_str(result, context.config.max_width(), shape)
+        wrap_str(result, context.config.max_width(), shape).max_width_error(shape.width, full_span)
     }
 }
 
@@ -564,16 +589,20 @@ trait ChainFormatter {
         parent: &ChainItem,
         context: &RewriteContext<'_>,
         shape: Shape,
-    ) -> Option<()>;
+    ) -> Result<(), RewriteError>;
     fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape>;
-    fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()>;
+    fn format_children(
+        &mut self,
+        context: &RewriteContext<'_>,
+        child_shape: Shape,
+    ) -> Result<(), RewriteError>;
     fn format_last_child(
         &mut self,
         context: &RewriteContext<'_>,
         shape: Shape,
         child_shape: Shape,
-    ) -> Option<()>;
-    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String>;
+    ) -> Result<(), RewriteError>;
+    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult;
     // Returns `Some` if the chain is only a root, None otherwise.
     fn pure_root(&mut self) -> Option<String>;
 }
@@ -616,12 +645,16 @@ impl<'a> ChainFormatterShared<'a> {
         }
     }
 
-    fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
+    fn format_children(
+        &mut self,
+        context: &RewriteContext<'_>,
+        child_shape: Shape,
+    ) -> Result<(), RewriteError> {
         for item in &self.children[..self.children.len() - 1] {
             let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?;
             self.rewrites.push(rewrite);
         }
-        Some(())
+        Ok(())
     }
 
     // Rewrite the last child. The last child of a chain requires special treatment. We need to
@@ -662,8 +695,8 @@ impl<'a> ChainFormatterShared<'a> {
         context: &RewriteContext<'_>,
         shape: Shape,
         child_shape: Shape,
-    ) -> Option<()> {
-        let last = self.children.last()?;
+    ) -> Result<(), RewriteError> {
+        let last = self.children.last().unknown_error()?;
         let extendable = may_extend && last_line_extendable(&self.rewrites[0]);
         let prev_last_line_width = last_line_width(&self.rewrites[0]);
 
@@ -687,11 +720,17 @@ impl<'a> ChainFormatterShared<'a> {
             && self.rewrites.iter().all(|s| !s.contains('\n'))
             && one_line_budget > 0;
         let last_shape = if all_in_one_line {
-            shape.sub_width(last.tries)?
+            shape
+                .sub_width(last.tries)
+                .max_width_error(shape.width, last.span)?
         } else if extendable {
-            child_shape.sub_width(last.tries)?
+            child_shape
+                .sub_width(last.tries)
+                .max_width_error(child_shape.width, last.span)?
         } else {
-            child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)?
+            child_shape
+                .sub_width(shape.rhs_overhead(context.config) + last.tries)
+                .max_width_error(child_shape.width, last.span)?
         };
 
         let mut last_subexpr_str = None;
@@ -707,7 +746,7 @@ impl<'a> ChainFormatterShared<'a> {
             };
 
             if let Some(one_line_shape) = one_line_shape {
-                if let Some(rw) = last.rewrite(context, one_line_shape) {
+                if let Ok(rw) = last.rewrite_result(context, one_line_shape) {
                     // We allow overflowing here only if both of the following conditions match:
                     // 1. The entire chain fits in a single line except the last child.
                     // 2. `last_child_str.lines().count() >= 5`.
@@ -722,17 +761,18 @@ impl<'a> ChainFormatterShared<'a> {
                         // last child on its own line, and compare two rewrites to choose which is
                         // better.
                         let last_shape = child_shape
-                            .sub_width(shape.rhs_overhead(context.config) + last.tries)?;
-                        match last.rewrite(context, last_shape) {
-                            Some(ref new_rw) if !could_fit_single_line => {
+                            .sub_width(shape.rhs_overhead(context.config) + last.tries)
+                            .max_width_error(child_shape.width, last.span)?;
+                        match last.rewrite_result(context, last_shape) {
+                            Ok(ref new_rw) if !could_fit_single_line => {
                                 last_subexpr_str = Some(new_rw.clone());
                             }
-                            Some(ref new_rw) if new_rw.lines().count() >= line_count => {
+                            Ok(ref new_rw) if new_rw.lines().count() >= line_count => {
                                 last_subexpr_str = Some(rw);
                                 self.fits_single_line = could_fit_single_line && all_in_one_line;
                             }
-                            new_rw @ Some(..) => {
-                                last_subexpr_str = new_rw;
+                            Ok(new_rw) => {
+                                last_subexpr_str = Some(new_rw);
                             }
                             _ => {
                                 last_subexpr_str = Some(rw);
@@ -747,22 +787,28 @@ impl<'a> ChainFormatterShared<'a> {
         let last_shape = if context.use_block_indent() {
             last_shape
         } else {
-            child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)?
+            child_shape
+                .sub_width(shape.rhs_overhead(context.config) + last.tries)
+                .max_width_error(child_shape.width, last.span)?
         };
 
-        last_subexpr_str = last_subexpr_str.or_else(|| last.rewrite(context, last_shape));
-        self.rewrites.push(last_subexpr_str?);
-        Some(())
+        let last_subexpr_str =
+            last_subexpr_str.unwrap_or(last.rewrite_result(context, last_shape)?);
+        self.rewrites.push(last_subexpr_str);
+        Ok(())
     }
 
-    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> {
+    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
         let connector = if self.fits_single_line {
             // Yay, we can put everything on one line.
             Cow::from("")
         } else {
             // Use new lines.
             if context.force_one_line_chain.get() {
-                return None;
+                return Err(RewriteError::ExceedsMaxWidth {
+                    configured_width: child_shape.width,
+                    span: self.children.last().unknown_error()?.span,
+                });
             }
             child_shape.to_string_with_newline(context.config)
         };
@@ -781,7 +827,7 @@ impl<'a> ChainFormatterShared<'a> {
             result.push_str(rewrite);
         }
 
-        Some(result)
+        Ok(result)
     }
 }
 
@@ -806,8 +852,8 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
         parent: &ChainItem,
         context: &RewriteContext<'_>,
         shape: Shape,
-    ) -> Option<()> {
-        let mut root_rewrite: String = parent.rewrite(context, shape)?;
+    ) -> Result<(), RewriteError> {
+        let mut root_rewrite: String = parent.rewrite_result(context, shape)?;
 
         let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite);
         let tab_width = context.config.tab_spaces().saturating_sub(shape.offset);
@@ -817,10 +863,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
             if let ChainItemKind::Comment(..) = item.kind {
                 break;
             }
-            let shape = shape.offset_left(root_rewrite.len())?;
-            match &item.rewrite(context, shape) {
-                Some(rewrite) => root_rewrite.push_str(rewrite),
-                None => break,
+            let shape = shape
+                .offset_left(root_rewrite.len())
+                .max_width_error(shape.width, item.span)?;
+            match &item.rewrite_result(context, shape) {
+                Ok(rewrite) => root_rewrite.push_str(rewrite),
+                Err(_) => break,
             }
 
             root_ends_with_block = last_line_extendable(&root_rewrite);
@@ -832,7 +880,7 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
         }
         self.shared.rewrites.push(root_rewrite);
         self.root_ends_with_block = root_ends_with_block;
-        Some(())
+        Ok(())
     }
 
     fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
@@ -840,7 +888,11 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
         Some(get_block_child_shape(block_end, context, shape))
     }
 
-    fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
+    fn format_children(
+        &mut self,
+        context: &RewriteContext<'_>,
+        child_shape: Shape,
+    ) -> Result<(), RewriteError> {
         self.shared.format_children(context, child_shape)
     }
 
@@ -849,12 +901,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
         context: &RewriteContext<'_>,
         shape: Shape,
         child_shape: Shape,
-    ) -> Option<()> {
+    ) -> Result<(), RewriteError> {
         self.shared
             .format_last_child(true, context, shape, child_shape)
     }
 
-    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> {
+    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
         self.shared.join_rewrites(context, child_shape)
     }
 
@@ -885,9 +937,9 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
         parent: &ChainItem,
         context: &RewriteContext<'_>,
         shape: Shape,
-    ) -> Option<()> {
+    ) -> Result<(), RewriteError> {
         let parent_shape = shape.visual_indent(0);
-        let mut root_rewrite = parent.rewrite(context, parent_shape)?;
+        let mut root_rewrite = parent.rewrite_result(context, parent_shape)?;
         let multiline = root_rewrite.contains('\n');
         self.offset = if multiline {
             last_line_width(&root_rewrite).saturating_sub(shape.used_width())
@@ -899,18 +951,19 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
             let item = &self.shared.children[0];
             if let ChainItemKind::Comment(..) = item.kind {
                 self.shared.rewrites.push(root_rewrite);
-                return Some(());
+                return Ok(());
             }
             let child_shape = parent_shape
                 .visual_indent(self.offset)
-                .sub_width(self.offset)?;
-            let rewrite = item.rewrite(context, child_shape)?;
+                .sub_width(self.offset)
+                .max_width_error(parent_shape.width, item.span)?;
+            let rewrite = item.rewrite_result(context, child_shape)?;
             if filtered_str_fits(&rewrite, context.config.max_width(), shape) {
                 root_rewrite.push_str(&rewrite);
             } else {
                 // We couldn't fit in at the visual indent, try the last
                 // indent.
-                let rewrite = item.rewrite(context, parent_shape)?;
+                let rewrite = item.rewrite_result(context, parent_shape)?;
                 root_rewrite.push_str(&rewrite);
                 self.offset = 0;
             }
@@ -919,7 +972,7 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
         }
 
         self.shared.rewrites.push(root_rewrite);
-        Some(())
+        Ok(())
     }
 
     fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
@@ -932,7 +985,11 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
         )
     }
 
-    fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
+    fn format_children(
+        &mut self,
+        context: &RewriteContext<'_>,
+        child_shape: Shape,
+    ) -> Result<(), RewriteError> {
         self.shared.format_children(context, child_shape)
     }
 
@@ -941,12 +998,12 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
         context: &RewriteContext<'_>,
         shape: Shape,
         child_shape: Shape,
-    ) -> Option<()> {
+    ) -> Result<(), RewriteError> {
         self.shared
             .format_last_child(false, context, shape, child_shape)
     }
 
-    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> {
+    fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult {
         self.shared.join_rewrites(context, child_shape)
     }
 
diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs
index b5c26235e77..a37b47e3bc9 100644
--- a/src/tools/rustfmt/src/closures.rs
+++ b/src/tools/rustfmt/src/closures.rs
@@ -4,17 +4,17 @@ use thin_vec::thin_vec;
 use tracing::debug;
 
 use crate::attr::get_attrs_from_stmt;
+use crate::config::StyleEdition;
 use crate::config::lists::*;
-use crate::config::Version;
 use crate::expr::{block_contains_comment, is_simple_block, is_unsafe_block, rewrite_cond};
 use crate::items::{span_hi_for_param, span_lo_for_param};
-use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
+use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list};
 use crate::overflow::OverflowableItem;
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
 use crate::source_map::SpanUtils;
 use crate::types::rewrite_bound_params;
-use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt};
+use crate::utils::{NodeIdExt, last_line_width, left_most_sub_expr, stmt_expr};
 
 // This module is pretty messy because of the rules around closures and blocks:
 // FIXME - the below is probably no longer true in full.
@@ -37,7 +37,7 @@ pub(crate) fn rewrite_closure(
     span: Span,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     debug!("rewrite_closure {:?}", body);
 
     let (prefix, extra_offset) = rewrite_closure_fn_decl(
@@ -53,13 +53,15 @@ pub(crate) fn rewrite_closure(
         shape,
     )?;
     // 1 = space between `|...|` and body.
-    let body_shape = shape.offset_left(extra_offset)?;
+    let body_shape = shape
+        .offset_left(extra_offset)
+        .max_width_error(shape.width, span)?;
 
     if let ast::ExprKind::Block(ref block, _) = body.kind {
         // The body of the closure is an empty block.
         if block.stmts.is_empty() && !block_contains_comment(context, block) {
             return body
-                .rewrite(context, shape)
+                .rewrite_result(context, shape)
                 .map(|s| format!("{} {}", prefix, s));
         }
 
@@ -67,15 +69,15 @@ pub(crate) fn rewrite_closure(
             ast::FnRetTy::Default(_) if !context.inside_macro() => {
                 try_rewrite_without_block(body, &prefix, context, shape, body_shape)
             }
-            _ => None,
+            _ => Err(RewriteError::Unknown),
         };
 
-        result.or_else(|| {
+        result.or_else(|_| {
             // Either we require a block, or tried without and failed.
             rewrite_closure_block(block, &prefix, context, body_shape)
         })
     } else {
-        rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|| {
+        rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|_| {
             // The closure originally had a non-block expression, but we can't fit on
             // one line, so we'll insert a block.
             rewrite_closure_with_block(body, &prefix, context, body_shape)
@@ -89,7 +91,7 @@ fn try_rewrite_without_block(
     context: &RewriteContext<'_>,
     shape: Shape,
     body_shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let expr = get_inner_expr(expr, prefix, context);
 
     if is_block_closure_forced(context, expr) {
@@ -153,11 +155,11 @@ fn rewrite_closure_with_block(
     prefix: &str,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let left_most = left_most_sub_expr(body);
     let veto_block = veto_block(body) && !expr_requires_semi_to_be_stmt(left_most);
     if veto_block {
-        return None;
+        return Err(RewriteError::Unknown);
     }
 
     let block = ast::Block {
@@ -185,7 +187,7 @@ fn rewrite_closure_with_block(
         shape,
         false,
     )?;
-    Some(format!("{prefix} {block}"))
+    Ok(format!("{prefix} {block}"))
 }
 
 // Rewrite closure with a single expression without wrapping its body with block.
@@ -194,7 +196,7 @@ fn rewrite_closure_expr(
     prefix: &str,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     fn allow_multi_line(expr: &ast::Expr) -> bool {
         match expr.kind {
             ast::ExprKind::Match(..)
@@ -217,12 +219,12 @@ fn rewrite_closure_expr(
     // unless it is a block-like expression or we are inside macro call.
     let veto_multiline = (!allow_multi_line(expr) && !context.inside_macro())
         || context.config.force_multiline_blocks();
-    expr.rewrite(context, shape)
+    expr.rewrite_result(context, shape)
         .and_then(|rw| {
             if veto_multiline && rw.contains('\n') {
-                None
+                Err(RewriteError::Unknown)
             } else {
-                Some(rw)
+                Ok(rw)
             }
         })
         .map(|rw| format!("{} {}", prefix, rw))
@@ -234,8 +236,12 @@ fn rewrite_closure_block(
     prefix: &str,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
-    Some(format!("{} {}", prefix, block.rewrite(context, shape)?))
+) -> RewriteResult {
+    Ok(format!(
+        "{} {}",
+        prefix,
+        block.rewrite_result(context, shape)?
+    ))
 }
 
 // Return type is (prefix, extra_offset)
@@ -250,13 +256,14 @@ fn rewrite_closure_fn_decl(
     span: Span,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<(String, usize)> {
+) -> Result<(String, usize), RewriteError> {
     let binder = match binder {
         ast::ClosureBinder::For { generic_params, .. } if generic_params.is_empty() => {
             "for<> ".to_owned()
         }
         ast::ClosureBinder::For { generic_params, .. } => {
-            let lifetime_str = rewrite_bound_params(context, shape, generic_params)?;
+            let lifetime_str =
+                rewrite_bound_params(context, shape, generic_params).unknown_error()?;
             format!("for<{lifetime_str}> ")
         }
         ast::ClosureBinder::NotPresent => "".to_owned(),
@@ -287,13 +294,17 @@ fn rewrite_closure_fn_decl(
     // 4 = "|| {".len(), which is overconservative when the closure consists of
     // a single expression.
     let nested_shape = shape
-        .shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())?
-        .sub_width(4)?;
+        .shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())
+        .and_then(|shape| shape.sub_width(4))
+        .max_width_error(shape.width, span)?;
 
     // 1 = |
     let param_offset = nested_shape.indent + 1;
-    let param_shape = nested_shape.offset_left(1)?.visual_indent(0);
-    let ret_str = fn_decl.output.rewrite(context, param_shape)?;
+    let param_shape = nested_shape
+        .offset_left(1)
+        .max_width_error(nested_shape.width, span)?
+        .visual_indent(0);
+    let ret_str = fn_decl.output.rewrite_result(context, param_shape)?;
 
     let param_items = itemize_list(
         context.snippet_provider,
@@ -302,7 +313,7 @@ fn rewrite_closure_fn_decl(
         ",",
         |param| span_lo_for_param(param),
         |param| span_hi_for_param(context, param),
-        |param| param.rewrite(context, param_shape),
+        |param| param.rewrite_result(context, param_shape),
         context.snippet_provider.span_after(span, "|"),
         body.span.lo(),
         false,
@@ -317,7 +328,9 @@ fn rewrite_closure_fn_decl(
         horizontal_budget,
     );
     let param_shape = match tactic {
-        DefinitiveListTactic::Horizontal => param_shape.sub_width(ret_str.len() + 1)?,
+        DefinitiveListTactic::Horizontal => param_shape
+            .sub_width(ret_str.len() + 1)
+            .max_width_error(param_shape.width, span)?,
         _ => param_shape,
     };
 
@@ -339,7 +352,7 @@ fn rewrite_closure_fn_decl(
     // 1 = space between `|...|` and body.
     let extra_offset = last_line_width(&prefix) + 1;
 
-    Some((prefix, extra_offset))
+    Ok((prefix, extra_offset))
 }
 
 // Rewriting closure which is placed at the end of the function call's arg.
@@ -348,7 +361,7 @@ pub(crate) fn rewrite_last_closure(
     context: &RewriteContext<'_>,
     expr: &ast::Expr,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     if let ast::ExprKind::Closure(ref closure) = expr.kind {
         let ast::Closure {
             ref binder,
@@ -385,10 +398,12 @@ pub(crate) fn rewrite_last_closure(
         )?;
         // If the closure goes multi line before its body, do not overflow the closure.
         if prefix.contains('\n') {
-            return None;
+            return Err(RewriteError::Unknown);
         }
 
-        let body_shape = shape.offset_left(extra_offset)?;
+        let body_shape = shape
+            .offset_left(extra_offset)
+            .max_width_error(shape.width, expr.span)?;
 
         // We force to use block for the body of the closure for certain kinds of expressions.
         if is_block_closure_forced(context, body) {
@@ -400,7 +415,7 @@ pub(crate) fn rewrite_last_closure(
                             // closure.  However, if the closure has a return type, then we must
                             // keep the blocks.
                             match rewrite_closure_expr(body, &prefix, context, shape) {
-                                Some(single_line_body_str)
+                                Ok(single_line_body_str)
                                     if !single_line_body_str.contains('\n') =>
                                 {
                                     single_line_body_str
@@ -424,9 +439,9 @@ pub(crate) fn rewrite_last_closure(
         }
 
         // Seems fine, just format the closure in usual manner.
-        return expr.rewrite(context, shape);
+        return expr.rewrite_result(context, shape);
     }
-    None
+    Err(RewriteError::Unknown)
 }
 
 /// Returns `true` if the given vector of arguments has more than one `ast::ExprKind::Closure`.
@@ -443,18 +458,18 @@ fn is_block_closure_forced(context: &RewriteContext<'_>, expr: &ast::Expr) -> bo
     if context.inside_macro() {
         false
     } else {
-        is_block_closure_forced_inner(expr, context.config.version())
+        is_block_closure_forced_inner(expr, context.config.style_edition())
     }
 }
 
-fn is_block_closure_forced_inner(expr: &ast::Expr, version: Version) -> bool {
+fn is_block_closure_forced_inner(expr: &ast::Expr, style_edition: StyleEdition) -> bool {
     match expr.kind {
         ast::ExprKind::If(..) | ast::ExprKind::While(..) | ast::ExprKind::ForLoop { .. } => true,
-        ast::ExprKind::Loop(..) if version == Version::Two => true,
+        ast::ExprKind::Loop(..) if style_edition >= StyleEdition::Edition2024 => true,
         ast::ExprKind::AddrOf(_, _, ref expr)
         | ast::ExprKind::Try(ref expr)
         | ast::ExprKind::Unary(_, ref expr)
-        | ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, version),
+        | ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, style_edition),
         _ => false,
     }
 }
diff --git a/src/tools/rustfmt/src/comment.rs b/src/tools/rustfmt/src/comment.rs
index e76be0fd162..b7d7a396a67 100644
--- a/src/tools/rustfmt/src/comment.rs
+++ b/src/tools/rustfmt/src/comment.rs
@@ -2,14 +2,14 @@
 
 use std::{borrow::Cow, iter};
 
-use itertools::{multipeek, MultiPeek};
+use itertools::{Itertools as _, MultiPeek, multipeek};
 use rustc_span::Span;
 use tracing::{debug, trace};
 
 use crate::config::Config;
-use crate::rewrite::RewriteContext;
+use crate::rewrite::{RewriteContext, RewriteErrorExt, RewriteResult};
 use crate::shape::{Indent, Shape};
-use crate::string::{rewrite_string, StringFormat};
+use crate::string::{StringFormat, rewrite_string};
 use crate::utils::{
     count_newlines, first_line_width, last_line_width, trim_left_preserve_layout,
     trimmed_last_line_width, unicode_str_width,
@@ -158,7 +158,7 @@ pub(crate) fn combine_strs_with_missing_comments(
     span: Span,
     shape: Shape,
     allow_extend: bool,
-) -> Option<String> {
+) -> RewriteResult {
     trace!(
         "combine_strs_with_missing_comments `{}` `{}` {:?} {:?}",
         prev_str, next_str, span, shape
@@ -188,7 +188,7 @@ pub(crate) fn combine_strs_with_missing_comments(
             result.push_str(&indent.to_string_with_newline(config))
         }
         result.push_str(next_str);
-        return Some(result);
+        return Ok(result);
     }
 
     // We have a missing comment between the first expression and the second expression.
@@ -233,10 +233,10 @@ pub(crate) fn combine_strs_with_missing_comments(
     result.push_str(&second_sep);
     result.push_str(next_str);
 
-    Some(result)
+    Ok(result)
 }
 
-pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> Option<String> {
+pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> RewriteResult {
     identify_comment(orig, false, shape, config, true)
 }
 
@@ -245,7 +245,7 @@ pub(crate) fn rewrite_comment(
     block_style: bool,
     shape: Shape,
     config: &Config,
-) -> Option<String> {
+) -> RewriteResult {
     identify_comment(orig, block_style, shape, config, false)
 }
 
@@ -255,7 +255,7 @@ fn identify_comment(
     shape: Shape,
     config: &Config,
     is_doc_comment: bool,
-) -> Option<String> {
+) -> RewriteResult {
     let style = comment_style(orig, false);
 
     // Computes the byte length of line taking into account a newline if the line is part of a
@@ -347,7 +347,7 @@ fn identify_comment(
     let (first_group, rest) = orig.split_at(first_group_ending);
     let rewritten_first_group =
         if !config.normalize_comments() && has_bare_lines && style.is_block_comment() {
-            trim_left_preserve_layout(first_group, shape.indent, config)?
+            trim_left_preserve_layout(first_group, shape.indent, config).unknown_error()?
         } else if !config.normalize_comments()
             && !config.wrap_comments()
             && !(
@@ -368,7 +368,7 @@ fn identify_comment(
             )?
         };
     if rest.is_empty() {
-        Some(rewritten_first_group)
+        Ok(rewritten_first_group)
     } else {
         identify_comment(
             rest.trim_start(),
@@ -534,10 +534,11 @@ impl ItemizedBlock {
 
     /// Returns the block as a string, with each line trimmed at the start.
     fn trimmed_block_as_string(&self) -> String {
-        self.lines
-            .iter()
-            .map(|line| format!("{} ", line.trim_start()))
-            .collect::<String>()
+        self.lines.iter().fold(String::new(), |mut acc, line| {
+            acc.push_str(line.trim_start());
+            acc.push(' ');
+            acc
+        })
     }
 
     /// Returns the block as a string under its original form.
@@ -900,7 +901,7 @@ fn rewrite_comment_inner(
     shape: Shape,
     config: &Config,
     is_doc_comment: bool,
-) -> Option<String> {
+) -> RewriteResult {
     let mut rewriter = CommentRewrite::new(orig, block_style, shape, config);
 
     let line_breaks = count_newlines(orig.trim_end());
@@ -934,7 +935,7 @@ fn rewrite_comment_inner(
         }
     }
 
-    Some(rewriter.finish())
+    Ok(rewriter.finish())
 }
 
 const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### ";
@@ -999,7 +1000,7 @@ pub(crate) fn rewrite_missing_comment(
     span: Span,
     shape: Shape,
     context: &RewriteContext<'_>,
-) -> Option<String> {
+) -> RewriteResult {
     let missing_snippet = context.snippet(span);
     let trimmed_snippet = missing_snippet.trim();
     // check the span starts with a comment
@@ -1007,7 +1008,7 @@ pub(crate) fn rewrite_missing_comment(
     if !trimmed_snippet.is_empty() && pos.is_some() {
         rewrite_comment(trimmed_snippet, false, shape, context.config)
     } else {
-        Some(String::new())
+        Ok(String::new())
     }
 }
 
@@ -1019,13 +1020,13 @@ pub(crate) fn recover_missing_comment_in_span(
     shape: Shape,
     context: &RewriteContext<'_>,
     used_width: usize,
-) -> Option<String> {
+) -> RewriteResult {
     let missing_comment = rewrite_missing_comment(span, shape, context)?;
     if missing_comment.is_empty() {
-        Some(String::new())
+        Ok(String::new())
     } else {
         let missing_snippet = context.snippet(span);
-        let pos = missing_snippet.find('/')?;
+        let pos = missing_snippet.find('/').unknown_error()?;
         // 1 = ` `
         let total_width = missing_comment.len() + used_width + 1;
         let force_new_line_before_comment =
@@ -1035,7 +1036,7 @@ pub(crate) fn recover_missing_comment_in_span(
         } else {
             Cow::from(" ")
         };
-        Some(format!("{sep}{missing_comment}"))
+        Ok(format!("{sep}{missing_comment}"))
     }
 }
 
@@ -1055,8 +1056,7 @@ fn light_rewrite_comment(
     config: &Config,
     is_doc_comment: bool,
 ) -> String {
-    let lines: Vec<&str> = orig
-        .lines()
+    orig.lines()
         .map(|l| {
             // This is basically just l.trim(), but in the case that a line starts
             // with `*` we want to leave one space before it, so it aligns with the
@@ -1074,8 +1074,7 @@ fn light_rewrite_comment(
             // Preserve markdown's double-space line break syntax in doc comment.
             trim_end_unless_two_whitespaces(left_trimmed, is_doc_comment)
         })
-        .collect();
-    lines.join(&format!("\n{}", offset.to_string(config)))
+        .join(&format!("\n{}", offset.to_string(config)))
 }
 
 /// Trims comment characters and possibly a single space from the left of a string.
@@ -1704,12 +1703,11 @@ impl<'a> Iterator for CommentCodeSlices<'a> {
 }
 
 /// Checks is `new` didn't miss any comment from `span`, if it removed any, return previous text
-/// (if it fits in the width/offset, else return `None`), else return `new`
 pub(crate) fn recover_comment_removed(
     new: String,
     span: Span,
     context: &RewriteContext<'_>,
-) -> Option<String> {
+) -> String {
     let snippet = context.snippet(span);
     if snippet != new && changed_comment_content(snippet, &new) {
         // We missed some comments. Warn and keep the original text.
@@ -1723,9 +1721,9 @@ pub(crate) fn recover_comment_removed(
                 )],
             );
         }
-        Some(snippet.to_owned())
+        snippet.to_owned()
     } else {
-        Some(new)
+        new
     }
 }
 
diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs
index f7cff1a1729..14217caba0a 100644
--- a/src/tools/rustfmt/src/config/config_type.rs
+++ b/src/tools/rustfmt/src/config/config_type.rs
@@ -70,15 +70,15 @@ macro_rules! create_config {
     //
     // - $i: the ident name of the option
     // - $ty: the type of the option value
-    // - $def: the default value of the option
     // - $stb: true if the option is stable
     // - $dstring: description of the option
-    ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
+    ($($i:ident: $ty:ty, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
         #[cfg(test)]
         use std::collections::HashSet;
         use std::io::Write;
 
         use serde::{Deserialize, Serialize};
+        use $crate::config::style_edition::StyleEditionDefault;
 
         #[derive(Clone)]
         #[allow(unreachable_pub)]
@@ -89,7 +89,10 @@ macro_rules! create_config {
             // - 1: true if the option was manually initialized
             // - 2: the option value
             // - 3: true if the option is unstable
-            $($i: (Cell<bool>, bool, $ty, bool)),+
+            // - 4: true if the option was set manually from a CLI flag
+            // FIXME: 4 is probably unnecessary and duplicative
+            // https://github.com/rust-lang/rustfmt/issues/6252
+            $($i: (Cell<bool>, bool, <$ty as StyleEditionDefault>::ConfigType, bool, bool)),+
         }
 
         // Just like the Config struct but with each property wrapped
@@ -100,7 +103,7 @@ macro_rules! create_config {
         #[derive(Deserialize, Serialize, Clone)]
         #[allow(unreachable_pub)]
         pub struct PartialConfig {
-            $(pub $i: Option<$ty>),+
+            $(pub $i: Option<<$ty as StyleEditionDefault>::ConfigType>),+
         }
 
         // Macro hygiene won't allow us to make `set_$i()` methods on Config
@@ -114,7 +117,7 @@ macro_rules! create_config {
         impl<'a> ConfigSetter<'a> {
             $(
             #[allow(unreachable_pub)]
-            pub fn $i(&mut self, value: $ty) {
+            pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) {
                 (self.0).$i.2 = value;
                 match stringify!($i) {
                     "max_width"
@@ -130,6 +133,37 @@ macro_rules! create_config {
                     "merge_imports" => self.0.set_merge_imports(),
                     "fn_args_layout" => self.0.set_fn_args_layout(),
                     "hide_parse_errors" => self.0.set_hide_parse_errors(),
+                    "version" => self.0.set_version(),
+                    &_ => (),
+                }
+            }
+            )+
+        }
+
+        #[allow(unreachable_pub)]
+        pub struct CliConfigSetter<'a>(&'a mut Config);
+
+        impl<'a> CliConfigSetter<'a> {
+            $(
+            #[allow(unreachable_pub)]
+            pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) {
+                (self.0).$i.2 = value;
+                (self.0).$i.4 = true;
+                match stringify!($i) {
+                    "max_width"
+                    | "use_small_heuristics"
+                    | "fn_call_width"
+                    | "single_line_if_else_max_width"
+                    | "single_line_let_else_max_width"
+                    | "attr_fn_like_width"
+                    | "struct_lit_width"
+                    | "struct_variant_width"
+                    | "array_width"
+                    | "chain_width" => self.0.set_heuristics(),
+                    "merge_imports" => self.0.set_merge_imports(),
+                    "fn_args_layout" => self.0.set_fn_args_layout(),
+                    "hide_parse_errors" => self.0.set_hide_parse_errors(),
+                    "version" => self.0.set_version(),
                     &_ => (),
                 }
             }
@@ -150,25 +184,66 @@ macro_rules! create_config {
             )+
         }
 
+        // Query each option, returns true if the user set the option via a CLI flag,
+        // false if a default was used.
+        #[allow(unreachable_pub)]
+        pub struct CliConfigWasSet<'a>(&'a Config);
+
+        impl<'a> CliConfigWasSet<'a> {
+            $(
+            #[allow(unreachable_pub)]
+            pub fn $i(&self) -> bool {
+                (self.0).$i.4
+            }
+            )+
+        }
+
         impl Config {
             $(
             #[allow(unreachable_pub)]
-            pub fn $i(&self) -> $ty {
+            pub fn $i(&self) -> <$ty as StyleEditionDefault>::ConfigType {
                 self.$i.0.set(true);
                 self.$i.2.clone()
             }
             )+
 
             #[allow(unreachable_pub)]
+            pub(super) fn default_with_style_edition(style_edition: StyleEdition) -> Config {
+                Config {
+                    $(
+                        $i: (
+                                Cell::new(false),
+                                false,
+                                <$ty as StyleEditionDefault>::style_edition_default(
+                                    style_edition
+                                ),
+                                $stb,
+                                false,
+                            ),
+                    )+
+                }
+            }
+
+            #[allow(unreachable_pub)]
             pub fn set(&mut self) -> ConfigSetter<'_> {
                 ConfigSetter(self)
             }
 
             #[allow(unreachable_pub)]
+            pub fn set_cli(&mut self) -> CliConfigSetter<'_> {
+                CliConfigSetter(self)
+            }
+
+            #[allow(unreachable_pub)]
             pub fn was_set(&self) -> ConfigWasSet<'_> {
                 ConfigWasSet(self)
             }
 
+            #[allow(unreachable_pub)]
+            pub fn was_set_cli(&self) -> CliConfigWasSet<'_> {
+                CliConfigWasSet(self)
+            }
+
             fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
             $(
                 if let Some(option_value) = parsed.$i {
@@ -186,6 +261,7 @@ macro_rules! create_config {
                 self.set_merge_imports();
                 self.set_fn_args_layout();
                 self.set_hide_parse_errors();
+                self.set_version();
                 self
             }
 
@@ -212,7 +288,9 @@ macro_rules! create_config {
             pub fn is_valid_key_val(key: &str, val: &str) -> bool {
                 match key {
                     $(
-                        stringify!($i) => val.parse::<$ty>().is_ok(),
+                        stringify!($i) => {
+                            val.parse::<<$ty as StyleEditionDefault>::ConfigType>().is_ok()
+                        }
                     )+
                         _ => false,
                 }
@@ -246,11 +324,15 @@ macro_rules! create_config {
                 match key {
                     $(
                         stringify!($i) => {
-                            let option_value = val.parse::<$ty>()
-                                .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
-                                                 stringify!($i),
-                                                 val,
-                                                 stringify!($ty)));
+                            let value = val.parse::<<$ty as StyleEditionDefault>::ConfigType>()
+                                .expect(
+                                    &format!(
+                                        "Failed to parse override for {} (\"{}\") as a {}",
+                                        stringify!($i),
+                                        val,
+                                        stringify!(<$ty as StyleEditionDefault>::ConfigType)
+                                    )
+                                );
 
                             // Users are currently allowed to set unstable
                             // options/variants via the `--config` options override.
@@ -261,7 +343,7 @@ macro_rules! create_config {
                             // For now, do not validate whether the option or value is stable,
                             // just always set it.
                             self.$i.1 = true;
-                            self.$i.2 = option_value;
+                            self.$i.2 = value;
                         }
                     )+
                     _ => panic!("Unknown config key in override: {}", key)
@@ -281,6 +363,7 @@ macro_rules! create_config {
                     "merge_imports" => self.set_merge_imports(),
                     "fn_args_layout" => self.set_fn_args_layout(),
                     "hide_parse_errors" => self.set_hide_parse_errors(),
+                    "version" => self.set_version(),
                     &_ => (),
                 }
             }
@@ -301,6 +384,7 @@ macro_rules! create_config {
 
             #[allow(unreachable_pub)]
             pub fn print_docs(out: &mut dyn Write, include_unstable: bool) {
+                let style_edition = StyleEdition::Edition2015;
                 use std::cmp;
                 let max = 0;
                 $( let max = cmp::max(max, stringify!($i).len()+1); )+
@@ -317,14 +401,17 @@ macro_rules! create_config {
                             }
                             name_out.push_str(name_raw);
                             name_out.push(' ');
-                            let mut default_str = format!("{}", $def);
+                            let default_value = <$ty as StyleEditionDefault>::style_edition_default(
+                                style_edition
+                            );
+                            let mut default_str = format!("{}", default_value);
                             if default_str.is_empty() {
                                 default_str = String::from("\"\"");
                             }
                             writeln!(out,
                                     "{}{} Default: {}{}",
                                     name_out,
-                                    <$ty>::doc_hint(),
+                                    <<$ty as StyleEditionDefault>::ConfigType>::doc_hint(),
                                     default_str,
                                     if !$stb { " (unstable)" } else { "" }).unwrap();
                             $(
@@ -477,12 +564,36 @@ macro_rules! create_config {
                 }
             }
 
+            fn set_version(&mut self) {
+                if !self.was_set().version() {
+                    return;
+                }
+
+                eprintln!(
+                    "Warning: the `version` option is deprecated. \
+                    Use `style_edition` instead."
+                );
+
+                if self.was_set().style_edition() || self.was_set_cli().style_edition() {
+                    eprintln!(
+                        "Warning: the deprecated `version` option was \
+                        used in conjunction with the `style_edition` \
+                        option which takes precedence. \
+                        The value of the `version` option will be ignored."
+                    );
+                }
+            }
+
             #[allow(unreachable_pub)]
             /// Returns `true` if the config key was explicitly set and is the default value.
             pub fn is_default(&self, key: &str) -> bool {
+                let style_edition = StyleEdition::Edition2015;
                 $(
+                    let default_value = <$ty as StyleEditionDefault>::style_edition_default(
+                        style_edition
+                    );
                     if let stringify!($i) = key {
-                        return self.$i.1 && self.$i.2 == $def;
+                        return self.$i.1 && self.$i.2 == default_value;
                     }
                  )+
                 false
@@ -492,11 +603,7 @@ macro_rules! create_config {
         // Template for the default configuration
         impl Default for Config {
             fn default() -> Config {
-                Config {
-                    $(
-                        $i: (Cell::new(false), false, $def, $stb),
-                    )+
-                }
+                Config::default_with_style_edition(StyleEdition::Edition2015)
             }
         }
     )
diff --git a/src/tools/rustfmt/src/config/file_lines.rs b/src/tools/rustfmt/src/config/file_lines.rs
index 224864393d3..c53ec6371e9 100644
--- a/src/tools/rustfmt/src/config/file_lines.rs
+++ b/src/tools/rustfmt/src/config/file_lines.rs
@@ -7,7 +7,7 @@ use std::{cmp, fmt, iter, str};
 
 use rustc_data_structures::sync::Lrc;
 use rustc_span::SourceFile;
-use serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
+use serde::{Deserialize, Deserializer, Serialize, Serializer, ser};
 use serde_json as json;
 use thiserror::Error;
 
@@ -38,7 +38,7 @@ impl From<rustc_span::FileName> for FileName {
 impl fmt::Display for FileName {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()),
+            FileName::Real(p) => write!(f, "{}", p.display()),
             FileName::Stdin => write!(f, "<stdin>"),
         }
     }
@@ -201,7 +201,7 @@ impl FileLines {
     }
 
     /// Returns `true` if this `FileLines` contains all lines in all files.
-    pub(crate) fn is_all(&self) -> bool {
+    pub fn is_all(&self) -> bool {
         self.0.is_none()
     }
 
diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs
index 9484b2e5829..ea257c0a8ba 100644
--- a/src/tools/rustfmt/src/config/mod.rs
+++ b/src/tools/rustfmt/src/config/mod.rs
@@ -10,9 +10,7 @@ use crate::config::config_type::ConfigType;
 #[allow(unreachable_pub)]
 pub use crate::config::file_lines::{FileLines, FileName, Range};
 #[allow(unreachable_pub)]
-pub use crate::config::lists::*;
-#[allow(unreachable_pub)]
-pub use crate::config::macro_names::{MacroSelector, MacroSelectors};
+pub use crate::config::macro_names::MacroSelector;
 #[allow(unreachable_pub)]
 pub use crate::config::options::*;
 
@@ -34,161 +32,169 @@ pub(crate) mod style_edition;
 // `name: value type, default value, is stable, description;`
 create_config! {
     // Fundamental stuff
-    max_width: usize, 100, true, "Maximum width of each line";
-    hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment";
-    tab_spaces: usize, 4, true, "Number of spaces per tab";
-    newline_style: NewlineStyle, NewlineStyle::Auto, true, "Unix or Windows line endings";
-    indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items";
+    max_width: MaxWidth, true, "Maximum width of each line";
+    hard_tabs: HardTabs, true, "Use tab characters for indentation, spaces for alignment";
+    tab_spaces: TabSpaces, true, "Number of spaces per tab";
+    newline_style: NewlineStyleConfig, true, "Unix or Windows line endings";
+    indent_style: IndentStyleConfig, false, "How do we indent expressions or items";
 
     // Width Heuristics
-    use_small_heuristics: Heuristics, Heuristics::Default, true, "Whether to use different \
+    use_small_heuristics: UseSmallHeuristics, true, "Whether to use different \
         formatting for items and expressions if they satisfy a heuristic notion of 'small'";
-    width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false,
-        "'small' heuristic values";
-    fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \
+    width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values";
+    fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \
         falling back to vertical formatting.";
-    attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \
+    attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a function-like \
         attributes before falling back to vertical formatting.";
-    struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \
-        falling back to vertical formatting.";
-    struct_variant_width: usize, 35, true, "Maximum width in the body of a struct variant before \
+    struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit before \
         falling back to vertical formatting.";
-    array_width: usize, 60, true,  "Maximum width of an array literal before falling \
+    struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct variant \
+        before falling back to vertical formatting.";
+    array_width: ArrayWidth, true,  "Maximum width of an array literal before falling \
         back to vertical formatting.";
-    chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line.";
-    single_line_if_else_max_width: usize, 50, true, "Maximum line length for single line if-else \
-        expressions. A value of zero means always break if-else expressions.";
-    single_line_let_else_max_width: usize, 50, true, "Maximum line length for single line \
-        let-else statements. A value of zero means always format the divergent `else` block \
-        over multiple lines.";
+    chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line.";
+    single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length for single \
+        line if-else expressions. A value of zero means always break if-else expressions.";
+    single_line_let_else_max_width: SingleLineLetElseMaxWidth, true, "Maximum line length for \
+        single line let-else statements. A value of zero means always format the divergent `else` \
+        block over multiple lines.";
 
     // Comments. macros, and strings
-    wrap_comments: bool, false, false, "Break comments to fit on the line";
-    format_code_in_doc_comments: bool, false, false, "Format the code snippet in doc comments.";
-    doc_comment_code_block_width: usize, 100, false, "Maximum width for code snippets in doc \
-        comments. No effect unless format_code_in_doc_comments = true";
-    comment_width: usize, 80, false,
+    wrap_comments: WrapComments, false, "Break comments to fit on the line";
+    format_code_in_doc_comments: FormatCodeInDocComments, false, "Format the code snippet in \
+        doc comments.";
+    doc_comment_code_block_width: DocCommentCodeBlockWidth, false, "Maximum width for code \
+        snippets in doc comments. No effect unless format_code_in_doc_comments = true";
+    comment_width: CommentWidth, false,
         "Maximum length of comments. No effect unless wrap_comments = true";
-    normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible";
-    normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments";
-    format_strings: bool, false, false, "Format string literals where necessary";
-    format_macro_matchers: bool, false, false,
+    normalize_comments: NormalizeComments, false, "Convert /* */ comments to // comments where \
+        possible";
+    normalize_doc_attributes: NormalizeDocAttributes, false, "Normalize doc attributes as doc \
+        comments";
+    format_strings: FormatStrings, false, "Format string literals where necessary";
+    format_macro_matchers: FormatMacroMatchers, false,
         "Format the metavariable matching patterns in macros";
-    format_macro_bodies: bool, true, false, "Format the bodies of declarative macro definitions";
-    skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false,
+    format_macro_bodies: FormatMacroBodies, false,
+        "Format the bodies of declarative macro definitions";
+    skip_macro_invocations: SkipMacroInvocations, false,
         "Skip formatting the bodies of macros invoked with the following names.";
-    hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false,
-        "Format hexadecimal integer literals";
+    hex_literal_case: HexLiteralCaseConfig, false, "Format hexadecimal integer literals";
 
     // Single line expressions and items
-    empty_item_single_line: bool, true, false,
+    empty_item_single_line: EmptyItemSingleLine, false,
         "Put empty-body functions and impls on a single line";
-    struct_lit_single_line: bool, true, false,
+    struct_lit_single_line: StructLitSingleLine, false,
         "Put small struct literals on a single line";
-    fn_single_line: bool, false, false, "Put single-expression functions on a single line";
-    where_single_line: bool, false, false, "Force where-clauses to be on a single line";
+    fn_single_line: FnSingleLine, false, "Put single-expression functions on a single line";
+    where_single_line: WhereSingleLine, false, "Force where-clauses to be on a single line";
 
     // Imports
-    imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports";
-    imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
-    imports_granularity: ImportGranularity, ImportGranularity::Preserve, false,
+    imports_indent: ImportsIndent, false, "Indent of imports";
+    imports_layout: ImportsLayout, false, "Item layout inside a import block";
+    imports_granularity: ImportsGranularityConfig, false,
         "Merge or split imports to the provided granularity";
-    group_imports: GroupImportsTactic, GroupImportsTactic::Preserve, false,
+    group_imports: GroupImportsTacticConfig, false,
         "Controls the strategy for how imports are grouped together";
-    merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
+    merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)";
 
     // Ordering
-    reorder_imports: bool, true, true, "Reorder import and extern crate statements alphabetically";
-    reorder_modules: bool, true, true, "Reorder module statements alphabetically in group";
-    reorder_impl_items: bool, false, false, "Reorder impl items";
+    reorder_imports: ReorderImports, true, "Reorder import and extern crate statements \
+        alphabetically";
+    reorder_modules: ReorderModules, true, "Reorder module statements alphabetically in group";
+    reorder_impl_items: ReorderImplItems, false, "Reorder impl items";
 
     // Spaces around punctuation
-    type_punctuation_density: TypeDensity, TypeDensity::Wide, false,
+    type_punctuation_density: TypePunctuationDensity, false,
         "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
-    space_before_colon: bool, false, false, "Leave a space before the colon";
-    space_after_colon: bool, true, false, "Leave a space after the colon";
-    spaces_around_ranges: bool, false, false, "Put spaces around the  .. and ..= range operators";
-    binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
+    space_before_colon: SpaceBeforeColon, false, "Leave a space before the colon";
+    space_after_colon: SpaceAfterColon, false, "Leave a space after the colon";
+    spaces_around_ranges: SpacesAroundRanges, false, "Put spaces around the  .. and ..= range \
+        operators";
+    binop_separator: BinopSeparator, false,
         "Where to put a binary operator when a binary expression goes multiline";
 
     // Misc.
-    remove_nested_parens: bool, true, true, "Remove nested parens";
-    combine_control_expr: bool, true, false, "Combine control expressions with function calls";
-    short_array_element_width_threshold: usize, 10, true,
+    remove_nested_parens: RemoveNestedParens, true, "Remove nested parens";
+    combine_control_expr: CombineControlExpr, false, "Combine control expressions with function \
+        calls";
+    short_array_element_width_threshold: ShortArrayElementWidthThreshold, true,
         "Width threshold for an array element to be considered short";
-    overflow_delimited_expr: bool, false, false,
+    overflow_delimited_expr: OverflowDelimitedExpr, false,
         "Allow trailing bracket/brace delimited expressions to overflow";
-    struct_field_align_threshold: usize, 0, false,
+    struct_field_align_threshold: StructFieldAlignThreshold, false,
         "Align struct fields if their diffs fits within threshold";
-    enum_discrim_align_threshold: usize, 0, false,
+    enum_discrim_align_threshold: EnumDiscrimAlignThreshold, false,
         "Align enum variants discrims, if their diffs fit within threshold";
-    match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
-        the same line with the pattern of arms";
-    match_arm_leading_pipes: MatchArmLeadingPipe, MatchArmLeadingPipe::Never, true,
+    match_arm_blocks: MatchArmBlocks, false, "Wrap the body of arms in blocks when it does not fit \
+        on the same line with the pattern of arms";
+    match_arm_leading_pipes: MatchArmLeadingPipeConfig, true,
         "Determines whether leading pipes are emitted on match arms";
-    force_multiline_blocks: bool, false, false,
+    force_multiline_blocks: ForceMultilineBlocks, false,
         "Force multiline closure bodies and match arms to be wrapped in a block";
-    fn_args_layout: Density, Density::Tall, true,
+    fn_args_layout: FnArgsLayout, true,
         "(deprecated: use fn_params_layout instead)";
-    fn_params_layout: Density, Density::Tall, true,
+    fn_params_layout: FnParamsLayout, true,
         "Control the layout of parameters in function signatures.";
-    brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
-    control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
+    brace_style: BraceStyleConfig, false, "Brace style for items";
+    control_brace_style: ControlBraceStyleConfig, false,
         "Brace style for control flow constructs";
-    trailing_semicolon: bool, true, false,
+    trailing_semicolon: TrailingSemicolon, false,
         "Add trailing semicolon after break, continue and return";
-    trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false,
+    trailing_comma: TrailingComma, false,
         "How to handle trailing commas for lists";
-    match_block_trailing_comma: bool, false, true,
+    match_block_trailing_comma: MatchBlockTrailingComma, true,
         "Put a trailing comma after a block based match arm (non-block arms are not affected)";
-    blank_lines_upper_bound: usize, 1, false,
+    blank_lines_upper_bound: BlankLinesUpperBound, false,
         "Maximum number of blank lines which can be put between items";
-    blank_lines_lower_bound: usize, 0, false,
+    blank_lines_lower_bound: BlankLinesLowerBound, false,
         "Minimum number of blank lines which must be put between items";
-    edition: Edition, Edition::Edition2015, true, "The edition of the parser (RFC 2052)";
-    version: Version, Version::One, false, "Version of formatting rules";
-    inline_attribute_width: usize, 0, false,
+    edition: EditionConfig, true, "The edition of the parser (RFC 2052)";
+    style_edition: StyleEditionConfig, false, "The edition of the Style Guide (RFC 3338)";
+    version: VersionConfig, false, "Version of formatting rules";
+    inline_attribute_width: InlineAttributeWidth, false,
         "Write an item and its attribute on the same line \
         if their combined width is below a threshold";
-    format_generated_files: bool, true, false, "Format generated files";
-    generated_marker_line_search_limit: usize, 5, false, "Number of lines to check for a \
-        `@generated` marker when `format_generated_files` is enabled";
+    format_generated_files: FormatGeneratedFiles, false, "Format generated files";
+    generated_marker_line_search_limit: GeneratedMarkerLineSearchLimit, false, "Number of lines to \
+        check for a `@generated` marker when `format_generated_files` is enabled";
 
     // Options that can change the source code beyond whitespace/blocks (somewhat linty things)
-    merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one";
-    use_try_shorthand: bool, false, true, "Replace uses of the try! macro by the ? shorthand";
-    use_field_init_shorthand: bool, false, true, "Use field initialization shorthand if possible";
-    force_explicit_abi: bool, true, true, "Always print the abi for extern items";
-    condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \
-                                                     in tuple patterns";
+    merge_derives: MergeDerives, true, "Merge multiple `#[derive(...)]` into a single one";
+    use_try_shorthand: UseTryShorthand, true, "Replace uses of the try! macro by the ? shorthand";
+    use_field_init_shorthand: UseFieldInitShorthand, true, "Use field initialization shorthand if \
+        possible";
+    force_explicit_abi: ForceExplicitAbi, true, "Always print the abi for extern items";
+    condense_wildcard_suffixes: CondenseWildcardSuffixes, false, "Replace strings of _ wildcards \
+        by a single .. in tuple patterns";
 
     // Control options (changes the operation of rustfmt, rather than the formatting)
-    color: Color, Color::Auto, false,
+    color: ColorConfig, false,
         "What Color option to use when none is supplied: Always, Never, Auto";
-    required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
+    required_version: RequiredVersion, false,
         "Require a specific version of rustfmt";
-    unstable_features: bool, false, false,
+    unstable_features: UnstableFeatures, false,
             "Enables unstable features. Only available on nightly channel";
-    disable_all_formatting: bool, false, true, "Don't reformat anything";
-    skip_children: bool, false, false, "Don't reformat out of line modules";
-    hide_parse_errors: bool, false, false, "(deprecated: use show_parse_errors instead)";
-    show_parse_errors: bool, true, false, "Show errors from the parser (unstable)";
-    error_on_line_overflow: bool, false, false, "Error if unable to get all lines within max_width";
-    error_on_unformatted: bool, false, false,
+    disable_all_formatting: DisableAllFormatting, true, "Don't reformat anything";
+    skip_children: SkipChildren, false, "Don't reformat out of line modules";
+    hide_parse_errors: HideParseErrors, false, "Hide errors from the parser";
+    show_parse_errors: ShowParseErrors, false, "Show errors from the parser (unstable)";
+    error_on_line_overflow: ErrorOnLineOverflow, false, "Error if unable to get all lines within \
+        max_width";
+    error_on_unformatted: ErrorOnUnformatted, false,
         "Error if unable to get comments or string literals within max_width, \
          or they are left with trailing whitespaces";
-    ignore: IgnoreList, IgnoreList::default(), false,
+    ignore: Ignore, false,
         "Skip formatting the specified files and directories";
 
     // Not user-facing
-    verbose: Verbosity, Verbosity::Normal, false, "How much to information to emit to the user";
-    file_lines: FileLines, FileLines::all(), false,
+    verbose: Verbose, false, "How much to information to emit to the user";
+    file_lines: FileLinesConfig, false,
         "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
          via the --file-lines option";
-    emit_mode: EmitMode, EmitMode::Files, false,
+    emit_mode: EmitModeConfig, false,
         "What emit Mode to use when none is supplied";
-    make_backup: bool, false, false, "Backup changed files";
-    print_misformatted_file_names: bool, false, true,
+    make_backup: MakeBackup, false, "Backup changed files";
+    print_misformatted_file_names: PrintMisformattedFileNames, true,
         "Prints the names of mismatched files that were formatted. Prints the names of \
          files that would be formatted when used with `--check` mode. ";
 }
@@ -211,9 +217,48 @@ impl PartialConfig {
 
         ::toml::to_string(&cloned).map_err(ToTomlError)
     }
+
+    pub(super) fn to_parsed_config(
+        self,
+        style_edition_override: Option<StyleEdition>,
+        edition_override: Option<Edition>,
+        version_override: Option<Version>,
+        dir: &Path,
+    ) -> Config {
+        Config::default_for_possible_style_edition(
+            style_edition_override.or(self.style_edition),
+            edition_override.or(self.edition),
+            version_override.or(self.version),
+        )
+        .fill_from_parsed_config(self, dir)
+    }
 }
 
 impl Config {
+    pub fn default_for_possible_style_edition(
+        style_edition: Option<StyleEdition>,
+        edition: Option<Edition>,
+        version: Option<Version>,
+    ) -> Config {
+        // Ensures the configuration defaults associated with Style Editions
+        // follow the precedence set in
+        // https://rust-lang.github.io/rfcs/3338-style-evolution.html
+        // 'version' is a legacy alias for 'style_edition' that we'll support
+        // for some period of time
+        // FIXME(calebcartwright) - remove 'version' at some point
+        match (style_edition, version, edition) {
+            (Some(se), _, _) => Self::default_with_style_edition(se),
+            (None, Some(Version::Two), _) => {
+                Self::default_with_style_edition(StyleEdition::Edition2024)
+            }
+            (None, Some(Version::One), _) => {
+                Self::default_with_style_edition(StyleEdition::Edition2015)
+            }
+            (None, None, Some(e)) => Self::default_with_style_edition(e.into()),
+            (None, None, None) => Config::default(),
+        }
+    }
+
     pub(crate) fn version_meets_requirement(&self) -> bool {
         if self.was_set().required_version() {
             let version = env!("CARGO_PKG_VERSION");
@@ -237,11 +282,16 @@ impl Config {
     ///
     /// Returns a `Config` if the config could be read and parsed from
     /// the file, otherwise errors.
-    pub(super) fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
+    pub(super) fn from_toml_path(
+        file_path: &Path,
+        edition: Option<Edition>,
+        style_edition: Option<StyleEdition>,
+        version: Option<Version>,
+    ) -> Result<Config, Error> {
         let mut file = File::open(&file_path)?;
         let mut toml = String::new();
         file.read_to_string(&mut toml)?;
-        Config::from_toml(&toml, file_path.parent().unwrap())
+        Config::from_toml_for_style_edition(&toml, file_path, edition, style_edition, version)
             .map_err(|err| Error::new(ErrorKind::InvalidData, err))
     }
 
@@ -254,9 +304,14 @@ impl Config {
     ///
     /// Returns the `Config` to use, and the path of the project file if there was
     /// one.
-    pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> {
+    pub(super) fn from_resolved_toml_path(
+        dir: &Path,
+        edition: Option<Edition>,
+        style_edition: Option<StyleEdition>,
+        version: Option<Version>,
+    ) -> Result<(Config, Option<PathBuf>), Error> {
         /// Try to find a project file in the given directory and its parents.
-        /// Returns the path of a the nearest project file if one exists,
+        /// Returns the path of the nearest project file if one exists,
         /// or `None` if no project file was found.
         fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> {
             let mut current = if dir.is_relative() {
@@ -299,12 +354,27 @@ impl Config {
         }
 
         match resolve_project_file(dir)? {
-            None => Ok((Config::default(), None)),
-            Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))),
+            None => Ok((
+                Config::default_for_possible_style_edition(style_edition, edition, version),
+                None,
+            )),
+            Some(path) => Config::from_toml_path(&path, edition, style_edition, version)
+                .map(|config| (config, Some(path))),
         }
     }
 
-    pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> {
+    #[allow(dead_code)]
+    pub(super) fn from_toml(toml: &str, file_path: &Path) -> Result<Config, String> {
+        Self::from_toml_for_style_edition(toml, file_path, None, None, None)
+    }
+
+    pub(crate) fn from_toml_for_style_edition(
+        toml: &str,
+        file_path: &Path,
+        edition: Option<Edition>,
+        style_edition: Option<StyleEdition>,
+        version: Option<Version>,
+    ) -> Result<Config, String> {
         let parsed: ::toml::Value = toml
             .parse()
             .map_err(|e| format!("Could not parse TOML: {}", e))?;
@@ -318,18 +388,25 @@ impl Config {
                 err.push_str(msg)
             }
         }
-        match parsed.try_into() {
+
+        match parsed.try_into::<PartialConfig>() {
             Ok(parsed_config) => {
                 if !err.is_empty() {
                     eprint!("{err}");
                 }
-                Ok(Config::default().fill_from_parsed_config(parsed_config, dir))
+                let dir = file_path.parent().ok_or_else(|| {
+                    format!("failed to get parent directory for {}", file_path.display())
+                })?;
+
+                Ok(parsed_config.to_parsed_config(style_edition, edition, version, dir))
             }
             Err(e) => {
-                err.push_str("Error: Decoding config file failed:\n");
-                err.push_str(format!("{e}\n").as_str());
-                err.push_str("Please check your config file.");
-                Err(err)
+                let err_msg = format!(
+                    "The file `{}` failed to parse.\nError details: {e}",
+                    file_path.display()
+                );
+                err.push_str(&err_msg);
+                Err(err_msg)
             }
         }
     }
@@ -341,17 +418,26 @@ pub fn load_config<O: CliOptions>(
     file_path: Option<&Path>,
     options: Option<O>,
 ) -> Result<(Config, Option<PathBuf>), Error> {
-    let over_ride = match options {
-        Some(ref opts) => config_path(opts)?,
-        None => None,
+    let (over_ride, edition, style_edition, version) = match options {
+        Some(ref opts) => (
+            config_path(opts)?,
+            opts.edition(),
+            opts.style_edition(),
+            opts.version(),
+        ),
+        None => (None, None, None, None),
     };
 
     let result = if let Some(over_ride) = over_ride {
-        Config::from_toml_path(over_ride.as_ref()).map(|p| (p, Some(over_ride.to_owned())))
+        Config::from_toml_path(over_ride.as_ref(), edition, style_edition, version)
+            .map(|p| (p, Some(over_ride.to_owned())))
     } else if let Some(file_path) = file_path {
-        Config::from_resolved_toml_path(file_path)
+        Config::from_resolved_toml_path(file_path, edition, style_edition, version)
     } else {
-        Ok((Config::default(), None))
+        Ok((
+            Config::default_for_possible_style_edition(style_edition, edition, version),
+            None,
+        ))
     };
 
     result.map(|(mut c, p)| {
@@ -362,7 +448,7 @@ pub fn load_config<O: CliOptions>(
     })
 }
 
-// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir`
+// Check for the presence of known config file names (`rustfmt.toml`, `.rustfmt.toml`) in `dir`
 //
 // Return the path if a config file exists, empty if no file exists, and Error for IO errors
 fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
@@ -372,7 +458,7 @@ fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
         match fs::metadata(&config_file) {
             // Only return if it's a file to handle the unlikely situation of a directory named
             // `rustfmt.toml`.
-            Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
+            Ok(ref md) if md.is_file() => return Ok(Some(config_file.canonicalize()?)),
             // Return the error if it's something other than `NotFound`; otherwise we didn't
             // find the project file yet, and continue searching.
             Err(e) => {
@@ -411,7 +497,11 @@ fn config_path(options: &dyn CliOptions) -> Result<Option<PathBuf>, Error> {
                 config_path_not_found(path.to_str().unwrap())
             }
         }
-        path => Ok(path.map(ToOwned::to_owned)),
+        Some(path) => Ok(Some(
+            // Canonicalize only after checking above that the `path.exists()`.
+            path.canonicalize()?,
+        )),
+        None => Ok(None),
     }
 }
 
@@ -420,12 +510,13 @@ mod test {
     use super::*;
     use std::str;
 
-    use crate::config::macro_names::MacroName;
+    use crate::config::macro_names::{MacroName, MacroSelectors};
     use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
 
     #[allow(dead_code)]
     mod mock {
         use super::super::*;
+        use crate::config_option_with_style_edition_default;
         use rustfmt_config_proc_macro::config_type;
 
         #[config_type]
@@ -436,66 +527,69 @@ mod test {
             V3,
         }
 
+        config_option_with_style_edition_default!(
+            StableOption, bool, _ => false;
+            UnstableOption, bool, _ => false;
+            PartiallyUnstable, PartiallyUnstableOption, _ => PartiallyUnstableOption::V1;
+        );
+
         create_config! {
             // Options that are used by the generated functions
-            max_width: usize, 100, true, "Maximum width of each line";
-            required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
-                "Require a specific version of rustfmt.";
-            ignore: IgnoreList, IgnoreList::default(), false,
-                "Skip formatting the specified files and directories.";
-            verbose: Verbosity, Verbosity::Normal, false,
-                "How much to information to emit to the user";
-            file_lines: FileLines, FileLines::all(), false,
+            max_width: MaxWidth, true, "Maximum width of each line";
+            required_version: RequiredVersion, false, "Require a specific version of rustfmt.";
+            ignore: Ignore, false, "Skip formatting the specified files and directories.";
+            verbose: Verbose, false, "How much to information to emit to the user";
+            file_lines: FileLinesConfig, false,
                 "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
                     via the --file-lines option";
 
             // merge_imports deprecation
-            imports_granularity: ImportGranularity, ImportGranularity::Preserve, false,
-                "Merge imports";
-            merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
+            imports_granularity: ImportsGranularityConfig, false, "Merge imports";
+            merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)";
 
             // fn_args_layout renamed to fn_params_layout
-            fn_args_layout: Density, Density::Tall, true,
-                "(deprecated: use fn_params_layout instead)";
-            fn_params_layout: Density, Density::Tall, true,
+            fn_args_layout: FnArgsLayout, true, "(deprecated: use fn_params_layout instead)";
+            fn_params_layout: FnParamsLayout, true,
                 "Control the layout of parameters in a function signatures.";
 
             // hide_parse_errors renamed to show_parse_errors
-            hide_parse_errors: bool, false, false,
+            hide_parse_errors: HideParseErrors, false,
                 "(deprecated: use show_parse_errors instead)";
-            show_parse_errors: bool, true, false,
+            show_parse_errors: ShowParseErrors, false,
                 "Show errors from the parser (unstable)";
 
 
             // Width Heuristics
-            use_small_heuristics: Heuristics, Heuristics::Default, true,
+            use_small_heuristics: UseSmallHeuristics, true,
                 "Whether to use different formatting for items and \
                  expressions if they satisfy a heuristic notion of 'small'.";
-            width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false,
-                "'small' heuristic values";
+            width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values";
 
-            fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \
+            fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \
                 falling back to vertical formatting.";
-            attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \
-                attributes before falling back to vertical formatting.";
-            struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \
-                falling back to vertical formatting.";
-            struct_variant_width: usize, 35, true, "Maximum width in the body of a struct \
+            attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a \
+                function-like attributes before falling back to vertical formatting.";
+            struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit \
+                before falling back to vertical formatting.";
+            struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct \
                 variant before falling back to vertical formatting.";
-            array_width: usize, 60, true,  "Maximum width of an array literal before falling \
+            array_width: ArrayWidth, true,  "Maximum width of an array literal before falling \
                 back to vertical formatting.";
-            chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line.";
-            single_line_if_else_max_width: usize, 50, true, "Maximum line length for single \
-                line if-else expressions. A value of zero means always break if-else expressions.";
-            single_line_let_else_max_width: usize, 50, false, "Maximum line length for single \
-                line let-else statements. A value of zero means always format the divergent \
-                `else` block over multiple lines.";
+            chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line.";
+            single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length \
+                for single line if-else expressions. A value of zero means always break if-else \
+                expressions.";
+            single_line_let_else_max_width: SingleLineLetElseMaxWidth, false, "Maximum line length \
+                for single line let-else statements. A value of zero means always format the \
+                divergent `else` block over multiple lines.";
 
             // Options that are used by the tests
-            stable_option: bool, false, true, "A stable option";
-            unstable_option: bool, false, false, "An unstable option";
-            partially_unstable_option: PartiallyUnstableOption, PartiallyUnstableOption::V1, true,
-                "A partially unstable option";
+            stable_option: StableOption, true, "A stable option";
+            unstable_option: UnstableOption, false, "An unstable option";
+            partially_unstable_option: PartiallyUnstable, true, "A partially unstable option";
+            edition: EditionConfig, true, "blah";
+            style_edition: StyleEditionConfig, true, "blah";
+            version: VersionConfig, false, "blah blah"
         }
 
         #[cfg(test)]
@@ -580,7 +674,7 @@ mod test {
 
     #[test]
     fn test_was_set() {
-        let config = Config::from_toml("hard_tabs = true", Path::new("")).unwrap();
+        let config = Config::from_toml("hard_tabs = true", Path::new("./rustfmt.toml")).unwrap();
 
         assert_eq!(config.was_set().hard_tabs(), true);
         assert_eq!(config.was_set().verbose(), false);
@@ -679,6 +773,7 @@ match_block_trailing_comma = false
 blank_lines_upper_bound = 1
 blank_lines_lower_bound = 0
 edition = "2015"
+style_edition = "2015"
 version = "One"
 inline_attribute_width = 0
 format_generated_files = true
@@ -706,6 +801,114 @@ make_backup = false
         assert_eq!(&toml, &default_config);
     }
 
+    #[test]
+    fn test_dump_style_edition_2024_config() {
+        let edition_2024_config = format!(
+            r#"max_width = 100
+hard_tabs = false
+tab_spaces = 4
+newline_style = "Auto"
+indent_style = "Block"
+use_small_heuristics = "Default"
+fn_call_width = 60
+attr_fn_like_width = 70
+struct_lit_width = 18
+struct_variant_width = 35
+array_width = 60
+chain_width = 60
+single_line_if_else_max_width = 50
+single_line_let_else_max_width = 50
+wrap_comments = false
+format_code_in_doc_comments = false
+doc_comment_code_block_width = 100
+comment_width = 80
+normalize_comments = false
+normalize_doc_attributes = false
+format_strings = false
+format_macro_matchers = false
+format_macro_bodies = true
+skip_macro_invocations = []
+hex_literal_case = "Preserve"
+empty_item_single_line = true
+struct_lit_single_line = true
+fn_single_line = false
+where_single_line = false
+imports_indent = "Block"
+imports_layout = "Mixed"
+imports_granularity = "Preserve"
+group_imports = "Preserve"
+reorder_imports = true
+reorder_modules = true
+reorder_impl_items = false
+type_punctuation_density = "Wide"
+space_before_colon = false
+space_after_colon = true
+spaces_around_ranges = false
+binop_separator = "Front"
+remove_nested_parens = true
+combine_control_expr = true
+short_array_element_width_threshold = 10
+overflow_delimited_expr = true
+struct_field_align_threshold = 0
+enum_discrim_align_threshold = 0
+match_arm_blocks = true
+match_arm_leading_pipes = "Never"
+force_multiline_blocks = false
+fn_params_layout = "Tall"
+brace_style = "SameLineWhere"
+control_brace_style = "AlwaysSameLine"
+trailing_semicolon = true
+trailing_comma = "Vertical"
+match_block_trailing_comma = false
+blank_lines_upper_bound = 1
+blank_lines_lower_bound = 0
+edition = "2015"
+style_edition = "2024"
+version = "Two"
+inline_attribute_width = 0
+format_generated_files = true
+generated_marker_line_search_limit = 5
+merge_derives = true
+use_try_shorthand = false
+use_field_init_shorthand = false
+force_explicit_abi = true
+condense_wildcard_suffixes = false
+color = "Auto"
+required_version = "{}"
+unstable_features = false
+disable_all_formatting = false
+skip_children = false
+show_parse_errors = true
+error_on_line_overflow = false
+error_on_unformatted = false
+ignore = []
+emit_mode = "Files"
+make_backup = false
+"#,
+            env!("CARGO_PKG_VERSION")
+        );
+        let toml = Config::default_with_style_edition(StyleEdition::Edition2024)
+            .all_options()
+            .to_toml()
+            .unwrap();
+        assert_eq!(&toml, &edition_2024_config);
+    }
+
+    #[test]
+    fn test_editions_2015_2018_2021_identical() {
+        let get_edition_toml = |style_edition: StyleEdition| {
+            Config::default_with_style_edition(style_edition)
+                .all_options()
+                .to_toml()
+                .unwrap()
+        };
+        let edition2015 = get_edition_toml(StyleEdition::Edition2015);
+        let edition2018 = get_edition_toml(StyleEdition::Edition2018);
+        let edition2021 = get_edition_toml(StyleEdition::Edition2021);
+        assert_eq!(edition2015, edition2018);
+        assert_eq!(edition2018, edition2021);
+    }
+
     #[stable_only_test]
     #[test]
     fn test_as_not_nightly_channel() {
@@ -730,11 +933,26 @@ make_backup = false
     #[nightly_only_test]
     #[test]
     fn test_unstable_from_toml() {
-        let config = Config::from_toml("unstable_features = true", Path::new("")).unwrap();
+        let config =
+            Config::from_toml("unstable_features = true", Path::new("./rustfmt.toml")).unwrap();
         assert_eq!(config.was_set().unstable_features(), true);
         assert_eq!(config.unstable_features(), true);
     }
 
+    #[test]
+    fn test_set_cli() {
+        let mut config = Config::default();
+        assert_eq!(config.was_set().edition(), false);
+        assert_eq!(config.was_set_cli().edition(), false);
+        config.set().edition(Edition::Edition2021);
+        assert_eq!(config.was_set().edition(), false);
+        assert_eq!(config.was_set_cli().edition(), false);
+        config.set_cli().edition(Edition::Edition2021);
+        assert_eq!(config.was_set().edition(), false);
+        assert_eq!(config.was_set_cli().edition(), true);
+        assert_eq!(config.was_set_cli().emit_mode(), false);
+    }
+
     #[cfg(test)]
     mod deprecated_option_merge_imports {
         use super::*;
@@ -746,7 +964,7 @@ make_backup = false
                 unstable_features = true
                 merge_imports = true
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.imports_granularity(), ImportGranularity::Crate);
         }
 
@@ -758,7 +976,7 @@ make_backup = false
                 merge_imports = true
                 imports_granularity = "Preserve"
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.imports_granularity(), ImportGranularity::Preserve);
         }
 
@@ -769,7 +987,7 @@ make_backup = false
                 unstable_features = true
                 merge_imports = true
             "#;
-            let mut config = Config::from_toml(toml, Path::new("")).unwrap();
+            let mut config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             config.override_value("imports_granularity", "Preserve");
             assert_eq!(config.imports_granularity(), ImportGranularity::Preserve);
         }
@@ -781,7 +999,7 @@ make_backup = false
                 unstable_features = true
                 imports_granularity = "Module"
             "#;
-            let mut config = Config::from_toml(toml, Path::new("")).unwrap();
+            let mut config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             config.override_value("merge_imports", "true");
             // no effect: the new option always takes precedence
             assert_eq!(config.imports_granularity(), ImportGranularity::Module);
@@ -798,7 +1016,7 @@ make_backup = false
                 use_small_heuristics = "Default"
                 max_width = 200
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.array_width(), 120);
             assert_eq!(config.attr_fn_like_width(), 140);
             assert_eq!(config.chain_width(), 120);
@@ -814,7 +1032,7 @@ make_backup = false
                 use_small_heuristics = "Max"
                 max_width = 120
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.array_width(), 120);
             assert_eq!(config.attr_fn_like_width(), 120);
             assert_eq!(config.chain_width(), 120);
@@ -830,11 +1048,11 @@ make_backup = false
                 use_small_heuristics = "Off"
                 max_width = 100
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
-            assert_eq!(config.array_width(), usize::max_value());
-            assert_eq!(config.attr_fn_like_width(), usize::max_value());
-            assert_eq!(config.chain_width(), usize::max_value());
-            assert_eq!(config.fn_call_width(), usize::max_value());
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
+            assert_eq!(config.array_width(), usize::MAX);
+            assert_eq!(config.attr_fn_like_width(), usize::MAX);
+            assert_eq!(config.chain_width(), usize::MAX);
+            assert_eq!(config.fn_call_width(), usize::MAX);
             assert_eq!(config.single_line_if_else_max_width(), 0);
             assert_eq!(config.struct_lit_width(), 0);
             assert_eq!(config.struct_variant_width(), 0);
@@ -852,7 +1070,7 @@ make_backup = false
                 struct_lit_width = 30
                 struct_variant_width = 34
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.array_width(), 20);
             assert_eq!(config.attr_fn_like_width(), 40);
             assert_eq!(config.chain_width(), 20);
@@ -874,7 +1092,7 @@ make_backup = false
                 struct_lit_width = 30
                 struct_variant_width = 34
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.array_width(), 20);
             assert_eq!(config.attr_fn_like_width(), 40);
             assert_eq!(config.chain_width(), 20);
@@ -896,7 +1114,7 @@ make_backup = false
                 struct_lit_width = 30
                 struct_variant_width = 34
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.array_width(), 20);
             assert_eq!(config.attr_fn_like_width(), 40);
             assert_eq!(config.chain_width(), 20);
@@ -912,7 +1130,7 @@ make_backup = false
                 max_width = 90
                 fn_call_width = 95
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.fn_call_width(), 90);
         }
 
@@ -922,7 +1140,7 @@ make_backup = false
                 max_width = 80
                 attr_fn_like_width = 90
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.attr_fn_like_width(), 80);
         }
 
@@ -932,7 +1150,7 @@ make_backup = false
                 max_width = 78
                 struct_lit_width = 90
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.struct_lit_width(), 78);
         }
 
@@ -942,7 +1160,7 @@ make_backup = false
                 max_width = 80
                 struct_variant_width = 90
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.struct_variant_width(), 80);
         }
 
@@ -952,7 +1170,7 @@ make_backup = false
                 max_width = 60
                 array_width = 80
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.array_width(), 60);
         }
 
@@ -962,7 +1180,7 @@ make_backup = false
                 max_width = 80
                 chain_width = 90
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.chain_width(), 80);
         }
 
@@ -972,7 +1190,7 @@ make_backup = false
                 max_width = 70
                 single_line_if_else_max_width = 90
             "#;
-            let config = Config::from_toml(toml, Path::new("")).unwrap();
+            let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
             assert_eq!(config.single_line_if_else_max_width(), 70);
         }
 
diff --git a/src/tools/rustfmt/src/config/options.rs b/src/tools/rustfmt/src/config/options.rs
index 3c5c713a33a..c526b5c678f 100644
--- a/src/tools/rustfmt/src/config/options.rs
+++ b/src/tools/rustfmt/src/config/options.rs
@@ -1,6 +1,6 @@
 #![allow(unused_imports)]
 
-use std::collections::{hash_set, HashSet};
+use std::collections::{HashSet, hash_set};
 use std::fmt;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
@@ -11,8 +11,10 @@ use serde::de::{SeqAccess, Visitor};
 use serde::ser::SerializeSeq;
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
 
-use crate::config::lists::*;
 use crate::config::Config;
+use crate::config::file_lines::FileLines;
+use crate::config::lists::*;
+use crate::config::macro_names::MacroSelectors;
 
 #[config_type]
 pub enum NewlineStyle {
@@ -253,12 +255,12 @@ impl WidthHeuristics {
     // Using this WidthHeuristics means we ignore heuristics.
     pub fn null() -> WidthHeuristics {
         WidthHeuristics {
-            fn_call_width: usize::max_value(),
-            attr_fn_like_width: usize::max_value(),
+            fn_call_width: usize::MAX,
+            attr_fn_like_width: usize::MAX,
             struct_lit_width: 0,
             struct_variant_width: 0,
-            array_width: usize::max_value(),
-            chain_width: usize::max_value(),
+            array_width: usize::MAX,
+            chain_width: usize::MAX,
             single_line_if_else_max_width: 0,
             single_line_let_else_max_width: 0,
         }
@@ -413,7 +415,13 @@ impl FromStr for IgnoreList {
 /// values in a config with values from the command line.
 pub trait CliOptions {
     fn apply_to(self, config: &mut Config);
+
+    /// It is ok if the returned path doesn't exist or is not canonicalized
+    /// (i.e. the callers are expected to handle such cases).
     fn config_path(&self) -> Option<&Path>;
+    fn edition(&self) -> Option<Edition>;
+    fn style_edition(&self) -> Option<StyleEdition>;
+    fn version(&self) -> Option<Version>;
 }
 
 /// The edition of the syntax and semantics of code (RFC 2052).
@@ -454,6 +462,17 @@ impl From<Edition> for rustc_span::edition::Edition {
     }
 }
 
+impl From<Edition> for StyleEdition {
+    fn from(edition: Edition) -> Self {
+        match edition {
+            Edition::Edition2015 => StyleEdition::Edition2015,
+            Edition::Edition2018 => StyleEdition::Edition2018,
+            Edition::Edition2021 => StyleEdition::Edition2021,
+            Edition::Edition2024 => StyleEdition::Edition2024,
+        }
+    }
+}
+
 impl PartialOrd for Edition {
     fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
         rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
@@ -471,10 +490,11 @@ pub enum MatchArmLeadingPipe {
     Preserve,
 }
 
-/// Defines the default values for each config according to [the style guide].
-/// rustfmt output may differ between style editions.
+/// Defines the default values for each config according to the edition of the
+/// [Style Guide] as per [RFC 3338]. Rustfmt output may differ between Style editions.
 ///
-/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/
+/// [Style Guide]: https://doc.rust-lang.org/nightly/style-guide/
+/// [RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html
 #[config_type]
 pub enum StyleEdition {
     #[value = "2015"]
@@ -491,6 +511,169 @@ pub enum StyleEdition {
     Edition2021,
     #[value = "2024"]
     #[doc_hint = "2024"]
+    #[unstable_variant]
     /// [Edition 2024]().
     Edition2024,
 }
+
+impl From<StyleEdition> for rustc_span::edition::Edition {
+    fn from(edition: StyleEdition) -> Self {
+        match edition {
+            StyleEdition::Edition2015 => Self::Edition2015,
+            StyleEdition::Edition2018 => Self::Edition2018,
+            StyleEdition::Edition2021 => Self::Edition2021,
+            StyleEdition::Edition2024 => Self::Edition2024,
+        }
+    }
+}
+
+impl PartialOrd for StyleEdition {
+    fn partial_cmp(&self, other: &StyleEdition) -> Option<std::cmp::Ordering> {
+        rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
+    }
+}
+
+/// Defines unit structs to implement `StyleEditionDefault` for.
+#[macro_export]
+macro_rules! config_option_with_style_edition_default {
+    ($name:ident, $config_ty:ty, _ => $default:expr) => {
+        #[allow(unreachable_pub)]
+        pub struct $name;
+        $crate::style_edition_default!($name, $config_ty, _ => $default);
+    };
+    ($name:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => {
+        pub struct $name;
+        $crate::style_edition_default!(
+            $name,
+            $config_ty,
+            Edition2024 => $default_2024,
+            _ => $default_2015
+        );
+    };
+    (
+        $($name:ident, $config_ty:ty, $(Edition2024 => $default_2024:expr,)? _ => $default:expr);*
+        $(;)*
+    ) => {
+        $(
+            config_option_with_style_edition_default!(
+                $name, $config_ty, $(Edition2024 => $default_2024,)? _ => $default
+            );
+        )*
+    };
+}
+
+// TODO(ytmimi) Some of the configuration values have a `Config` suffix, while others don't.
+// I chose to add a `Config` suffix in cases where a type for the config option was already
+// defined. For example, `NewlineStyle` and `NewlineStyleConfig`. There was some discussion
+// about using the `Config` suffix more consistently.
+config_option_with_style_edition_default!(
+    // Fundamental stuff
+    MaxWidth, usize, _ => 100;
+    HardTabs, bool, _ => false;
+    TabSpaces, usize, _ => 4;
+    NewlineStyleConfig, NewlineStyle, _ => NewlineStyle::Auto;
+    IndentStyleConfig, IndentStyle, _ => IndentStyle::Block;
+
+    // Width Heuristics
+    UseSmallHeuristics, Heuristics, _ => Heuristics::Default;
+    WidthHeuristicsConfig, WidthHeuristics, _ => WidthHeuristics::scaled(100);
+    FnCallWidth, usize, _ => 60;
+    AttrFnLikeWidth, usize, _ => 70;
+    StructLitWidth, usize, _ => 18;
+    StructVariantWidth, usize, _ => 35;
+    ArrayWidth, usize, _ => 60;
+    ChainWidth, usize, _ => 60;
+    SingleLineIfElseMaxWidth, usize, _ => 50;
+    SingleLineLetElseMaxWidth, usize, _ => 50;
+
+    // Comments. macros, and strings
+    WrapComments, bool, _ => false;
+    FormatCodeInDocComments, bool, _ => false;
+    DocCommentCodeBlockWidth, usize, _ => 100;
+    CommentWidth, usize, _ => 80;
+    NormalizeComments, bool, _ => false;
+    NormalizeDocAttributes, bool, _ => false;
+    FormatStrings, bool, _ => false;
+    FormatMacroMatchers, bool, _ => false;
+    FormatMacroBodies, bool, _ => true;
+    SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default();
+    HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve;
+
+    // Single line expressions and items
+    EmptyItemSingleLine, bool, _ => true;
+    StructLitSingleLine, bool, _ => true;
+    FnSingleLine, bool, _ => false;
+    WhereSingleLine, bool, _ => false;
+
+    // Imports
+    ImportsIndent, IndentStyle, _ => IndentStyle::Block;
+    ImportsLayout, ListTactic, _ => ListTactic::Mixed;
+    ImportsGranularityConfig, ImportGranularity, _ => ImportGranularity::Preserve;
+    GroupImportsTacticConfig, GroupImportsTactic, _ => GroupImportsTactic::Preserve;
+    MergeImports, bool, _ => false;
+
+    // Ordering
+    ReorderImports, bool, _ => true;
+    ReorderModules, bool, _ => true;
+    ReorderImplItems, bool, _ => false;
+
+    // Spaces around punctuation
+    TypePunctuationDensity, TypeDensity, _ => TypeDensity::Wide;
+    SpaceBeforeColon, bool, _ => false;
+    SpaceAfterColon, bool, _ => true;
+    SpacesAroundRanges, bool, _ => false;
+    BinopSeparator, SeparatorPlace, _ => SeparatorPlace::Front;
+
+    // Misc.
+    RemoveNestedParens, bool, _ => true;
+    CombineControlExpr, bool, _ => true;
+    ShortArrayElementWidthThreshold, usize, _ => 10;
+    OverflowDelimitedExpr, bool, Edition2024 => true, _ => false;
+    StructFieldAlignThreshold, usize, _ => 0;
+    EnumDiscrimAlignThreshold, usize, _ => 0;
+    MatchArmBlocks, bool, _ => true;
+    MatchArmLeadingPipeConfig, MatchArmLeadingPipe, _ => MatchArmLeadingPipe::Never;
+    ForceMultilineBlocks, bool, _ => false;
+    FnArgsLayout, Density, _ => Density::Tall;
+    FnParamsLayout, Density, _ => Density::Tall;
+    BraceStyleConfig, BraceStyle, _ => BraceStyle::SameLineWhere;
+    ControlBraceStyleConfig, ControlBraceStyle, _ => ControlBraceStyle::AlwaysSameLine;
+    TrailingSemicolon, bool, _ => true;
+    TrailingComma, SeparatorTactic, _ => SeparatorTactic::Vertical;
+    MatchBlockTrailingComma, bool, _ => false;
+    BlankLinesUpperBound, usize, _ => 1;
+    BlankLinesLowerBound, usize, _ => 0;
+    EditionConfig, Edition, _ => Edition::Edition2015;
+    StyleEditionConfig, StyleEdition,
+        Edition2024 =>  StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
+    VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One;
+    InlineAttributeWidth, usize, _ => 0;
+    FormatGeneratedFiles, bool, _ => true;
+    GeneratedMarkerLineSearchLimit, usize, _ => 5;
+
+    // Options that can change the source code beyond whitespace/blocks (somewhat linty things)
+    MergeDerives, bool, _ => true;
+    UseTryShorthand, bool, _ => false;
+    UseFieldInitShorthand, bool, _ => false;
+    ForceExplicitAbi, bool, _ => true;
+    CondenseWildcardSuffixes, bool, _ => false;
+
+    // Control options (changes the operation of rustfmt, rather than the formatting)
+    ColorConfig, Color, _ => Color::Auto;
+    RequiredVersion, String, _ => env!("CARGO_PKG_VERSION").to_owned();
+    UnstableFeatures, bool, _ => false;
+    DisableAllFormatting, bool, _ => false;
+    SkipChildren, bool, _ => false;
+    HideParseErrors, bool, _ => false;
+    ShowParseErrors, bool, _ => true;
+    ErrorOnLineOverflow, bool, _ => false;
+    ErrorOnUnformatted, bool, _ => false;
+    Ignore, IgnoreList, _ => IgnoreList::default();
+
+    // Not user-facing
+    Verbose, Verbosity, _ => Verbosity::Normal;
+    FileLinesConfig, FileLines, _ => FileLines::all();
+    EmitModeConfig, EmitMode, _ => EmitMode::Files;
+    MakeBackup, bool, _ => false;
+    PrintMisformattedFileNames, bool, _ => false;
+);
diff --git a/src/tools/rustfmt/src/config/style_edition.rs b/src/tools/rustfmt/src/config/style_edition.rs
index 7b3ea3bc119..c34eca9c8e1 100644
--- a/src/tools/rustfmt/src/config/style_edition.rs
+++ b/src/tools/rustfmt/src/config/style_edition.rs
@@ -2,7 +2,7 @@ use crate::config::StyleEdition;
 
 /// Defines the default value for the given style edition
 #[allow(dead_code)]
-pub(crate) trait StyleEditionDefault {
+pub trait StyleEditionDefault {
     type ConfigType;
     fn style_edition_default(style_edition: StyleEdition) -> Self::ConfigType;
 }
diff --git a/src/tools/rustfmt/src/emitter/checkstyle.rs b/src/tools/rustfmt/src/emitter/checkstyle.rs
index 9385ae59a06..c320c16bd1d 100644
--- a/src/tools/rustfmt/src/emitter/checkstyle.rs
+++ b/src/tools/rustfmt/src/emitter/checkstyle.rs
@@ -1,6 +1,6 @@
 use self::xml::XmlEscaped;
 use super::*;
-use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
+use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff};
 
 mod xml;
 
diff --git a/src/tools/rustfmt/src/emitter/json.rs b/src/tools/rustfmt/src/emitter/json.rs
index 084f565804c..a99626f783d 100644
--- a/src/tools/rustfmt/src/emitter/json.rs
+++ b/src/tools/rustfmt/src/emitter/json.rs
@@ -1,5 +1,5 @@
 use super::*;
-use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
+use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff};
 use serde::Serialize;
 use serde_json::to_string as to_json_string;
 
diff --git a/src/tools/rustfmt/src/emitter/modified_lines.rs b/src/tools/rustfmt/src/emitter/modified_lines.rs
index 81f0a31b974..0d5124bc33e 100644
--- a/src/tools/rustfmt/src/emitter/modified_lines.rs
+++ b/src/tools/rustfmt/src/emitter/modified_lines.rs
@@ -1,5 +1,5 @@
 use super::*;
-use crate::rustfmt_diff::{make_diff, ModifiedLines};
+use crate::rustfmt_diff::{ModifiedLines, make_diff};
 
 #[derive(Debug, Default)]
 pub(crate) struct ModifiedLinesEmitter;
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index 75c75c523b0..77c9818b66b 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -3,33 +3,33 @@ use std::cmp::min;
 
 use itertools::Itertools;
 use rustc_ast::token::{Delimiter, Lit, LitKind};
-use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind};
+use rustc_ast::{ForLoopKind, MatchKind, ast, ptr, token};
 use rustc_span::{BytePos, Span};
 use tracing::debug;
 
 use crate::chains::rewrite_chain;
 use crate::closures;
 use crate::comment::{
-    combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment,
-    rewrite_missing_comment, CharClasses, FindUncommented,
+    CharClasses, FindUncommented, combine_strs_with_missing_comments, contains_comment,
+    recover_comment_removed, rewrite_comment, rewrite_missing_comment,
 };
 use crate::config::lists::*;
-use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, Version};
+use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, StyleEdition};
 use crate::lists::{
-    definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape,
-    struct_lit_tactic, write_list, ListFormatting, Separator,
+    ListFormatting, Separator, definitive_tactic, itemize_list, shape_for_tactic,
+    struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list,
 };
-use crate::macros::{rewrite_macro, MacroPosition};
+use crate::macros::{MacroPosition, rewrite_macro};
 use crate::matches::rewrite_match;
 use crate::overflow::{self, IntoOverflowableItem, OverflowableItem};
-use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts};
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::pairs::{PairParts, rewrite_all_pairs, rewrite_pair};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::{Indent, Shape};
 use crate::source_map::{LineRangeUtils, SpanUtils};
 use crate::spanned::Spanned;
 use crate::stmt;
-use crate::string::{rewrite_string, StringFormat};
-use crate::types::{rewrite_path, PathContext};
+use crate::string::{StringFormat, rewrite_string};
+use crate::types::{PathContext, rewrite_path};
 use crate::utils::{
     colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with,
     inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes,
@@ -40,6 +40,10 @@ use crate::visitor::FmtVisitor;
 
 impl Rewrite for ast::Expr {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         format_expr(self, ExprType::SubExpression, context, shape)
     }
 }
@@ -59,14 +63,14 @@ pub(crate) fn format_expr(
     expr_type: ExprType,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
-    skip_out_of_file_lines_range!(context, expr.span);
+) -> RewriteResult {
+    skip_out_of_file_lines_range_err!(context, expr.span);
 
     if contains_skip(&*expr.attrs) {
-        return Some(context.snippet(expr.span()).to_owned());
+        return Ok(context.snippet(expr.span()).to_owned());
     }
     let shape = if expr_type == ExprType::Statement && semicolon_for_expr(context, expr) {
-        shape.sub_width(1)?
+        shape.sub_width(1).max_width_error(shape.width, expr.span)?
     } else {
         shape
     };
@@ -82,25 +86,25 @@ pub(crate) fn format_expr(
             None,
         ),
         ast::ExprKind::Lit(token_lit) => {
-            if let Some(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) {
-                Some(expr_rw)
+            if let Ok(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) {
+                Ok(expr_rw)
             } else {
                 if let LitKind::StrRaw(_) = token_lit.kind {
-                    Some(context.snippet(expr.span).trim().into())
+                    Ok(context.snippet(expr.span).trim().into())
                 } else {
-                    None
+                    Err(RewriteError::Unknown)
                 }
             }
         }
         ast::ExprKind::Call(ref callee, ref args) => {
             let inner_span = mk_sp(callee.span.hi(), expr.span.hi());
-            let callee_str = callee.rewrite(context, shape)?;
+            let callee_str = callee.rewrite_result(context, shape)?;
             rewrite_call(context, &callee_str, args, inner_span, shape)
         }
         ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span),
         ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
             // FIXME: format comments between operands and operator
-            rewrite_all_pairs(expr, shape, context).or_else(|| {
+            rewrite_all_pairs(expr, shape, context).or_else(|_| {
                 rewrite_pair(
                     &**lhs,
                     &**rhs,
@@ -138,7 +142,8 @@ pub(crate) fn format_expr(
         | ast::ExprKind::ForLoop { .. }
         | ast::ExprKind::Loop(..)
         | ast::ExprKind::While(..) => to_control_flow(expr, expr_type)
-            .and_then(|control_flow| control_flow.rewrite(context, shape)),
+            .unknown_error()
+            .and_then(|control_flow| control_flow.rewrite_result(context, shape)),
         ast::ExprKind::ConstBlock(ref anon_const) => {
             let rewrite = match anon_const.value.kind {
                 ast::ExprKind::Block(ref block, opt_label) => {
@@ -148,20 +153,20 @@ pub(crate) fn format_expr(
                     // See https://github.com/rust-lang/rustfmt/issues/6158
                     rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)?
                 }
-                _ => anon_const.rewrite(context, shape)?,
+                _ => anon_const.rewrite_result(context, shape)?,
             };
-            Some(format!("const {}", rewrite))
+            Ok(format!("const {}", rewrite))
         }
         ast::ExprKind::Block(ref block, opt_label) => {
             match expr_type {
                 ExprType::Statement => {
                     if is_unsafe_block(block) {
                         rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)
-                    } else if let rw @ Some(_) =
+                    } else if let Some(rw) =
                         rewrite_empty_block(context, block, Some(&expr.attrs), opt_label, "", shape)
                     {
                         // Rewrite block without trying to put it in a single line.
-                        rw
+                        Ok(rw)
                     } else {
                         let prefix = block_prefix(context, block, shape)?;
 
@@ -198,7 +203,7 @@ pub(crate) fn format_expr(
                 Some(label) => format!(" {}", label.ident),
                 None => String::new(),
             };
-            Some(format!("continue{id_str}"))
+            Ok(format!("continue{id_str}"))
         }
         ast::ExprKind::Break(ref opt_label, ref opt_expr) => {
             let id_str = match *opt_label {
@@ -209,14 +214,14 @@ pub(crate) fn format_expr(
             if let Some(ref expr) = *opt_expr {
                 rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape)
             } else {
-                Some(format!("break{id_str}"))
+                Ok(format!("break{id_str}"))
             }
         }
         ast::ExprKind::Yield(ref opt_expr) => {
             if let Some(ref expr) = *opt_expr {
                 rewrite_unary_prefix(context, "yield ", &**expr, shape)
             } else {
-                Some("yield".to_string())
+                Ok("yield".to_string())
             }
         }
         ast::ExprKind::Closure(ref cl) => closures::rewrite_closure(
@@ -236,20 +241,21 @@ pub(crate) fn format_expr(
         | ast::ExprKind::MethodCall(..)
         | ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape),
         ast::ExprKind::MacCall(ref mac) => {
-            rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| {
+            rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|_| {
                 wrap_str(
                     context.snippet(expr.span).to_owned(),
                     context.config.max_width(),
                     shape,
                 )
+                .max_width_error(shape.width, expr.span)
             })
         }
-        ast::ExprKind::Ret(None) => Some("return".to_owned()),
+        ast::ExprKind::Ret(None) => Ok("return".to_owned()),
         ast::ExprKind::Ret(Some(ref expr)) => {
             rewrite_unary_prefix(context, "return ", &**expr, shape)
         }
         ast::ExprKind::Become(ref expr) => rewrite_unary_prefix(context, "become ", &**expr, shape),
-        ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()),
+        ast::ExprKind::Yeet(None) => Ok("do yeet".to_owned()),
         ast::ExprKind::Yeet(Some(ref expr)) => {
             rewrite_unary_prefix(context, "do yeet ", &**expr, shape)
         }
@@ -344,23 +350,23 @@ pub(crate) fn format_expr(
                     };
                     rewrite_unary_suffix(context, &sp_delim, &*lhs, shape)
                 }
-                (None, None) => Some(delim.to_owned()),
+                (None, None) => Ok(delim.to_owned()),
             }
         }
         // We do not format these expressions yet, but they should still
         // satisfy our width restrictions.
         // Style Guide RFC for InlineAsm variant pending
         // https://github.com/rust-dev-tools/fmt-rfcs/issues/152
-        ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()),
+        ast::ExprKind::InlineAsm(..) => Ok(context.snippet(expr.span).to_owned()),
         ast::ExprKind::TryBlock(ref block) => {
-            if let rw @ Some(_) =
+            if let rw @ Ok(_) =
                 rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape)
             {
                 rw
             } else {
                 // 9 = `try `
                 let budget = shape.width.saturating_sub(9);
-                Some(format!(
+                Ok(format!(
                     "{}{}",
                     "try ",
                     rewrite_block(
@@ -379,7 +385,7 @@ pub(crate) fn format_expr(
             } else {
                 ""
             };
-            if let rw @ Some(_) = rewrite_single_line_block(
+            if let rw @ Ok(_) = rewrite_single_line_block(
                 context,
                 format!("{kind} {mover}").as_str(),
                 block,
@@ -391,7 +397,7 @@ pub(crate) fn format_expr(
             } else {
                 // 6 = `async `
                 let budget = shape.width.saturating_sub(6);
-                Some(format!(
+                Ok(format!(
                     "{kind} {mover}{}",
                     rewrite_block(
                         block,
@@ -403,7 +409,7 @@ pub(crate) fn format_expr(
                 ))
             }
         }
-        ast::ExprKind::Underscore => Some("_".to_owned()),
+        ast::ExprKind::Underscore => Ok("_".to_owned()),
         ast::ExprKind::FormatArgs(..)
         | ast::ExprKind::Type(..)
         | ast::ExprKind::IncludedBytes(..)
@@ -412,16 +418,16 @@ pub(crate) fn format_expr(
             // rustfmt tries to parse macro arguments when formatting macros, so it's not totally
             // impossible for rustfmt to come across one of these nodes when formatting a file.
             // Also, rustfmt might get passed the output from `-Zunpretty=expanded`.
-            None
+            Err(RewriteError::Unknown)
         }
-        ast::ExprKind::Err(_) | ast::ExprKind::Dummy => None,
+        ast::ExprKind::Err(_) | ast::ExprKind::Dummy => Err(RewriteError::Unknown),
     };
 
     expr_rw
-        .and_then(|expr_str| recover_comment_removed(expr_str, expr.span, context))
+        .map(|expr_str| recover_comment_removed(expr_str, expr.span, context))
         .and_then(|expr_str| {
             let attrs = outer_attributes(&expr.attrs);
-            let attrs_str = attrs.rewrite(context, shape)?;
+            let attrs_str = attrs.rewrite_result(context, shape)?;
             let span = mk_sp(
                 attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()),
                 expr.span.lo(),
@@ -438,7 +444,7 @@ pub(crate) fn rewrite_array<'a, T: 'a + IntoOverflowableItem<'a>>(
     shape: Shape,
     force_separator_tactic: Option<SeparatorTactic>,
     delim_token: Option<Delimiter>,
-) -> Option<String> {
+) -> RewriteResult {
     overflow::rewrite_with_square_brackets(
         context,
         name,
@@ -488,17 +494,20 @@ fn rewrite_empty_block(
     None
 }
 
-fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> Option<String> {
-    Some(match block.rules {
+fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> RewriteResult {
+    Ok(match block.rules {
         ast::BlockCheckMode::Unsafe(..) => {
             let snippet = context.snippet(block.span);
-            let open_pos = snippet.find_uncommented("{")?;
+            let open_pos = snippet.find_uncommented("{").unknown_error()?;
             // Extract comment between unsafe and block start.
             let trimmed = &snippet[6..open_pos].trim();
 
             if !trimmed.is_empty() {
                 // 9 = "unsafe  {".len(), 7 = "unsafe ".len()
-                let budget = shape.width.checked_sub(9)?;
+                let budget = shape
+                    .width
+                    .checked_sub(9)
+                    .max_width_error(shape.width, block.span)?;
                 format!(
                     "unsafe {} ",
                     rewrite_comment(
@@ -523,17 +532,19 @@ fn rewrite_single_line_block(
     attrs: Option<&[ast::Attribute]>,
     label: Option<ast::Label>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) {
-        let expr_shape = shape.offset_left(last_line_width(prefix))?;
-        let expr_str = block_expr.rewrite(context, expr_shape)?;
+        let expr_shape = shape
+            .offset_left(last_line_width(prefix))
+            .max_width_error(shape.width, block_expr.span())?;
+        let expr_str = block_expr.rewrite_result(context, expr_shape)?;
         let label_str = rewrite_label(context, label);
         let result = format!("{prefix}{label_str}{{ {expr_str} }}");
         if result.len() <= shape.width && !result.contains('\n') {
-            return Some(result);
+            return Ok(result);
         }
     }
-    None
+    Err(RewriteError::Unknown)
 }
 
 pub(crate) fn rewrite_block_with_visitor(
@@ -544,9 +555,9 @@ pub(crate) fn rewrite_block_with_visitor(
     label: Option<ast::Label>,
     shape: Shape,
     has_braces: bool,
-) -> Option<String> {
-    if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, prefix, shape) {
-        return rw;
+) -> RewriteResult {
+    if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, prefix, shape) {
+        return Ok(rw_str);
     }
 
     let mut visitor = FmtVisitor::from_context(context);
@@ -555,7 +566,7 @@ pub(crate) fn rewrite_block_with_visitor(
     match (block.rules, label) {
         (ast::BlockCheckMode::Unsafe(..), _) | (ast::BlockCheckMode::Default, Some(_)) => {
             let snippet = context.snippet(block.span);
-            let open_pos = snippet.find_uncommented("{")?;
+            let open_pos = snippet.find_uncommented("{").unknown_error()?;
             visitor.last_pos = block.span.lo() + BytePos(open_pos as u32)
         }
         (ast::BlockCheckMode::Default, None) => visitor.last_pos = block.span.lo(),
@@ -569,11 +580,15 @@ pub(crate) fn rewrite_block_with_visitor(
         .skipped_range
         .borrow_mut()
         .append(&mut visitor_context.skipped_range.borrow_mut());
-    Some(format!("{}{}{}", prefix, label_str, visitor.buffer))
+    Ok(format!("{}{}{}", prefix, label_str, visitor.buffer))
 }
 
 impl Rewrite for ast::Block {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         rewrite_block(self, None, None, context, shape)
     }
 }
@@ -584,7 +599,7 @@ fn rewrite_block(
     label: Option<ast::Label>,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     rewrite_block_inner(block, attrs, label, true, context, shape)
 }
 
@@ -595,27 +610,24 @@ fn rewrite_block_inner(
     allow_single_line: bool,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let prefix = block_prefix(context, block, shape)?;
 
     // shape.width is used only for the single line case: either the empty block `{}`,
     // or an unsafe expression `unsafe { e }`.
-    if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) {
-        return rw;
+    if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) {
+        return Ok(rw_str);
     }
 
-    let result = rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true);
-    if let Some(ref result_str) = result {
-        if allow_single_line && result_str.lines().count() <= 3 {
-            if let rw @ Some(_) =
-                rewrite_single_line_block(context, &prefix, block, attrs, label, shape)
-            {
-                return rw;
-            }
+    let result_str =
+        rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true)?;
+    if allow_single_line && result_str.lines().count() <= 3 {
+        if let rw @ Ok(_) = rewrite_single_line_block(context, &prefix, block, attrs, label, shape)
+        {
+            return rw;
         }
     }
-
-    result
+    Ok(result_str)
 }
 
 /// Rewrite the divergent block of a `let-else` statement.
@@ -624,7 +636,7 @@ pub(crate) fn rewrite_let_else_block(
     allow_single_line: bool,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     rewrite_block_inner(block, None, None, allow_single_line, context, shape)
 }
 
@@ -648,6 +660,7 @@ pub(crate) fn rewrite_cond(
                 String::from("\n") + &shape.indent.block_only().to_string(context.config);
             control_flow
                 .rewrite_cond(context, shape, &alt_block_sep)
+                .ok()
                 .map(|rw| rw.0)
         }),
     }
@@ -872,10 +885,12 @@ impl<'a> ControlFlow<'a> {
         expr: &ast::Expr,
         shape: Shape,
         offset: usize,
-    ) -> Option<String> {
+    ) -> RewriteResult {
         debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, self.pat, expr);
 
-        let cond_shape = shape.offset_left(offset)?;
+        let cond_shape = shape
+            .offset_left(offset)
+            .max_width_error(shape.width, expr.span)?;
         if let Some(pat) = self.pat {
             let matcher = if self.matcher.is_empty() {
                 self.matcher.to_owned()
@@ -883,9 +898,10 @@ impl<'a> ControlFlow<'a> {
                 format!("{} ", self.matcher)
             };
             let pat_shape = cond_shape
-                .offset_left(matcher.len())?
-                .sub_width(self.connector.len())?;
-            let pat_string = pat.rewrite(context, pat_shape)?;
+                .offset_left(matcher.len())
+                .and_then(|s| s.sub_width(self.connector.len()))
+                .max_width_error(cond_shape.width, pat.span)?;
+            let pat_string = pat.rewrite_result(context, pat_shape)?;
             let comments_lo = context
                 .snippet_provider
                 .span_after(self.span.with_lo(pat.span.hi()), self.connector.trim());
@@ -902,10 +918,10 @@ impl<'a> ControlFlow<'a> {
             );
         }
 
-        let expr_rw = expr.rewrite(context, cond_shape);
+        let expr_rw = expr.rewrite_result(context, cond_shape);
         // The expression may (partially) fit on the current line.
         // We do not allow splitting between `if` and condition.
-        if self.keyword == "if" || expr_rw.is_some() {
+        if self.keyword == "if" || expr_rw.is_ok() {
             return expr_rw;
         }
 
@@ -914,7 +930,7 @@ impl<'a> ControlFlow<'a> {
             .block_indent(context.config.tab_spaces())
             .with_max_width(context.config);
         let nested_indent_str = nested_shape.indent.to_string_with_newline(context.config);
-        expr.rewrite(context, nested_shape)
+        expr.rewrite_result(context, nested_shape)
             .map(|expr_rw| format!("{}{}", nested_indent_str, expr_rw))
     }
 
@@ -923,7 +939,7 @@ impl<'a> ControlFlow<'a> {
         context: &RewriteContext<'_>,
         shape: Shape,
         alt_block_sep: &str,
-    ) -> Option<(String, usize)> {
+    ) -> Result<(String, usize), RewriteError> {
         // Do not take the rhs overhead from the upper expressions into account
         // when rewriting pattern.
         let new_width = context.budget(shape.used_width());
@@ -934,7 +950,9 @@ impl<'a> ControlFlow<'a> {
         let constr_shape = if self.nested_if {
             // We are part of an if-elseif-else chain. Our constraints are tightened.
             // 7 = "} else " .len()
-            fresh_shape.offset_left(7)?
+            fresh_shape
+                .offset_left(7)
+                .max_width_error(fresh_shape.width, self.span)?
         } else {
             fresh_shape
         };
@@ -970,7 +988,7 @@ impl<'a> ControlFlow<'a> {
 
             if let Some(cond_str) = trial {
                 if cond_str.len() <= context.config.single_line_if_else_max_width() {
-                    return Some((cond_str, 0));
+                    return Ok((cond_str, 0));
                 }
             }
         }
@@ -1023,7 +1041,7 @@ impl<'a> ControlFlow<'a> {
             label_string.len() + self.keyword.len() + pat_expr_string.len() + 2
         };
 
-        Some((
+        Ok((
             format!(
                 "{}{}{}{}{}",
                 label_string,
@@ -1089,13 +1107,17 @@ pub(crate) fn rewrite_else_kw_with_comments(
 
 impl<'a> Rewrite for ControlFlow<'a> {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         debug!("ControlFlow::rewrite {:?} {:?}", self, shape);
 
         let alt_block_sep = &shape.indent.to_string_with_newline(context.config);
         let (cond_str, used_width) = self.rewrite_cond(context, shape, alt_block_sep)?;
         // If `used_width` is 0, it indicates that whole control flow is written in a single line.
         if used_width == 0 {
-            return Some(cond_str);
+            return Ok(cond_str);
         }
 
         let block_width = shape.width.saturating_sub(used_width);
@@ -1139,7 +1161,7 @@ impl<'a> Rewrite for ControlFlow<'a> {
                         true,
                         mk_sp(else_block.span.lo(), self.span.hi()),
                     )
-                    .rewrite(context, shape)
+                    .rewrite_result(context, shape)
                 }
                 _ => {
                     last_in_chain = true;
@@ -1164,7 +1186,7 @@ impl<'a> Rewrite for ControlFlow<'a> {
             result.push_str(&rewrite?);
         }
 
-        Some(result)
+        Ok(result)
     }
 }
 
@@ -1177,7 +1199,7 @@ fn rewrite_label(context: &RewriteContext<'_>, opt_label: Option<ast::Label>) ->
 
 fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
     match rewrite_missing_comment(span, shape, context) {
-        Some(ref comment) if !comment.is_empty() => Some(format!(
+        Ok(ref comment) if !comment.is_empty() => Some(format!(
             "{indent}{comment}{indent}",
             indent = shape.indent.to_string_with_newline(context.config)
         )),
@@ -1248,7 +1270,7 @@ pub(crate) fn rewrite_literal(
     token_lit: token::Lit,
     span: Span,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     match token_lit.kind {
         token::LitKind::Str => rewrite_string_lit(context, span, shape),
         token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape),
@@ -1256,11 +1278,12 @@ pub(crate) fn rewrite_literal(
             context.snippet(span).to_owned(),
             context.config.max_width(),
             shape,
-        ),
+        )
+        .max_width_error(shape.width, span),
     }
 }
 
-fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> Option<String> {
+fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> RewriteResult {
     let string_lit = context.snippet(span);
 
     if !context.config.format_strings() {
@@ -1268,11 +1291,12 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) ->
             .lines()
             .dropping_back(1)
             .all(|line| line.ends_with('\\'))
-            && context.config.version() == Version::Two
+            && context.config.style_edition() >= StyleEdition::Edition2024
         {
-            return Some(string_lit.to_owned());
+            return Ok(string_lit.to_owned());
         } else {
-            return wrap_str(string_lit.to_owned(), context.config.max_width(), shape);
+            return wrap_str(string_lit.to_owned(), context.config.max_width(), shape)
+                .max_width_error(shape.width, span);
         }
     }
 
@@ -1284,6 +1308,7 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) ->
         &StringFormat::new(shape.visual_indent(0), context.config),
         shape.width.saturating_sub(2),
     )
+    .max_width_error(shape.width, span)
 }
 
 fn rewrite_int_lit(
@@ -1291,7 +1316,7 @@ fn rewrite_int_lit(
     token_lit: token::Lit,
     span: Span,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let symbol = token_lit.symbol.as_str();
 
     if let Some(symbol_stripped) = symbol.strip_prefix("0x") {
@@ -1305,11 +1330,12 @@ fn rewrite_int_lit(
                 format!(
                     "0x{}{}",
                     hex_lit,
-                    token_lit.suffix.map_or(String::new(), |s| s.to_string())
+                    token_lit.suffix.as_ref().map_or("", |s| s.as_str())
                 ),
                 context.config.max_width(),
                 shape,
-            );
+            )
+            .max_width_error(shape.width, span);
         }
     }
 
@@ -1318,6 +1344,7 @@ fn rewrite_int_lit(
         context.config.max_width(),
         shape,
     )
+    .max_width_error(shape.width, span)
 }
 
 fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option<SeparatorTactic> {
@@ -1338,7 +1365,7 @@ pub(crate) fn rewrite_call(
     args: &[ptr::P<ast::Expr>],
     span: Span,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     overflow::rewrite_with_parens(
         context,
         callee,
@@ -1458,7 +1485,7 @@ pub(crate) fn rewrite_paren(
     mut subexpr: &ast::Expr,
     shape: Shape,
     mut span: Span,
-) -> Option<String> {
+) -> RewriteResult {
     debug!("rewrite_paren, shape: {:?}", shape);
 
     // Extract comments within parens.
@@ -1487,11 +1514,14 @@ pub(crate) fn rewrite_paren(
     }
 
     // 1 = `(` and `)`
-    let sub_shape = shape.offset_left(1)?.sub_width(1)?;
-    let subexpr_str = subexpr.rewrite(context, sub_shape)?;
+    let sub_shape = shape
+        .offset_left(1)
+        .and_then(|s| s.sub_width(1))
+        .max_width_error(shape.width, span)?;
+    let subexpr_str = subexpr.rewrite_result(context, sub_shape)?;
     let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//");
     if fits_single_line {
-        Some(format!("({pre_comment}{subexpr_str}{post_comment})"))
+        Ok(format!("({pre_comment}{subexpr_str}{post_comment})"))
     } else {
         rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span)
     }
@@ -1503,12 +1533,12 @@ fn rewrite_paren_in_multi_line(
     shape: Shape,
     pre_span: Span,
     post_span: Span,
-) -> Option<String> {
+) -> RewriteResult {
     let nested_indent = shape.indent.block_indent(context.config);
     let nested_shape = Shape::indented(nested_indent, context.config);
     let pre_comment = rewrite_missing_comment(pre_span, nested_shape, context)?;
     let post_comment = rewrite_missing_comment(post_span, nested_shape, context)?;
-    let subexpr_str = subexpr.rewrite(context, nested_shape)?;
+    let subexpr_str = subexpr.rewrite_result(context, nested_shape)?;
 
     let mut result = String::with_capacity(subexpr_str.len() * 2);
     result.push('(');
@@ -1525,7 +1555,7 @@ fn rewrite_paren_in_multi_line(
     result.push_str(&shape.indent.to_string_with_newline(context.config));
     result.push(')');
 
-    Some(result)
+    Ok(result)
 }
 
 fn rewrite_index(
@@ -1533,8 +1563,8 @@ fn rewrite_index(
     index: &ast::Expr,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
-    let expr_str = expr.rewrite(context, shape)?;
+) -> RewriteResult {
+    let expr_str = expr.rewrite_result(context, shape)?;
 
     let offset = last_line_width(&expr_str) + 1;
     let rhs_overhead = shape.rhs_overhead(context.config);
@@ -1549,37 +1579,45 @@ fn rewrite_index(
                 .and_then(|shape| shape.sub_width(1)),
             IndentStyle::Visual => shape.visual_indent(offset).sub_width(offset + 1),
         }
-    };
-    let orig_index_rw = index_shape.and_then(|s| index.rewrite(context, s));
+    }
+    .max_width_error(shape.width, index.span());
+    let orig_index_rw = index_shape.and_then(|s| index.rewrite_result(context, s));
 
     // Return if index fits in a single line.
     match orig_index_rw {
-        Some(ref index_str) if !index_str.contains('\n') => {
-            return Some(format!("{expr_str}[{index_str}]"));
+        Ok(ref index_str) if !index_str.contains('\n') => {
+            return Ok(format!("{expr_str}[{index_str}]"));
         }
         _ => (),
     }
 
     // Try putting index on the next line and see if it fits in a single line.
     let indent = shape.indent.block_indent(context.config);
-    let index_shape = Shape::indented(indent, context.config).offset_left(1)?;
-    let index_shape = index_shape.sub_width(1 + rhs_overhead)?;
-    let new_index_rw = index.rewrite(context, index_shape);
+    let index_shape = Shape::indented(indent, context.config)
+        .offset_left(1)
+        .max_width_error(shape.width, index.span())?;
+    let index_shape = index_shape
+        .sub_width(1 + rhs_overhead)
+        .max_width_error(index_shape.width, index.span())?;
+    let new_index_rw = index.rewrite_result(context, index_shape);
     match (orig_index_rw, new_index_rw) {
-        (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => Some(format!(
+        (_, Ok(ref new_index_str)) if !new_index_str.contains('\n') => Ok(format!(
             "{}{}[{}]",
             expr_str,
             indent.to_string_with_newline(context.config),
             new_index_str,
         )),
-        (None, Some(ref new_index_str)) => Some(format!(
+        (Err(_), Ok(ref new_index_str)) => Ok(format!(
             "{}{}[{}]",
             expr_str,
             indent.to_string_with_newline(context.config),
             new_index_str,
         )),
-        (Some(ref index_str), _) => Some(format!("{expr_str}[{index_str}]")),
-        _ => None,
+        (Ok(ref index_str), _) => Ok(format!("{expr_str}[{index_str}]")),
+        // When both orig_index_rw and new_index_rw result in errors, we currently propagate the
+        // error from the second attempt since it is more generous with width constraints.
+        // This decision is somewhat arbitrary and is open to change.
+        (Err(_), Err(new_index_rw_err)) => Err(new_index_rw_err),
     }
 }
 
@@ -1596,7 +1634,7 @@ fn rewrite_struct_lit<'a>(
     attrs: &[ast::Attribute],
     span: Span,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     debug!("rewrite_struct_lit: shape {:?}", shape);
 
     enum StructLitField<'a> {
@@ -1606,20 +1644,21 @@ fn rewrite_struct_lit<'a>(
     }
 
     // 2 = " {".len()
-    let path_shape = shape.sub_width(2)?;
+    let path_shape = shape.sub_width(2).max_width_error(shape.width, span)?;
     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} {{}}")),
+        ast::StructRest::None if fields.is_empty() => return Ok(format!("{path_str} {{}}")),
         ast::StructRest::Rest(_) if fields.is_empty() => {
-            return Some(format!("{path_str} {{ .. }}"));
+            return Ok(format!("{path_str} {{ .. }}"));
         }
         ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true,
         _ => false,
     };
 
     // Foo { a: Foo } - indent is +3, width is -5.
-    let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2)?;
+    let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2)
+        .max_width_error(shape.width, span)?;
 
     let one_line_width = h_shape.map_or(0, |shape| shape.width);
     let body_lo = context.snippet_provider.span_after(span, "{");
@@ -1632,7 +1671,8 @@ fn rewrite_struct_lit<'a>(
             v_shape,
             mk_sp(body_lo, span.hi()),
             one_line_width,
-        )?
+        )
+        .unknown_error()?
     } else {
         let field_iter = fields.iter().map(StructLitField::Regular).chain(
             match struct_rest {
@@ -1661,14 +1701,24 @@ fn rewrite_struct_lit<'a>(
         let rewrite = |item: &StructLitField<'_>| match *item {
             StructLitField::Regular(field) => {
                 // The 1 taken from the v_budget is for the comma.
-                rewrite_field(context, field, v_shape.sub_width(1)?, 0)
+                rewrite_field(
+                    context,
+                    field,
+                    v_shape.sub_width(1).max_width_error(v_shape.width, span)?,
+                    0,
+                )
             }
             StructLitField::Base(expr) => {
                 // 2 = ..
-                expr.rewrite(context, v_shape.offset_left(2)?)
-                    .map(|s| format!("..{}", s))
+                expr.rewrite_result(
+                    context,
+                    v_shape
+                        .offset_left(2)
+                        .max_width_error(v_shape.width, span)?,
+                )
+                .map(|s| format!("..{}", s))
             }
-            StructLitField::Rest(_) => Some("..".to_owned()),
+            StructLitField::Rest(_) => Ok("..".to_owned()),
         };
 
         let items = itemize_list(
@@ -1703,7 +1753,7 @@ fn rewrite_struct_lit<'a>(
 
     let fields_str =
         wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?;
-    Some(format!("{path_str} {{{fields_str}}}"))
+    Ok(format!("{path_str} {{{fields_str}}}"))
 
     // FIXME if context.config.indent_style() == Visual, but we run out
     // of space, we should fall back to BlockIndent.
@@ -1716,7 +1766,7 @@ pub(crate) fn wrap_struct_field(
     shape: Shape,
     nested_shape: Shape,
     one_line_width: usize,
-) -> Option<String> {
+) -> RewriteResult {
     let should_vertical = context.config.indent_style() == IndentStyle::Block
         && (fields_str.contains('\n')
             || !context.config.struct_lit_single_line()
@@ -1725,7 +1775,7 @@ pub(crate) fn wrap_struct_field(
     let inner_attrs = &inner_attributes(attrs);
     if inner_attrs.is_empty() {
         if should_vertical {
-            Some(format!(
+            Ok(format!(
                 "{}{}{}",
                 nested_shape.indent.to_string_with_newline(context.config),
                 fields_str,
@@ -1733,13 +1783,13 @@ pub(crate) fn wrap_struct_field(
             ))
         } else {
             // One liner or visual indent.
-            Some(format!(" {fields_str} "))
+            Ok(format!(" {fields_str} "))
         }
     } else {
-        Some(format!(
+        Ok(format!(
             "{}{}{}{}{}",
             nested_shape.indent.to_string_with_newline(context.config),
-            inner_attrs.rewrite(context, shape)?,
+            inner_attrs.rewrite_result(context, shape)?,
             nested_shape.indent.to_string_with_newline(context.config),
             fields_str,
             shape.indent.to_string_with_newline(context.config)
@@ -1756,38 +1806,40 @@ pub(crate) fn rewrite_field(
     field: &ast::ExprField,
     shape: Shape,
     prefix_max_width: usize,
-) -> Option<String> {
+) -> RewriteResult {
     if contains_skip(&field.attrs) {
-        return Some(context.snippet(field.span()).to_owned());
+        return Ok(context.snippet(field.span()).to_owned());
     }
-    let mut attrs_str = field.attrs.rewrite(context, shape)?;
+    let mut attrs_str = field.attrs.rewrite_result(context, shape)?;
     if !attrs_str.is_empty() {
         attrs_str.push_str(&shape.indent.to_string_with_newline(context.config));
     };
     let name = context.snippet(field.ident.span);
     if field.is_shorthand {
-        Some(attrs_str + name)
+        Ok(attrs_str + name)
     } else {
         let mut separator = String::from(struct_lit_field_separator(context.config));
         for _ in 0..prefix_max_width.saturating_sub(name.len()) {
             separator.push(' ');
         }
         let overhead = name.len() + separator.len();
-        let expr_shape = shape.offset_left(overhead)?;
-        let expr = field.expr.rewrite(context, expr_shape);
+        let expr_shape = shape
+            .offset_left(overhead)
+            .max_width_error(shape.width, field.span)?;
+        let expr = field.expr.rewrite_result(context, expr_shape);
         let is_lit = matches!(field.expr.kind, ast::ExprKind::Lit(_));
         match expr {
-            Some(ref e)
+            Ok(ref e)
                 if !is_lit && e.as_str() == name && context.config.use_field_init_shorthand() =>
             {
-                Some(attrs_str + name)
+                Ok(attrs_str + name)
             }
-            Some(e) => Some(format!("{attrs_str}{name}{separator}{e}")),
-            None => {
+            Ok(e) => Ok(format!("{attrs_str}{name}{separator}{e}")),
+            Err(_) => {
                 let expr_offset = shape.indent.block_indent(context.config);
                 let expr = field
                     .expr
-                    .rewrite(context, Shape::indented(expr_offset, context.config));
+                    .rewrite_result(context, Shape::indented(expr_offset, context.config));
                 expr.map(|s| {
                     format!(
                         "{}{}:\n{}{}",
@@ -1808,21 +1860,27 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>(
     span: Span,
     shape: Shape,
     is_singleton_tuple: bool,
-) -> Option<String> {
+) -> RewriteResult {
     // In case of length 1, need a trailing comma
     debug!("rewrite_tuple_in_visual_indent_style {:?}", shape);
     if is_singleton_tuple {
         // 3 = "(" + ",)"
-        let nested_shape = shape.sub_width(3)?.visual_indent(1);
+        let nested_shape = shape
+            .sub_width(3)
+            .max_width_error(shape.width, span)?
+            .visual_indent(1);
         return items
             .next()
             .unwrap()
-            .rewrite(context, nested_shape)
+            .rewrite_result(context, nested_shape)
             .map(|s| format!("({},)", s));
     }
 
     let list_lo = context.snippet_provider.span_after(span, "(");
-    let nested_shape = shape.sub_width(2)?.visual_indent(1);
+    let nested_shape = shape
+        .sub_width(2)
+        .max_width_error(shape.width, span)?
+        .visual_indent(1);
     let items = itemize_list(
         context.snippet_provider,
         items,
@@ -1830,7 +1888,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>(
         ",",
         |item| item.span().lo(),
         |item| item.span().hi(),
-        |item| item.rewrite(context, nested_shape),
+        |item| item.rewrite_result(context, nested_shape),
         list_lo,
         span.hi() - BytePos(1),
         false,
@@ -1847,7 +1905,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>(
         .ends_with_newline(false);
     let list_str = write_list(&item_vec, &fmt)?;
 
-    Some(format!("({list_str})"))
+    Ok(format!("({list_str})"))
 }
 
 fn rewrite_let(
@@ -1855,14 +1913,16 @@ fn rewrite_let(
     shape: Shape,
     pat: &ast::Pat,
     expr: &ast::Expr,
-) -> Option<String> {
+) -> RewriteResult {
     let mut result = "let ".to_owned();
 
     // TODO(ytmimi) comments could appear between `let` and the `pat`
 
     // 4 = "let ".len()
-    let pat_shape = shape.offset_left(4)?;
-    let pat_str = pat.rewrite(context, pat_shape)?;
+    let pat_shape = shape
+        .offset_left(4)
+        .max_width_error(shape.width, pat.span)?;
+    let pat_str = pat.rewrite_result(context, pat_shape)?;
     result.push_str(&pat_str);
 
     // TODO(ytmimi) comments could appear between `pat` and `=`
@@ -1890,7 +1950,7 @@ pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>(
     span: Span,
     shape: Shape,
     is_singleton_tuple: bool,
-) -> Option<String> {
+) -> RewriteResult {
     debug!("rewrite_tuple {:?}", shape);
     if context.use_block_indent() {
         // We use the same rule as function calls for rewriting tuples.
@@ -1919,31 +1979,35 @@ pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>(
     }
 }
 
-pub(crate) fn rewrite_unary_prefix<R: Rewrite>(
+pub(crate) fn rewrite_unary_prefix<R: Rewrite + Spanned>(
     context: &RewriteContext<'_>,
     prefix: &str,
     rewrite: &R,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
+    let shape = shape
+        .offset_left(prefix.len())
+        .max_width_error(shape.width, rewrite.span())?;
     rewrite
-        .rewrite(context, shape.offset_left(prefix.len())?)
+        .rewrite_result(context, shape)
         .map(|r| format!("{}{}", prefix, r))
 }
 
 // FIXME: this is probably not correct for multi-line Rewrites. we should
 // subtract suffix.len() from the last line budget, not the first!
-pub(crate) fn rewrite_unary_suffix<R: Rewrite>(
+pub(crate) fn rewrite_unary_suffix<R: Rewrite + Spanned>(
     context: &RewriteContext<'_>,
     suffix: &str,
     rewrite: &R,
     shape: Shape,
-) -> Option<String> {
-    rewrite
-        .rewrite(context, shape.sub_width(suffix.len())?)
-        .map(|mut r| {
-            r.push_str(suffix);
-            r
-        })
+) -> RewriteResult {
+    let shape = shape
+        .sub_width(suffix.len())
+        .max_width_error(shape.width, rewrite.span())?;
+    rewrite.rewrite_result(context, shape).map(|mut r| {
+        r.push_str(suffix);
+        r
+    })
 }
 
 fn rewrite_unary_op(
@@ -1951,7 +2015,7 @@ fn rewrite_unary_op(
     op: ast::UnOp,
     expr: &ast::Expr,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     // For some reason, an UnOp is not spanned like BinOp!
     rewrite_unary_prefix(context, op.as_str(), expr, shape)
 }
@@ -1990,15 +2054,21 @@ fn rewrite_assignment(
     rhs: &ast::Expr,
     op: Option<&ast::BinOp>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let operator_str = match op {
         Some(op) => context.snippet(op.span),
         None => "=",
     };
 
     // 1 = space between lhs and operator.
-    let lhs_shape = shape.sub_width(operator_str.len() + 1)?;
-    let lhs_str = format!("{} {}", lhs.rewrite(context, lhs_shape)?, operator_str);
+    let lhs_shape = shape
+        .sub_width(operator_str.len() + 1)
+        .max_width_error(shape.width, lhs.span())?;
+    let lhs_str = format!(
+        "{} {}",
+        lhs.rewrite_result(context, lhs_shape)?,
+        operator_str
+    );
 
     rewrite_assign_rhs(
         context,
@@ -2029,7 +2099,7 @@ pub(crate) fn rewrite_assign_rhs<S: Into<String>, R: Rewrite>(
     ex: &R,
     rhs_kind: &RhsAssignKind<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_kind, RhsTactics::Default)
 }
 
@@ -2040,7 +2110,7 @@ pub(crate) fn rewrite_assign_rhs_expr<R: Rewrite>(
     shape: Shape,
     rhs_kind: &RhsAssignKind<'_>,
     rhs_tactics: RhsTactics,
-) -> Option<String> {
+) -> RewriteResult {
     let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') {
         shape.indent.width()
     } else {
@@ -2062,7 +2132,7 @@ pub(crate) fn rewrite_assign_rhs_expr<R: Rewrite>(
         context,
         ex,
         orig_shape,
-        ex.rewrite(context, orig_shape),
+        ex.rewrite_result(context, orig_shape),
         rhs_kind,
         rhs_tactics,
         has_rhs_comment,
@@ -2076,13 +2146,13 @@ pub(crate) fn rewrite_assign_rhs_with<S: Into<String>, R: Rewrite>(
     shape: Shape,
     rhs_kind: &RhsAssignKind<'_>,
     rhs_tactics: RhsTactics,
-) -> Option<String> {
+) -> RewriteResult {
     let lhs = lhs.into();
     let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?;
-    Some(lhs + &rhs)
+    Ok(lhs + &rhs)
 }
 
-pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite>(
+pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite + Spanned>(
     context: &RewriteContext<'_>,
     lhs: S,
     ex: &R,
@@ -2091,21 +2161,22 @@ pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite>(
     rhs_tactics: RhsTactics,
     between_span: Span,
     allow_extend: bool,
-) -> Option<String> {
+) -> RewriteResult {
     let lhs = lhs.into();
     let contains_comment = contains_comment(context.snippet(between_span));
     let shape = if contains_comment {
-        shape.block_left(context.config.tab_spaces())?
+        shape
+            .block_left(context.config.tab_spaces())
+            .max_width_error(shape.width, between_span.with_hi(ex.span().hi()))?
     } else {
         shape
     };
     let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?;
-
     if contains_comment {
         let rhs = rhs.trim_start();
         combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend)
     } else {
-        Some(lhs + &rhs)
+        Ok(lhs + &rhs)
     }
 }
 
@@ -2113,23 +2184,25 @@ fn choose_rhs<R: Rewrite>(
     context: &RewriteContext<'_>,
     expr: &R,
     shape: Shape,
-    orig_rhs: Option<String>,
+    orig_rhs: RewriteResult,
     _rhs_kind: &RhsAssignKind<'_>,
     rhs_tactics: RhsTactics,
     has_rhs_comment: bool,
-) -> Option<String> {
+) -> RewriteResult {
     match orig_rhs {
-        Some(ref new_str) if new_str.is_empty() => Some(String::new()),
-        Some(ref new_str)
-            if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width =>
-        {
-            Some(format!(" {new_str}"))
+        Ok(ref new_str) if new_str.is_empty() => Ok(String::new()),
+        Ok(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => {
+            Ok(format!(" {new_str}"))
         }
         _ => {
             // Expression did not fit on the same line as the identifier.
             // Try splitting the line and see if that works better.
-            let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)?;
-            let new_rhs = expr.rewrite(context, new_shape);
+            let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)
+                // TODO(ding-young) Ideally, we can replace unknown_error() with max_width_error(),
+                // but this requires either implementing the Spanned trait for ast::GenericBounds
+                // or grabbing the span from the call site.
+                .unknown_error()?;
+            let new_rhs = expr.rewrite_result(context, new_shape);
             let new_indent_str = &shape
                 .indent
                 .block_indent(context.config)
@@ -2137,24 +2210,27 @@ fn choose_rhs<R: Rewrite>(
             let before_space_str = if has_rhs_comment { "" } else { " " };
 
             match (orig_rhs, new_rhs) {
-                (Some(ref orig_rhs), Some(ref new_rhs))
+                (Ok(ref orig_rhs), Ok(ref new_rhs))
                     if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) =>
                 {
-                    Some(format!("{before_space_str}{orig_rhs}"))
+                    Ok(format!("{before_space_str}{orig_rhs}"))
                 }
-                (Some(ref orig_rhs), Some(ref new_rhs))
+                (Ok(ref orig_rhs), Ok(ref new_rhs))
                     if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) =>
                 {
-                    Some(format!("{new_indent_str}{new_rhs}"))
+                    Ok(format!("{new_indent_str}{new_rhs}"))
                 }
-                (None, Some(ref new_rhs)) => Some(format!("{new_indent_str}{new_rhs}")),
-                (None, None) if rhs_tactics == RhsTactics::AllowOverflow => {
+                (Err(_), Ok(ref new_rhs)) => Ok(format!("{new_indent_str}{new_rhs}")),
+                (Err(_), Err(_)) if rhs_tactics == RhsTactics::AllowOverflow => {
                     let shape = shape.infinite_width();
-                    expr.rewrite(context, shape)
+                    expr.rewrite_result(context, shape)
                         .map(|s| format!("{}{}", before_space_str, s))
                 }
-                (None, None) => None,
-                (Some(orig_rhs), _) => Some(format!("{before_space_str}{orig_rhs}")),
+                // When both orig_rhs and new_rhs result in errors, we currently propagate
+                // the error from the second attempt since it is more generous with
+                // width constraints. This decision is somewhat arbitrary and is open to change.
+                (Err(_), Err(new_rhs_err)) => Err(new_rhs_err),
+                (Ok(orig_rhs), _) => Ok(format!("{before_space_str}{orig_rhs}")),
             }
         }
     }
@@ -2204,7 +2280,7 @@ fn rewrite_expr_addrof(
     mutability: ast::Mutability,
     expr: &ast::Expr,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let operator_str = match (mutability, borrow_kind) {
         (ast::Mutability::Not, ast::BorrowKind::Ref) => "&",
         (ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ",
diff --git a/src/tools/rustfmt/src/formatting.rs b/src/tools/rustfmt/src/formatting.rs
index 5e71fe107eb..1e1e329f624 100644
--- a/src/tools/rustfmt/src/formatting.rs
+++ b/src/tools/rustfmt/src/formatting.rs
@@ -17,7 +17,7 @@ use crate::parse::parser::{DirectoryOwnership, Parser, ParserError};
 use crate::parse::session::ParseSess;
 use crate::utils::{contains_skip, count_newlines};
 use crate::visitor::FmtVisitor;
-use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session};
+use crate::{ErrorKind, FormatReport, Input, Session, modules, source_file};
 
 mod generated;
 mod newline_style;
diff --git a/src/tools/rustfmt/src/git-rustfmt/main.rs b/src/tools/rustfmt/src/git-rustfmt/main.rs
index b8b0432aa95..64458420f59 100644
--- a/src/tools/rustfmt/src/git-rustfmt/main.rs
+++ b/src/tools/rustfmt/src/git-rustfmt/main.rs
@@ -13,7 +13,9 @@ use rustfmt_nightly as rustfmt;
 use tracing::debug;
 use tracing_subscriber::EnvFilter;
 
-use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session};
+use crate::rustfmt::{
+    CliOptions, FormatReportFormatterBuilder, Input, Session, Version, load_config,
+};
 
 fn prune_files(files: Vec<&str>) -> Vec<&str> {
     let prefixes: Vec<_> = files
@@ -87,6 +89,15 @@ impl CliOptions for NullOptions {
     fn config_path(&self) -> Option<&Path> {
         unreachable!();
     }
+    fn edition(&self) -> Option<rustfmt_nightly::Edition> {
+        unreachable!();
+    }
+    fn style_edition(&self) -> Option<rustfmt_nightly::StyleEdition> {
+        unreachable!();
+    }
+    fn version(&self) -> Option<Version> {
+        unreachable!();
+    }
 }
 
 fn uncommitted_files() -> Vec<String> {
diff --git a/src/tools/rustfmt/src/ignore_path.rs b/src/tools/rustfmt/src/ignore_path.rs
index 5c25f233ce3..2f804ef5a25 100644
--- a/src/tools/rustfmt/src/ignore_path.rs
+++ b/src/tools/rustfmt/src/ignore_path.rs
@@ -41,8 +41,11 @@ mod test {
         use crate::ignore_path::IgnorePathSet;
         use std::path::{Path, PathBuf};
 
-        let config =
-            Config::from_toml(r#"ignore = ["foo.rs", "bar_dir/*"]"#, Path::new("")).unwrap();
+        let config = Config::from_toml(
+            r#"ignore = ["foo.rs", "bar_dir/*"]"#,
+            Path::new("./rustfmt.toml"),
+        )
+        .unwrap();
         let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap();
 
         assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/foo.rs"))));
@@ -59,7 +62,7 @@ mod test {
 
         let config = Config::from_toml(
             r#"ignore = ["foo.rs", "bar_dir/*", "!bar_dir/*/what.rs"]"#,
-            Path::new(""),
+            Path::new("./rustfmt.toml"),
         )
         .unwrap();
         let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap();
diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs
index 12c178e136f..b741dd9b5da 100644
--- a/src/tools/rustfmt/src/imports.rs
+++ b/src/tools/rustfmt/src/imports.rs
@@ -8,19 +8,20 @@ use itertools::Itertools;
 
 use rustc_ast::ast::{self, UseTreeKind};
 use rustc_span::{
+    BytePos, DUMMY_SP, Span,
     symbol::{self, sym},
-    BytePos, Span, DUMMY_SP,
 };
 
 use crate::comment::combine_strs_with_missing_comments;
-use crate::config::lists::*;
 use crate::config::ImportGranularity;
-use crate::config::{Edition, IndentStyle, Version};
+use crate::config::lists::*;
+use crate::config::{Edition, IndentStyle, StyleEdition};
 use crate::lists::{
-    definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
+    ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
 };
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
+use crate::sort::version_sort;
 use crate::source_map::SpanUtils;
 use crate::spanned::Spanned;
 use crate::utils::{is_same_visibility, mk_sp, rewrite_ident};
@@ -44,7 +45,8 @@ impl<'a> FmtVisitor<'a> {
             Some(item.span.lo()),
             Some(item.attrs.clone()),
         )
-        .rewrite_top_level(&self.get_context(), shape);
+        .rewrite_top_level(&self.get_context(), shape)
+        .ok();
         match rw {
             Some(ref s) if s.is_empty() => {
                 // Format up to last newline
@@ -104,7 +106,7 @@ pub(crate) enum UseSegmentKind {
 #[derive(Clone, Eq, PartialEq)]
 pub(crate) struct UseSegment {
     pub(crate) kind: UseSegmentKind,
-    pub(crate) version: Version,
+    pub(crate) style_edition: StyleEdition,
 }
 
 #[derive(Clone)]
@@ -149,7 +151,7 @@ impl UseSegment {
         };
         UseSegment {
             kind,
-            version: self.version,
+            style_edition: self.style_edition,
         }
     }
 
@@ -197,7 +199,7 @@ impl UseSegment {
 
         Some(UseSegment {
             kind,
-            version: context.config.version(),
+            style_edition: context.config.style_edition(),
         })
     }
 
@@ -331,12 +333,17 @@ impl UseTree {
         &self,
         context: &RewriteContext<'_>,
         shape: Shape,
-    ) -> Option<String> {
+    ) -> RewriteResult {
         let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| {
             crate::utils::format_visibility(context, vis)
         });
         let use_str = self
-            .rewrite(context, shape.offset_left(vis.len())?)
+            .rewrite_result(
+                context,
+                shape
+                    .offset_left(vis.len())
+                    .max_width_error(shape.width, self.span())?,
+            )
             .map(|s| {
                 if s.is_empty() {
                     s
@@ -346,8 +353,8 @@ impl UseTree {
             })?;
         match self.attrs {
             Some(ref attrs) if !attrs.is_empty() => {
-                let attr_str = attrs.rewrite(context, shape)?;
-                let lo = attrs.last().as_ref()?.span.hi();
+                let attr_str = attrs.rewrite_result(context, shape)?;
+                let lo = attrs.last().unknown_error()?.span.hi();
                 let hi = self.span.lo();
                 let span = mk_sp(lo, hi);
 
@@ -368,7 +375,7 @@ impl UseTree {
                     allow_extend,
                 )
             }
-            _ => Some(use_str),
+            _ => Ok(use_str),
         }
     }
 
@@ -444,18 +451,21 @@ impl UseTree {
             }
         }
 
-        let version = context.config.version();
+        let style_edition = context.config.style_edition();
 
         match a.kind {
             UseTreeKind::Glob => {
                 // in case of a global path and the glob starts at the root, e.g., "::*"
                 if a.prefix.segments.len() == 1 && leading_modsep {
                     let kind = UseSegmentKind::Ident("".to_owned(), None);
-                    result.path.push(UseSegment { kind, version });
+                    result.path.push(UseSegment {
+                        kind,
+                        style_edition,
+                    });
                 }
                 result.path.push(UseSegment {
                     kind: UseSegmentKind::Glob,
-                    version,
+                    style_edition,
                 });
             }
             UseTreeKind::Nested {
@@ -470,7 +480,7 @@ impl UseTree {
                     ",",
                     |tree| tree.span.lo(),
                     |tree| tree.span.hi(),
-                    |_| Some("".to_owned()), // We only need comments for now.
+                    |_| Ok("".to_owned()), // We only need comments for now.
                     context.snippet_provider.span_after(a.span, "{"),
                     a.span.hi(),
                     false,
@@ -480,7 +490,10 @@ impl UseTree {
                 // e.g., "::{foo, bar}"
                 if a.prefix.segments.len() == 1 && leading_modsep {
                     let kind = UseSegmentKind::Ident("".to_owned(), None);
-                    result.path.push(UseSegment { kind, version });
+                    result.path.push(UseSegment {
+                        kind,
+                        style_edition,
+                    });
                 }
                 let kind = UseSegmentKind::List(
                     list.iter()
@@ -490,7 +503,10 @@ impl UseTree {
                         })
                         .collect(),
                 );
-                result.path.push(UseSegment { kind, version });
+                result.path.push(UseSegment {
+                    kind,
+                    style_edition,
+                });
             }
             UseTreeKind::Simple(ref rename) => {
                 // If the path has leading double colons and is composed of only 2 segments, then we
@@ -519,7 +535,10 @@ impl UseTree {
                     _ => UseSegmentKind::Ident(name, alias),
                 };
 
-                let segment = UseSegment { kind, version };
+                let segment = UseSegment {
+                    kind,
+                    style_edition,
+                };
 
                 // `name` is already in result.
                 result.path.pop();
@@ -614,7 +633,7 @@ impl UseTree {
             list.sort();
             last = UseSegment {
                 kind: UseSegmentKind::List(list),
-                version: last.version,
+                style_edition: last.style_edition,
             };
         }
 
@@ -732,9 +751,12 @@ impl UseTree {
         }) = self.path.last()
         {
             let self_segment = self.path.pop().unwrap();
-            let version = self_segment.version;
+            let style_edition = self_segment.style_edition;
             let kind = UseSegmentKind::List(vec![UseTree::from_path(vec![self_segment], DUMMY_SP)]);
-            self.path.push(UseSegment { kind, version });
+            self.path.push(UseSegment {
+                kind,
+                style_edition,
+            });
         }
         self
     }
@@ -750,7 +772,7 @@ fn merge_rest(
         return None;
     }
     if a.len() != len && b.len() != len {
-        let version = a[len].version;
+        let style_edition = a[len].style_edition;
         if let UseSegmentKind::List(ref list) = a[len].kind {
             let mut list = list.clone();
             merge_use_trees_inner(
@@ -760,7 +782,10 @@ fn merge_rest(
             );
             let mut new_path = b[..len].to_vec();
             let kind = UseSegmentKind::List(list);
-            new_path.push(UseSegment { kind, version });
+            new_path.push(UseSegment {
+                kind,
+                style_edition,
+            });
             return Some(new_path);
         }
     } else if len == 1 {
@@ -770,9 +795,12 @@ fn merge_rest(
             (&b[0], &a[1..])
         };
         let kind = UseSegmentKind::Slf(common.get_alias().map(ToString::to_string));
-        let version = a[0].version;
+        let style_edition = a[0].style_edition;
         let mut list = vec![UseTree::from_path(
-            vec![UseSegment { kind, version }],
+            vec![UseSegment {
+                kind,
+                style_edition,
+            }],
             DUMMY_SP,
         )];
         match rest {
@@ -788,7 +816,7 @@ fn merge_rest(
             b[0].clone(),
             UseSegment {
                 kind: UseSegmentKind::List(list),
-                version,
+                style_edition,
             },
         ]);
     } else {
@@ -801,8 +829,11 @@ fn merge_rest(
     list.sort();
     let mut new_path = b[..len].to_vec();
     let kind = UseSegmentKind::List(list);
-    let version = a[0].version;
-    new_path.push(UseSegment { kind, version });
+    let style_edition = a[0].style_edition;
+    new_path.push(UseSegment {
+        kind,
+        style_edition,
+    });
     Some(new_path)
 }
 
@@ -892,8 +923,8 @@ impl Ord for UseSegment {
             | (Super(ref a), Super(ref b))
             | (Crate(ref a), Crate(ref b)) => match (a, b) {
                 (Some(sa), Some(sb)) => {
-                    if self.version == Version::Two {
-                        sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#"))
+                    if self.style_edition >= StyleEdition::Edition2024 {
+                        version_sort(sa.trim_start_matches("r#"), sb.trim_start_matches("r#"))
                     } else {
                         a.cmp(b)
                     }
@@ -902,25 +933,31 @@ impl Ord for UseSegment {
             },
             (Glob, Glob) => Ordering::Equal,
             (Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => {
-                let (ia, ib) = if self.version == Version::Two {
+                let (ia, ib) = if self.style_edition >= StyleEdition::Edition2024 {
                     (pia.trim_start_matches("r#"), pib.trim_start_matches("r#"))
                 } else {
                     (pia.as_str(), pib.as_str())
                 };
-                // snake_case < CamelCase < UPPER_SNAKE_CASE
-                if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) {
-                    return Ordering::Greater;
-                }
-                if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) {
-                    return Ordering::Less;
-                }
-                if is_upper_snake_case(ia) && !is_upper_snake_case(ib) {
-                    return Ordering::Greater;
-                }
-                if !is_upper_snake_case(ia) && is_upper_snake_case(ib) {
-                    return Ordering::Less;
-                }
-                let ident_ord = ia.cmp(ib);
+
+                let ident_ord = if self.style_edition >= StyleEdition::Edition2024 {
+                    version_sort(ia, ib)
+                } else {
+                    // snake_case < CamelCase < UPPER_SNAKE_CASE
+                    if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) {
+                        return Ordering::Greater;
+                    }
+                    if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) {
+                        return Ordering::Less;
+                    }
+                    if is_upper_snake_case(ia) && !is_upper_snake_case(ib) {
+                        return Ordering::Greater;
+                    }
+                    if !is_upper_snake_case(ia) && is_upper_snake_case(ib) {
+                        return Ordering::Less;
+                    }
+                    ia.cmp(ib)
+                };
+
                 if ident_ord != Ordering::Equal {
                     return ident_ord;
                 }
@@ -928,9 +965,8 @@ impl Ord for UseSegment {
                     (None, Some(_)) => Ordering::Less,
                     (Some(_), None) => Ordering::Greater,
                     (Some(aas), Some(abs)) => {
-                        if self.version == Version::Two {
-                            aas.trim_start_matches("r#")
-                                .cmp(abs.trim_start_matches("r#"))
+                        if self.style_edition >= StyleEdition::Edition2024 {
+                            version_sort(aas.trim_start_matches("r#"), abs.trim_start_matches("r#"))
                         } else {
                             aas.cmp(abs)
                         }
@@ -982,21 +1018,24 @@ fn rewrite_nested_use_tree(
     context: &RewriteContext<'_>,
     use_tree_list: &[UseTree],
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let mut list_items = Vec::with_capacity(use_tree_list.len());
     let nested_shape = match context.config.imports_indent() {
         IndentStyle::Block => shape
             .block_indent(context.config.tab_spaces())
             .with_max_width(context.config)
-            .sub_width(1)?,
+            .sub_width(1)
+            .unknown_error()?,
         IndentStyle::Visual => shape.visual_indent(0),
     };
     for use_tree in use_tree_list {
         if let Some(mut list_item) = use_tree.list_item.clone() {
-            list_item.item = use_tree.rewrite(context, nested_shape);
+            list_item.item = use_tree.rewrite_result(context, nested_shape);
             list_items.push(list_item);
         } else {
-            list_items.push(ListItem::from_str(use_tree.rewrite(context, nested_shape)?));
+            list_items.push(ListItem::from_str(
+                use_tree.rewrite_result(context, nested_shape)?,
+            ));
         }
     }
     let has_nested_list = use_tree_list.iter().any(|use_segment| {
@@ -1049,12 +1088,16 @@ fn rewrite_nested_use_tree(
         format!("{{{list_str}}}")
     };
 
-    Some(result)
+    Ok(result)
 }
 
 impl Rewrite for UseSegment {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
-        Some(match self.kind {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        Ok(match self.kind {
             UseSegmentKind::Ident(ref ident, Some(ref rename)) => {
                 format!("{ident} as {rename}")
             }
@@ -1066,31 +1109,42 @@ impl Rewrite for UseSegment {
             UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"),
             UseSegmentKind::Crate(None) => "crate".to_owned(),
             UseSegmentKind::Glob => "*".to_owned(),
-            UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree(
-                context,
-                use_tree_list,
-                // 1 = "{" and "}"
-                shape.offset_left(1)?.sub_width(1)?,
-            )?,
+            UseSegmentKind::List(ref use_tree_list) => {
+                rewrite_nested_use_tree(
+                    context,
+                    use_tree_list,
+                    // 1 = "{" and "}"
+                    shape
+                        .offset_left(1)
+                        .and_then(|s| s.sub_width(1))
+                        .unknown_error()?,
+                )?
+            }
         })
     }
 }
 
 impl Rewrite for UseTree {
+    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
     // This does NOT format attributes and visibility or add a trailing `;`.
-    fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option<String> {
+    fn rewrite_result(&self, context: &RewriteContext<'_>, mut shape: Shape) -> RewriteResult {
         let mut result = String::with_capacity(256);
         let mut iter = self.path.iter().peekable();
         while let Some(segment) = iter.next() {
-            let segment_str = segment.rewrite(context, shape)?;
+            let segment_str = segment.rewrite_result(context, shape)?;
             result.push_str(&segment_str);
             if iter.peek().is_some() {
                 result.push_str("::");
                 // 2 = "::"
-                shape = shape.offset_left(2 + segment_str.len())?;
+                shape = shape
+                    .offset_left(2 + segment_str.len())
+                    .max_width_error(shape.width, self.span())?;
             }
         }
-        Some(result)
+        Ok(result)
     }
 }
 
@@ -1114,7 +1168,7 @@ mod test {
 
         struct Parser<'a> {
             input: Peekable<Chars<'a>>,
-            version: Version,
+            style_edition: StyleEdition,
         }
 
         impl<'a> Parser<'a> {
@@ -1132,7 +1186,7 @@ mod test {
                 buf: &mut String,
                 alias_buf: &mut Option<String>,
             ) {
-                let version = self.version;
+                let style_edition = self.style_edition;
                 if !buf.is_empty() {
                     let mut alias = None;
                     swap(alias_buf, &mut alias);
@@ -1140,19 +1194,28 @@ mod test {
                     match buf.as_ref() {
                         "self" => {
                             let kind = UseSegmentKind::Slf(alias);
-                            result.push(UseSegment { kind, version });
+                            result.push(UseSegment {
+                                kind,
+                                style_edition,
+                            });
                             *buf = String::new();
                             *alias_buf = None;
                         }
                         "super" => {
                             let kind = UseSegmentKind::Super(alias);
-                            result.push(UseSegment { kind, version });
+                            result.push(UseSegment {
+                                kind,
+                                style_edition,
+                            });
                             *buf = String::new();
                             *alias_buf = None;
                         }
                         "crate" => {
                             let kind = UseSegmentKind::Crate(alias);
-                            result.push(UseSegment { kind, version });
+                            result.push(UseSegment {
+                                kind,
+                                style_edition,
+                            });
                             *buf = String::new();
                             *alias_buf = None;
                         }
@@ -1160,7 +1223,10 @@ mod test {
                             let mut name = String::new();
                             swap(buf, &mut name);
                             let kind = UseSegmentKind::Ident(name, alias);
-                            result.push(UseSegment { kind, version });
+                            result.push(UseSegment {
+                                kind,
+                                style_edition,
+                            });
                         }
                     }
                 }
@@ -1178,7 +1244,7 @@ mod test {
                             let kind = UseSegmentKind::List(self.parse_list());
                             result.push(UseSegment {
                                 kind,
-                                version: self.version,
+                                style_edition: self.style_edition,
                             });
                             self.eat('}');
                         }
@@ -1188,7 +1254,7 @@ mod test {
                             let kind = UseSegmentKind::Glob;
                             result.push(UseSegment {
                                 kind,
-                                version: self.version,
+                                style_edition: self.style_edition,
                             });
                         }
                         ':' => {
@@ -1249,7 +1315,7 @@ mod test {
 
         let mut parser = Parser {
             input: s.chars().peekable(),
-            version: Version::One,
+            style_edition: StyleEdition::Edition2015,
         };
         parser.parse_in_list()
     }
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index cbf7ce90e37..fc043a697e0 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -1,31 +1,30 @@
 // Formatting top-level items - functions, structs, enums, traits, impls.
 
 use std::borrow::Cow;
-use std::cmp::{max, min, Ordering};
+use std::cmp::{Ordering, max, min};
 
 use regex::Regex;
 use rustc_ast::visit;
 use rustc_ast::{ast, ptr};
-use rustc_span::{symbol, BytePos, Span, DUMMY_SP};
+use rustc_span::{BytePos, DUMMY_SP, Span, symbol};
 use tracing::debug;
 
 use crate::attr::filter_inline_attrs;
 use crate::comment::{
-    combine_strs_with_missing_comments, contains_comment, is_last_comment_block,
+    FindUncommented, combine_strs_with_missing_comments, contains_comment, is_last_comment_block,
     recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment,
-    FindUncommented,
 };
 use crate::config::lists::*;
-use crate::config::{BraceStyle, Config, IndentStyle, Version};
+use crate::config::{BraceStyle, Config, IndentStyle, StyleEdition};
 use crate::expr::{
-    is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with,
-    rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, rewrite_let_else_block,
-    RhsAssignKind, RhsTactics,
+    RhsAssignKind, RhsTactics, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs,
+    rewrite_assign_rhs_with, rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments,
+    rewrite_let_else_block,
 };
-use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator};
-use crate::macros::{rewrite_macro, MacroPosition};
+use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list};
+use crate::macros::{MacroPosition, rewrite_macro};
 use crate::overflow;
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::{Indent, Shape};
 use crate::source_map::{LineRangeUtils, SpanUtils};
 use crate::spanned::Spanned;
@@ -49,18 +48,22 @@ fn type_annotation_separator(config: &Config) -> &str {
 // let pat: ty = init; or let pat: ty = init else { .. };
 impl Rewrite for ast::Local {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         debug!(
             "Local::rewrite {:?} {} {:?}",
             self, shape.width, shape.indent
         );
 
-        skip_out_of_file_lines_range!(context, self.span);
+        skip_out_of_file_lines_range_err!(context, self.span);
 
         if contains_skip(&self.attrs) {
-            return None;
+            return Err(RewriteError::SkipFormatting);
         }
 
-        let attrs_str = self.attrs.rewrite(context, shape)?;
+        let attrs_str = self.attrs.rewrite_result(context, shape)?;
         let mut result = if attrs_str.is_empty() {
             "let ".to_owned()
         } else {
@@ -79,10 +82,15 @@ impl Rewrite for ast::Local {
         let let_kw_offset = result.len() - "let ".len();
 
         // 4 = "let ".len()
-        let pat_shape = shape.offset_left(4)?;
+        let pat_shape = shape
+            .offset_left(4)
+            .max_width_error(shape.width, self.span())?;
         // 1 = ;
-        let pat_shape = pat_shape.sub_width(1)?;
-        let pat_str = self.pat.rewrite(context, pat_shape)?;
+        let pat_shape = pat_shape
+            .sub_width(1)
+            .max_width_error(shape.width, self.span())?;
+        let pat_str = self.pat.rewrite_result(context, pat_shape)?;
+
         result.push_str(&pat_str);
 
         // String that is placed within the assignment pattern and expression.
@@ -96,11 +104,13 @@ impl Rewrite for ast::Local {
                 } else {
                     shape
                 }
-                .offset_left(last_line_width(&result) + separator.len())?
+                .offset_left(last_line_width(&result) + separator.len())
+                .max_width_error(shape.width, self.span())?
                 // 2 = ` =`
-                .sub_width(2)?;
+                .sub_width(2)
+                .max_width_error(shape.width, self.span())?;
 
-                let rewrite = ty.rewrite(context, ty_shape)?;
+                let rewrite = ty.rewrite_result(context, ty_shape)?;
 
                 infix.push_str(separator);
                 infix.push_str(&rewrite);
@@ -117,7 +127,9 @@ impl Rewrite for ast::Local {
 
         if let Some((init, else_block)) = self.kind.init_else_opt() {
             // 1 = trailing semicolon;
-            let nested_shape = shape.sub_width(1)?;
+            let nested_shape = shape
+                .sub_width(1)
+                .max_width_error(shape.width, self.span())?;
 
             result = rewrite_assign_rhs(
                 context,
@@ -131,7 +143,8 @@ impl Rewrite for ast::Local {
                 let else_kw_span = init.span.between(block.span);
                 // Strip attributes and comments to check if newline is needed before the else
                 // keyword from the initializer part. (#5901)
-                let init_str = if context.config.version() == Version::Two {
+                let style_edition = context.config.style_edition();
+                let init_str = if style_edition >= StyleEdition::Edition2024 {
                     &result[let_kw_offset..]
                 } else {
                     result.as_str()
@@ -155,7 +168,8 @@ impl Rewrite for ast::Local {
                     std::cmp::min(shape.width, context.config.single_line_let_else_max_width());
 
                 // If available_space hits zero we know for sure this will be a multi-lined block
-                let assign_str_with_else_kw = if context.config.version() == Version::Two {
+                let style_edition = context.config.style_edition();
+                let assign_str_with_else_kw = if style_edition >= StyleEdition::Edition2024 {
                     &result[let_kw_offset..]
                 } else {
                     result.as_str()
@@ -184,7 +198,7 @@ impl Rewrite for ast::Local {
         }
 
         result.push(';');
-        Some(result)
+        Ok(result)
     }
 }
 
@@ -420,7 +434,7 @@ impl<'a> FmtVisitor<'a> {
 
         let mut fn_brace_style = newline_for_brace(self.config, &fn_sig.generics.where_clause);
         let (result, _, force_newline_brace) =
-            rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style)?;
+            rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style).ok()?;
 
         // 2 = ` {`
         if self.config.brace_style() == BraceStyle::AlwaysNextLine
@@ -441,7 +455,7 @@ impl<'a> FmtVisitor<'a> {
         vis: &ast::Visibility,
         generics: &ast::Generics,
         span: Span,
-    ) -> Option<String> {
+    ) -> RewriteResult {
         // Drop semicolon or it will be interpreted as comment.
         let span = mk_sp(span.lo(), span.hi() - BytePos(1));
         let context = self.get_context();
@@ -463,7 +477,7 @@ impl<'a> FmtVisitor<'a> {
         // Re-attach semicolon
         result.push(';');
 
-        Some(result)
+        Ok(result)
     }
 
     pub(crate) fn single_line_fn(
@@ -615,7 +629,10 @@ impl<'a> FmtVisitor<'a> {
                     }
                 },
                 |f| f.span.hi(),
-                |f| self.format_variant(f, one_line_width, pad_discrim_ident_to),
+                |f| {
+                    self.format_variant(f, one_line_width, pad_discrim_ident_to)
+                        .unknown_error()
+                },
                 body_lo,
                 body_hi,
                 false,
@@ -636,7 +653,7 @@ impl<'a> FmtVisitor<'a> {
             .trailing_separator(self.config.trailing_comma())
             .preserve_newline(true);
 
-        let list = write_list(&items, &fmt)?;
+        let list = write_list(&items, &fmt).ok()?;
         result.push_str(&list);
         result.push_str(&original_offset.to_string_with_newline(self.config));
         result.push('}');
@@ -658,10 +675,10 @@ impl<'a> FmtVisitor<'a> {
 
         let context = self.get_context();
         let shape = self.shape();
-        let attrs_str = if context.config.version() == Version::Two {
+        let attrs_str = if context.config.style_edition() >= StyleEdition::Edition2024 {
             field.attrs.rewrite(&context, shape)?
         } else {
-            // Version::One formatting that was off by 1. See issue #5801
+            // StyleEdition::Edition20{15|18|21} formatting that was off by 1. See issue #5801
             field.attrs.rewrite(&context, shape.sub_width(1)?)?
         };
         // sub_width(1) to take the trailing comma into account
@@ -693,12 +710,14 @@ impl<'a> FmtVisitor<'a> {
                 shape,
                 &RhsAssignKind::Expr(&ex.kind, ex.span),
                 RhsTactics::AllowOverflow,
-            )?
+            )
+            .ok()?
         } else {
             variant_body
         };
 
         combine_strs_with_missing_comments(&context, &attrs_str, &variant_body, span, shape, false)
+            .ok()
     }
 
     fn visit_impl_items(&mut self, items: &[ptr::P<ast::AssocItem>]) {
@@ -822,7 +841,8 @@ pub(crate) fn format_impl(
         where_span_end,
         self_ty.span.hi(),
         option,
-    )?;
+    )
+    .ok()?;
 
     // If there is no where-clause, we may have missing comments between the trait name and
     // the opening brace.
@@ -834,7 +854,7 @@ pub(crate) fn format_impl(
                 context,
                 last_line_width(&result),
             ) {
-                Some(ref missing_comment) if !missing_comment.is_empty() => {
+                Ok(ref missing_comment) if !missing_comment.is_empty() => {
                     result.push_str(missing_comment);
                 }
                 _ => (),
@@ -949,7 +969,7 @@ fn format_impl_ref_and_type(
     result.push_str(format_defaultness(defaultness));
     result.push_str(format_safety(safety));
 
-    let shape = if context.config.version() == Version::Two {
+    let shape = if context.config.style_edition() >= StyleEdition::Edition2024 {
         Shape::indented(offset + last_line_width(&result), context.config)
     } else {
         generics_shape_from_config(
@@ -958,7 +978,7 @@ fn format_impl_ref_and_type(
             0,
         )?
     };
-    let generics_str = rewrite_generics(context, "impl", generics, shape)?;
+    let generics_str = rewrite_generics(context, "impl", generics, shape).ok()?;
     result.push_str(&generics_str);
     result.push_str(format_constness_right(constness));
 
@@ -1166,7 +1186,7 @@ pub(crate) fn format_trait(
 
     let shape = Shape::indented(offset, context.config).offset_left(result.len())?;
     let generics_str =
-        rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?;
+        rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape).ok()?;
     result.push_str(&generics_str);
 
     // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds.
@@ -1187,7 +1207,8 @@ pub(crate) fn format_trait(
             shape,
             &RhsAssignKind::Bounds,
             RhsTactics::ForceNextLineWithoutIndent,
-        )?;
+        )
+        .ok()?;
     }
 
     // Rewrite where-clause.
@@ -1212,7 +1233,8 @@ pub(crate) fn format_trait(
             None,
             pos_before_where,
             option,
-        )?;
+        )
+        .ok()?;
         // If the where-clause cannot fit on the same line,
         // put the where-clause on a new line
         if !where_clause_str.contains('\n')
@@ -1241,7 +1263,7 @@ pub(crate) fn format_trait(
                     context,
                     last_line_width(&result),
                 ) {
-                    Some(ref missing_comment) if !missing_comment.is_empty() => {
+                    Ok(ref missing_comment) if !missing_comment.is_empty() => {
                         result.push_str(missing_comment);
                     }
                     _ => (),
@@ -1317,7 +1339,11 @@ pub(crate) struct TraitAliasBounds<'a> {
 
 impl<'a> Rewrite for TraitAliasBounds<'a> {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
-        let generic_bounds_str = self.generic_bounds.rewrite(context, shape)?;
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        let generic_bounds_str = self.generic_bounds.rewrite_result(context, shape)?;
 
         let mut option = WhereClauseOption::new(true, WhereClauseSpace::None);
         option.allow_single_line();
@@ -1346,7 +1372,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> {
             shape.indent.to_string_with_newline(context.config)
         };
 
-        Some(format!("{generic_bounds_str}{space}{where_str}"))
+        Ok(format!("{generic_bounds_str}{space}{where_str}"))
     }
 }
 
@@ -1361,7 +1387,7 @@ pub(crate) fn format_trait_alias(
     let alias = rewrite_ident(context, ident);
     // 6 = "trait ", 2 = " ="
     let g_shape = shape.offset_left(6)?.sub_width(2)?;
-    let generics_str = rewrite_generics(context, alias, generics, g_shape)?;
+    let generics_str = rewrite_generics(context, alias, generics, g_shape).ok()?;
     let vis_str = format_visibility(context, vis);
     let lhs = format!("{vis_str}trait {generics_str} =");
     // 1 = ";"
@@ -1377,6 +1403,7 @@ pub(crate) fn format_trait_alias(
         shape.sub_width(1)?,
     )
     .map(|s| s + ";")
+    .ok()
 }
 
 fn format_unit_struct(
@@ -1531,8 +1558,8 @@ fn format_empty_struct_or_tuple(
     // indented shape for proper indenting of multi-line comments
     let shape = Shape::indented(offset.block_indent(context.config), context.config);
     match rewrite_missing_comment(span, shape, context) {
-        Some(ref s) if s.is_empty() => (),
-        Some(ref s) => {
+        Ok(ref s) if s.is_empty() => (),
+        Ok(ref s) => {
             let is_multi_line = !is_single_line(s);
             if is_multi_line || first_line_contains_single_line_comment(s) {
                 let nested_indent_str = offset
@@ -1545,7 +1572,7 @@ fn format_empty_struct_or_tuple(
                 result.push_str(&offset.to_string_with_newline(context.config));
             }
         }
-        None => result.push_str(context.snippet(span)),
+        Err(_) => result.push_str(context.snippet(span)),
     }
     result.push_str(closer);
 }
@@ -1587,7 +1614,7 @@ fn format_tuple_struct(
         Some(generics) => {
             let budget = context.budget(last_line_width(&header_str));
             let shape = Shape::legacy(budget, offset);
-            let generics_str = rewrite_generics(context, "", generics, shape)?;
+            let generics_str = rewrite_generics(context, "", generics, shape).ok()?;
             result.push_str(&generics_str);
 
             let where_budget = context.budget(last_line_width(&result));
@@ -1603,7 +1630,8 @@ fn format_tuple_struct(
                 None,
                 body_hi,
                 option,
-            )?
+            )
+            .ok()?
         }
         None => "".to_owned(),
     };
@@ -1629,7 +1657,8 @@ fn format_tuple_struct(
             mk_sp(lo, span.hi()),
             context.config.fn_call_width(),
             None,
-        )?;
+        )
+        .ok()?;
     }
 
     if !where_clause_str.is_empty()
@@ -1672,7 +1701,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>(
     indent: Indent,
     visitor_kind: &ItemVisitorKind<'b>,
     span: Span,
-) -> Option<String> {
+) -> RewriteResult {
     use ItemVisitorKind::*;
 
     let ast::TyAlias {
@@ -1683,6 +1712,9 @@ pub(crate) fn rewrite_type_alias<'a, 'b>(
         where_clauses,
     } = *ty_alias_kind;
     let ty_opt = ty.as_ref();
+    let rhs_hi = ty
+        .as_ref()
+        .map_or(where_clauses.before.span.hi(), |ty| ty.span.hi());
     let (ident, vis) = match visitor_kind {
         Item(i) => (i.ident, &i.vis),
         AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis),
@@ -1697,21 +1729,27 @@ pub(crate) fn rewrite_type_alias<'a, 'b>(
     match (visitor_kind, &op_ty) {
         (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(op_bounds)) => {
             let op = OpaqueType { bounds: op_bounds };
-            rewrite_ty(rw_info, Some(bounds), Some(&op), vis)
+            rewrite_ty(rw_info, Some(bounds), Some(&op), rhs_hi, vis)
         }
         (Item(_) | AssocTraitItem(_) | ForeignItem(_), None) => {
-            rewrite_ty(rw_info, Some(bounds), ty_opt, vis)
+            rewrite_ty(rw_info, Some(bounds), ty_opt, rhs_hi, vis)
         }
         (AssocImplItem(_), _) => {
             let result = if let Some(op_bounds) = op_ty {
                 let op = OpaqueType { bounds: op_bounds };
-                rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY)
+                rewrite_ty(
+                    rw_info,
+                    Some(bounds),
+                    Some(&op),
+                    rhs_hi,
+                    &DEFAULT_VISIBILITY,
+                )
             } else {
-                rewrite_ty(rw_info, Some(bounds), ty_opt, vis)
+                rewrite_ty(rw_info, Some(bounds), ty_opt, rhs_hi, vis)
             }?;
             match defaultness {
-                ast::Defaultness::Default(..) => Some(format!("default {result}")),
-                _ => Some(result),
+                ast::Defaultness::Default(..) => Ok(format!("default {result}")),
+                _ => Ok(result),
             }
         }
     }
@@ -1721,17 +1759,16 @@ fn rewrite_ty<R: Rewrite>(
     rw_info: &TyAliasRewriteInfo<'_, '_>,
     generic_bounds_opt: Option<&ast::GenericBounds>,
     rhs: Option<&R>,
+    // the span of the end of the RHS (or the end of the generics, if there is no RHS)
+    rhs_hi: BytePos,
     vis: &ast::Visibility,
-) -> Option<String> {
+) -> RewriteResult {
     let mut result = String::with_capacity(128);
     let TyAliasRewriteInfo(context, indent, generics, where_clauses, ident, span) = *rw_info;
     let (before_where_predicates, after_where_predicates) = generics
         .where_clause
         .predicates
         .split_at(where_clauses.split);
-    if !after_where_predicates.is_empty() {
-        return None;
-    }
     result.push_str(&format!("{}type ", format_visibility(context, vis)));
     let ident_str = rewrite_ident(context, ident);
 
@@ -1739,9 +1776,11 @@ fn rewrite_ty<R: Rewrite>(
         result.push_str(ident_str)
     } else {
         // 2 = `= `
-        let g_shape = Shape::indented(indent, context.config)
-            .offset_left(result.len())?
-            .sub_width(2)?;
+        let g_shape = Shape::indented(indent, context.config);
+        let g_shape = g_shape
+            .offset_left(result.len())
+            .and_then(|s| s.sub_width(2))
+            .max_width_error(g_shape.width, span)?;
         let generics_str = rewrite_generics(context, ident_str, generics, g_shape)?;
         result.push_str(&generics_str);
     }
@@ -1749,8 +1788,13 @@ fn rewrite_ty<R: Rewrite>(
     if let Some(bounds) = generic_bounds_opt {
         if !bounds.is_empty() {
             // 2 = `: `
-            let shape = Shape::indented(indent, context.config).offset_left(result.len() + 2)?;
-            let type_bounds = bounds.rewrite(context, shape).map(|s| format!(": {}", s))?;
+            let shape = Shape::indented(indent, context.config);
+            let shape = shape
+                .offset_left(result.len() + 2)
+                .max_width_error(shape.width, span)?;
+            let type_bounds = bounds
+                .rewrite_result(context, shape)
+                .map(|s| format!(": {}", s))?;
             result.push_str(&type_bounds);
         }
     }
@@ -1760,7 +1804,7 @@ fn rewrite_ty<R: Rewrite>(
     if rhs.is_none() {
         option.suppress_comma();
     }
-    let where_clause_str = rewrite_where_clause(
+    let before_where_clause_str = rewrite_where_clause(
         context,
         before_where_predicates,
         where_clauses.before.span,
@@ -1772,14 +1816,20 @@ fn rewrite_ty<R: Rewrite>(
         generics.span.hi(),
         option,
     )?;
-    result.push_str(&where_clause_str);
+    result.push_str(&before_where_clause_str);
 
-    if let Some(ty) = rhs {
-        // If there's a where clause, add a newline before the assignment. Otherwise just add a
-        // space.
-        let has_where = !before_where_predicates.is_empty();
-        if has_where {
+    let mut result = if let Some(ty) = rhs {
+        // If there are any where clauses, add a newline before the assignment.
+        // If there is a before where clause, do not indent, but if there is
+        // only an after where clause, additionally indent the type.
+        if !before_where_predicates.is_empty() {
             result.push_str(&indent.to_string_with_newline(context.config));
+        } else if !after_where_predicates.is_empty() {
+            result.push_str(
+                &indent
+                    .block_indent(context.config)
+                    .to_string_with_newline(context.config),
+            );
         } else {
             result.push(' ');
         }
@@ -1791,13 +1841,20 @@ fn rewrite_ty<R: Rewrite>(
 
         let lhs = match comment_span {
             Some(comment_span)
-                if contains_comment(context.snippet_provider.span_to_snippet(comment_span)?) =>
+                if contains_comment(
+                    context
+                        .snippet_provider
+                        .span_to_snippet(comment_span)
+                        .unknown_error()?,
+                ) =>
             {
-                let comment_shape = if has_where {
+                let comment_shape = if !before_where_predicates.is_empty() {
                     Shape::indented(indent, context.config)
                 } else {
-                    Shape::indented(indent, context.config)
-                        .block_left(context.config.tab_spaces())?
+                    let shape = Shape::indented(indent, context.config);
+                    shape
+                        .block_left(context.config.tab_spaces())
+                        .max_width_error(shape.width, span)?
                 };
 
                 combine_strs_with_missing_comments(
@@ -1812,12 +1869,39 @@ fn rewrite_ty<R: Rewrite>(
             _ => format!("{result}="),
         };
 
-        // 1 = `;`
-        let shape = Shape::indented(indent, context.config).sub_width(1)?;
-        rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";")
+        // 1 = `;` unless there's a trailing where clause
+        let shape = Shape::indented(indent, context.config);
+        let shape = if after_where_predicates.is_empty() {
+            Shape::indented(indent, context.config)
+                .sub_width(1)
+                .max_width_error(shape.width, span)?
+        } else {
+            shape
+        };
+        rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape)?
     } else {
-        Some(format!("{result};"))
+        result
+    };
+
+    if !after_where_predicates.is_empty() {
+        let option = WhereClauseOption::new(true, WhereClauseSpace::Newline);
+        let after_where_clause_str = rewrite_where_clause(
+            context,
+            after_where_predicates,
+            where_clauses.after.span,
+            context.config.brace_style(),
+            Shape::indented(indent, context.config),
+            false,
+            ";",
+            None,
+            rhs_hi,
+            option,
+        )?;
+        result.push_str(&after_where_clause_str);
     }
+
+    result += ";";
+    Ok(result)
 }
 
 fn type_annotation_spacing(config: &Config) -> (&str, &str) {
@@ -1830,10 +1914,10 @@ fn type_annotation_spacing(config: &Config) -> (&str, &str) {
 pub(crate) fn rewrite_struct_field_prefix(
     context: &RewriteContext<'_>,
     field: &ast::FieldDef,
-) -> Option<String> {
+) -> RewriteResult {
     let vis = format_visibility(context, &field.vis);
     let type_annotation_spacing = type_annotation_spacing(context.config);
-    Some(match field.ident {
+    Ok(match field.ident {
         Some(name) => format!(
             "{}{}{}:",
             vis,
@@ -1846,6 +1930,10 @@ pub(crate) fn rewrite_struct_field_prefix(
 
 impl Rewrite for ast::FieldDef {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         rewrite_struct_field(context, self, shape, 0)
     }
 }
@@ -1855,15 +1943,15 @@ pub(crate) fn rewrite_struct_field(
     field: &ast::FieldDef,
     shape: Shape,
     lhs_max_width: usize,
-) -> Option<String> {
+) -> RewriteResult {
     if contains_skip(&field.attrs) {
-        return Some(context.snippet(field.span()).to_owned());
+        return Ok(context.snippet(field.span()).to_owned());
     }
 
     let type_annotation_spacing = type_annotation_spacing(context.config);
     let prefix = rewrite_struct_field_prefix(context, field)?;
 
-    let attrs_str = field.attrs.rewrite(context, shape)?;
+    let attrs_str = field.attrs.rewrite_result(context, shape)?;
     let attrs_extendable = field.ident.is_none() && is_attributes_extendable(&attrs_str);
     let missing_span = if field.attrs.is_empty() {
         mk_sp(field.span.lo(), field.span.lo())
@@ -1893,12 +1981,14 @@ pub(crate) fn rewrite_struct_field(
     if prefix.is_empty() && !attrs_str.is_empty() && attrs_extendable && spacing.is_empty() {
         spacing.push(' ');
     }
+
     let orig_ty = shape
         .offset_left(overhead + spacing.len())
-        .and_then(|ty_shape| field.ty.rewrite(context, ty_shape));
+        .and_then(|ty_shape| field.ty.rewrite_result(context, ty_shape).ok());
+
     if let Some(ref ty) = orig_ty {
         if !ty.contains('\n') && !contains_comment(context.snippet(missing_span)) {
-            return Some(attr_prefix + &spacing + ty);
+            return Ok(attr_prefix + &spacing + ty);
         }
     }
 
@@ -2045,7 +2135,8 @@ fn rewrite_static(
             comments_span,
             true,
         )
-        .and_then(|res| recover_comment_removed(res, static_parts.span, context))
+        .ok()
+        .map(|res| recover_comment_removed(res, static_parts.span, context))
         .map(|s| if s.ends_with(';') { s } else { s + ";" })
     } else {
         Some(format!("{prefix}{ty_str};"))
@@ -2072,19 +2163,34 @@ impl<'a> Rewrite for OpaqueType<'a> {
 
 impl Rewrite for ast::FnRetTy {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match *self {
-            ast::FnRetTy::Default(_) => Some(String::new()),
+            ast::FnRetTy::Default(_) => Ok(String::new()),
             ast::FnRetTy::Ty(ref ty) => {
-                if context.config.version() == Version::One
+                let arrow_width = "-> ".len();
+                if context.config.style_edition() <= StyleEdition::Edition2021
                     || context.config.indent_style() == IndentStyle::Visual
                 {
-                    let inner_width = shape.width.checked_sub(3)?;
+                    let inner_width = shape
+                        .width
+                        .checked_sub(arrow_width)
+                        .max_width_error(shape.width, self.span())?;
                     return ty
-                        .rewrite(context, Shape::legacy(inner_width, shape.indent + 3))
+                        .rewrite_result(
+                            context,
+                            Shape::legacy(inner_width, shape.indent + arrow_width),
+                        )
                         .map(|r| format!("-> {}", r));
                 }
 
-                ty.rewrite(context, shape.offset_left(3)?)
+                let shape = shape
+                    .offset_left(arrow_width)
+                    .max_width_error(shape.width, self.span())?;
+
+                ty.rewrite_result(context, shape)
                     .map(|s| format!("-> {}", s))
             }
         }
@@ -2126,9 +2232,11 @@ fn get_missing_param_comments(
     };
 
     let comment_before_colon = rewrite_missing_comment(span_before_colon, shape, context)
+        .ok()
         .filter(|comment| !comment.is_empty())
         .map_or(String::new(), |comment| format!(" {}", comment));
     let comment_after_colon = rewrite_missing_comment(span_after_colon, shape, context)
+        .ok()
         .filter(|comment| !comment.is_empty())
         .map_or(String::new(), |comment| format!("{} ", comment));
     (comment_before_colon, comment_after_colon)
@@ -2136,9 +2244,13 @@ fn get_missing_param_comments(
 
 impl Rewrite for ast::Param {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         let param_attrs_result = self
             .attrs
-            .rewrite(context, Shape::legacy(shape.width, shape.indent))?;
+            .rewrite_result(context, Shape::legacy(shape.width, shape.indent))?;
         // N.B. Doc comments aren't typically valid syntax, but could appear
         // in the presence of certain macros - https://github.com/rust-lang/rustfmt/issues/4936
         let (span, has_multiple_attr_lines, has_doc_comments) = if !self.attrs.is_empty() {
@@ -2164,7 +2276,7 @@ impl Rewrite for ast::Param {
         } else if is_named_param(self) {
             let param_name = &self
                 .pat
-                .rewrite(context, Shape::legacy(shape.width, shape.indent))?;
+                .rewrite_result(context, Shape::legacy(shape.width, shape.indent))?;
             let mut result = combine_strs_with_missing_comments(
                 context,
                 &param_attrs_result,
@@ -2181,10 +2293,13 @@ impl Rewrite for ast::Param {
                 result.push_str(colon_spaces(context.config));
                 result.push_str(&after_comment);
                 let overhead = last_line_width(&result);
-                let max_width = shape.width.checked_sub(overhead)?;
-                if let Some(ty_str) = self
+                let max_width = shape
+                    .width
+                    .checked_sub(overhead)
+                    .max_width_error(shape.width, self.span())?;
+                if let Ok(ty_str) = self
                     .ty
-                    .rewrite(context, Shape::legacy(max_width, shape.indent))
+                    .rewrite_result(context, Shape::legacy(max_width, shape.indent))
                 {
                     result.push_str(&ty_str);
                 } else {
@@ -2206,17 +2321,20 @@ impl Rewrite for ast::Param {
                     result.push_str(colon_spaces(context.config));
                     result.push_str(&after_comment);
                     let overhead = last_line_width(&result);
-                    let max_width = shape.width.checked_sub(overhead)?;
+                    let max_width = shape
+                        .width
+                        .checked_sub(overhead)
+                        .max_width_error(shape.width, self.span())?;
                     let ty_str = self
                         .ty
-                        .rewrite(context, Shape::legacy(max_width, shape.indent))?;
+                        .rewrite_result(context, Shape::legacy(max_width, shape.indent))?;
                     result.push_str(&ty_str);
                 }
             }
 
-            Some(result)
+            Ok(result)
         } else {
-            self.ty.rewrite(context, shape)
+            self.ty.rewrite_result(context, shape)
         }
     }
 }
@@ -2228,17 +2346,17 @@ fn rewrite_explicit_self(
     span: Span,
     shape: Shape,
     has_multiple_attr_lines: bool,
-) -> Option<String> {
+) -> RewriteResult {
     match explicit_self.node {
         ast::SelfKind::Region(lt, m) => {
             let mut_str = format_mutability(m);
             match lt {
                 Some(ref l) => {
-                    let lifetime_str = l.rewrite(
+                    let lifetime_str = l.rewrite_result(
                         context,
                         Shape::legacy(context.config.max_width(), Indent::empty()),
                     )?;
-                    Some(combine_strs_with_missing_comments(
+                    Ok(combine_strs_with_missing_comments(
                         context,
                         param_attrs,
                         &format!("&{lifetime_str} {mut_str}self"),
@@ -2247,7 +2365,7 @@ fn rewrite_explicit_self(
                         !has_multiple_attr_lines,
                     )?)
                 }
-                None => Some(combine_strs_with_missing_comments(
+                None => Ok(combine_strs_with_missing_comments(
                     context,
                     param_attrs,
                     &format!("&{mut_str}self"),
@@ -2258,12 +2376,12 @@ fn rewrite_explicit_self(
             }
         }
         ast::SelfKind::Explicit(ref ty, mutability) => {
-            let type_str = ty.rewrite(
+            let type_str = ty.rewrite_result(
                 context,
                 Shape::legacy(context.config.max_width(), Indent::empty()),
             )?;
 
-            Some(combine_strs_with_missing_comments(
+            Ok(combine_strs_with_missing_comments(
                 context,
                 param_attrs,
                 &format!("{}self: {}", format_mutability(mutability), type_str),
@@ -2272,7 +2390,7 @@ fn rewrite_explicit_self(
                 !has_multiple_attr_lines,
             )?)
         }
-        ast::SelfKind::Value(mutability) => Some(combine_strs_with_missing_comments(
+        ast::SelfKind::Value(mutability) => Ok(combine_strs_with_missing_comments(
             context,
             param_attrs,
             &format!("{}self", format_mutability(mutability)),
@@ -2326,7 +2444,7 @@ fn rewrite_fn_base(
     fn_sig: &FnSig<'_>,
     span: Span,
     fn_brace_style: FnBraceStyle,
-) -> Option<(String, bool, bool)> {
+) -> Result<(String, bool, bool), RewriteError> {
     let mut force_new_line_for_brace = false;
 
     let where_clause = &fn_sig.generics.where_clause;
@@ -2370,7 +2488,7 @@ fn rewrite_fn_base(
     // return type later anyway.
     let ret_str = fd
         .output
-        .rewrite(context, Shape::indented(indent, context.config))?;
+        .rewrite_result(context, Shape::indented(indent, context.config))?;
 
     let multi_line_ret_str = ret_str.contains('\n');
     let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() };
@@ -2383,7 +2501,7 @@ fn rewrite_fn_base(
         ret_str_len,
         fn_brace_style,
         multi_line_ret_str,
-    )?;
+    );
 
     debug!(
         "rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, param_indent: {:?}",
@@ -2452,7 +2570,7 @@ fn rewrite_fn_base(
             .last()
             .map_or(false, |last_line| last_line.contains("//"));
 
-        if context.config.version() == Version::Two {
+        if context.config.style_edition() >= StyleEdition::Edition2024 {
             if closing_paren_overflow_max_width {
                 result.push(')');
                 result.push_str(&indent.to_string_with_newline(context.config));
@@ -2495,7 +2613,7 @@ fn rewrite_fn_base(
             }
         };
         let ret_shape = if ret_should_indent {
-            if context.config.version() == Version::One
+            if context.config.style_edition() <= StyleEdition::Edition2021
                 || context.config.indent_style() == IndentStyle::Visual
             {
                 let indent = if param_str.is_empty() {
@@ -2528,7 +2646,7 @@ fn rewrite_fn_base(
                 ret_shape
             }
         } else {
-            if context.config.version() == Version::Two {
+            if context.config.style_edition() >= StyleEdition::Edition2024 {
                 if !param_str.is_empty() || !no_params_and_over_max_width {
                     result.push(' ');
                 }
@@ -2545,7 +2663,7 @@ fn rewrite_fn_base(
         if multi_line_ret_str || ret_should_indent {
             // Now that we know the proper indent and width, we need to
             // re-layout the return type.
-            let ret_str = fd.output.rewrite(context, ret_shape)?;
+            let ret_str = fd.output.rewrite_result(context, ret_shape)?;
             result.push_str(&ret_str);
         } else {
             result.push_str(&ret_str);
@@ -2617,7 +2735,7 @@ fn rewrite_fn_base(
                 context,
                 last_line_width(&result),
             ) {
-                Some(ref missing_comment) if !missing_comment.is_empty() => {
+                Ok(ref missing_comment) if !missing_comment.is_empty() => {
                     result.push_str(missing_comment);
                     force_new_line_for_brace = true;
                 }
@@ -2632,7 +2750,7 @@ fn rewrite_fn_base(
     force_new_line_for_brace |= ends_with_comment;
     force_new_line_for_brace |=
         is_params_multi_lined && context.config.where_single_line() && !where_clause_str.is_empty();
-    Some((result, ends_with_comment, force_new_line_for_brace))
+    Ok((result, ends_with_comment, force_new_line_for_brace))
 }
 
 /// Kind of spaces to put before `where`.
@@ -2703,7 +2821,7 @@ fn rewrite_params(
     param_indent: Indent,
     span: Span,
     variadic: bool,
-) -> Option<String> {
+) -> RewriteResult {
     if params.is_empty() {
         let comment = context
             .snippet(mk_sp(
@@ -2712,7 +2830,7 @@ fn rewrite_params(
                 span.hi() - BytePos(1),
             ))
             .trim();
-        return Some(comment.to_owned());
+        return Ok(comment.to_owned());
     }
     let param_items: Vec<_> = itemize_list(
         context.snippet_provider,
@@ -2723,8 +2841,8 @@ fn rewrite_params(
         |param| param.ty.span.hi(),
         |param| {
             param
-                .rewrite(context, Shape::legacy(multi_line_budget, param_indent))
-                .or_else(|| Some(context.snippet(param.span()).to_owned()))
+                .rewrite_result(context, Shape::legacy(multi_line_budget, param_indent))
+                .or_else(|_| Ok(context.snippet(param.span()).to_owned()))
         },
         span.lo(),
         span.hi(),
@@ -2772,7 +2890,7 @@ fn compute_budgets_for_params(
     ret_str_len: usize,
     fn_brace_style: FnBraceStyle,
     force_vertical_layout: bool,
-) -> Option<(usize, usize, Indent)> {
+) -> (usize, usize, Indent) {
     debug!(
         "compute_budgets_for_params {} {:?}, {}, {:?}",
         result.len(),
@@ -2809,7 +2927,7 @@ fn compute_budgets_for_params(
                 }
             };
 
-            return Some((one_line_budget, multi_line_budget, indent));
+            return (one_line_budget, multi_line_budget, indent);
         }
     }
 
@@ -2821,7 +2939,7 @@ fn compute_budgets_for_params(
         // Account for `)` and possibly ` {`.
         IndentStyle::Visual => new_indent.width() + if ret_str_len == 0 { 1 } else { 3 },
     };
-    Some((0, context.budget(used_space), new_indent))
+    (0, context.budget(used_space), new_indent)
 }
 
 fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> FnBraceStyle {
@@ -2846,12 +2964,12 @@ fn rewrite_generics(
     ident: &str,
     generics: &ast::Generics,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     // FIXME: convert bounds to where-clauses where they get too big or if
     // there is a where-clause at all.
 
     if generics.params.is_empty() {
-        return Some(ident.to_owned());
+        return Ok(ident.to_owned());
     }
 
     let params = generics.params.iter();
@@ -2881,7 +2999,7 @@ fn rewrite_where_clause_rfc_style(
     span_end: Option<BytePos>,
     span_end_before_where: BytePos,
     where_clause_option: WhereClauseOption,
-) -> Option<String> {
+) -> RewriteResult {
     let (where_keyword, allow_single_line) = rewrite_where_keyword(
         context,
         predicates,
@@ -2895,8 +3013,9 @@ fn rewrite_where_clause_rfc_style(
     let clause_shape = shape
         .block()
         .with_max_width(context.config)
-        .block_left(context.config.tab_spaces())?
-        .sub_width(1)?;
+        .block_left(context.config.tab_spaces())
+        .and_then(|s| s.sub_width(1))
+        .max_width_error(shape.width, where_span)?;
     let force_single_line = context.config.where_single_line()
         && predicates.len() == 1
         && !where_clause_option.veto_single_line;
@@ -2921,7 +3040,7 @@ fn rewrite_where_clause_rfc_style(
             clause_shape.indent.to_string_with_newline(context.config)
         };
 
-    Some(format!("{where_keyword}{clause_sep}{preds_str}"))
+    Ok(format!("{where_keyword}{clause_sep}{preds_str}"))
 }
 
 /// Rewrite `where` and comment around it.
@@ -2932,12 +3051,13 @@ fn rewrite_where_keyword(
     shape: Shape,
     span_end_before_where: BytePos,
     where_clause_option: WhereClauseOption,
-) -> Option<(String, bool)> {
+) -> Result<(String, bool), RewriteError> {
     let block_shape = shape.block().with_max_width(context.config);
     // 1 = `,`
     let clause_shape = block_shape
-        .block_left(context.config.tab_spaces())?
-        .sub_width(1)?;
+        .block_left(context.config.tab_spaces())
+        .and_then(|s| s.sub_width(1))
+        .max_width_error(block_shape.width, where_span)?;
 
     let comment_separator = |comment: &str, shape: Shape| {
         if comment.is_empty() {
@@ -2968,7 +3088,7 @@ fn rewrite_where_keyword(
         && comment_before.is_empty()
         && comment_after.is_empty();
 
-    Some((result, allow_single_line))
+    Ok((result, allow_single_line))
 }
 
 /// Rewrite bounds on a where clause.
@@ -2980,7 +3100,7 @@ fn rewrite_bounds_on_where_clause(
     span_end: Option<BytePos>,
     where_clause_option: WhereClauseOption,
     force_single_line: bool,
-) -> Option<String> {
+) -> RewriteResult {
     let span_start = predicates[0].span().lo();
     // If we don't have the start of the next span, then use the end of the
     // predicates, but that means we miss comments.
@@ -2994,7 +3114,7 @@ fn rewrite_bounds_on_where_clause(
         ",",
         |pred| pred.span().lo(),
         |pred| pred.span().hi(),
-        |pred| pred.rewrite(context, shape),
+        |pred| pred.rewrite_result(context, shape),
         span_start,
         span_end,
         false,
@@ -3013,7 +3133,7 @@ fn rewrite_bounds_on_where_clause(
         DefinitiveListTactic::Vertical
     };
 
-    let preserve_newline = context.config.version() == Version::One;
+    let preserve_newline = context.config.style_edition() <= StyleEdition::Edition2021;
 
     let fmt = ListFormatting::new(shape, context.config)
         .tactic(shape_tactic)
@@ -3033,9 +3153,9 @@ fn rewrite_where_clause(
     span_end: Option<BytePos>,
     span_end_before_where: BytePos,
     where_clause_option: WhereClauseOption,
-) -> Option<String> {
+) -> RewriteResult {
     if predicates.is_empty() {
-        return Some(String::new());
+        return Ok(String::new());
     }
 
     if context.config.indent_style() == IndentStyle::Block {
@@ -3075,7 +3195,7 @@ fn rewrite_where_clause(
         ",",
         |pred| pred.span().lo(),
         |pred| pred.span().hi(),
-        |pred| pred.rewrite(context, Shape::legacy(budget, offset)),
+        |pred| pred.rewrite_result(context, Shape::legacy(budget, offset)),
         span_start,
         span_end,
         false,
@@ -3113,13 +3233,13 @@ fn rewrite_where_clause(
         || preds_str.contains('\n')
         || shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width
     {
-        Some(format!(
+        Ok(format!(
             "\n{}where {}",
             (shape.indent + extra_indent).to_string(context.config),
             preds_str
         ))
     } else {
-        Some(format!(" where {preds_str}"))
+        Ok(format!(" where {preds_str}"))
     }
 }
 
@@ -3140,14 +3260,14 @@ fn rewrite_comments_before_after_where(
     span_before_where: Span,
     span_after_where: Span,
     shape: Shape,
-) -> Option<(String, String)> {
+) -> Result<(String, String), RewriteError> {
     let before_comment = rewrite_missing_comment(span_before_where, shape, context)?;
     let after_comment = rewrite_missing_comment(
         span_after_where,
         shape.block_indent(context.config.tab_spaces()),
         context,
     )?;
-    Some((before_comment, after_comment))
+    Ok((before_comment, after_comment))
 }
 
 fn format_header(
@@ -3169,7 +3289,7 @@ fn format_header(
         .opt_span_before(mk_sp(vis.span.lo(), ident.span.hi()), item_name.trim())
     {
         let missing_span = mk_sp(after_vis, before_item_name);
-        if let Some(result_with_comment) = combine_strs_with_missing_comments(
+        if let Ok(result_with_comment) = combine_strs_with_missing_comments(
             context,
             &result,
             item_name,
@@ -3203,7 +3323,7 @@ fn format_generics(
     used_width: usize,
 ) -> Option<String> {
     let shape = Shape::legacy(context.budget(used_width + offset.width()), offset);
-    let mut result = rewrite_generics(context, "", generics, shape)?;
+    let mut result = rewrite_generics(context, "", generics, shape).ok()?;
 
     // If the generics are not parameterized then generics.span.hi() == 0,
     // so we use span.lo(), which is the position after `struct Foo`.
@@ -3229,7 +3349,8 @@ fn format_generics(
             Some(span.hi()),
             span_end_before_where,
             option,
-        )?;
+        )
+        .ok()?;
         result.push_str(&where_clause_str);
         (
             brace_pos == BracePos::ForceSameLine || brace_style == BraceStyle::PreferSameLine,
@@ -3253,7 +3374,8 @@ fn format_generics(
                 ),
                 shape,
                 context,
-            ),
+            )
+            .ok(),
         )
     };
     // add missing comments
@@ -3295,7 +3417,11 @@ fn format_generics(
 
 impl Rewrite for ast::ForeignItem {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
-        let attrs_str = self.attrs.rewrite(context, shape)?;
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        let attrs_str = self.attrs.rewrite_result(context, shape)?;
         // Drop semicolon or it will be interpreted as comment.
         // FIXME: this may be a faulty span from libsyntax.
         let span = mk_sp(self.span.lo(), self.span.hi() - BytePos(1));
@@ -3328,7 +3454,7 @@ impl Rewrite for ast::ForeignItem {
                         defaultness,
                         Some(&inner_attrs),
                     );
-                    Some(visitor.buffer.to_owned())
+                    Ok(visitor.buffer.to_owned())
                 } else {
                     rewrite_fn_base(
                         context,
@@ -3360,7 +3486,9 @@ impl Rewrite for ast::ForeignItem {
                     prefix,
                     &static_foreign_item.ty,
                     &RhsAssignKind::Ty,
-                    shape.sub_width(1)?,
+                    shape
+                        .sub_width(1)
+                        .max_width_error(shape.width, static_foreign_item.ty.span)?,
                 )
                 .map(|s| s + ";")
             }
@@ -3421,6 +3549,7 @@ fn rewrite_attrs(
         shape,
         allow_extend,
     )
+    .ok()
 }
 
 /// Rewrite an inline mod.
diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs
index 4263a49fd72..08cda6913b9 100644
--- a/src/tools/rustfmt/src/lib.rs
+++ b/src/tools/rustfmt/src/lib.rs
@@ -5,9 +5,6 @@
 #![allow(clippy::match_like_matches_macro)]
 #![allow(unreachable_pub)]
 
-// #[macro_use]
-// extern crate tracing;
-
 // N.B. these crates are loaded from the sysroot, so they need extern crate.
 extern crate rustc_ast;
 extern crate rustc_ast_pretty;
@@ -48,8 +45,8 @@ use crate::shape::Indent;
 use crate::utils::indent_next_line;
 
 pub use crate::config::{
-    load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle,
-    Range, Verbosity,
+    CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, Range,
+    StyleEdition, Verbosity, Version, load_config,
 };
 
 pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder};
@@ -94,6 +91,7 @@ mod rewrite;
 pub(crate) mod rustfmt_diff;
 mod shape;
 mod skip;
+mod sort;
 pub(crate) mod source_file;
 pub(crate) mod source_map;
 mod spanned;
diff --git a/src/tools/rustfmt/src/lists.rs b/src/tools/rustfmt/src/lists.rs
index 41afef279e9..415144a8abc 100644
--- a/src/tools/rustfmt/src/lists.rs
+++ b/src/tools/rustfmt/src/lists.rs
@@ -5,10 +5,10 @@ use std::iter::Peekable;
 
 use rustc_span::BytePos;
 
-use crate::comment::{find_comment_end, rewrite_comment, FindUncommented};
+use crate::comment::{FindUncommented, find_comment_end, rewrite_comment};
 use crate::config::lists::*;
 use crate::config::{Config, IndentStyle};
-use crate::rewrite::RewriteContext;
+use crate::rewrite::{RewriteContext, RewriteError, RewriteResult};
 use crate::shape::{Indent, Shape};
 use crate::utils::{
     count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline,
@@ -125,18 +125,18 @@ pub(crate) struct ListItem {
     pub(crate) pre_comment_style: ListItemCommentStyle,
     // Item should include attributes and doc comments. None indicates a failed
     // rewrite.
-    pub(crate) item: Option<String>,
+    pub(crate) item: RewriteResult,
     pub(crate) post_comment: Option<String>,
     // Whether there is extra whitespace before this item.
     pub(crate) new_lines: bool,
 }
 
 impl ListItem {
-    pub(crate) fn empty() -> ListItem {
+    pub(crate) fn from_item(item: RewriteResult) -> ListItem {
         ListItem {
             pre_comment: None,
             pre_comment_style: ListItemCommentStyle::None,
-            item: None,
+            item: item,
             post_comment: None,
             new_lines: false,
         }
@@ -185,7 +185,7 @@ impl ListItem {
         ListItem {
             pre_comment: None,
             pre_comment_style: ListItemCommentStyle::None,
-            item: Some(s.into()),
+            item: Ok(s.into()),
             post_comment: None,
             new_lines: false,
         }
@@ -197,7 +197,11 @@ impl ListItem {
             !matches!(*s, Some(ref s) if !s.is_empty())
         }
 
-        !(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment))
+        fn empty_result(s: &RewriteResult) -> bool {
+            !matches!(*s, Ok(ref s) if !s.is_empty())
+        }
+
+        !(empty(&self.pre_comment) && empty_result(&self.item) && empty(&self.post_comment))
     }
 }
 
@@ -257,7 +261,7 @@ where
 }
 
 // Format a list of commented items into a string.
-pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<String>
+pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> RewriteResult
 where
     I: IntoIterator<Item = T> + Clone,
     T: AsRef<ListItem>,
@@ -281,7 +285,7 @@ where
     let indent_str = &formatting.shape.indent.to_string(formatting.config);
     while let Some((i, item)) = iter.next() {
         let item = item.as_ref();
-        let inner_item = item.item.as_ref()?;
+        let inner_item = item.item.as_ref().or_else(|err| Err(err.clone()))?;
         let first = i == 0;
         let last = iter.peek().is_none();
         let mut separate = match sep_place {
@@ -516,7 +520,7 @@ where
         prev_item_is_nested_import = inner_item.contains("::");
     }
 
-    Some(result)
+    Ok(result)
 }
 
 fn max_width_of_item_with_post_comment<I, T>(
@@ -741,7 +745,7 @@ where
     I: Iterator<Item = T>,
     F1: Fn(&T) -> BytePos,
     F2: Fn(&T) -> BytePos,
-    F3: Fn(&T) -> Option<String>,
+    F3: Fn(&T) -> RewriteResult,
 {
     type Item = ListItem;
 
@@ -775,8 +779,9 @@ where
             ListItem {
                 pre_comment,
                 pre_comment_style,
+                // leave_last is set to true only for rewrite_items
                 item: if self.inner.peek().is_none() && self.leave_last {
-                    None
+                    Err(RewriteError::SkipFormatting)
                 } else {
                     (self.get_item_string)(&item)
                 },
@@ -805,7 +810,7 @@ where
     I: Iterator<Item = T>,
     F1: Fn(&T) -> BytePos,
     F2: Fn(&T) -> BytePos,
-    F3: Fn(&T) -> Option<String>,
+    F3: Fn(&T) -> RewriteResult,
 {
     ListItems {
         snippet_provider,
diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs
index 524fc666fae..5a35e115d8f 100644
--- a/src/tools/rustfmt/src/macros.rs
+++ b/src/tools/rustfmt/src/macros.rs
@@ -10,35 +10,37 @@
 // and those with brackets will be formatted as array literals.
 
 use std::collections::HashMap;
-use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::panic::{AssertUnwindSafe, catch_unwind};
 
 use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
 use rustc_ast::{ast, ptr};
 use rustc_ast_pretty::pprust;
 use rustc_span::{
+    BytePos, DUMMY_SP, Span, Symbol,
     symbol::{self, kw},
-    BytePos, Span, Symbol, DUMMY_SP,
 };
 use tracing::debug;
 
 use crate::comment::{
-    contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses,
+    CharClasses, FindUncommented, FullCodeCharKind, LineClasses, contains_comment,
 };
+use crate::config::StyleEdition;
 use crate::config::lists::*;
-use crate::config::Version;
-use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind};
-use crate::lists::{itemize_list, write_list, ListFormatting};
+use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs};
+use crate::lists::{ListFormatting, itemize_list, write_list};
 use crate::overflow;
 use crate::parse::macros::lazy_static::parse_lazy_static;
-use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs};
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args};
+use crate::rewrite::{
+    MacroErrorKind, Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult,
+};
 use crate::shape::{Indent, Shape};
 use crate::source_map::SpanUtils;
 use crate::spanned::Spanned;
 use crate::utils::{
-    filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp,
-    remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt,
+    NodeIdExt, filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp,
+    remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout,
 };
 use crate::visitor::FmtVisitor;
 
@@ -72,22 +74,30 @@ impl MacroArg {
 
 impl Rewrite for ast::Item {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         let mut visitor = crate::visitor::FmtVisitor::from_context(context);
         visitor.block_indent = shape.indent;
         visitor.last_pos = self.span().lo();
         visitor.visit_item(self);
-        Some(visitor.buffer.to_owned())
+        Ok(visitor.buffer.to_owned())
     }
 }
 
 impl Rewrite for MacroArg {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match *self {
-            MacroArg::Expr(ref expr) => expr.rewrite(context, shape),
-            MacroArg::Ty(ref ty) => ty.rewrite(context, shape),
-            MacroArg::Pat(ref pat) => pat.rewrite(context, shape),
-            MacroArg::Item(ref item) => item.rewrite(context, shape),
-            MacroArg::Keyword(ident, _) => Some(ident.name.to_string()),
+            MacroArg::Expr(ref expr) => expr.rewrite_result(context, shape),
+            MacroArg::Ty(ref ty) => ty.rewrite_result(context, shape),
+            MacroArg::Pat(ref pat) => pat.rewrite_result(context, shape),
+            MacroArg::Item(ref item) => item.rewrite_result(context, shape),
+            MacroArg::Keyword(ident, _) => Ok(ident.name.to_string()),
         }
     }
 }
@@ -111,12 +121,14 @@ fn rewrite_macro_name(
 }
 
 // Use this on failing to format the macro call.
+// TODO(ding-young) We should also report macro parse failure to tell users why given snippet
+// is left unformatted. One possible improvement is appending formatting error to context.report
 fn return_macro_parse_failure_fallback(
     context: &RewriteContext<'_>,
     indent: Indent,
     position: MacroPosition,
     span: Span,
-) -> Option<String> {
+) -> RewriteResult {
     // Mark this as a failure however we format it
     context.macro_rewrite_failure.replace(true);
 
@@ -134,7 +146,8 @@ fn return_macro_parse_failure_fallback(
         })
         .unwrap_or(false);
     if is_like_block_indent_style {
-        return trim_left_preserve_layout(context.snippet(span), indent, context.config);
+        return trim_left_preserve_layout(context.snippet(span), indent, context.config)
+            .macro_error(MacroErrorKind::Unknown, span);
     }
 
     context.skipped_range.borrow_mut().push((
@@ -147,7 +160,7 @@ fn return_macro_parse_failure_fallback(
     if position == MacroPosition::Item {
         snippet.push(';');
     }
-    Some(snippet)
+    Ok(snippet)
 }
 
 pub(crate) fn rewrite_macro(
@@ -156,13 +169,13 @@ pub(crate) fn rewrite_macro(
     context: &RewriteContext<'_>,
     shape: Shape,
     position: MacroPosition,
-) -> Option<String> {
+) -> RewriteResult {
     let should_skip = context
         .skip_context
         .macros
         .skip(context.snippet(mac.path.span));
     if should_skip {
-        None
+        Err(RewriteError::SkipFormatting)
     } else {
         let guard = context.enter_macro();
         let result = catch_unwind(AssertUnwindSafe(|| {
@@ -176,9 +189,16 @@ pub(crate) fn rewrite_macro(
             )
         }));
         match result {
-            Err(..) | Ok(None) => {
+            Err(..) => {
                 context.macro_rewrite_failure.replace(true);
-                None
+                Err(RewriteError::MacroFailure {
+                    kind: MacroErrorKind::Unknown,
+                    span: mac.span(),
+                })
+            }
+            Ok(Err(e)) => {
+                context.macro_rewrite_failure.replace(true);
+                Err(e)
             }
             Ok(rw) => rw,
         }
@@ -192,11 +212,11 @@ fn rewrite_macro_inner(
     shape: Shape,
     position: MacroPosition,
     is_nested_macro: bool,
-) -> Option<String> {
+) -> RewriteResult {
     if context.config.use_try_shorthand() {
         if let Some(expr) = convert_try_mac(mac, context) {
             context.leave_macro();
-            return expr.rewrite(context, shape);
+            return expr.rewrite_result(context, shape);
         }
     }
 
@@ -216,21 +236,27 @@ fn rewrite_macro_inner(
     if ts.is_empty() && !has_comment {
         return match style {
             Delimiter::Parenthesis if position == MacroPosition::Item => {
-                Some(format!("{macro_name}();"))
+                Ok(format!("{macro_name}();"))
             }
-            Delimiter::Bracket if position == MacroPosition::Item => {
-                Some(format!("{macro_name}[];"))
-            }
-            Delimiter::Parenthesis => Some(format!("{macro_name}()")),
-            Delimiter::Bracket => Some(format!("{macro_name}[]")),
-            Delimiter::Brace => Some(format!("{macro_name} {{}}")),
+            Delimiter::Bracket if position == MacroPosition::Item => Ok(format!("{macro_name}[];")),
+            Delimiter::Parenthesis => Ok(format!("{macro_name}()")),
+            Delimiter::Bracket => Ok(format!("{macro_name}[]")),
+            Delimiter::Brace => Ok(format!("{macro_name} {{}}")),
             _ => unreachable!(),
         };
     }
     // Format well-known macros which cannot be parsed as a valid AST.
     if macro_name == "lazy_static!" && !has_comment {
-        if let success @ Some(..) = format_lazy_static(context, shape, ts.clone()) {
-            return success;
+        match format_lazy_static(context, shape, ts.clone(), mac.span()) {
+            Ok(rw) => return Ok(rw),
+            Err(err) => match err {
+                // We will move on to parsing macro args just like other macros
+                // if we could not parse lazy_static! with known syntax
+                RewriteError::MacroFailure { kind, span: _ }
+                    if kind == MacroErrorKind::ParseFailure => {}
+                // If formatting fails even though parsing succeeds, return the err early
+                _ => return Err(err),
+            },
         }
     }
 
@@ -267,7 +293,7 @@ fn rewrite_macro_inner(
         Delimiter::Parenthesis => {
             // Handle special case: `vec!(expr; expr)`
             if vec_with_semi {
-                handle_vec_semi(context, shape, arg_vec, macro_name, style)
+                handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span())
             } else {
                 // Format macro invocation as function call, preserve the trailing
                 // comma because not all macros support them.
@@ -293,7 +319,7 @@ fn rewrite_macro_inner(
         Delimiter::Bracket => {
             // Handle special case: `vec![expr; expr]`
             if vec_with_semi {
-                handle_vec_semi(context, shape, arg_vec, macro_name, style)
+                handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span())
             } else {
                 // If we are rewriting `vec!` macro or other special macros,
                 // then we can rewrite this as a usual array literal.
@@ -323,7 +349,7 @@ fn rewrite_macro_inner(
                     _ => "",
                 };
 
-                Some(format!("{rewrite}{comma}"))
+                Ok(format!("{rewrite}{comma}"))
             }
         }
         Delimiter::Brace => {
@@ -332,8 +358,8 @@ fn rewrite_macro_inner(
             // anything in between the braces (for now).
             let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{');
             match trim_left_preserve_layout(snippet, shape.indent, context.config) {
-                Some(macro_body) => Some(format!("{macro_name} {macro_body}")),
-                None => Some(format!("{macro_name} {snippet}")),
+                Some(macro_body) => Ok(format!("{macro_name} {macro_body}")),
+                None => Ok(format!("{macro_name} {snippet}")),
             }
         }
         _ => unreachable!(),
@@ -346,28 +372,32 @@ fn handle_vec_semi(
     arg_vec: Vec<MacroArg>,
     macro_name: String,
     delim_token: Delimiter,
-) -> Option<String> {
+    span: Span,
+) -> RewriteResult {
     let (left, right) = match delim_token {
         Delimiter::Parenthesis => ("(", ")"),
         Delimiter::Bracket => ("[", "]"),
         _ => unreachable!(),
     };
 
-    let mac_shape = shape.offset_left(macro_name.len())?;
+    // Should we return MaxWidthError, Or Macro failure
+    let mac_shape = shape
+        .offset_left(macro_name.len())
+        .max_width_error(shape.width, span)?;
     // 8 = `vec![]` + `; ` or `vec!()` + `; `
     let total_overhead = 8;
     let nested_shape = mac_shape.block_indent(context.config.tab_spaces());
-    let lhs = arg_vec[0].rewrite(context, nested_shape)?;
-    let rhs = arg_vec[1].rewrite(context, nested_shape)?;
+    let lhs = arg_vec[0].rewrite_result(context, nested_shape)?;
+    let rhs = arg_vec[1].rewrite_result(context, nested_shape)?;
     if !lhs.contains('\n')
         && !rhs.contains('\n')
         && lhs.len() + rhs.len() + total_overhead <= shape.width
     {
         // macro_name(lhs; rhs) or macro_name[lhs; rhs]
-        Some(format!("{macro_name}{left}{lhs}; {rhs}{right}"))
+        Ok(format!("{macro_name}{left}{lhs}; {rhs}{right}"))
     } else {
         // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n]
-        Some(format!(
+        Ok(format!(
             "{}{}{}{};{}{}{}{}",
             macro_name,
             left,
@@ -385,7 +415,7 @@ fn rewrite_empty_macro_def_body(
     context: &RewriteContext<'_>,
     span: Span,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     // Create an empty, dummy `ast::Block` representing an empty macro body
     let block = ast::Block {
         stmts: vec![].into(),
@@ -395,7 +425,7 @@ fn rewrite_empty_macro_def_body(
         tokens: None,
         could_be_bare_literal: false,
     };
-    block.rewrite(context, shape)
+    block.rewrite_result(context, shape)
 }
 
 pub(crate) fn rewrite_macro_def(
@@ -406,8 +436,8 @@ pub(crate) fn rewrite_macro_def(
     ident: symbol::Ident,
     vis: &ast::Visibility,
     span: Span,
-) -> Option<String> {
-    let snippet = Some(remove_trailing_white_spaces(context.snippet(span)));
+) -> RewriteResult {
+    let snippet = Ok(remove_trailing_white_spaces(context.snippet(span)));
     if snippet.as_ref().map_or(true, |s| s.ends_with(';')) {
         return snippet;
     }
@@ -442,7 +472,7 @@ pub(crate) fn rewrite_macro_def(
         let lo = context.snippet_provider.span_before(span, "{");
         result += " ";
         result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?;
-        return Some(result);
+        return Ok(result);
     }
 
     let branch_items = itemize_list(
@@ -453,13 +483,14 @@ pub(crate) fn rewrite_macro_def(
         |branch| branch.span.lo(),
         |branch| branch.span.hi(),
         |branch| match branch.rewrite(context, arm_shape, multi_branch_style) {
-            Some(v) => Some(v),
+            Ok(v) => Ok(v),
             // if the rewrite returned None because a macro could not be rewritten, then return the
             // original body
-            None if context.macro_rewrite_failure.get() => {
-                Some(context.snippet(branch.body).trim().to_string())
+            // TODO(ding-young) report rewrite error even if we return Ok with original snippet
+            Err(_) if context.macro_rewrite_failure.get() => {
+                Ok(context.snippet(branch.body).trim().to_string())
             }
-            None => None,
+            Err(e) => Err(e),
         },
         context.snippet_provider.span_after(span, "{"),
         span.hi(),
@@ -478,8 +509,8 @@ pub(crate) fn rewrite_macro_def(
     }
 
     match write_list(&branch_items, &fmt) {
-        Some(ref s) => result += s,
-        None => return snippet,
+        Ok(ref s) => result += s,
+        Err(_) => return snippet,
     }
 
     if multi_branch_style {
@@ -487,7 +518,7 @@ pub(crate) fn rewrite_macro_def(
         result += "}";
     }
 
-    Some(result)
+    Ok(result)
 }
 
 fn register_metavariable(
@@ -639,12 +670,13 @@ impl MacroArgKind {
         context: &RewriteContext<'_>,
         shape: Shape,
         use_multiple_lines: bool,
-    ) -> Option<String> {
-        let rewrite_delimited_inner = |delim_tok, args| -> Option<(String, String, String)> {
+    ) -> RewriteResult {
+        type DelimitedArgsRewrite = Result<(String, String, String), RewriteError>;
+        let rewrite_delimited_inner = |delim_tok, args| -> DelimitedArgsRewrite {
             let inner = wrap_macro_args(context, args, shape)?;
             let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty());
             if lhs.len() + inner.len() + rhs.len() <= shape.width {
-                return Some((lhs, inner, rhs));
+                return Ok((lhs, inner, rhs));
             }
 
             let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false);
@@ -652,27 +684,27 @@ impl MacroArgKind {
                 .block_indent(context.config.tab_spaces())
                 .with_max_width(context.config);
             let inner = wrap_macro_args(context, args, nested_shape)?;
-            Some((lhs, inner, rhs))
+            Ok((lhs, inner, rhs))
         };
 
         match *self {
-            MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${name}:{ty}")),
+            MacroArgKind::MetaVariable(ty, ref name) => Ok(format!("${name}:{ty}")),
             MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => {
                 let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?;
                 let another = another
                     .as_ref()
-                    .and_then(|a| a.rewrite(context, shape, use_multiple_lines))
+                    .and_then(|a| a.rewrite(context, shape, use_multiple_lines).ok())
                     .unwrap_or_else(|| "".to_owned());
                 let repeat_tok = pprust::token_to_string(tok);
 
-                Some(format!("${lhs}{inner}{rhs}{another}{repeat_tok}"))
+                Ok(format!("${lhs}{inner}{rhs}{another}{repeat_tok}"))
             }
             MacroArgKind::Delimited(delim_tok, ref args) => {
                 rewrite_delimited_inner(delim_tok, args)
                     .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs))
             }
-            MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")),
-            MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")),
+            MacroArgKind::Separator(ref sep, ref prefix) => Ok(format!("{prefix}{sep} ")),
+            MacroArgKind::Other(ref inner, ref prefix) => Ok(format!("{prefix}{inner}")),
         }
     }
 }
@@ -688,7 +720,7 @@ impl ParsedMacroArg {
         context: &RewriteContext<'_>,
         shape: Shape,
         use_multiple_lines: bool,
-    ) -> Option<String> {
+    ) -> RewriteResult {
         self.kind.rewrite(context, shape, use_multiple_lines)
     }
 }
@@ -966,9 +998,9 @@ fn wrap_macro_args(
     context: &RewriteContext<'_>,
     args: &[ParsedMacroArg],
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     wrap_macro_args_inner(context, args, shape, false)
-        .or_else(|| wrap_macro_args_inner(context, args, shape, true))
+        .or_else(|_| wrap_macro_args_inner(context, args, shape, true))
 }
 
 fn wrap_macro_args_inner(
@@ -976,7 +1008,7 @@ fn wrap_macro_args_inner(
     args: &[ParsedMacroArg],
     shape: Shape,
     use_multiple_lines: bool,
-) -> Option<String> {
+) -> RewriteResult {
     let mut result = String::with_capacity(128);
     let mut iter = args.iter().peekable();
     let indent_str = shape.indent.to_string_with_newline(context.config);
@@ -1002,9 +1034,9 @@ fn wrap_macro_args_inner(
     }
 
     if !use_multiple_lines && result.len() >= shape.width {
-        None
+        Err(RewriteError::Unknown)
     } else {
-        Some(result)
+        Ok(result)
     }
 }
 
@@ -1012,22 +1044,21 @@ fn wrap_macro_args_inner(
 // for some common cases. I hope the basic logic is sufficient. Note that the
 // meaning of some tokens is a bit different here from usual Rust, e.g., `*`
 // and `(`/`)` have special meaning.
-//
-// We always try and format on one line.
-// FIXME: Use multi-line when every thing does not fit on one line.
 fn format_macro_args(
     context: &RewriteContext<'_>,
     token_stream: TokenStream,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
+    let span = span_for_token_stream(&token_stream);
     if !context.config.format_macro_matchers() {
-        let span = span_for_token_stream(&token_stream);
-        return Some(match span {
+        return Ok(match span {
             Some(span) => context.snippet(span).to_owned(),
             None => String::new(),
         });
     }
-    let parsed_args = MacroArgParser::new().parse(token_stream)?;
+    let parsed_args = MacroArgParser::new()
+        .parse(token_stream)
+        .macro_error(MacroErrorKind::ParseFailure, span.unwrap())?;
     wrap_macro_args(context, &parsed_args, shape)
 }
 
@@ -1132,9 +1163,9 @@ pub(crate) fn convert_try_mac(
 
 pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> Delimiter {
     let snippet = context.snippet(mac.span());
-    let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value());
-    let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value());
-    let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value());
+    let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::MAX);
+    let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::MAX);
+    let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::MAX);
 
     if paren_pos < bracket_pos && paren_pos < brace_pos {
         Delimiter::Parenthesis
@@ -1242,23 +1273,31 @@ impl MacroBranch {
         context: &RewriteContext<'_>,
         shape: Shape,
         multi_branch_style: bool,
-    ) -> Option<String> {
+    ) -> RewriteResult {
         // Only attempt to format function-like macros.
         if self.args_paren_kind != Delimiter::Parenthesis {
             // FIXME(#1539): implement for non-sugared macros.
-            return None;
+            return Err(RewriteError::MacroFailure {
+                kind: MacroErrorKind::Unknown,
+                span: self.span,
+            });
         }
 
         let old_body = context.snippet(self.body).trim();
         let has_block_body = old_body.starts_with('{');
         let mut prefix_width = 5; // 5 = " => {"
-        if context.config.version() == Version::Two {
+        if context.config.style_edition() >= StyleEdition::Edition2024 {
             if has_block_body {
                 prefix_width = 6; // 6 = " => {{"
             }
         }
-        let mut result =
-            format_macro_args(context, self.args.clone(), shape.sub_width(prefix_width)?)?;
+        let mut result = format_macro_args(
+            context,
+            self.args.clone(),
+            shape
+                .sub_width(prefix_width)
+                .max_width_error(shape.width, self.span)?,
+        )?;
 
         if multi_branch_style {
             result += " =>";
@@ -1267,7 +1306,7 @@ impl MacroBranch {
         if !context.config.format_macro_bodies() {
             result += " ";
             result += context.snippet(self.whole_body);
-            return Some(result);
+            return Ok(result);
         }
 
         // The macro body is the most interesting part. It might end up as various
@@ -1276,7 +1315,8 @@ impl MacroBranch {
         // `$$`). We'll try and format like an AST node, but we'll substitute
         // variables for new names with the same length first.
 
-        let (body_str, substs) = replace_names(old_body)?;
+        let (body_str, substs) =
+            replace_names(old_body).macro_error(MacroErrorKind::ReplaceMacroVariable, self.span)?;
 
         let mut config = context.config.clone();
         config.set().show_parse_errors(false);
@@ -1299,13 +1339,21 @@ impl MacroBranch {
                 config.set().max_width(new_width);
                 match crate::format_code_block(&body_str, &config, true) {
                     Some(new_body) => new_body,
-                    None => return None,
+                    None => {
+                        return Err(RewriteError::MacroFailure {
+                            kind: MacroErrorKind::Unknown,
+                            span: self.span,
+                        });
+                    }
                 }
             }
         };
 
         if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) {
-            return None;
+            return Err(RewriteError::ExceedsMaxWidth {
+                configured_width: shape.width,
+                span: self.span,
+            });
         }
 
         // Indent the body since it is in a block.
@@ -1331,7 +1379,10 @@ impl MacroBranch {
         for (old, new) in &substs {
             if old_body.contains(new) {
                 debug!("rewrite_macro_def: bailing matching variable: `{}`", new);
-                return None;
+                return Err(RewriteError::MacroFailure {
+                    kind: MacroErrorKind::ReplaceMacroVariable,
+                    span: self.span,
+                });
             }
             new_body = new_body.replace(new, old);
         }
@@ -1346,7 +1397,7 @@ impl MacroBranch {
 
         result += "}";
 
-        Some(result)
+        Ok(result)
     }
 }
 
@@ -1366,7 +1417,8 @@ fn format_lazy_static(
     context: &RewriteContext<'_>,
     shape: Shape,
     ts: TokenStream,
-) -> Option<String> {
+    span: Span,
+) -> RewriteResult {
     let mut result = String::with_capacity(1024);
     let nested_shape = shape
         .block_indent(context.config.tab_spaces())
@@ -1375,7 +1427,8 @@ fn format_lazy_static(
     result.push_str("lazy_static! {");
     result.push_str(&nested_shape.indent.to_string_with_newline(context.config));
 
-    let parsed_elems = parse_lazy_static(context, ts)?;
+    let parsed_elems =
+        parse_lazy_static(context, ts).macro_error(MacroErrorKind::ParseFailure, span)?;
     let last = parsed_elems.len() - 1;
     for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() {
         // Rewrite as a static item.
@@ -1385,14 +1438,16 @@ fn format_lazy_static(
             "{}static ref {}: {} =",
             vis,
             id,
-            ty.rewrite(context, nested_shape)?
+            ty.rewrite_result(context, nested_shape)?
         ));
         result.push_str(&rewrite_assign_rhs(
             context,
             stmt,
             &*expr,
             &RhsAssignKind::Expr(&expr.kind, expr.span),
-            nested_shape.sub_width(1)?,
+            nested_shape
+                .sub_width(1)
+                .max_width_error(nested_shape.width, expr.span)?,
         )?);
         result.push(';');
         if i != last {
@@ -1403,7 +1458,7 @@ fn format_lazy_static(
     result.push_str(&shape.indent.to_string_with_newline(context.config));
     result.push('}');
 
-    Some(result)
+    Ok(result)
 }
 
 fn rewrite_macro_with_items(
@@ -1415,12 +1470,12 @@ fn rewrite_macro_with_items(
     original_style: Delimiter,
     position: MacroPosition,
     span: Span,
-) -> Option<String> {
+) -> RewriteResult {
     let style_to_delims = |style| match style {
-        Delimiter::Parenthesis => Some(("(", ")")),
-        Delimiter::Bracket => Some(("[", "]")),
-        Delimiter::Brace => Some((" {", "}")),
-        _ => None,
+        Delimiter::Parenthesis => Ok(("(", ")")),
+        Delimiter::Bracket => Ok(("[", "]")),
+        Delimiter::Brace => Ok((" {", "}")),
+        _ => Err(RewriteError::Unknown),
     };
 
     let (opener, closer) = style_to_delims(style)?;
@@ -1442,7 +1497,7 @@ fn rewrite_macro_with_items(
     for item in items {
         let item = match item {
             MacroArg::Item(item) => item,
-            _ => return None,
+            _ => return Err(RewriteError::Unknown),
         };
         visitor.visit_item(item);
     }
@@ -1455,5 +1510,5 @@ fn rewrite_macro_with_items(
     result.push_str(&shape.indent.to_string_with_newline(context.config));
     result.push_str(closer);
     result.push_str(trailing_semicolon);
-    Some(result)
+    Ok(result)
 }
diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs
index 30bf6271b2e..8f62648e576 100644
--- a/src/tools/rustfmt/src/matches.rs
+++ b/src/tools/rustfmt/src/matches.rs
@@ -2,19 +2,19 @@
 
 use std::iter::repeat;
 
-use rustc_ast::{ast, ptr, MatchKind};
+use rustc_ast::{MatchKind, ast, ptr};
 use rustc_span::{BytePos, Span};
 use tracing::debug;
 
-use crate::comment::{combine_strs_with_missing_comments, rewrite_comment, FindUncommented};
+use crate::comment::{FindUncommented, combine_strs_with_missing_comments, rewrite_comment};
 use crate::config::lists::*;
-use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, Version};
+use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, StyleEdition};
 use crate::expr::{
-    format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, rewrite_cond,
-    ExprType, RhsTactics,
+    ExprType, RhsTactics, format_expr, is_empty_block, is_simple_block, is_unsafe_block,
+    prefer_next_line, rewrite_cond,
 };
-use crate::lists::{itemize_list, write_list, ListFormatting};
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::lists::{ListFormatting, itemize_list, write_list};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
 use crate::source_map::SpanUtils;
 use crate::spanned::Spanned;
@@ -56,6 +56,10 @@ impl<'a> Spanned for ArmWrapper<'a> {
 
 impl<'a> Rewrite for ArmWrapper<'a> {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         rewrite_match_arm(
             context,
             self.arm,
@@ -74,7 +78,7 @@ pub(crate) fn rewrite_match(
     span: Span,
     attrs: &[ast::Attribute],
     match_kind: MatchKind,
-) -> Option<String> {
+) -> RewriteResult {
     // Do not take the rhs overhead from the upper expressions into account
     // when rewriting match condition.
     let cond_shape = Shape {
@@ -83,10 +87,14 @@ pub(crate) fn rewrite_match(
     };
     // 6 = `match `
     let cond_shape = match context.config.indent_style() {
-        IndentStyle::Visual => cond_shape.shrink_left(6)?,
-        IndentStyle::Block => cond_shape.offset_left(6)?,
+        IndentStyle::Visual => cond_shape
+            .shrink_left(6)
+            .max_width_error(shape.width, span)?,
+        IndentStyle::Block => cond_shape
+            .offset_left(6)
+            .max_width_error(shape.width, span)?,
     };
-    let cond_str = cond.rewrite(context, cond_shape)?;
+    let cond_str = cond.rewrite_result(context, cond_shape)?;
     let alt_block_sep = &shape.indent.to_string_with_newline(context.config);
     let block_sep = match context.config.control_brace_style() {
         ControlBraceStyle::AlwaysNextLine => alt_block_sep,
@@ -105,12 +113,13 @@ pub(crate) fn rewrite_match(
     let inner_attrs_str = if inner_attrs.is_empty() {
         String::new()
     } else {
-        let shape = match context.config.version() {
-            Version::One => shape,
-            _ => shape.block_indent(context.config.tab_spaces()),
+        let shape = if context.config.style_edition() <= StyleEdition::Edition2021 {
+            shape
+        } else {
+            shape.block_indent(context.config.tab_spaces())
         };
         inner_attrs
-            .rewrite(context, shape)
+            .rewrite_result(context, shape)
             .map(|s| format!("{}{}\n", nested_indent_str, s))?
     };
 
@@ -130,16 +139,16 @@ pub(crate) fn rewrite_match(
     if arms.is_empty() {
         let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1)));
         if snippet.trim().is_empty() {
-            Some(format!("match {cond_str} {{}}"))
+            Ok(format!("match {cond_str} {{}}"))
         } else {
             // Empty match with comments or inner attributes? We are not going to bother, sorry ;)
-            Some(context.snippet(span).to_owned())
+            Ok(context.snippet(span).to_owned())
         }
     } else {
         let span_after_cond = mk_sp(cond.span.hi(), span.hi());
 
         match match_kind {
-            MatchKind::Prefix => Some(format!(
+            MatchKind::Prefix => Ok(format!(
                 "match {}{}{{\n{}{}{}\n{}}}",
                 cond_str,
                 block_sep,
@@ -148,7 +157,7 @@ pub(crate) fn rewrite_match(
                 rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?,
                 shape.indent.to_string(context.config),
             )),
-            MatchKind::Postfix => Some(format!(
+            MatchKind::Postfix => Ok(format!(
                 "{}.match{}{{\n{}{}{}\n{}}}",
                 cond_str,
                 block_sep,
@@ -198,7 +207,7 @@ fn rewrite_match_arms(
     shape: Shape,
     span: Span,
     open_brace_pos: BytePos,
-) -> Option<String> {
+) -> RewriteResult {
     let arm_shape = shape
         .block_indent(context.config.tab_spaces())
         .with_max_width(context.config);
@@ -218,7 +227,7 @@ fn rewrite_match_arms(
         "|",
         |arm| arm.span().lo(),
         |arm| arm.span().hi(),
-        |arm| arm.rewrite(context, arm_shape),
+        |arm| arm.rewrite_result(context, arm_shape),
         open_brace_pos,
         span.hi(),
         false,
@@ -238,19 +247,19 @@ fn rewrite_match_arm(
     shape: Shape,
     is_last: bool,
     has_leading_pipe: bool,
-) -> Option<String> {
+) -> RewriteResult {
     let (missing_span, attrs_str) = if !arm.attrs.is_empty() {
         if contains_skip(&arm.attrs) {
-            let (_, body) = flatten_arm_body(context, arm.body.as_deref()?, None);
+            let (_, body) = flatten_arm_body(context, arm.body.as_deref().unknown_error()?, None);
             // `arm.span()` does not include trailing comma, add it manually.
-            return Some(format!(
+            return Ok(format!(
                 "{}{}",
                 context.snippet(arm.span()),
                 arm_comma(context.config, body, is_last),
             ));
         }
         let missing_span = mk_sp(arm.attrs[arm.attrs.len() - 1].span.hi(), arm.pat.span.lo());
-        (missing_span, arm.attrs.rewrite(context, shape)?)
+        (missing_span, arm.attrs.rewrite_result(context, shape)?)
     } else {
         (mk_sp(arm.span().lo(), arm.span().lo()), String::new())
     };
@@ -264,19 +273,25 @@ fn rewrite_match_arm(
     };
 
     // Patterns
-    let pat_shape = match &arm.body.as_ref()?.kind {
+    let pat_shape = match &arm.body.as_ref().unknown_error()?.kind {
         ast::ExprKind::Block(_, Some(label)) => {
             // Some block with a label ` => 'label: {`
             // 7 = ` => : {`
             let label_len = label.ident.as_str().len();
-            shape.sub_width(7 + label_len)?.offset_left(pipe_offset)?
+            shape
+                .sub_width(7 + label_len)
+                .and_then(|s| s.offset_left(pipe_offset))
+                .max_width_error(shape.width, arm.span)?
         }
         _ => {
             // 5 = ` => {`
-            shape.sub_width(5)?.offset_left(pipe_offset)?
+            shape
+                .sub_width(5)
+                .and_then(|s| s.offset_left(pipe_offset))
+                .max_width_error(shape.width, arm.span)?
         }
     };
-    let pats_str = arm.pat.rewrite(context, pat_shape)?;
+    let pats_str = arm.pat.rewrite_result(context, pat_shape)?;
 
     // Guard
     let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces();
@@ -298,10 +313,13 @@ fn rewrite_match_arm(
         false,
     )?;
 
-    let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.as_ref()?.span().lo());
+    let arrow_span = mk_sp(
+        arm.pat.span.hi(),
+        arm.body.as_ref().unknown_error()?.span().lo(),
+    );
     rewrite_match_body(
         context,
-        arm.body.as_ref()?,
+        arm.body.as_ref().unknown_error()?,
         &lhs_str,
         shape,
         guard_str.contains('\n'),
@@ -382,7 +400,7 @@ fn rewrite_match_body(
     has_guard: bool,
     arrow_span: Span,
     is_last: bool,
-) -> Option<String> {
+) -> RewriteResult {
     let (extend, body) = flatten_arm_body(
         context,
         body,
@@ -403,7 +421,7 @@ fn rewrite_match_body(
             _ => " ",
         };
 
-        Some(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma))
+        Ok(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma))
     };
 
     let next_line_indent = if !is_block || is_empty_block {
@@ -420,7 +438,7 @@ fn rewrite_match_body(
         let arrow_snippet = context.snippet(arrow_span).trim();
         // search for the arrow starting from the end of the snippet since there may be a match
         // expression within the guard
-        let arrow_index = if context.config.version() == Version::One {
+        let arrow_index = if context.config.style_edition() <= StyleEdition::Edition2021 {
             arrow_snippet.rfind("=>").unwrap()
         } else {
             arrow_snippet.find_last_uncommented("=>").unwrap()
@@ -447,7 +465,7 @@ fn rewrite_match_body(
             result.push_str(&nested_indent_str);
             result.push_str(body_str);
             result.push_str(comma);
-            return Some(result);
+            return Ok(result);
         }
 
         let indent_str = shape.indent.to_string_with_newline(context.config);
@@ -458,7 +476,7 @@ fn rewrite_match_body(
                 } else {
                     ""
                 };
-                let semicolon = if context.config.version() == Version::One {
+                let semicolon = if context.config.style_edition() <= StyleEdition::Edition2021 {
                     ""
                 } else {
                     if semicolon_for_expr(context, body) {
@@ -490,7 +508,7 @@ fn rewrite_match_body(
         result.push_str(&block_sep);
         result.push_str(body_str);
         result.push_str(&body_suffix);
-        Some(result)
+        Ok(result)
     };
 
     // Let's try and get the arm body on the same line as the condition.
@@ -499,7 +517,7 @@ fn rewrite_match_body(
         .offset_left(extra_offset(pats_str, shape) + 4)
         .and_then(|shape| shape.sub_width(comma.len()));
     let orig_body = if forbid_same_line || !arrow_comment.is_empty() {
-        None
+        Err(RewriteError::Unknown)
     } else if let Some(body_shape) = orig_body_shape {
         let rewrite = nop_block_collapse(
             format_expr(body, ExprType::Statement, context, body_shape),
@@ -507,7 +525,7 @@ fn rewrite_match_body(
         );
 
         match rewrite {
-            Some(ref body_str)
+            Ok(ref body_str)
                 if is_block
                     || (!body_str.contains('\n')
                         && unicode_str_width(body_str) <= body_shape.width) =>
@@ -517,7 +535,7 @@ fn rewrite_match_body(
             _ => rewrite,
         }
     } else {
-        None
+        Err(RewriteError::Unknown)
     };
     let orig_budget = orig_body_shape.map_or(0, |shape| shape.width);
 
@@ -528,20 +546,23 @@ fn rewrite_match_body(
         next_line_body_shape.width,
     );
     match (orig_body, next_line_body) {
-        (Some(ref orig_str), Some(ref next_line_str))
+        (Ok(ref orig_str), Ok(ref next_line_str))
             if prefer_next_line(orig_str, next_line_str, RhsTactics::Default) =>
         {
             combine_next_line_body(next_line_str)
         }
-        (Some(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => {
+        (Ok(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => {
             combine_orig_body(orig_str)
         }
-        (Some(ref orig_str), Some(ref next_line_str)) if orig_str.contains('\n') => {
+        (Ok(ref orig_str), Ok(ref next_line_str)) if orig_str.contains('\n') => {
             combine_next_line_body(next_line_str)
         }
-        (None, Some(ref next_line_str)) => combine_next_line_body(next_line_str),
-        (None, None) => None,
-        (Some(ref orig_str), _) => combine_orig_body(orig_str),
+        (Err(_), Ok(ref next_line_str)) => combine_next_line_body(next_line_str),
+        // When both orig_body and next_line_body result in errors, we currently propagate the
+        // error from the second attempt since it is more generous with width constraints.
+        // This decision is somewhat arbitrary and is open to change.
+        (Err(_), Err(next_line_err)) => Err(next_line_err),
+        (Ok(ref orig_str), _) => combine_orig_body(orig_str),
     }
 }
 
@@ -554,7 +575,7 @@ fn rewrite_guard(
     // the arm (excludes offset).
     pattern_width: usize,
     multiline_pattern: bool,
-) -> Option<String> {
+) -> RewriteResult {
     if let Some(ref guard) = *guard {
         // First try to fit the guard string on the same line as the pattern.
         // 4 = ` if `, 5 = ` => {`
@@ -563,9 +584,9 @@ fn rewrite_guard(
             .and_then(|s| s.sub_width(5));
         if !multiline_pattern {
             if let Some(cond_shape) = cond_shape {
-                if let Some(cond_str) = guard.rewrite(context, cond_shape) {
+                if let Ok(cond_str) = guard.rewrite_result(context, cond_shape) {
                     if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() {
-                        return Some(format!(" if {cond_str}"));
+                        return Ok(format!(" if {cond_str}"));
                     }
                 }
             }
@@ -575,24 +596,20 @@ fn rewrite_guard(
         // 3 = `if `, 5 = ` => {`
         let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config)
             .offset_left(3)
-            .and_then(|s| s.sub_width(5));
-        if let Some(cond_shape) = cond_shape {
-            if let Some(cond_str) = guard.rewrite(context, cond_shape) {
-                return Some(format!(
-                    "{}if {}",
-                    cond_shape.indent.to_string_with_newline(context.config),
-                    cond_str
-                ));
-            }
-        }
-
-        None
+            .and_then(|s| s.sub_width(5))
+            .max_width_error(shape.width, guard.span)?;
+        let cond_str = guard.rewrite_result(context, cond_shape)?;
+        Ok(format!(
+            "{}if {}",
+            cond_shape.indent.to_string_with_newline(context.config),
+            cond_str
+        ))
     } else {
-        Some(String::new())
+        Ok(String::new())
     }
 }
 
-fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> {
+fn nop_block_collapse(block_str: RewriteResult, budget: usize) -> RewriteResult {
     debug!("nop_block_collapse {:?} {}", block_str, budget);
     block_str.map(|block_str| {
         if block_str.starts_with('{')
diff --git a/src/tools/rustfmt/src/missed_spans.rs b/src/tools/rustfmt/src/missed_spans.rs
index 56adb2e5e69..384de1ce9ae 100644
--- a/src/tools/rustfmt/src/missed_spans.rs
+++ b/src/tools/rustfmt/src/missed_spans.rs
@@ -1,10 +1,10 @@
 use rustc_span::{BytePos, Pos, Span};
 use tracing::debug;
 
-use crate::comment::{is_last_comment_block, rewrite_comment, CodeCharKind, CommentCodeSlices};
-use crate::config::file_lines::FileLines;
+use crate::comment::{CodeCharKind, CommentCodeSlices, is_last_comment_block, rewrite_comment};
 use crate::config::FileName;
-use crate::config::Version;
+use crate::config::StyleEdition;
+use crate::config::file_lines::FileLines;
 use crate::coverage::transform_missing_snippet;
 use crate::shape::{Indent, Shape};
 use crate::source_map::LineRangeUtils;
@@ -247,7 +247,9 @@ impl<'a> FmtVisitor<'a> {
             let indent_str = self.block_indent.to_string(self.config);
             self.push_str(&indent_str);
             self.block_indent
-        } else if self.config.version() == Version::Two && !snippet.starts_with('\n') {
+        } else if self.config.style_edition() >= StyleEdition::Edition2024
+            && !snippet.starts_with('\n')
+        {
             // The comment appears on the same line as the previous formatted code.
             // Assuming that comment is logically associated with that code, we want to keep it on
             // the same level and avoid mixing it with possible other comment.
@@ -283,13 +285,13 @@ impl<'a> FmtVisitor<'a> {
                     let other_lines = &subslice[offset + 1..];
                     let comment_str =
                         rewrite_comment(other_lines, false, comment_shape, self.config)
-                            .unwrap_or_else(|| String::from(other_lines));
+                            .unwrap_or_else(|_| String::from(other_lines));
                     self.push_str(&comment_str);
                 }
             }
         } else {
             let comment_str = rewrite_comment(subslice, false, comment_shape, self.config)
-                .unwrap_or_else(|| String::from(subslice));
+                .unwrap_or_else(|_| String::from(subslice));
             self.push_str(&comment_str);
         }
 
diff --git a/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs
index 0590f28ee05..493b04f16c6 100644
--- a/src/tools/rustfmt/src/modules.rs
+++ b/src/tools/rustfmt/src/modules.rs
@@ -4,8 +4,8 @@ use std::path::{Path, PathBuf};
 
 use rustc_ast::ast;
 use rustc_ast::visit::Visitor;
-use rustc_span::symbol::{self, sym, Symbol};
 use rustc_span::Span;
+use rustc_span::symbol::{self, Symbol, sym};
 use thin_vec::ThinVec;
 use thiserror::Error;
 
diff --git a/src/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs
index cdb735be8a4..fdc8e23e72b 100644
--- a/src/tools/rustfmt/src/overflow.rs
+++ b/src/tools/rustfmt/src/overflow.rs
@@ -9,22 +9,22 @@ use rustc_span::Span;
 use tracing::debug;
 
 use crate::closures;
-use crate::config::Version;
-use crate::config::{lists::*, Config};
+use crate::config::StyleEdition;
+use crate::config::{Config, lists::*};
 use crate::expr::{
     can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
     rewrite_cond,
 };
 use crate::lists::{
-    definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
+    ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
 };
 use crate::macros::MacroArg;
-use crate::patterns::{can_be_overflowed_pat, TuplePatField};
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::patterns::{TuplePatField, can_be_overflowed_pat};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
 use crate::source_map::SpanUtils;
 use crate::spanned::Spanned;
-use crate::types::{can_be_overflowed_type, SegmentParam};
+use crate::types::{SegmentParam, can_be_overflowed_type};
 use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
 
 /// A list of `format!`-like macros, that take a long format string and a list of arguments to
@@ -91,6 +91,10 @@ impl<'a> Rewrite for OverflowableItem<'a> {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
         self.map(|item| item.rewrite(context, shape))
     }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        self.map(|item| item.rewrite_result(context, shape))
+    }
 }
 
 impl<'a> Spanned for OverflowableItem<'a> {
@@ -201,8 +205,12 @@ impl<'a> OverflowableItem<'a> {
             OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR,
             _ => &[],
         };
-        let additional_cases = match (self, config.version()) {
-            (OverflowableItem::MacroArg(..), Version::Two) => SPECIAL_CASE_MACROS_V2,
+        let additional_cases = match self {
+            OverflowableItem::MacroArg(..)
+                if config.style_edition() >= StyleEdition::Edition2024 =>
+            {
+                SPECIAL_CASE_MACROS_V2
+            }
             _ => &[],
         };
         base_cases.iter().chain(additional_cases)
@@ -278,7 +286,7 @@ pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
     span: Span,
     item_max_width: usize,
     force_separator_tactic: Option<SeparatorTactic>,
-) -> Option<String> {
+) -> RewriteResult {
     Context::new(
         context,
         items,
@@ -300,7 +308,7 @@ pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
     items: impl Iterator<Item = &'a T>,
     shape: Shape,
     span: Span,
-) -> Option<String> {
+) -> RewriteResult {
     Context::new(
         context,
         items,
@@ -324,7 +332,7 @@ pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>
     span: Span,
     force_separator_tactic: Option<SeparatorTactic>,
     delim_token: Option<Delimiter>,
-) -> Option<String> {
+) -> RewriteResult {
     let (lhs, rhs) = match delim_token {
         Some(Delimiter::Parenthesis) => ("(", ")"),
         Some(Delimiter::Brace) => ("{", "}"),
@@ -428,7 +436,7 @@ impl<'a> Context<'a> {
                         if closures::args_have_many_closure(&self.items) {
                             None
                         } else {
-                            closures::rewrite_last_closure(self.context, expr, shape)
+                            closures::rewrite_last_closure(self.context, expr, shape).ok()
                         }
                     }
 
@@ -457,7 +465,7 @@ impl<'a> Context<'a> {
 
         if let Some(rewrite) = rewrite {
             // splitn(2, *).next().unwrap() is always safe.
-            let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned());
+            let rewrite_first_line = Ok(rewrite.splitn(2, '\n').next().unwrap().to_owned());
             last_list_item.item = rewrite_first_line;
             Some(rewrite)
         } else {
@@ -495,7 +503,7 @@ impl<'a> Context<'a> {
                 Some(OverflowableItem::MacroArg(MacroArg::Expr(expr)))
                     if !combine_arg_with_callee
                         && is_method_call(expr)
-                        && self.context.config.version() == Version::Two =>
+                        && self.context.config.style_edition() >= StyleEdition::Edition2024 =>
                 {
                     self.context.force_one_line_chain.replace(true);
                 }
@@ -545,22 +553,23 @@ impl<'a> Context<'a> {
                         .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
                     let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
                     if no_newline {
-                        list_items[self.items.len() - 1].item = rw;
+                        list_items[self.items.len() - 1].item = rw.unknown_error();
                     } else {
-                        list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
+                        list_items[self.items.len() - 1].item = Ok(overflowed.to_owned());
                     }
                 } else {
-                    list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
+                    list_items[self.items.len() - 1].item = Ok(overflowed.to_owned());
                 }
             }
             (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
-                list_items[self.items.len() - 1].item = placeholder;
+                list_items[self.items.len() - 1].item = placeholder.unknown_error();
             }
             _ if !self.items.is_empty() => {
                 list_items[self.items.len() - 1].item = self
                     .items
                     .last()
-                    .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
+                    .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape))
+                    .unknown_error();
 
                 // Use horizontal layout for a function with a single argument as long as
                 // everything fits in a single line.
@@ -613,7 +622,7 @@ impl<'a> Context<'a> {
         tactic
     }
 
-    fn rewrite_items(&self) -> Option<(bool, String)> {
+    fn rewrite_items(&self) -> Result<(bool, String), RewriteError> {
         let span = self.items_span();
         debug!("items: {:?}", self.items);
 
@@ -624,7 +633,7 @@ impl<'a> Context<'a> {
             ",",
             |item| item.span().lo(),
             |item| item.span().hi(),
-            |item| item.rewrite(self.context, self.nested_shape),
+            |item| item.rewrite_result(self.context, self.nested_shape),
             span.lo(),
             span.hi(),
             true,
@@ -689,7 +698,8 @@ impl<'a> Context<'a> {
         );
         result.push_str(self.ident);
         result.push_str(prefix);
-        let force_single_line = if self.context.config.version() == Version::Two {
+        let force_single_line = if self.context.config.style_edition() >= StyleEdition::Edition2024
+        {
             !self.context.use_block_indent() || (is_extendable && extend_width <= shape.width)
         } else {
             // 2 = `()`
@@ -711,7 +721,7 @@ impl<'a> Context<'a> {
         result
     }
 
-    fn rewrite(&self, shape: Shape) -> Option<String> {
+    fn rewrite(&self, shape: Shape) -> RewriteResult {
         let (extendable, items_str) = self.rewrite_items()?;
 
         // If we are using visual indent style and failed to format, retry with block indent.
@@ -725,7 +735,7 @@ impl<'a> Context<'a> {
             return result;
         }
 
-        Some(self.wrap_items(&items_str, shape, extendable))
+        Ok(self.wrap_items(&items_str, shape, extendable))
     }
 }
 
diff --git a/src/tools/rustfmt/src/pairs.rs b/src/tools/rustfmt/src/pairs.rs
index bfc2ffed383..9c51298416b 100644
--- a/src/tools/rustfmt/src/pairs.rs
+++ b/src/tools/rustfmt/src/pairs.rs
@@ -1,9 +1,11 @@
 use rustc_ast::ast;
+use rustc_span::Span;
 
-use crate::config::lists::*;
 use crate::config::IndentStyle;
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::config::lists::*;
+use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
+use crate::spanned::Spanned;
 use crate::utils::{
     first_line_width, is_single_line, last_line_width, trimmed_last_line_width, wrap_str,
 };
@@ -40,16 +42,19 @@ pub(crate) fn rewrite_all_pairs(
     expr: &ast::Expr,
     shape: Shape,
     context: &RewriteContext<'_>,
-) -> Option<String> {
-    expr.flatten(context, shape).and_then(|list| {
-        if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() {
-            rewrite_pairs_multiline(&list, shape, context)
-        } else {
-            // First we try formatting on one line.
-            rewrite_pairs_one_line(&list, shape, context)
-                .or_else(|| rewrite_pairs_multiline(&list, shape, context))
-        }
-    })
+) -> RewriteResult {
+    expr.flatten(context, shape)
+        .unknown_error()
+        .and_then(|list| {
+            if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() {
+                rewrite_pairs_multiline(&list, shape, context)
+            } else {
+                // First we try formatting on one line.
+                rewrite_pairs_one_line(&list, shape, context)
+                    .unknown_error()
+                    .or_else(|_| rewrite_pairs_multiline(&list, shape, context))
+            }
+        })
 }
 
 // This may return a multi-line result since we allow the last expression to go
@@ -65,7 +70,7 @@ fn rewrite_pairs_one_line<T: Rewrite>(
     let base_shape = shape.block();
 
     for ((_, rewrite), s) in list.list.iter().zip(list.separators.iter()) {
-        if let Some(rewrite) = rewrite {
+        if let Ok(rewrite) = rewrite {
             if !is_single_line(rewrite) || result.len() > shape.width {
                 return None;
             }
@@ -104,19 +109,20 @@ fn rewrite_pairs_multiline<T: Rewrite>(
     list: &PairList<'_, '_, T>,
     shape: Shape,
     context: &RewriteContext<'_>,
-) -> Option<String> {
+) -> RewriteResult {
     let rhs_offset = shape.rhs_overhead(context.config);
     let nested_shape = (match context.config.indent_style() {
         IndentStyle::Visual => shape.visual_indent(0),
         IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
     })
     .with_max_width(context.config)
-    .sub_width(rhs_offset)?;
+    .sub_width(rhs_offset)
+    .max_width_error(shape.width, list.span)?;
 
     let indent_str = nested_shape.indent.to_string_with_newline(context.config);
     let mut result = String::new();
 
-    result.push_str(list.list[0].1.as_ref()?);
+    result.push_str(list.list[0].1.as_ref().map_err(|err| err.clone())?);
 
     for ((e, default_rw), s) in list.list[1..].iter().zip(list.separators.iter()) {
         // The following test checks if we should keep two subexprs on the same
@@ -132,7 +138,7 @@ fn rewrite_pairs_multiline<T: Rewrite>(
             if let Some(line_shape) =
                 shape.offset_left(s.len() + 2 + trimmed_last_line_width(&result))
             {
-                if let Some(rewrite) = e.rewrite(context, line_shape) {
+                if let Ok(rewrite) = e.rewrite_result(context, line_shape) {
                     result.push(' ');
                     result.push_str(s);
                     result.push(' ');
@@ -155,9 +161,9 @@ fn rewrite_pairs_multiline<T: Rewrite>(
             }
         }
 
-        result.push_str(default_rw.as_ref()?);
+        result.push_str(default_rw.as_ref().map_err(|err| err.clone())?);
     }
-    Some(result)
+    Ok(result)
 }
 
 // Rewrites a single pair.
@@ -168,10 +174,10 @@ pub(crate) fn rewrite_pair<LHS, RHS>(
     context: &RewriteContext<'_>,
     shape: Shape,
     separator_place: SeparatorPlace,
-) -> Option<String>
+) -> RewriteResult
 where
-    LHS: Rewrite,
-    RHS: Rewrite,
+    LHS: Rewrite + Spanned,
+    RHS: Rewrite + Spanned,
 {
     let tab_spaces = context.config.tab_spaces();
     let lhs_overhead = match separator_place {
@@ -183,15 +189,17 @@ where
         ..shape
     };
     let lhs_result = lhs
-        .rewrite(context, lhs_shape)
+        .rewrite_result(context, lhs_shape)
         .map(|lhs_str| format!("{}{}", pp.prefix, lhs_str))?;
 
     // Try to put both lhs and rhs on the same line.
     let rhs_orig_result = shape
         .offset_left(last_line_width(&lhs_result) + pp.infix.len())
         .and_then(|s| s.sub_width(pp.suffix.len()))
-        .and_then(|rhs_shape| rhs.rewrite(context, rhs_shape));
-    if let Some(ref rhs_result) = rhs_orig_result {
+        .max_width_error(shape.width, rhs.span())
+        .and_then(|rhs_shape| rhs.rewrite_result(context, rhs_shape));
+
+    if let Ok(ref rhs_result) = rhs_orig_result {
         // If the length of the lhs is equal to or shorter than the tab width or
         // the rhs looks like block expression, we put the rhs on the same
         // line with the lhs even if the rhs is multi-lined.
@@ -207,7 +215,7 @@ where
                 + first_line_width(rhs_result)
                 + pp.suffix.len();
             if one_line_width <= shape.width {
-                return Some(format!(
+                return Ok(format!(
                     "{}{}{}{}",
                     lhs_result, pp.infix, rhs_result, pp.suffix
                 ));
@@ -219,13 +227,15 @@ where
     // Re-evaluate the rhs because we have more space now:
     let mut rhs_shape = match context.config.indent_style() {
         IndentStyle::Visual => shape
-            .sub_width(pp.suffix.len() + pp.prefix.len())?
+            .sub_width(pp.suffix.len() + pp.prefix.len())
+            .max_width_error(shape.width, rhs.span())?
             .visual_indent(pp.prefix.len()),
         IndentStyle::Block => {
             // Try to calculate the initial constraint on the right hand side.
             let rhs_overhead = shape.rhs_overhead(context.config);
             Shape::indented(shape.indent.block_indent(context.config), context.config)
-                .sub_width(rhs_overhead)?
+                .sub_width(rhs_overhead)
+                .max_width_error(shape.width, rhs.span())?
         }
     };
     let infix = match separator_place {
@@ -233,15 +243,17 @@ where
         SeparatorPlace::Front => pp.infix.trim_start(),
     };
     if separator_place == SeparatorPlace::Front {
-        rhs_shape = rhs_shape.offset_left(infix.len())?;
+        rhs_shape = rhs_shape
+            .offset_left(infix.len())
+            .max_width_error(rhs_shape.width, rhs.span())?;
     }
-    let rhs_result = rhs.rewrite(context, rhs_shape)?;
+    let rhs_result = rhs.rewrite_result(context, rhs_shape)?;
     let indent_str = rhs_shape.indent.to_string_with_newline(context.config);
     let infix_with_sep = match separator_place {
         SeparatorPlace::Back => format!("{infix}{indent_str}"),
         SeparatorPlace::Front => format!("{indent_str}{infix}"),
     };
-    Some(format!(
+    Ok(format!(
         "{}{}{}{}",
         lhs_result, infix_with_sep, rhs_result, pp.suffix
     ))
@@ -255,8 +267,9 @@ trait FlattenPair: Rewrite + Sized {
 }
 
 struct PairList<'a, 'b, T: Rewrite> {
-    list: Vec<(&'b T, Option<String>)>,
+    list: Vec<(&'b T, RewriteResult)>,
     separators: Vec<&'a str>,
+    span: Span,
 }
 
 fn is_ident(expr: &ast::Expr) -> bool {
@@ -303,7 +316,7 @@ impl FlattenPair for ast::Expr {
 
         let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| {
             if is_first {
-                return node.rewrite(context, shape);
+                return node.rewrite_result(context, shape);
             }
             let nested_overhead = sep + 1;
             let rhs_offset = shape.rhs_overhead(context.config);
@@ -312,12 +325,17 @@ impl FlattenPair for ast::Expr {
                 IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
             })
             .with_max_width(context.config)
-            .sub_width(rhs_offset)?;
+            .sub_width(rhs_offset)
+            .max_width_error(shape.width, node.span)?;
             let default_shape = match context.config.binop_separator() {
-                SeparatorPlace::Back => nested_shape.sub_width(nested_overhead)?,
-                SeparatorPlace::Front => nested_shape.offset_left(nested_overhead)?,
+                SeparatorPlace::Back => nested_shape
+                    .sub_width(nested_overhead)
+                    .max_width_error(nested_shape.width, node.span)?,
+                SeparatorPlace::Front => nested_shape
+                    .offset_left(nested_overhead)
+                    .max_width_error(nested_shape.width, node.span)?,
             };
-            node.rewrite(context, default_shape)
+            node.rewrite_result(context, default_shape)
         };
 
         // Turn a tree of binop expressions into a list using a depth-first,
@@ -326,6 +344,7 @@ impl FlattenPair for ast::Expr {
         let mut list = vec![];
         let mut separators = vec![];
         let mut node = self;
+        let span = self.span();
         loop {
             match node.kind {
                 ast::ExprKind::Binary(op, ref lhs, _) if op.node == top_op => {
@@ -352,7 +371,11 @@ impl FlattenPair for ast::Expr {
         }
 
         assert_eq!(list.len() - 1, separators.len());
-        Some(PairList { list, separators })
+        Some(PairList {
+            list,
+            separators,
+            span,
+        })
     }
 }
 
diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs
index 6373d0251f8..5ee083da539 100644
--- a/src/tools/rustfmt/src/parse/macros/asm.rs
+++ b/src/tools/rustfmt/src/parse/macros/asm.rs
@@ -1,5 +1,5 @@
 use rustc_ast::ast;
-use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs};
+use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args};
 
 use crate::rewrite::RewriteContext;
 
diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
index b91d203d531..ec771f96a3f 100644
--- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs
+++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
@@ -1,4 +1,4 @@
-use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::panic::{AssertUnwindSafe, catch_unwind};
 
 use rustc_ast::ast;
 use rustc_ast::token::{Delimiter, TokenKind};
diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs
index 8d5f7f90958..7271e73db8d 100644
--- a/src/tools/rustfmt/src/parse/macros/mod.rs
+++ b/src/tools/rustfmt/src/parse/macros/mod.rs
@@ -1,11 +1,11 @@
 use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::{ast, ptr};
-use rustc_parse::parser::{ForceCollect, Parser, Recovery};
 use rustc_parse::MACRO_ARGUMENTS;
+use rustc_parse::parser::{ForceCollect, Parser, Recovery};
 use rustc_session::parse::ParseSess;
-use rustc_span::symbol::{self, kw};
 use rustc_span::Symbol;
+use rustc_span::symbol::{self, kw};
 
 use crate::macros::MacroArg;
 use crate::rewrite::RewriteContext;
diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs
index 6051241309d..28b4c2b612f 100644
--- a/src/tools/rustfmt/src/parse/parser.rs
+++ b/src/tools/rustfmt/src/parse/parser.rs
@@ -1,4 +1,4 @@
-use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::panic::{AssertUnwindSafe, catch_unwind};
 use std::path::{Path, PathBuf};
 
 use rustc_ast::token::TokenKind;
@@ -6,11 +6,11 @@ use rustc_ast::{ast, attr, ptr};
 use rustc_errors::Diag;
 use rustc_parse::parser::Parser as RawParser;
 use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use thin_vec::ThinVec;
 
-use crate::parse::session::ParseSess;
 use crate::Input;
+use crate::parse::session::ParseSess;
 
 pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership;
 pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index 05cf467167c..9f27a05939e 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -2,13 +2,14 @@ use std::path::Path;
 use std::sync::atomic::{AtomicBool, Ordering};
 
 use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
-use rustc_errors::emitter::{stderr_destination, DynEmitter, Emitter, HumanEmitter, SilentEmitter};
+use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
 use rustc_errors::translation::Translate;
 use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
 use rustc_session::parse::ParseSess as RawParseSess;
 use rustc_span::{
+    BytePos, Span,
     source_map::{FilePathMapping, SourceMap},
-    symbol, BytePos, Span,
+    symbol,
 };
 
 use crate::config::file_lines::LineRange;
@@ -394,7 +395,9 @@ mod tests {
         }
 
         fn get_ignore_list(config: &str) -> IgnoreList {
-            Config::from_toml(config, Path::new("")).unwrap().ignore()
+            Config::from_toml(config, Path::new("./rustfmt.toml"))
+                .unwrap()
+                .ignore()
         }
 
         #[test]
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index d8cb26a20f1..6fe2d4a8520 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -2,22 +2,22 @@ use rustc_ast::ast::{self, BindingMode, ByRef, Pat, PatField, PatKind, RangeEnd,
 use rustc_ast::ptr;
 use rustc_span::{BytePos, Span};
 
-use crate::comment::{combine_strs_with_missing_comments, FindUncommented};
+use crate::comment::{FindUncommented, combine_strs_with_missing_comments};
+use crate::config::StyleEdition;
 use crate::config::lists::*;
-use crate::config::Version;
 use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field};
 use crate::lists::{
-    definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape,
-    struct_lit_tactic, write_list, ListFormatting, ListItem, Separator,
+    ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, shape_for_tactic,
+    struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list,
 };
-use crate::macros::{rewrite_macro, MacroPosition};
+use crate::macros::{MacroPosition, rewrite_macro};
 use crate::overflow;
-use crate::pairs::{rewrite_pair, PairParts};
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::pairs::{PairParts, rewrite_pair};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
 use crate::source_map::SpanUtils;
 use crate::spanned::Spanned;
-use crate::types::{rewrite_path, PathContext};
+use crate::types::{PathContext, rewrite_path};
 use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident};
 
 /// Returns `true` if the given pattern is "short".
@@ -61,25 +61,36 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
     }
 }
 
-struct RangeOperand<'a>(&'a Option<ptr::P<ast::Expr>>);
+pub(crate) struct RangeOperand<'a> {
+    operand: &'a Option<ptr::P<ast::Expr>>,
+    pub(crate) span: Span,
+}
 
 impl<'a> Rewrite for RangeOperand<'a> {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
-        match &self.0 {
-            None => Some("".to_owned()),
-            Some(ref exp) => exp.rewrite(context, shape),
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        match &self.operand {
+            None => Ok("".to_owned()),
+            Some(ref exp) => exp.rewrite_result(context, shape),
         }
     }
 }
 
 impl Rewrite for Pat {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match self.kind {
             PatKind::Or(ref pats) => {
                 let pat_strs = pats
                     .iter()
-                    .map(|p| p.rewrite(context, shape))
-                    .collect::<Option<Vec<_>>>()?;
+                    .map(|p| p.rewrite_result(context, shape))
+                    .collect::<Result<Vec<_>, RewriteError>>()?;
 
                 let use_mixed_layout = pats
                     .iter()
@@ -115,14 +126,21 @@ impl Rewrite for Pat {
                 let sub_pat = match *sub_pat {
                     Some(ref p) => {
                         // 2 - `@ `.
-                        let width = shape.width.checked_sub(
-                            mut_prefix.len() + ref_kw.len() + mut_infix.len() + id_str.len() + 2,
-                        )?;
+                        let width = shape
+                            .width
+                            .checked_sub(
+                                mut_prefix.len()
+                                    + ref_kw.len()
+                                    + mut_infix.len()
+                                    + id_str.len()
+                                    + 2,
+                            )
+                            .max_width_error(shape.width, p.span())?;
                         let lo = context.snippet_provider.span_after(self.span, "@");
                         combine_strs_with_missing_comments(
                             context,
                             "@",
-                            &p.rewrite(context, Shape::legacy(width, shape.indent))?,
+                            &p.rewrite_result(context, Shape::legacy(width, shape.indent))?,
                             mk_sp(lo, p.span.lo()),
                             shape,
                             true,
@@ -207,19 +225,25 @@ impl Rewrite for Pat {
             }
             PatKind::Wild => {
                 if 1 <= shape.width {
-                    Some("_".to_owned())
+                    Ok("_".to_owned())
                 } else {
-                    None
+                    Err(RewriteError::ExceedsMaxWidth {
+                        configured_width: 1,
+                        span: self.span,
+                    })
                 }
             }
             PatKind::Rest => {
                 if 1 <= shape.width {
-                    Some("..".to_owned())
+                    Ok("..".to_owned())
                 } else {
-                    None
+                    Err(RewriteError::ExceedsMaxWidth {
+                        configured_width: 1,
+                        span: self.span,
+                    })
                 }
             }
-            PatKind::Never => None,
+            PatKind::Never => Err(RewriteError::Unknown),
             PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
                 let infix = match end_kind.node {
                     RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
@@ -239,9 +263,17 @@ impl Rewrite for Pat {
                 } else {
                     infix.to_owned()
                 };
+                let lspan = self.span.with_hi(end_kind.span.lo());
+                let rspan = self.span.with_lo(end_kind.span.hi());
                 rewrite_pair(
-                    &RangeOperand(lhs),
-                    &RangeOperand(rhs),
+                    &RangeOperand {
+                        operand: lhs,
+                        span: lspan,
+                    },
+                    &RangeOperand {
+                        operand: rhs,
+                        span: rspan,
+                    },
                     PairParts::infix(&infix),
                     context,
                     shape,
@@ -260,19 +292,21 @@ impl Rewrite for Pat {
                 let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?;
                 rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape)
             }
-            PatKind::Lit(ref expr) => expr.rewrite(context, shape),
-            PatKind::Slice(ref slice_pat) if context.config.version() == Version::One => {
+            PatKind::Lit(ref expr) => expr.rewrite_result(context, shape),
+            PatKind::Slice(ref slice_pat)
+                if context.config.style_edition() <= StyleEdition::Edition2021 =>
+            {
                 let rw: Vec<String> = slice_pat
                     .iter()
                     .map(|p| {
-                        if let Some(rw) = p.rewrite(context, shape) {
+                        if let Ok(rw) = p.rewrite_result(context, shape) {
                             rw
                         } else {
                             context.snippet(p.span).to_string()
                         }
                     })
                     .collect();
-                Some(format!("[{}]", rw.join(", ")))
+                Ok(format!("[{}]", rw.join(", ")))
             }
             PatKind::Slice(ref slice_pat) => overflow::rewrite_with_square_brackets(
                 context,
@@ -296,10 +330,16 @@ impl Rewrite for Pat {
                 rewrite_macro(mac, None, context, shape, MacroPosition::Pat)
             }
             PatKind::Paren(ref pat) => pat
-                .rewrite(context, shape.offset_left(1)?.sub_width(1)?)
+                .rewrite_result(
+                    context,
+                    shape
+                        .offset_left(1)
+                        .and_then(|s| s.sub_width(1))
+                        .max_width_error(shape.width, self.span)?,
+                )
                 .map(|inner_pat| format!("({})", inner_pat)),
-            PatKind::Err(_) => None,
-            PatKind::Deref(_) => None,
+            PatKind::Err(_) => Err(RewriteError::Unknown),
+            PatKind::Deref(_) => Err(RewriteError::Unknown),
         }
     }
 }
@@ -312,20 +352,21 @@ fn rewrite_struct_pat(
     span: Span,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     // 2 =  ` {`
-    let path_shape = shape.sub_width(2)?;
+    let path_shape = shape.sub_width(2).max_width_error(shape.width, span)?;
     let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?;
 
     if fields.is_empty() && !ellipsis {
-        return Some(format!("{path_str} {{}}"));
+        return Ok(format!("{path_str} {{}}"));
     }
 
     let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") };
 
     // 3 = ` { `, 2 = ` }`.
     let (h_shape, v_shape) =
-        struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)?;
+        struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)
+            .max_width_error(shape.width, span)?;
 
     let items = itemize_list(
         context.snippet_provider,
@@ -340,7 +381,7 @@ fn rewrite_struct_pat(
             }
         },
         |f| f.span.hi(),
-        |f| f.rewrite(context, v_shape),
+        |f| f.rewrite_result(context, v_shape),
         context.snippet_provider.span_after(span, "{"),
         span.hi(),
         false,
@@ -379,11 +420,15 @@ fn rewrite_struct_pat(
 
     // ast::Pat doesn't have attrs so use &[]
     let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?;
-    Some(format!("{path_str} {{{fields_str}}}"))
+    Ok(format!("{path_str} {{{fields_str}}}"))
 }
 
 impl Rewrite for PatField {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         let hi_pos = if let Some(last) = self.attrs.last() {
             last.span.hi()
         } else {
@@ -393,10 +438,10 @@ impl Rewrite for PatField {
         let attrs_str = if self.attrs.is_empty() {
             String::from("")
         } else {
-            self.attrs.rewrite(context, shape)?
+            self.attrs.rewrite_result(context, shape)?
         };
 
-        let pat_str = self.pat.rewrite(context, shape)?;
+        let pat_str = self.pat.rewrite_result(context, shape)?;
         if self.is_shorthand {
             combine_strs_with_missing_comments(
                 context,
@@ -417,7 +462,7 @@ impl Rewrite for PatField {
                     "{}:\n{}{}",
                     id_str,
                     nested_shape.indent.to_string(context.config),
-                    self.pat.rewrite(context, nested_shape)?
+                    self.pat.rewrite_result(context, nested_shape)?
                 )
             };
             combine_strs_with_missing_comments(
@@ -440,9 +485,13 @@ pub(crate) enum TuplePatField<'a> {
 
 impl<'a> Rewrite for TuplePatField<'a> {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match *self {
-            TuplePatField::Pat(p) => p.rewrite(context, shape),
-            TuplePatField::Dotdot(_) => Some("..".to_string()),
+            TuplePatField::Pat(p) => p.rewrite_result(context, shape),
+            TuplePatField::Dotdot(_) => Ok("..".to_string()),
         }
     }
 }
@@ -492,9 +541,9 @@ fn rewrite_tuple_pat(
     span: Span,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     if pats.is_empty() {
-        return Some(format!("{}()", path_str.unwrap_or_default()));
+        return Ok(format!("{}()", path_str.unwrap_or_default()));
     }
     let mut pat_vec: Vec<_> = pats.iter().map(TuplePatField::Pat).collect();
 
@@ -548,7 +597,7 @@ fn count_wildcard_suffix_len(
         ",",
         |item| item.span().lo(),
         |item| item.span().hi(),
-        |item| item.rewrite(context, shape),
+        |item| item.rewrite_result(context, shape),
         context.snippet_provider.span_after(span, "("),
         span.hi() - BytePos(1),
         false,
@@ -558,7 +607,7 @@ fn count_wildcard_suffix_len(
     for item in items
         .iter()
         .rev()
-        .take_while(|i| matches!(i.item, Some(ref internal_string) if internal_string == "_"))
+        .take_while(|i| matches!(i.item, Ok(ref internal_string) if internal_string == "_"))
     {
         suffix_len += 1;
 
diff --git a/src/tools/rustfmt/src/reorder.rs b/src/tools/rustfmt/src/reorder.rs
index fdbed939af5..8a31e0ac816 100644
--- a/src/tools/rustfmt/src/reorder.rs
+++ b/src/tools/rustfmt/src/reorder.rs
@@ -9,13 +9,13 @@
 use std::cmp::Ordering;
 
 use rustc_ast::{ast, attr};
-use rustc_span::{symbol::sym, Span};
+use rustc_span::{Span, symbol::sym};
 
 use crate::config::{Config, GroupImportsTactic};
-use crate::imports::{normalize_use_trees_with_granularity, UseSegmentKind, UseTree};
+use crate::imports::{UseSegmentKind, UseTree, normalize_use_trees_with_granularity};
 use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod};
-use crate::lists::{itemize_list, write_list, ListFormatting, ListItem};
-use crate::rewrite::RewriteContext;
+use crate::lists::{ListFormatting, ListItem, itemize_list, write_list};
+use crate::rewrite::{RewriteContext, RewriteErrorExt};
 use crate::shape::Shape;
 use crate::source_map::LineRangeUtils;
 use crate::spanned::Spanned;
@@ -59,7 +59,7 @@ fn wrap_reorderable_items(
     let fmt = ListFormatting::new(shape, context.config)
         .separator("")
         .align_comments(false);
-    write_list(list_items, &fmt)
+    write_list(list_items, &fmt).ok()
 }
 
 fn rewrite_reorderable_item(
@@ -99,7 +99,7 @@ fn rewrite_reorderable_or_regroupable_items(
                 ";",
                 |item| item.span().lo(),
                 |item| item.span().hi(),
-                |_item| Some("".to_owned()),
+                |_item| Ok("".to_owned()),
                 span.lo(),
                 span.hi(),
                 false,
@@ -131,9 +131,16 @@ fn rewrite_reorderable_or_regroupable_items(
                 .map(|use_group| {
                     let item_vec: Vec<_> = use_group
                         .into_iter()
-                        .map(|use_tree| ListItem {
-                            item: use_tree.rewrite_top_level(context, nested_shape),
-                            ..use_tree.list_item.unwrap_or_else(ListItem::empty)
+                        .map(|use_tree| {
+                            let item = use_tree.rewrite_top_level(context, nested_shape);
+                            if let Some(list_item) = use_tree.list_item {
+                                ListItem {
+                                    item: item,
+                                    ..list_item
+                                }
+                            } else {
+                                ListItem::from_item(item)
+                            }
                         })
                         .collect();
                     wrap_reorderable_items(context, &item_vec, nested_shape)
@@ -151,7 +158,7 @@ fn rewrite_reorderable_or_regroupable_items(
                 ";",
                 |item| item.span().lo(),
                 |item| item.span().hi(),
-                |item| rewrite_reorderable_item(context, item, shape),
+                |item| rewrite_reorderable_item(context, item, shape).unknown_error(),
                 span.lo(),
                 span.hi(),
                 false,
diff --git a/src/tools/rustfmt/src/rewrite.rs b/src/tools/rustfmt/src/rewrite.rs
index e2498a3500a..83020709797 100644
--- a/src/tools/rustfmt/src/rewrite.rs
+++ b/src/tools/rustfmt/src/rewrite.rs
@@ -5,17 +5,23 @@ use std::rc::Rc;
 
 use rustc_ast::ptr;
 use rustc_span::Span;
+use thiserror::Error;
 
+use crate::FormatReport;
 use crate::config::{Config, IndentStyle};
 use crate::parse::session::ParseSess;
 use crate::shape::Shape;
 use crate::skip::SkipContext;
 use crate::visitor::SnippetProvider;
-use crate::FormatReport;
 
+pub(crate) type RewriteResult = Result<String, RewriteError>;
 pub(crate) trait Rewrite {
     /// Rewrite self into shape.
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>;
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        self.rewrite(context, shape).unknown_error()
+    }
 }
 
 impl<T: Rewrite> Rewrite for ptr::P<T> {
@@ -24,6 +30,66 @@ impl<T: Rewrite> Rewrite for ptr::P<T> {
     }
 }
 
+#[derive(Clone, Debug, PartialEq)]
+pub(crate) enum MacroErrorKind {
+    ParseFailure,
+    ReplaceMacroVariable,
+    Unknown,
+}
+
+impl std::fmt::Display for MacroErrorKind {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            MacroErrorKind::ParseFailure => write!(f, "(parse failure)"),
+            MacroErrorKind::ReplaceMacroVariable => write!(f, "(replacing macro variables with $)"),
+            MacroErrorKind::Unknown => write!(f, ""),
+        }
+    }
+}
+
+#[derive(Clone, Error, Debug)]
+pub(crate) enum RewriteError {
+    #[error("Formatting was skipped due to skip attribute or out of file range.")]
+    SkipFormatting,
+
+    #[error("It exceeds the required width of {configured_width} for the span: {span:?}")]
+    ExceedsMaxWidth { configured_width: usize, span: Span },
+
+    #[error("Failed to format given macro{} at: {span:?}", kind)]
+    MacroFailure { kind: MacroErrorKind, span: Span },
+
+    /// Format failure that does not fit to above categories.
+    #[error("An unknown error occurred during formatting.")]
+    Unknown,
+}
+
+/// Extension trait used to conveniently convert to RewriteError
+pub(crate) trait RewriteErrorExt<T> {
+    fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError>;
+    fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError>;
+    fn unknown_error(self) -> Result<T, RewriteError>;
+}
+
+impl<T> RewriteErrorExt<T> for Option<T> {
+    fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError> {
+        self.ok_or_else(|| RewriteError::ExceedsMaxWidth {
+            configured_width: width,
+            span: span,
+        })
+    }
+
+    fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError> {
+        self.ok_or_else(|| RewriteError::MacroFailure {
+            kind: kind,
+            span: span,
+        })
+    }
+
+    fn unknown_error(self) -> Result<T, RewriteError> {
+        self.ok_or_else(|| RewriteError::Unknown)
+    }
+}
+
 #[derive(Clone)]
 pub(crate) struct RewriteContext<'a> {
     pub(crate) psess: &'a ParseSess,
diff --git a/src/tools/rustfmt/src/rustfmt_diff.rs b/src/tools/rustfmt/src/rustfmt_diff.rs
index c9883452185..4624683fa05 100644
--- a/src/tools/rustfmt/src/rustfmt_diff.rs
+++ b/src/tools/rustfmt/src/rustfmt_diff.rs
@@ -282,7 +282,7 @@ where
 #[cfg(test)]
 mod test {
     use super::DiffLine::*;
-    use super::{make_diff, Mismatch};
+    use super::{Mismatch, make_diff};
     use super::{ModifiedChunk, ModifiedLines};
 
     #[test]
diff --git a/src/tools/rustfmt/src/sort.rs b/src/tools/rustfmt/src/sort.rs
new file mode 100644
index 00000000000..670f664a119
--- /dev/null
+++ b/src/tools/rustfmt/src/sort.rs
@@ -0,0 +1,368 @@
+use itertools::EitherOrBoth;
+use itertools::Itertools;
+
+/// Iterator which breaks an identifier into various [VersionChunk]s.
+struct VersionChunkIter<'a> {
+    ident: &'a str,
+    start: usize,
+}
+
+impl<'a> VersionChunkIter<'a> {
+    pub(crate) fn new(ident: &'a str) -> Self {
+        Self { ident, start: 0 }
+    }
+
+    fn parse_numeric_chunk(
+        &mut self,
+        mut chars: std::str::CharIndices<'a>,
+    ) -> Option<VersionChunk<'a>> {
+        let mut end = self.start;
+        let mut is_end_of_chunk = false;
+
+        while let Some((idx, c)) = chars.next() {
+            end = self.start + idx;
+
+            if c.is_ascii_digit() {
+                continue;
+            }
+
+            is_end_of_chunk = true;
+            break;
+        }
+
+        let source = if is_end_of_chunk {
+            let value = &self.ident[self.start..end];
+            self.start = end;
+            value
+        } else {
+            let value = &self.ident[self.start..];
+            self.start = self.ident.len();
+            value
+        };
+
+        let zeros = source.chars().take_while(|c| *c == '0').count();
+        let value = source.parse::<usize>().ok()?;
+
+        Some(VersionChunk::Number {
+            value,
+            zeros,
+            source,
+        })
+    }
+
+    fn parse_str_chunk(
+        &mut self,
+        mut chars: std::str::CharIndices<'a>,
+    ) -> Option<VersionChunk<'a>> {
+        let mut end = self.start;
+        let mut is_end_of_chunk = false;
+
+        while let Some((idx, c)) = chars.next() {
+            end = self.start + idx;
+
+            if c == '_' {
+                is_end_of_chunk = true;
+                break;
+            }
+
+            if !c.is_numeric() {
+                continue;
+            }
+
+            is_end_of_chunk = true;
+            break;
+        }
+
+        let source = if is_end_of_chunk {
+            let value = &self.ident[self.start..end];
+            self.start = end;
+            value
+        } else {
+            let value = &self.ident[self.start..];
+            self.start = self.ident.len();
+            value
+        };
+
+        Some(VersionChunk::Str(source))
+    }
+}
+
+impl<'a> Iterator for VersionChunkIter<'a> {
+    type Item = VersionChunk<'a>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let mut chars = self.ident[self.start..].char_indices();
+        let (_, next) = chars.next()?;
+
+        if next == '_' {
+            self.start = self.start + next.len_utf8();
+            return Some(VersionChunk::Underscore);
+        }
+
+        if next.is_ascii_digit() {
+            return self.parse_numeric_chunk(chars);
+        }
+
+        self.parse_str_chunk(chars)
+    }
+}
+
+/// Represents a chunk in the version-sort algorithm
+#[derive(Debug, PartialEq, Eq)]
+enum VersionChunk<'a> {
+    /// A single `_` in an identifier. Underscores are sorted before all other characters.
+    Underscore,
+    /// A &str chunk in the version sort.
+    Str(&'a str),
+    /// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros.
+    Number {
+        value: usize,
+        zeros: usize,
+        source: &'a str,
+    },
+}
+
+/// Determine which side of the version-sort comparison had more leading zeros.
+#[derive(Debug, PartialEq, Eq)]
+enum MoreLeadingZeros {
+    Left,
+    Right,
+    Equal,
+}
+
+/// Compare two identifiers based on the version sorting algorithm described in [the style guide]
+///
+/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/#sorting
+pub(crate) fn version_sort(a: &str, b: &str) -> std::cmp::Ordering {
+    let iter_a = VersionChunkIter::new(a);
+    let iter_b = VersionChunkIter::new(b);
+    let mut more_leading_zeros = MoreLeadingZeros::Equal;
+
+    for either_or_both in iter_a.zip_longest(iter_b) {
+        match either_or_both {
+            EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater,
+            EitherOrBoth::Right(_) => return std::cmp::Ordering::Less,
+            EitherOrBoth::Both(a, b) => match (a, b) {
+                (VersionChunk::Underscore, VersionChunk::Underscore) => {
+                    continue;
+                }
+                (VersionChunk::Underscore, _) => return std::cmp::Ordering::Less,
+                (_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater,
+                (VersionChunk::Str(ca), VersionChunk::Str(cb))
+                | (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. })
+                | (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => {
+                    match ca.cmp(&cb) {
+                        std::cmp::Ordering::Equal => {
+                            continue;
+                        }
+                        order @ _ => return order,
+                    }
+                }
+                (
+                    VersionChunk::Number {
+                        value: va,
+                        zeros: lza,
+                        ..
+                    },
+                    VersionChunk::Number {
+                        value: vb,
+                        zeros: lzb,
+                        ..
+                    },
+                ) => match va.cmp(&vb) {
+                    std::cmp::Ordering::Equal => {
+                        if lza == lzb {
+                            continue;
+                        }
+
+                        if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb {
+                            more_leading_zeros = MoreLeadingZeros::Left;
+                        } else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb {
+                            more_leading_zeros = MoreLeadingZeros::Right;
+                        }
+                        continue;
+                    }
+                    order @ _ => return order,
+                },
+            },
+        }
+    }
+
+    match more_leading_zeros {
+        MoreLeadingZeros::Equal => std::cmp::Ordering::Equal,
+        MoreLeadingZeros::Left => std::cmp::Ordering::Less,
+        MoreLeadingZeros::Right => std::cmp::Ordering::Greater,
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_chunks() {
+        let mut iter = VersionChunkIter::new("x86_128");
+        assert_eq!(iter.next(), Some(VersionChunk::Str("x")));
+        assert_eq!(
+            iter.next(),
+            Some(VersionChunk::Number {
+                value: 86,
+                zeros: 0,
+                source: "86"
+            })
+        );
+        assert_eq!(iter.next(), Some(VersionChunk::Underscore));
+        assert_eq!(
+            iter.next(),
+            Some(VersionChunk::Number {
+                value: 128,
+                zeros: 0,
+                source: "128"
+            })
+        );
+        assert_eq!(iter.next(), None);
+
+        let mut iter = VersionChunkIter::new("w005s09t");
+        assert_eq!(iter.next(), Some(VersionChunk::Str("w")));
+        assert_eq!(
+            iter.next(),
+            Some(VersionChunk::Number {
+                value: 5,
+                zeros: 2,
+                source: "005"
+            })
+        );
+        assert_eq!(iter.next(), Some(VersionChunk::Str("s")));
+        assert_eq!(
+            iter.next(),
+            Some(VersionChunk::Number {
+                value: 9,
+                zeros: 1,
+                source: "09"
+            })
+        );
+        assert_eq!(iter.next(), Some(VersionChunk::Str("t")));
+        assert_eq!(iter.next(), None);
+
+        let mut iter = VersionChunkIter::new("ZY_WX");
+        assert_eq!(iter.next(), Some(VersionChunk::Str("ZY")));
+        assert_eq!(iter.next(), Some(VersionChunk::Underscore));
+        assert_eq!(iter.next(), Some(VersionChunk::Str("WX")));
+
+        let mut iter = VersionChunkIter::new("_v1");
+        assert_eq!(iter.next(), Some(VersionChunk::Underscore));
+        assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
+        assert_eq!(
+            iter.next(),
+            Some(VersionChunk::Number {
+                value: 1,
+                zeros: 0,
+                source: "1"
+            })
+        );
+
+        let mut iter = VersionChunkIter::new("_1v");
+        assert_eq!(iter.next(), Some(VersionChunk::Underscore));
+        assert_eq!(
+            iter.next(),
+            Some(VersionChunk::Number {
+                value: 1,
+                zeros: 0,
+                source: "1"
+            })
+        );
+        assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
+
+        let mut iter = VersionChunkIter::new("v009");
+        assert_eq!(iter.next(), Some(VersionChunk::Str("v")));
+        assert_eq!(
+            iter.next(),
+            Some(VersionChunk::Number {
+                value: 9,
+                zeros: 2,
+                source: "009"
+            })
+        );
+    }
+
+    #[test]
+    fn test_version_sort() {
+        let mut input = vec!["", "b", "a"];
+        let expected = vec!["", "a", "b"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec!["x7x", "xxx"];
+        let expected = vec!["x7x", "xxx"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec!["applesauce", "apple"];
+        let expected = vec!["apple", "applesauce"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec!["aaaaa", "aaa_a"];
+        let expected = vec!["aaa_a", "aaaaa"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec!["AAAAA", "AAA1A", "BBBBB", "BB_BB", "C3CCC"];
+        let expected = vec!["AAA1A", "AAAAA", "BB_BB", "BBBBB", "C3CCC"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec!["1_000_000", "1_010_001"];
+        let expected = vec!["1_000_000", "1_010_001"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec![
+            "5", "50", "500", "5_000", "5_005", "5_050", "5_500", "50_000", "50_005", "50_050",
+            "50_500",
+        ];
+        let expected = vec![
+            "5", "5_000", "5_005", "5_050", "5_500", "50", "50_000", "50_005", "50_050", "50_500",
+            "500",
+        ];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec!["X86_64", "x86_64", "X86_128", "x86_128"];
+        let expected = vec!["X86_64", "X86_128", "x86_64", "x86_128"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec!["__", "_"];
+        let expected = vec!["_", "__"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec!["foo_", "foo"];
+        let expected = vec!["foo", "foo_"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec!["A", "AA", "B", "a", "aA", "aa", "b"];
+        let expected = vec!["A", "AA", "B", "a", "aA", "aa", "b"];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected);
+
+        let mut input = vec![
+            "x86_128", "usize", "uz", "v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01",
+            "v1", "v009", "x87", "zyxw", "_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW",
+            "ZYXW", "v09", "v9", "v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32",
+            "ua", "x86_64", "ZYXW_", "a1", "abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128",
+            "u256",
+        ];
+        let expected = vec![
+            "_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW", "ZYXW", "ZYXW_", "a1",
+            "abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128", "u256", "ua", "usize", "uz",
+            "v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01", "v1", "v009", "v09", "v9",
+            "v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32", "x86_64", "x86_128",
+            "x87", "zyxw",
+        ];
+        input.sort_by(|a, b| version_sort(a, b));
+        assert_eq!(input, expected)
+    }
+}
diff --git a/src/tools/rustfmt/src/source_file.rs b/src/tools/rustfmt/src/source_file.rs
index 5eea8021b32..73f8ecb5529 100644
--- a/src/tools/rustfmt/src/source_file.rs
+++ b/src/tools/rustfmt/src/source_file.rs
@@ -2,10 +2,10 @@ use std::fs;
 use std::io::{self, Write};
 use std::path::Path;
 
+use crate::NewlineStyle;
 use crate::config::FileName;
 use crate::emitter::{self, Emitter};
 use crate::parse::session::ParseSess;
-use crate::NewlineStyle;
 
 #[cfg(test)]
 use crate::config::Config;
diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs
index 1ee691b4ade..b4424e476ee 100644
--- a/src/tools/rustfmt/src/spanned.rs
+++ b/src/tools/rustfmt/src/spanned.rs
@@ -1,9 +1,10 @@
 use std::cmp::max;
 
 use rustc_ast::{ast, ptr};
-use rustc_span::{source_map, Span};
+use rustc_span::{Span, source_map};
 
 use crate::macros::MacroArg;
+use crate::patterns::RangeOperand;
 use crate::utils::{mk_sp, outer_attributes};
 
 /// Spanned returns a span including attributes, if available.
@@ -212,3 +213,9 @@ impl Spanned for ast::PreciseCapturingArg {
         }
     }
 }
+
+impl<'a> Spanned for RangeOperand<'a> {
+    fn span(&self) -> Span {
+        self.span
+    }
+}
diff --git a/src/tools/rustfmt/src/stmt.rs b/src/tools/rustfmt/src/stmt.rs
index 73a9cce416c..2788159018d 100644
--- a/src/tools/rustfmt/src/stmt.rs
+++ b/src/tools/rustfmt/src/stmt.rs
@@ -2,9 +2,9 @@ use rustc_ast::ast;
 use rustc_span::Span;
 
 use crate::comment::recover_comment_removed;
-use crate::config::Version;
-use crate::expr::{format_expr, is_simple_block, ExprType};
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::config::StyleEdition;
+use crate::expr::{ExprType, format_expr, is_simple_block};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
 use crate::source_map::LineRangeUtils;
 use crate::spanned::Spanned;
@@ -90,11 +90,20 @@ impl<'a> Stmt<'a> {
 
 impl<'a> Rewrite for Stmt<'a> {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
-        let expr_type = if context.config.version() == Version::Two && self.is_last_expr() {
-            ExprType::SubExpression
-        } else {
-            ExprType::Statement
-        };
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(
+        &self,
+        context: &RewriteContext<'_>,
+        shape: Shape,
+    ) -> crate::rewrite::RewriteResult {
+        let expr_type =
+            if context.config.style_edition() >= StyleEdition::Edition2024 && self.is_last_expr() {
+                ExprType::SubExpression
+            } else {
+                ExprType::Statement
+            };
         format_stmt(
             context,
             shape,
@@ -111,11 +120,11 @@ fn format_stmt(
     stmt: &ast::Stmt,
     expr_type: ExprType,
     is_last_expr: bool,
-) -> Option<String> {
-    skip_out_of_file_lines_range!(context, stmt.span());
+) -> RewriteResult {
+    skip_out_of_file_lines_range_err!(context, stmt.span());
 
     let result = match stmt.kind {
-        ast::StmtKind::Let(ref local) => local.rewrite(context, shape),
+        ast::StmtKind::Let(ref local) => local.rewrite_result(context, shape),
         ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => {
             let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) {
                 ";"
@@ -123,10 +132,14 @@ fn format_stmt(
                 ""
             };
 
-            let shape = shape.sub_width(suffix.len())?;
+            let shape = shape
+                .sub_width(suffix.len())
+                .max_width_error(shape.width, ex.span())?;
             format_expr(ex, expr_type, context, shape).map(|s| s + suffix)
         }
-        ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => None,
+        ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => {
+            Err(RewriteError::Unknown)
+        }
     };
-    result.and_then(|res| recover_comment_removed(res, stmt.span(), context))
+    result.map(|res| recover_comment_removed(res, stmt.span(), context))
 }
diff --git a/src/tools/rustfmt/src/string.rs b/src/tools/rustfmt/src/string.rs
index cb666fff695..3b971188cd5 100644
--- a/src/tools/rustfmt/src/string.rs
+++ b/src/tools/rustfmt/src/string.rs
@@ -84,7 +84,7 @@ pub(crate) fn rewrite_string<'a>(
         stripped_str
             .len()
             .checked_next_power_of_two()
-            .unwrap_or(usize::max_value()),
+            .unwrap_or(usize::MAX),
     );
     result.push_str(fmt.opener);
 
@@ -375,7 +375,7 @@ fn graphemes_width(graphemes: &[&str]) -> usize {
 
 #[cfg(test)]
 mod test {
-    use super::{break_string, detect_url, rewrite_string, SnippetState, StringFormat};
+    use super::{SnippetState, StringFormat, break_string, detect_url, rewrite_string};
     use crate::config::Config;
     use crate::shape::{Indent, Shape};
     use unicode_segmentation::UnicodeSegmentation;
diff --git a/src/tools/rustfmt/src/test/configuration_snippet.rs b/src/tools/rustfmt/src/test/configuration_snippet.rs
index e4a390ada66..5b93703de0b 100644
--- a/src/tools/rustfmt/src/test/configuration_snippet.rs
+++ b/src/tools/rustfmt/src/test/configuration_snippet.rs
@@ -4,9 +4,9 @@ use std::io::{BufRead, BufReader, Write};
 use std::iter::Enumerate;
 use std::path::{Path, PathBuf};
 
-use super::{print_mismatches, write_message, DIFF_CONTEXT_SIZE};
+use super::{DIFF_CONTEXT_SIZE, print_mismatches, write_message};
 use crate::config::{Config, EmitMode, Verbosity};
-use crate::rustfmt_diff::{make_diff, Mismatch};
+use crate::rustfmt_diff::{Mismatch, make_diff};
 use crate::{Input, Session};
 
 const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md";
diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs
index 286e8b8760a..d62da08fff8 100644
--- a/src/tools/rustfmt/src/test/mod.rs
+++ b/src/tools/rustfmt/src/test/mod.rs
@@ -6,14 +6,17 @@ use std::iter::Peekable;
 use std::mem;
 use std::path::{Path, PathBuf};
 use std::process::{Command, Stdio};
-use std::str::Chars;
+use std::str::{Chars, FromStr};
 use std::thread;
 
 use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle};
 use crate::formatting::{ReportedErrors, SourceFile};
-use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter};
+use crate::rustfmt_diff::{DiffLine, Mismatch, ModifiedChunk, OutputWriter, make_diff, print_diff};
 use crate::source_file;
-use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session};
+use crate::{
+    Edition, FormatReport, FormatReportFormatterBuilder, Input, Session, StyleEdition, Version,
+    is_nightly_channel,
+};
 
 use rustfmt_config_proc_macro::nightly_only_test;
 use tracing::{debug, warn};
@@ -102,10 +105,9 @@ fn is_file_skip(path: &Path) -> bool {
 fn get_test_files(path: &Path, recursive: bool) -> Vec<PathBuf> {
     let mut files = vec![];
     if path.is_dir() {
-        for entry in fs::read_dir(path).expect(&format!(
-            "couldn't read directory {}",
-            path.to_str().unwrap()
-        )) {
+        for entry in
+            fs::read_dir(path).expect(&format!("couldn't read directory {}", path.display()))
+        {
             let entry = entry.expect("couldn't get `DirEntry`");
             let path = entry.path();
             if path.is_dir() && recursive {
@@ -119,10 +121,7 @@ fn get_test_files(path: &Path, recursive: bool) -> Vec<PathBuf> {
 }
 
 fn verify_config_used(path: &Path, config_name: &str) {
-    for entry in fs::read_dir(path).expect(&format!(
-        "couldn't read {} directory",
-        path.to_str().unwrap()
-    )) {
+    for entry in fs::read_dir(path).expect(&format!("couldn't read {} directory", path.display())) {
         let entry = entry.expect("couldn't get directory entry");
         let path = entry.path();
         if path.extension().map_or(false, |f| f == "rs") {
@@ -711,13 +710,24 @@ fn print_mismatches<T: Fn(u32) -> String>(
 
 fn read_config(filename: &Path) -> Config {
     let sig_comments = read_significant_comments(filename);
+    let (edition, style_edition, version) = get_editions_from_comments(&sig_comments);
     // Look for a config file. If there is a 'config' property in the significant comments, use
     // that. Otherwise, if there are no significant comments at all, look for a config file with
     // the same name as the test file.
     let mut config = if !sig_comments.is_empty() {
-        get_config(sig_comments.get("config").map(Path::new))
+        get_config(
+            sig_comments.get("config").map(Path::new),
+            edition,
+            style_edition,
+            version,
+        )
     } else {
-        get_config(filename.with_extension("toml").file_name().map(Path::new))
+        get_config(
+            filename.with_extension("toml").file_name().map(Path::new),
+            edition,
+            style_edition,
+            version,
+        )
     };
 
     for (key, val) in &sig_comments {
@@ -748,13 +758,31 @@ enum IdempotentCheckError {
     Parse,
 }
 
+fn get_editions_from_comments(
+    comments: &HashMap<String, String>,
+) -> (Option<Edition>, Option<StyleEdition>, Option<Version>) {
+    (
+        comments
+            .get("edition")
+            .map(|e| Edition::from_str(e).expect(&format!("invalid edition value: '{}'", e))),
+        comments.get("style_edition").map(|se| {
+            StyleEdition::from_str(se).expect(&format!("invalid style_edition value: '{}'", se))
+        }),
+        comments
+            .get("version")
+            .map(|v| Version::from_str(v).expect(&format!("invalid version value: '{}'", v))),
+    )
+}
+
 fn idempotent_check(
     filename: &PathBuf,
     opt_config: &Option<PathBuf>,
 ) -> Result<FormatReport, IdempotentCheckError> {
     let sig_comments = read_significant_comments(filename);
     let config = if let Some(ref config_file_path) = opt_config {
-        Config::from_toml_path(config_file_path).expect("`rustfmt.toml` not found")
+        let (edition, style_edition, version) = get_editions_from_comments(&sig_comments);
+        Config::from_toml_path(config_file_path, edition, style_edition, version)
+            .expect("`rustfmt.toml` not found")
     } else {
         read_config(filename)
     };
@@ -778,14 +806,19 @@ fn idempotent_check(
 // Reads test config file using the supplied (optional) file name. If there's no file name or the
 // file doesn't exist, just return the default config. Otherwise, the file must be read
 // successfully.
-fn get_config(config_file: Option<&Path>) -> Config {
+fn get_config(
+    config_file: Option<&Path>,
+    edition: Option<Edition>,
+    style_edition: Option<StyleEdition>,
+    version: Option<Version>,
+) -> Config {
     let config_file_name = match config_file {
-        None => return Default::default(),
+        None => return Config::default_for_possible_style_edition(style_edition, edition, version),
         Some(file_name) => {
             let mut full_path = PathBuf::from("tests/config/");
             full_path.push(file_name);
             if !full_path.exists() {
-                return Default::default();
+                return Config::default_for_possible_style_edition(style_edition, edition, version);
             };
             full_path
         }
@@ -797,7 +830,14 @@ fn get_config(config_file: Option<&Path>) -> Config {
         .read_to_string(&mut def_config)
         .expect("Couldn't read config");
 
-    Config::from_toml(&def_config, Path::new("tests/config/")).expect("invalid TOML")
+    Config::from_toml_for_style_edition(
+        &def_config,
+        Path::new("tests/config/"),
+        edition,
+        style_edition,
+        version,
+    )
+    .expect("invalid TOML")
 }
 
 // Reads significant comments of the form: `// rustfmt-key: value` into a hash map.
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 7730aa467ce..07b483b2b37 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -2,23 +2,23 @@ use std::ops::Deref;
 
 use rustc_ast::ast::{self, FnRetTy, Mutability, Term};
 use rustc_ast::ptr;
-use rustc_span::{symbol::kw, BytePos, Pos, Span};
+use rustc_span::{BytePos, Pos, Span, symbol::kw};
 use tracing::debug;
 
 use crate::comment::{combine_strs_with_missing_comments, contains_comment};
 use crate::config::lists::*;
-use crate::config::{IndentStyle, TypeDensity, Version};
+use crate::config::{IndentStyle, StyleEdition, TypeDensity};
 use crate::expr::{
-    format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType,
-    RhsAssignKind,
+    ExprType, RhsAssignKind, format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple,
+    rewrite_unary_prefix,
 };
 use crate::lists::{
-    definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
+    ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
 };
-use crate::macros::{rewrite_macro, MacroPosition};
+use crate::macros::{MacroPosition, rewrite_macro};
 use crate::overflow;
-use crate::pairs::{rewrite_pair, PairParts};
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::pairs::{PairParts, rewrite_pair};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
 use crate::shape::Shape;
 use crate::source_map::SpanUtils;
 use crate::spanned::Spanned;
@@ -41,7 +41,7 @@ pub(crate) fn rewrite_path(
     qself: &Option<ptr::P<ast::QSelf>>,
     path: &ast::Path,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let skip_count = qself.as_ref().map_or(0, |x| x.position);
 
     // 32 covers almost all path lengths measured when compiling core, and there isn't a big
@@ -57,7 +57,7 @@ pub(crate) fn rewrite_path(
     if let Some(qself) = qself {
         result.push('<');
 
-        let fmt_ty = qself.ty.rewrite(context, shape)?;
+        let fmt_ty = qself.ty.rewrite_result(context, shape)?;
         result.push_str(&fmt_ty);
 
         if skip_count > 0 {
@@ -67,7 +67,7 @@ pub(crate) fn rewrite_path(
             }
 
             // 3 = ">::".len()
-            let shape = shape.sub_width(3)?;
+            let shape = shape.sub_width(3).max_width_error(shape.width, path.span)?;
 
             result = rewrite_path_segments(
                 PathContext::Type,
@@ -103,7 +103,7 @@ fn rewrite_path_segments<'a, I>(
     span_hi: BytePos,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String>
+) -> RewriteResult
 where
     I: Iterator<Item = &'a ast::PathSegment>,
 {
@@ -122,7 +122,9 @@ where
         }
 
         let extra_offset = extra_offset(&buffer, shape);
-        let new_shape = shape.shrink_left(extra_offset)?;
+        let new_shape = shape
+            .shrink_left(extra_offset)
+            .max_width_error(shape.width, mk_sp(span_lo, span_hi))?;
         let segment_string = rewrite_segment(
             path_context,
             segment,
@@ -135,7 +137,7 @@ where
         buffer.push_str(&segment_string);
     }
 
-    Some(buffer)
+    Ok(buffer)
 }
 
 #[derive(Debug)]
@@ -169,19 +171,27 @@ impl<'a> Spanned for SegmentParam<'a> {
 
 impl<'a> Rewrite for SegmentParam<'a> {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match *self {
-            SegmentParam::Const(const_) => const_.rewrite(context, shape),
-            SegmentParam::LifeTime(lt) => lt.rewrite(context, shape),
-            SegmentParam::Type(ty) => ty.rewrite(context, shape),
-            SegmentParam::Binding(atc) => atc.rewrite(context, shape),
+            SegmentParam::Const(const_) => const_.rewrite_result(context, shape),
+            SegmentParam::LifeTime(lt) => lt.rewrite_result(context, shape),
+            SegmentParam::Type(ty) => ty.rewrite_result(context, shape),
+            SegmentParam::Binding(atc) => atc.rewrite_result(context, shape),
         }
     }
 }
 
 impl Rewrite for ast::PreciseCapturingArg {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match self {
-            ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite(context, shape),
+            ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite_result(context, shape),
             ast::PreciseCapturingArg::Arg(p, _) => {
                 rewrite_path(context, PathContext::Type, &None, p, shape)
             }
@@ -191,13 +201,20 @@ impl Rewrite for ast::PreciseCapturingArg {
 
 impl Rewrite for ast::AssocItemConstraint {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         use ast::AssocItemConstraintKind::{Bound, Equality};
 
         let mut result = String::with_capacity(128);
         result.push_str(rewrite_ident(context, self.ident));
 
         if let Some(ref gen_args) = self.gen_args {
-            let budget = shape.width.checked_sub(result.len())?;
+            let budget = shape
+                .width
+                .checked_sub(result.len())
+                .max_width_error(shape.width, self.span)?;
             let shape = Shape::legacy(budget, shape.indent + result.len());
             let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?;
             result.push_str(&gen_str);
@@ -210,23 +227,30 @@ impl Rewrite for ast::AssocItemConstraint {
         };
         result.push_str(infix);
 
-        let budget = shape.width.checked_sub(result.len())?;
+        let budget = shape
+            .width
+            .checked_sub(result.len())
+            .max_width_error(shape.width, self.span)?;
         let shape = Shape::legacy(budget, shape.indent + result.len());
-        let rewrite = self.kind.rewrite(context, shape)?;
+        let rewrite = self.kind.rewrite_result(context, shape)?;
         result.push_str(&rewrite);
 
-        Some(result)
+        Ok(result)
     }
 }
 
 impl Rewrite for ast::AssocItemConstraintKind {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match self {
             ast::AssocItemConstraintKind::Equality { term } => match term {
-                Term::Ty(ty) => ty.rewrite(context, shape),
-                Term::Const(c) => c.rewrite(context, shape),
+                Term::Ty(ty) => ty.rewrite_result(context, shape),
+                Term::Const(c) => c.rewrite_result(context, shape),
             },
-            ast::AssocItemConstraintKind::Bound { bounds } => bounds.rewrite(context, shape),
+            ast::AssocItemConstraintKind::Bound { bounds } => bounds.rewrite_result(context, shape),
         }
     }
 }
@@ -248,16 +272,17 @@ fn rewrite_segment(
     span_hi: BytePos,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     let mut result = String::with_capacity(128);
     result.push_str(rewrite_ident(context, segment.ident));
 
     let ident_len = result.len();
     let shape = if context.use_block_indent() {
-        shape.offset_left(ident_len)?
+        shape.offset_left(ident_len)
     } else {
-        shape.shrink_left(ident_len)?
-    };
+        shape.shrink_left(ident_len)
+    }
+    .max_width_error(shape.width, mk_sp(*span_lo, span_hi))?;
 
     if let Some(ref args) = segment.args {
         let generics_str = rewrite_generic_args(args, context, shape, mk_sp(*span_lo, span_hi))?;
@@ -288,7 +313,7 @@ fn rewrite_segment(
         result.push_str(&generics_str)
     }
 
-    Some(result)
+    Ok(result)
 }
 
 fn format_function_type<'a, I>(
@@ -298,7 +323,7 @@ fn format_function_type<'a, I>(
     span: Span,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String>
+) -> RewriteResult
 where
     I: ExactSizeIterator,
     <I as Iterator>::Item: Deref,
@@ -308,12 +333,12 @@ where
 
     let ty_shape = match context.config.indent_style() {
         // 4 = " -> "
-        IndentStyle::Block => shape.offset_left(4)?,
-        IndentStyle::Visual => shape.block_left(4)?,
+        IndentStyle::Block => shape.offset_left(4).max_width_error(shape.width, span)?,
+        IndentStyle::Visual => shape.block_left(4).max_width_error(shape.width, span)?,
     };
     let output = match *output {
         FnRetTy::Ty(ref ty) => {
-            let type_str = ty.rewrite(context, ty_shape)?;
+            let type_str = ty.rewrite_result(context, ty_shape)?;
             format!(" -> {type_str}")
         }
         FnRetTy::Default(..) => String::new(),
@@ -326,7 +351,10 @@ where
         )
     } else {
         // 2 for ()
-        let budget = shape.width.checked_sub(2)?;
+        let budget = shape
+            .width
+            .checked_sub(2)
+            .max_width_error(shape.width, span)?;
         // 1 for (
         let offset = shape.indent + 1;
         Shape::legacy(budget, offset)
@@ -339,7 +367,8 @@ where
         let list_hi = context.snippet_provider.span_before(span, ")");
         let comment = context
             .snippet_provider
-            .span_to_snippet(mk_sp(list_lo, list_hi))?
+            .span_to_snippet(mk_sp(list_lo, list_hi))
+            .unknown_error()?
             .trim();
         let comment = if comment.starts_with("//") {
             format!(
@@ -360,7 +389,7 @@ where
             ",",
             |arg| arg.span().lo(),
             |arg| arg.span().hi(),
-            |arg| arg.rewrite(context, list_shape),
+            |arg| arg.rewrite_result(context, list_shape),
             list_lo,
             span.hi(),
             false,
@@ -396,9 +425,9 @@ where
         )
     };
     if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width {
-        Some(format!("{args}{output}"))
+        Ok(format!("{args}{output}"))
     } else {
-        Some(format!(
+        Ok(format!(
             "{}\n{}{}",
             args,
             list_shape.indent.to_string(context.config),
@@ -429,6 +458,10 @@ fn get_tactics(item_vec: &[ListItem], output: &str, shape: Shape) -> DefinitiveL
 
 impl Rewrite for ast::WherePredicate {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         // FIXME: dead spans?
         let result = match *self {
             ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
@@ -437,7 +470,7 @@ impl Rewrite for ast::WherePredicate {
                 ref bounds,
                 ..
             }) => {
-                let type_str = bounded_ty.rewrite(context, shape)?;
+                let type_str = bounded_ty.rewrite_result(context, shape)?;
                 let colon = type_bound_colon(context).trim_end();
                 let lhs = if let Some(binder_str) =
                     rewrite_bound_params(context, shape, bound_generic_params)
@@ -452,28 +485,34 @@ impl Rewrite for ast::WherePredicate {
             ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
                 ref lifetime,
                 ref bounds,
-                ..
-            }) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?,
+                span,
+            }) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape)?,
             ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
                 ref lhs_ty,
                 ref rhs_ty,
                 ..
             }) => {
-                let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?;
+                let lhs_ty_str = lhs_ty
+                    .rewrite_result(context, shape)
+                    .map(|lhs| lhs + " =")?;
                 rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)?
             }
         };
 
-        Some(result)
+        Ok(result)
     }
 }
 
 impl Rewrite for ast::GenericArg {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match *self {
-            ast::GenericArg::Lifetime(ref lt) => lt.rewrite(context, shape),
-            ast::GenericArg::Type(ref ty) => ty.rewrite(context, shape),
-            ast::GenericArg::Const(ref const_) => const_.rewrite(context, shape),
+            ast::GenericArg::Lifetime(ref lt) => lt.rewrite_result(context, shape),
+            ast::GenericArg::Type(ref ty) => ty.rewrite_result(context, shape),
+            ast::GenericArg::Const(ref const_) => const_.rewrite_result(context, shape),
         }
     }
 }
@@ -483,11 +522,11 @@ fn rewrite_generic_args(
     context: &RewriteContext<'_>,
     shape: Shape,
     span: Span,
-) -> Option<String> {
+) -> RewriteResult {
     match gen_args {
         ast::GenericArgs::AngleBracketed(ref data) => {
             if data.args.is_empty() {
-                Some("".to_owned())
+                Ok("".to_owned())
             } else {
                 let args = data
                     .args
@@ -513,47 +552,63 @@ fn rewrite_generic_args(
             context,
             shape,
         ),
-        ast::GenericArgs::ParenthesizedElided(..) => Some("(..)".to_owned()),
+        ast::GenericArgs::ParenthesizedElided(..) => Ok("(..)".to_owned()),
     }
 }
 
 fn rewrite_bounded_lifetime(
     lt: &ast::Lifetime,
     bounds: &[ast::GenericBound],
+    span: Span,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
-    let result = lt.rewrite(context, shape)?;
+) -> RewriteResult {
+    let result = lt.rewrite_result(context, shape)?;
 
     if bounds.is_empty() {
-        Some(result)
+        Ok(result)
     } else {
         let colon = type_bound_colon(context);
         let overhead = last_line_width(&result) + colon.len();
+        let shape = shape
+            .sub_width(overhead)
+            .max_width_error(shape.width, span)?;
         let result = format!(
             "{}{}{}",
             result,
             colon,
-            join_bounds(context, shape.sub_width(overhead)?, bounds, true)?
+            join_bounds(context, shape, bounds, true)?
         );
-        Some(result)
+        Ok(result)
     }
 }
 
 impl Rewrite for ast::AnonConst {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         format_expr(&self.value, ExprType::SubExpression, context, shape)
     }
 }
 
 impl Rewrite for ast::Lifetime {
-    fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option<String> {
-        Some(context.snippet(self.ident.span).to_owned())
+    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, _: Shape) -> RewriteResult {
+        Ok(context.snippet(self.ident.span).to_owned())
     }
 }
 
 impl Rewrite for ast::GenericBound {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match *self {
             ast::GenericBound::Trait(
                 ref poly_trait_ref,
@@ -574,24 +629,30 @@ impl Rewrite for ast::GenericBound {
                     asyncness.push(' ');
                 }
                 let polarity = polarity.as_str();
-                let shape = shape.offset_left(constness.len() + polarity.len())?;
+                let shape = shape
+                    .offset_left(constness.len() + polarity.len())
+                    .max_width_error(shape.width, self.span())?;
                 poly_trait_ref
-                    .rewrite(context, shape)
+                    .rewrite_result(context, shape)
                     .map(|s| format!("{constness}{asyncness}{polarity}{s}"))
                     .map(|s| if has_paren { format!("({})", s) } else { s })
             }
             ast::GenericBound::Use(ref args, span) => {
                 overflow::rewrite_with_angle_brackets(context, "use", args.iter(), shape, span)
             }
-            ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape),
+            ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite_result(context, shape),
         }
     }
 }
 
 impl Rewrite for ast::GenericBounds {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         if self.is_empty() {
-            return Some(String::new());
+            return Ok(String::new());
         }
 
         join_bounds(context, shape, self, true)
@@ -600,8 +661,15 @@ impl Rewrite for ast::GenericBounds {
 
 impl Rewrite for ast::GenericParam {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         // FIXME: If there are more than one attributes, this will force multiline.
-        let mut result = self.attrs.rewrite(context, shape).unwrap_or(String::new());
+        let mut result = self
+            .attrs
+            .rewrite_result(context, shape)
+            .unwrap_or(String::new());
         let has_attrs = !result.is_empty();
 
         let mut param = String::with_capacity(128);
@@ -615,15 +683,19 @@ impl Rewrite for ast::GenericParam {
             param.push_str("const ");
             param.push_str(rewrite_ident(context, self.ident));
             param.push_str(": ");
-            param.push_str(&ty.rewrite(context, shape)?);
+            param.push_str(&ty.rewrite_result(context, shape)?);
             if let Some(default) = default {
                 let eq_str = match context.config.type_punctuation_density() {
                     TypeDensity::Compressed => "=",
                     TypeDensity::Wide => " = ",
                 };
                 param.push_str(eq_str);
-                let budget = shape.width.checked_sub(param.len())?;
-                let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?;
+                let budget = shape
+                    .width
+                    .checked_sub(param.len())
+                    .max_width_error(shape.width, self.span())?;
+                let rewrite =
+                    default.rewrite_result(context, Shape::legacy(budget, shape.indent))?;
                 param.push_str(&rewrite);
             }
             kw_span.lo()
@@ -634,7 +706,7 @@ impl Rewrite for ast::GenericParam {
 
         if !self.bounds.is_empty() {
             param.push_str(type_bound_colon(context));
-            param.push_str(&self.bounds.rewrite(context, shape)?)
+            param.push_str(&self.bounds.rewrite_result(context, shape)?)
         }
         if let ast::GenericParamKind::Type {
             default: Some(ref def),
@@ -645,9 +717,12 @@ impl Rewrite for ast::GenericParam {
                 TypeDensity::Wide => " = ",
             };
             param.push_str(eq_str);
-            let budget = shape.width.checked_sub(param.len())?;
+            let budget = shape
+                .width
+                .checked_sub(param.len())
+                .max_width_error(shape.width, self.span())?;
             let rewrite =
-                def.rewrite(context, Shape::legacy(budget, shape.indent + param.len()))?;
+                def.rewrite_result(context, Shape::legacy(budget, shape.indent + param.len()))?;
             param.push_str(&rewrite);
         }
 
@@ -673,44 +748,67 @@ impl Rewrite for ast::GenericParam {
             result.push_str(&param);
         }
 
-        Some(result)
+        Ok(result)
     }
 }
 
 impl Rewrite for ast::PolyTraitRef {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params)
         {
             // 6 is "for<> ".len()
             let extra_offset = lifetime_str.len() + 6;
-            let path_str = self
-                .trait_ref
-                .rewrite(context, shape.offset_left(extra_offset)?)?;
+            let shape = shape
+                .offset_left(extra_offset)
+                .max_width_error(shape.width, self.span)?;
+            let path_str = self.trait_ref.rewrite_result(context, shape)?;
 
-            Some(format!("for<{lifetime_str}> {path_str}"))
+            Ok(format!("for<{lifetime_str}> {path_str}"))
         } else {
-            self.trait_ref.rewrite(context, shape)
+            self.trait_ref.rewrite_result(context, shape)
         }
     }
 }
 
 impl Rewrite for ast::TraitRef {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         rewrite_path(context, PathContext::Type, &None, &self.path, shape)
     }
 }
 
 impl Rewrite for ast::Ty {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
+        self.rewrite_result(context, shape).ok()
+    }
+
+    fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match self.kind {
             ast::TyKind::TraitObject(ref bounds, tobj_syntax) => {
                 // we have to consider 'dyn' keyword is used or not!!!
                 let (shape, prefix) = match tobj_syntax {
-                    ast::TraitObjectSyntax::Dyn => (shape.offset_left(4)?, "dyn "),
-                    ast::TraitObjectSyntax::DynStar => (shape.offset_left(5)?, "dyn* "),
+                    ast::TraitObjectSyntax::Dyn => {
+                        let shape = shape
+                            .offset_left(4)
+                            .max_width_error(shape.width, self.span())?;
+                        (shape, "dyn ")
+                    }
+                    ast::TraitObjectSyntax::DynStar => {
+                        let shape = shape
+                            .offset_left(5)
+                            .max_width_error(shape.width, self.span())?;
+                        (shape, "dyn* ")
+                    }
                     ast::TraitObjectSyntax::None => (shape, ""),
                 };
-                let mut res = bounds.rewrite(context, shape)?;
+                let mut res = bounds.rewrite_result(context, shape)?;
                 // We may have falsely removed a trailing `+` inside macro call.
                 if context.inside_macro()
                     && bounds.len() == 1
@@ -719,7 +817,7 @@ impl Rewrite for ast::Ty {
                 {
                     res.push('+');
                 }
-                Some(format!("{prefix}{res}"))
+                Ok(format!("{prefix}{res}"))
             }
             ast::TyKind::Ptr(ref mt) => {
                 let prefix = match mt.mutbl {
@@ -738,8 +836,11 @@ impl Rewrite for ast::Ty {
                 let mut cmnt_lo = ref_hi;
 
                 if let Some(ref lifetime) = *lifetime {
-                    let lt_budget = shape.width.checked_sub(2 + mut_len)?;
-                    let lt_str = lifetime.rewrite(
+                    let lt_budget = shape
+                        .width
+                        .checked_sub(2 + mut_len)
+                        .max_width_error(shape.width, self.span())?;
+                    let lt_str = lifetime.rewrite_result(
                         context,
                         Shape::legacy(lt_budget, shape.indent + 2 + mut_len),
                     )?;
@@ -783,39 +884,46 @@ impl Rewrite for ast::Ty {
                     result = combine_strs_with_missing_comments(
                         context,
                         result.trim_end(),
-                        &mt.ty.rewrite(context, shape)?,
+                        &mt.ty.rewrite_result(context, shape)?,
                         before_ty_span,
                         shape,
                         true,
                     )?;
                 } else {
                     let used_width = last_line_width(&result);
-                    let budget = shape.width.checked_sub(used_width)?;
-                    let ty_str = mt
-                        .ty
-                        .rewrite(context, Shape::legacy(budget, shape.indent + used_width))?;
+                    let budget = shape
+                        .width
+                        .checked_sub(used_width)
+                        .max_width_error(shape.width, self.span())?;
+                    let ty_str = mt.ty.rewrite_result(
+                        context,
+                        Shape::legacy(budget, shape.indent + used_width),
+                    )?;
                     result.push_str(&ty_str);
                 }
 
-                Some(result)
+                Ok(result)
             }
             // FIXME: we drop any comments here, even though it's a silly place to put
             // comments.
             ast::TyKind::Paren(ref ty) => {
-                if context.config.version() == Version::One
+                if context.config.style_edition() <= StyleEdition::Edition2021
                     || context.config.indent_style() == IndentStyle::Visual
                 {
-                    let budget = shape.width.checked_sub(2)?;
+                    let budget = shape
+                        .width
+                        .checked_sub(2)
+                        .max_width_error(shape.width, self.span())?;
                     return ty
-                        .rewrite(context, Shape::legacy(budget, shape.indent + 1))
+                        .rewrite_result(context, Shape::legacy(budget, shape.indent + 1))
                         .map(|ty_str| format!("({})", ty_str));
                 }
 
                 // 2 = ()
                 if let Some(sh) = shape.sub_width(2) {
-                    if let Some(ref s) = ty.rewrite(context, sh) {
+                    if let Ok(ref s) = ty.rewrite_result(context, sh) {
                         if !s.contains('\n') {
-                            return Some(format!("({s})"));
+                            return Ok(format!("({s})"));
                         }
                     }
                 }
@@ -824,8 +932,8 @@ impl Rewrite for ast::Ty {
                 let shape = shape
                     .block_indent(context.config.tab_spaces())
                     .with_max_width(context.config);
-                let rw = ty.rewrite(context, shape)?;
-                Some(format!(
+                let rw = ty.rewrite_result(context, shape)?;
+                Ok(format!(
                     "({}{}{})",
                     shape.to_string_with_newline(context.config),
                     rw,
@@ -833,15 +941,18 @@ impl Rewrite for ast::Ty {
                 ))
             }
             ast::TyKind::Slice(ref ty) => {
-                let budget = shape.width.checked_sub(4)?;
-                ty.rewrite(context, Shape::legacy(budget, shape.indent + 1))
+                let budget = shape
+                    .width
+                    .checked_sub(4)
+                    .max_width_error(shape.width, self.span())?;
+                ty.rewrite_result(context, Shape::legacy(budget, shape.indent + 1))
                     .map(|ty_str| format!("[{}]", ty_str))
             }
             ast::TyKind::Tup(ref items) => {
                 rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1)
             }
-            ast::TyKind::AnonStruct(..) => Some(context.snippet(self.span).to_owned()),
-            ast::TyKind::AnonUnion(..) => Some(context.snippet(self.span).to_owned()),
+            ast::TyKind::AnonStruct(..) => Ok(context.snippet(self.span).to_owned()),
+            ast::TyKind::AnonUnion(..) => Ok(context.snippet(self.span).to_owned()),
             ast::TyKind::Path(ref q_self, ref path) => {
                 rewrite_path(context, PathContext::Type, q_self, path, shape)
             }
@@ -855,24 +966,27 @@ impl Rewrite for ast::Ty {
             ),
             ast::TyKind::Infer => {
                 if shape.width >= 1 {
-                    Some("_".to_owned())
+                    Ok("_".to_owned())
                 } else {
-                    None
+                    Err(RewriteError::ExceedsMaxWidth {
+                        configured_width: shape.width,
+                        span: self.span(),
+                    })
                 }
             }
             ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape),
-            ast::TyKind::Never => Some(String::from("!")),
+            ast::TyKind::Never => Ok(String::from("!")),
             ast::TyKind::MacCall(ref mac) => {
                 rewrite_macro(mac, None, context, shape, MacroPosition::Expression)
             }
-            ast::TyKind::ImplicitSelf => Some(String::from("")),
+            ast::TyKind::ImplicitSelf => Ok(String::from("")),
             ast::TyKind::ImplTrait(_, ref it) => {
                 // Empty trait is not a parser error.
                 if it.is_empty() {
-                    return Some("impl".to_owned());
+                    return Ok("impl".to_owned());
                 }
-                let rw = if context.config.version() == Version::One {
-                    it.rewrite(context, shape)
+                let rw = if context.config.style_edition() <= StyleEdition::Edition2021 {
+                    it.rewrite_result(context, shape)
                 } else {
                     join_bounds(context, shape, it, false)
                 };
@@ -881,8 +995,8 @@ impl Rewrite for ast::Ty {
                     format!("impl{}{}", space, it_str)
                 })
             }
-            ast::TyKind::CVarArgs => Some("...".to_owned()),
-            ast::TyKind::Dummy | ast::TyKind::Err(_) => Some(context.snippet(self.span).to_owned()),
+            ast::TyKind::CVarArgs => Ok("...".to_owned()),
+            ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()),
             ast::TyKind::Typeof(ref anon_const) => rewrite_call(
                 context,
                 "typeof",
@@ -891,9 +1005,9 @@ impl Rewrite for ast::Ty {
                 shape,
             ),
             ast::TyKind::Pat(ref ty, ref pat) => {
-                let ty = ty.rewrite(context, shape)?;
-                let pat = pat.rewrite(context, shape)?;
-                Some(format!("{ty} is {pat}"))
+                let ty = ty.rewrite_result(context, shape)?;
+                let pat = pat.rewrite_result(context, shape)?;
+                Ok(format!("{ty} is {pat}"))
             }
         }
     }
@@ -904,7 +1018,7 @@ fn rewrite_bare_fn(
     span: Span,
     context: &RewriteContext<'_>,
     shape: Shape,
-) -> Option<String> {
+) -> RewriteResult {
     debug!("rewrite_bare_fn {:#?}", shape);
 
     let mut result = String::with_capacity(128);
@@ -928,9 +1042,14 @@ fn rewrite_bare_fn(
     result.push_str("fn");
 
     let func_ty_shape = if context.use_block_indent() {
-        shape.offset_left(result.len())?
+        shape
+            .offset_left(result.len())
+            .max_width_error(shape.width, span)?
     } else {
-        shape.visual_indent(result.len()).sub_width(result.len())?
+        shape
+            .visual_indent(result.len())
+            .sub_width(result.len())
+            .max_width_error(shape.width, span)?
     };
 
     let rewrite = format_function_type(
@@ -944,7 +1063,7 @@ fn rewrite_bare_fn(
 
     result.push_str(&rewrite);
 
-    Some(result)
+    Ok(result)
 }
 
 fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool {
@@ -968,7 +1087,7 @@ fn join_bounds(
     shape: Shape,
     items: &[ast::GenericBound],
     need_indent: bool,
-) -> Option<String> {
+) -> RewriteResult {
     join_bounds_inner(context, shape, items, need_indent, false)
 }
 
@@ -978,7 +1097,7 @@ fn join_bounds_inner(
     items: &[ast::GenericBound],
     need_indent: bool,
     force_newline: bool,
-) -> Option<String> {
+) -> RewriteResult {
     debug_assert!(!items.is_empty());
 
     let generic_bounds_in_order = is_generic_bounds_in_order(items);
@@ -1073,10 +1192,10 @@ fn join_bounds_inner(
             };
 
             let (extendable, trailing_str) = if i == 0 {
-                let bound_str = item.rewrite(context, shape)?;
+                let bound_str = item.rewrite_result(context, shape)?;
                 (is_bound_extendable(&bound_str, item), bound_str)
             } else {
-                let bound_str = &item.rewrite(context, shape)?;
+                let bound_str = &item.rewrite_result(context, shape)?;
                 match leading_span {
                     Some(ls) if has_leading_comment => (
                         is_bound_extendable(bound_str, item),
@@ -1100,7 +1219,7 @@ fn join_bounds_inner(
                     true,
                 )
                 .map(|v| (v, trailing_span, extendable)),
-                _ => Some((strs + &trailing_str, trailing_span, extendable)),
+                _ => Ok((strs + &trailing_str, trailing_span, extendable)),
             }
         },
     )?;
@@ -1110,22 +1229,22 @@ fn join_bounds_inner(
     //   and either there is more than one item;
     //       or the single item is of type `Trait`,
     //          and any of the internal arrays contains more than one item;
-    let retry_with_force_newline = match context.config.version() {
-        Version::One => {
+    let retry_with_force_newline = match context.config.style_edition() {
+        style_edition @ _ if style_edition <= StyleEdition::Edition2021 => {
             !force_newline
                 && items.len() > 1
                 && (result.0.contains('\n') || result.0.len() > shape.width)
         }
-        Version::Two if force_newline => false,
-        Version::Two if (!result.0.contains('\n') && result.0.len() <= shape.width) => false,
-        Version::Two if items.len() > 1 => true,
-        Version::Two => is_item_with_multi_items_array(&items[0]),
+        _ if force_newline => false,
+        _ if (!result.0.contains('\n') && result.0.len() <= shape.width) => false,
+        _ if items.len() > 1 => true,
+        _ => is_item_with_multi_items_array(&items[0]),
     };
 
     if retry_with_force_newline {
         join_bounds_inner(context, shape, items, need_indent, true)
     } else {
-        Some(result.0)
+        Ok(result.0)
     }
 }
 
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index fd59aedadfe..d1cfc6acc49 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -6,11 +6,11 @@ use rustc_ast::ast::{
 };
 use rustc_ast::ptr;
 use rustc_ast_pretty::pprust;
-use rustc_span::{sym, symbol, BytePos, LocalExpnId, Span, Symbol, SyntaxContext};
+use rustc_span::{BytePos, LocalExpnId, Span, Symbol, SyntaxContext, sym, symbol};
 use unicode_width::UnicodeWidthStr;
 
-use crate::comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses};
-use crate::config::{Config, Version};
+use crate::comment::{CharClasses, FullCodeCharKind, LineClasses, filter_normal_code};
+use crate::config::{Config, StyleEdition};
 use crate::rewrite::RewriteContext;
 use crate::shape::{Indent, Shape};
 
@@ -367,10 +367,10 @@ macro_rules! out_of_file_lines_range {
     };
 }
 
-macro_rules! skip_out_of_file_lines_range {
+macro_rules! skip_out_of_file_lines_range_err {
     ($self:ident, $span:expr) => {
         if out_of_file_lines_range!($self, $span) {
-            return None;
+            return Err(RewriteError::SkipFormatting);
         }
     };
 }
@@ -596,7 +596,7 @@ pub(crate) fn trim_left_preserve_layout(
 
             // just InString{Commented} in order to allow the start of a string to be indented
             let new_veto_trim_value = (kind == FullCodeCharKind::InString
-                || (config.version() == Version::Two
+                || (config.style_edition() >= StyleEdition::Edition2024
                     && kind == FullCodeCharKind::InStringCommented))
                 && !line.ends_with('\\');
             let line = if veto_trim || new_veto_trim_value {
@@ -612,7 +612,7 @@ pub(crate) fn trim_left_preserve_layout(
             // such lines should not be taken into account when computing the minimum.
             match kind {
                 FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented
-                    if config.version() == Version::Two =>
+                    if config.style_edition() >= StyleEdition::Edition2024 =>
                 {
                     None
                 }
@@ -656,7 +656,7 @@ pub(crate) fn indent_next_line(kind: FullCodeCharKind, line: &str, config: &Conf
         // formatting the code block, therefore the string's indentation needs
         // to be adjusted for the code surrounding the code block.
         config.format_strings() && line.ends_with('\\')
-    } else if config.version() == Version::Two {
+    } else if config.style_edition() >= StyleEdition::Edition2024 {
         !kind.is_commented_string()
     } else {
         true
diff --git a/src/tools/rustfmt/src/vertical.rs b/src/tools/rustfmt/src/vertical.rs
index a06bc995aa5..1ec239c3821 100644
--- a/src/tools/rustfmt/src/vertical.rs
+++ b/src/tools/rustfmt/src/vertical.rs
@@ -11,9 +11,9 @@ use crate::config::lists::*;
 use crate::expr::rewrite_field;
 use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix};
 use crate::lists::{
-    definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
+    ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list,
 };
-use crate::rewrite::{Rewrite, RewriteContext};
+use crate::rewrite::{Rewrite, RewriteContext, RewriteResult};
 use crate::shape::{Indent, Shape};
 use crate::source_map::SpanUtils;
 use crate::spanned::Spanned;
@@ -24,13 +24,13 @@ use crate::utils::{
 pub(crate) trait AlignedItem {
     fn skip(&self) -> bool;
     fn get_span(&self) -> Span;
-    fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>;
+    fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult;
     fn rewrite_aligned_item(
         &self,
         context: &RewriteContext<'_>,
         shape: Shape,
         prefix_max_width: usize,
-    ) -> Option<String>;
+    ) -> RewriteResult;
 }
 
 impl AlignedItem for ast::FieldDef {
@@ -42,24 +42,23 @@ impl AlignedItem for ast::FieldDef {
         self.span()
     }
 
-    fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
-        let attrs_str = self.attrs.rewrite(context, shape)?;
+    fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        let attrs_str = self.attrs.rewrite_result(context, shape)?;
         let missing_span = if self.attrs.is_empty() {
             mk_sp(self.span.lo(), self.span.lo())
         } else {
             mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo())
         };
         let attrs_extendable = self.ident.is_none() && is_attributes_extendable(&attrs_str);
-        rewrite_struct_field_prefix(context, self).and_then(|field_str| {
-            combine_strs_with_missing_comments(
-                context,
-                &attrs_str,
-                &field_str,
-                missing_span,
-                shape,
-                attrs_extendable,
-            )
-        })
+        let field_str = rewrite_struct_field_prefix(context, self)?;
+        combine_strs_with_missing_comments(
+            context,
+            &attrs_str,
+            &field_str,
+            missing_span,
+            shape,
+            attrs_extendable,
+        )
     }
 
     fn rewrite_aligned_item(
@@ -67,7 +66,7 @@ impl AlignedItem for ast::FieldDef {
         context: &RewriteContext<'_>,
         shape: Shape,
         prefix_max_width: usize,
-    ) -> Option<String> {
+    ) -> RewriteResult {
         rewrite_struct_field(context, self, shape, prefix_max_width)
     }
 }
@@ -81,8 +80,8 @@ impl AlignedItem for ast::ExprField {
         self.span()
     }
 
-    fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
-        let attrs_str = self.attrs.rewrite(context, shape)?;
+    fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
+        let attrs_str = self.attrs.rewrite_result(context, shape)?;
         let name = rewrite_ident(context, self.ident);
         let missing_span = if self.attrs.is_empty() {
             mk_sp(self.span.lo(), self.span.lo())
@@ -104,7 +103,7 @@ impl AlignedItem for ast::ExprField {
         context: &RewriteContext<'_>,
         shape: Shape,
         prefix_max_width: usize,
-    ) -> Option<String> {
+    ) -> RewriteResult {
         rewrite_field(context, self, shape, prefix_max_width)
     }
 }
@@ -199,7 +198,7 @@ fn struct_field_prefix_max_min_width<T: AlignedItem>(
                 .rewrite_prefix(context, shape)
                 .map(|field_str| trimmed_last_line_width(&field_str))
         })
-        .fold_options((0, ::std::usize::MAX), |(max_len, min_len), len| {
+        .fold_ok((0, ::std::usize::MAX), |(max_len, min_len), len| {
             (cmp::max(max_len, len), cmp::min(min_len, len))
         })
         .unwrap_or((0, 0))
@@ -246,12 +245,12 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
     if tactic == DefinitiveListTactic::Horizontal {
         // since the items fits on a line, there is no need to align them
         let do_rewrite =
-            |field: &T| -> Option<String> { field.rewrite_aligned_item(context, item_shape, 0) };
+            |field: &T| -> RewriteResult { field.rewrite_aligned_item(context, item_shape, 0) };
         fields
             .iter()
             .zip(items.iter_mut())
             .for_each(|(field, list_item): (&T, &mut ListItem)| {
-                if list_item.item.is_some() {
+                if list_item.item.is_ok() {
                     list_item.item = do_rewrite(field);
                 }
             });
@@ -267,7 +266,7 @@ fn rewrite_aligned_items_inner<T: AlignedItem>(
         .tactic(tactic)
         .trailing_separator(separator_tactic)
         .preserve_newline(true);
-    write_list(&items, &fmt)
+    write_list(&items, &fmt).ok()
 }
 
 /// Returns the index in `fields` up to which a field belongs to the current group.
diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs
index ac68fc5fecf..8102fe7ad8f 100644
--- a/src/tools/rustfmt/src/visitor.rs
+++ b/src/tools/rustfmt/src/visitor.rs
@@ -3,24 +3,23 @@ use std::rc::Rc;
 
 use rustc_ast::{ast, token::Delimiter, visit};
 use rustc_data_structures::sync::Lrc;
-use rustc_span::{symbol, BytePos, Pos, Span};
+use rustc_span::{BytePos, Pos, Span, symbol};
 use tracing::debug;
 
 use crate::attr::*;
-use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices};
-use crate::config::Version;
-use crate::config::{BraceStyle, Config, MacroSelector};
+use crate::comment::{CodeCharKind, CommentCodeSlices, contains_comment, rewrite_comment};
+use crate::config::{BraceStyle, Config, MacroSelector, StyleEdition};
 use crate::coverage::transform_missing_snippet;
 use crate::items::{
-    format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate,
-    rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts,
+    FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, format_impl, format_trait,
+    format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, rewrite_type_alias,
 };
-use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition};
+use crate::macros::{MacroPosition, macro_style, rewrite_macro, rewrite_macro_def};
 use crate::modules::Module;
 use crate::parse::session::ParseSess;
 use crate::rewrite::{Rewrite, RewriteContext};
 use crate::shape::{Indent, Shape};
-use crate::skip::{is_skip_attr, SkipContext};
+use crate::skip::{SkipContext, is_skip_attr};
 use crate::source_map::{LineRangeUtils, SpanUtils};
 use crate::spanned::Spanned;
 use crate::stmt::Stmt;
@@ -291,7 +290,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
 
                     let mut comment_shape =
                         Shape::indented(self.block_indent, config).comment(config);
-                    if self.config.version() == Version::Two && comment_on_same_line {
+                    if self.config.style_edition() >= StyleEdition::Edition2024
+                        && comment_on_same_line
+                    {
                         self.push_str(" ");
                         // put the first line of the comment on the same line as the
                         // block's last line
@@ -312,8 +313,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                                 let comment_str =
                                     rewrite_comment(other_lines, false, comment_shape, config);
                                 match comment_str {
-                                    Some(ref s) => self.push_str(s),
-                                    None => self.push_str(other_lines),
+                                    Ok(ref s) => self.push_str(s),
+                                    Err(_) => self.push_str(other_lines),
                                 }
                             }
                         }
@@ -342,8 +343,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
 
                         let comment_str = rewrite_comment(&sub_slice, false, comment_shape, config);
                         match comment_str {
-                            Some(ref s) => self.push_str(s),
-                            None => self.push_str(&sub_slice),
+                            Ok(ref s) => self.push_str(s),
+                            Err(_) => self.push_str(&sub_slice),
                         }
                     }
                 }
@@ -561,9 +562,11 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                         )
                     } else {
                         let indent = self.block_indent;
-                        let rewrite = self.rewrite_required_fn(
-                            indent, item.ident, sig, &item.vis, generics, item.span,
-                        );
+                        let rewrite = self
+                            .rewrite_required_fn(
+                                indent, item.ident, sig, &item.vis, generics, item.span,
+                            )
+                            .ok();
                         self.push_rewrite(item.span, rewrite);
                     }
                 }
@@ -584,7 +587,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                         item.ident,
                         &item.vis,
                         item.span,
-                    );
+                    )
+                    .ok();
                     self.push_rewrite(item.span, rewrite);
                 }
                 ast::ItemKind::Delegation(..) | ast::ItemKind::DelegationMac(..) => {
@@ -609,7 +613,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
             self.block_indent,
             visitor_kind,
             span,
-        );
+        )
+        .ok();
         self.push_rewrite(span, rewrite);
     }
 
@@ -655,8 +660,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                     );
                 } else {
                     let indent = self.block_indent;
-                    let rewrite =
-                        self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span);
+                    let rewrite = self
+                        .rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span)
+                        .ok();
                     self.push_rewrite(ai.span, rewrite);
                 }
             }
@@ -683,7 +689,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
 
         // 1 = ;
         let shape = self.shape().saturating_sub_width(1);
-        let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos));
+        let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos).ok());
         // As of v638 of the rustc-ap-* crates, the associated span no longer includes
         // the trailing semicolon. This determines the correct span to ensure scenarios
         // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc)     ;`)
diff --git a/src/tools/rustfmt/tests/config/issue-5801-v2.toml b/src/tools/rustfmt/tests/config/issue-5801-v2.toml
index 948f4fb66bf..98e89d37916 100644
--- a/src/tools/rustfmt/tests/config/issue-5801-v2.toml
+++ b/src/tools/rustfmt/tests/config/issue-5801-v2.toml
@@ -1,3 +1,3 @@
 max_width = 120
 version = "Two"
-attr_fn_like_width = 120
\ No newline at end of file
+attr_fn_like_width = 120
diff --git a/src/tools/rustfmt/tests/config/issue-6302.toml b/src/tools/rustfmt/tests/config/issue-6302.toml
new file mode 100644
index 00000000000..8148b37b1f6
--- /dev/null
+++ b/src/tools/rustfmt/tests/config/issue-6302.toml
@@ -0,0 +1 @@
+edition = 2019
diff --git a/src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml
new file mode 100644
index 00000000000..3501136812c
--- /dev/null
+++ b/src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml
@@ -0,0 +1 @@
+style_edition = "2024"
diff --git a/src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml
new file mode 100644
index 00000000000..1082fd88889
--- /dev/null
+++ b/src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml
@@ -0,0 +1 @@
+version = "Two"
diff --git a/src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml
new file mode 100644
index 00000000000..24205692b1f
--- /dev/null
+++ b/src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml
@@ -0,0 +1,2 @@
+style_edition = "2024"
+overflow_delimited_expr = false
diff --git a/src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml
new file mode 100644
index 00000000000..92844e03ab6
--- /dev/null
+++ b/src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml
@@ -0,0 +1,2 @@
+style_edition = "2021"
+edition = "2024"
diff --git a/src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml
new file mode 100644
index 00000000000..16ea9a13f36
--- /dev/null
+++ b/src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml
@@ -0,0 +1,2 @@
+version = "Two"
+edition = "2018"
diff --git a/src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml
new file mode 100644
index 00000000000..187ba13cfb6
--- /dev/null
+++ b/src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml
@@ -0,0 +1,3 @@
+version = "Two"
+edition = "2018"
+style_edition = "2021"
diff --git a/src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml
new file mode 100644
index 00000000000..c894bd981bc
--- /dev/null
+++ b/src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml
@@ -0,0 +1,2 @@
+version = "Two"
+style_edition = "2021"
diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs
index 58cf0e5e4db..a9f58b9328e 100644
--- a/src/tools/rustfmt/tests/rustfmt/main.rs
+++ b/src/tools/rustfmt/tests/rustfmt/main.rs
@@ -5,7 +5,7 @@ use std::fs::remove_file;
 use std::path::Path;
 use std::process::Command;
 
-use rustfmt_config_proc_macro::rustfmt_only_ci_test;
+use rustfmt_config_proc_macro::{nightly_only_test, rustfmt_only_ci_test};
 
 /// Run the rustfmt executable and return its output.
 fn rustfmt(args: &[&str]) -> (String, String) {
@@ -207,3 +207,28 @@ fn rustfmt_emits_error_when_control_brace_style_is_always_next_line() {
     let (_stdout, stderr) = rustfmt(&args);
     assert!(!stderr.contains("error[internal]: left behind trailing whitespace"))
 }
+
+#[nightly_only_test]
+#[test]
+fn rustfmt_generates_no_error_if_failed_format_code_in_doc_comments() {
+    // See also https://github.com/rust-lang/rustfmt/issues/6109
+
+    let file = "tests/target/issue-6109.rs";
+    let args = ["--config", "format_code_in_doc_comments=true", file];
+    let (stdout, stderr) = rustfmt(&args);
+    assert!(stderr.is_empty());
+    assert!(stdout.is_empty());
+}
+
+#[test]
+fn rustfmt_error_improvement_regarding_invalid_toml() {
+    // See also https://github.com/rust-lang/rustfmt/issues/6302
+    let invalid_toml_config = "tests/config/issue-6302.toml";
+    let args = ["--config-path", invalid_toml_config];
+    let (_stdout, stderr) = rustfmt(&args);
+
+    let toml_path = Path::new(invalid_toml_config).canonicalize().unwrap();
+    let expected_error_message = format!("The file `{}` failed to parse", toml_path.display());
+
+    assert!(stderr.contains(&expected_error_message));
+}
diff --git a/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs b/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs
index fb0576a4822..38e8967e7e6 100644
--- a/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs
+++ b/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 fn main() {
     match a {
         _ =>
diff --git a/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs b/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs
index fc696b309f2..98ef919450f 100644
--- a/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs
+++ b/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 fn main() {
     match a {
         _ => // comment with => 
diff --git a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs
index 6d48ea742fc..66e2f538415 100644
--- a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs
+++ b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 // rustfmt-error_on_line_overflow: false
 // rustfmt-indent_style: Block
 
diff --git a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs
index 7a62d722c6e..51aa6376e23 100644
--- a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs
+++ b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-error_on_line_overflow: false
 // rustfmt-indent_style: Block
 
diff --git a/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs
new file mode 100644
index 00000000000..5cb4a870fc1
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs
@@ -0,0 +1,155 @@
+// rustfmt-style_edition: 2015
+
+fn combine_blocklike() {
+    do_thing(
+        |param| {
+            action();
+            foo(param)
+        },
+    );
+
+    do_thing(
+        x,
+        |param| {
+            action();
+            foo(param)
+        },
+    );
+
+    do_thing(
+        x,
+
+        // I'll be discussing the `action` with your para(m)legal counsel
+        |param| {
+            action();
+            foo(param)
+        },
+    );
+
+    do_thing(
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
+
+    do_thing(
+        x,
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
+
+    do_thing(
+        x,
+
+        // Let me tell you about that one time at the `Bar`
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
+
+    do_thing(
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+
+        // Just admit it; my list is longer than can be folded on to one line
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+
+        // Just admit it; my list is longer than can be folded on to one line
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        (
+            1,
+            2,
+            3,
+            |param| {
+                action();
+                foo(param)
+            },
+        ),
+    );
+}
+
+fn combine_struct_sample() {
+    let identity = verify(
+        &ctx,
+        VerifyLogin {
+            type_: LoginType::Username,
+            username: args.username.clone(),
+            password: Some(args.password.clone()),
+            domain: None,
+        },
+    )?;
+}
+
+fn combine_macro_sample() {
+    rocket::ignite()
+        .mount(
+            "/",
+            routes![
+                http::auth::login,
+                http::auth::logout,
+                http::cors::options,
+                http::action::dance,
+                http::action::sleep,
+            ],
+        )
+        .launch();
+}
diff --git a/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs
new file mode 100644
index 00000000000..66c95e71aa9
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs
@@ -0,0 +1,155 @@
+// rustfmt-style_edition: 2024
+
+fn combine_blocklike() {
+    do_thing(
+        |param| {
+            action();
+            foo(param)
+        },
+    );
+
+    do_thing(
+        x,
+        |param| {
+            action();
+            foo(param)
+        },
+    );
+
+    do_thing(
+        x,
+
+        // I'll be discussing the `action` with your para(m)legal counsel
+        |param| {
+            action();
+            foo(param)
+        },
+    );
+
+    do_thing(
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
+
+    do_thing(
+        x,
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
+
+    do_thing(
+        x,
+
+        // Let me tell you about that one time at the `Bar`
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
+
+    do_thing(
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+
+        // Just admit it; my list is longer than can be folded on to one line
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+
+        // Just admit it; my list is longer than can be folded on to one line
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        (
+            1,
+            2,
+            3,
+            |param| {
+                action();
+                foo(param)
+            },
+        ),
+    );
+}
+
+fn combine_struct_sample() {
+    let identity = verify(
+        &ctx,
+        VerifyLogin {
+            type_: LoginType::Username,
+            username: args.username.clone(),
+            password: Some(args.password.clone()),
+            domain: None,
+        },
+    )?;
+}
+
+fn combine_macro_sample() {
+    rocket::ignite()
+        .mount(
+            "/",
+            routes![
+                http::auth::login,
+                http::auth::logout,
+                http::cors::options,
+                http::action::dance,
+                http::action::sleep,
+            ],
+        )
+        .launch();
+}
diff --git a/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs b/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs
index 469ab621567..85957e3fdcc 100644
--- a/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs
+++ b/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs
@@ -1,5 +1,5 @@
 // rustfmt-fn_single_line: true
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 // Test single-line functions.
 
 fn foo_expr() {
diff --git a/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs b/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs
index bf381ff1065..f119e581217 100644
--- a/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs
+++ b/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs
@@ -1,5 +1,5 @@
 // rustfmt-fn_single_line: true
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // Test single-line functions.
 
 fn foo_expr() {
diff --git a/src/tools/rustfmt/tests/source/issue-2179/one.rs b/src/tools/rustfmt/tests/source/issue-2179/one.rs
index d23947931ff..8bbd56f0521 100644
--- a/src/tools/rustfmt/tests/source/issue-2179/one.rs
+++ b/src/tools/rustfmt/tests/source/issue-2179/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 // rustfmt-error_on_line_overflow: false
 
 fn issue_2179() {
diff --git a/src/tools/rustfmt/tests/source/issue-2179/two.rs b/src/tools/rustfmt/tests/source/issue-2179/two.rs
index f4cc9cc488b..631b0f3c86e 100644
--- a/src/tools/rustfmt/tests/source/issue-2179/two.rs
+++ b/src/tools/rustfmt/tests/source/issue-2179/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-error_on_line_overflow: false
 
 fn issue_2179() {
diff --git a/src/tools/rustfmt/tests/source/issue-3213/version_one.rs b/src/tools/rustfmt/tests/source/issue-3213/version_one.rs
index f9f4cab55e3..ed7d5145150 100644
--- a/src/tools/rustfmt/tests/source/issue-3213/version_one.rs
+++ b/src/tools/rustfmt/tests/source/issue-3213/version_one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn foo() {
     match 0 {
diff --git a/src/tools/rustfmt/tests/source/issue-3213/version_two.rs b/src/tools/rustfmt/tests/source/issue-3213/version_two.rs
index 0f068c19d74..c6d04aced8d 100644
--- a/src/tools/rustfmt/tests/source/issue-3213/version_two.rs
+++ b/src/tools/rustfmt/tests/source/issue-3213/version_two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn foo() {
     match 0 {
diff --git a/src/tools/rustfmt/tests/source/issue-3227/two.rs b/src/tools/rustfmt/tests/source/issue-3227/two.rs
index c1572c00d57..50c0ad47dc1 100644
--- a/src/tools/rustfmt/tests/source/issue-3227/two.rs
+++ b/src/tools/rustfmt/tests/source/issue-3227/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn main() {
     thread::spawn(|| {
diff --git a/src/tools/rustfmt/tests/source/issue-3270/one.rs b/src/tools/rustfmt/tests/source/issue-3270/one.rs
index 3c2e27e2293..64861176b91 100644
--- a/src/tools/rustfmt/tests/source/issue-3270/one.rs
+++ b/src/tools/rustfmt/tests/source/issue-3270/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 pub fn main() {
     /*   let s = String::from(
diff --git a/src/tools/rustfmt/tests/source/issue-3270/two.rs b/src/tools/rustfmt/tests/source/issue-3270/two.rs
index 0eb756471e7..1342cf03c39 100644
--- a/src/tools/rustfmt/tests/source/issue-3270/two.rs
+++ b/src/tools/rustfmt/tests/source/issue-3270/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 pub fn main() {
  /*   let s = String::from(
diff --git a/src/tools/rustfmt/tests/source/issue-3272/v1.rs b/src/tools/rustfmt/tests/source/issue-3272/v1.rs
index f4c1b7c992b..56dc048bf8a 100644
--- a/src/tools/rustfmt/tests/source/issue-3272/v1.rs
+++ b/src/tools/rustfmt/tests/source/issue-3272/v1.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn main() {
     assert!(HAYSTACK
diff --git a/src/tools/rustfmt/tests/source/issue-3272/v2.rs b/src/tools/rustfmt/tests/source/issue-3272/v2.rs
index 0148368edc8..f3adbe37c76 100644
--- a/src/tools/rustfmt/tests/source/issue-3272/v2.rs
+++ b/src/tools/rustfmt/tests/source/issue-3272/v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn main() {
     assert!(HAYSTACK
diff --git a/src/tools/rustfmt/tests/source/issue-3278/version_one.rs b/src/tools/rustfmt/tests/source/issue-3278/version_one.rs
index 580679fbae3..718a32b4c7e 100644
--- a/src/tools/rustfmt/tests/source/issue-3278/version_one.rs
+++ b/src/tools/rustfmt/tests/source/issue-3278/version_one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 pub fn parse_conditional<'a, I: 'a>(
 ) -> impl Parser<Input = I, Output = Expr, PartialState = ()> + 'a
diff --git a/src/tools/rustfmt/tests/source/issue-3278/version_two.rs b/src/tools/rustfmt/tests/source/issue-3278/version_two.rs
index c17b1742d39..eb605e509f9 100644
--- a/src/tools/rustfmt/tests/source/issue-3278/version_two.rs
+++ b/src/tools/rustfmt/tests/source/issue-3278/version_two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 pub fn parse_conditional<'a, I: 'a>()
 -> impl Parser<Input = I, Output = Expr, PartialState = ()> + 'a
diff --git a/src/tools/rustfmt/tests/source/issue-3295/two.rs b/src/tools/rustfmt/tests/source/issue-3295/two.rs
index 0eaf022249b..ae3d2ec28c0 100644
--- a/src/tools/rustfmt/tests/source/issue-3295/two.rs
+++ b/src/tools/rustfmt/tests/source/issue-3295/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 pub enum TestEnum {
     a,
     b,
diff --git a/src/tools/rustfmt/tests/source/issue-3302.rs b/src/tools/rustfmt/tests/source/issue-3302.rs
index c037584fd71..5e0862cb399 100644
--- a/src/tools/rustfmt/tests/source/issue-3302.rs
+++ b/src/tools/rustfmt/tests/source/issue-3302.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 macro_rules! moo1 {
     () => {
diff --git a/src/tools/rustfmt/tests/source/issue-3701/one.rs b/src/tools/rustfmt/tests/source/issue-3701/one.rs
index a7f0bd3aa17..4e8518b6f18 100644
--- a/src/tools/rustfmt/tests/source/issue-3701/one.rs
+++ b/src/tools/rustfmt/tests/source/issue-3701/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn build_sorted_static_get_entry_names(
     mut entries: Vec<(u8, &'static str)>,
diff --git a/src/tools/rustfmt/tests/source/issue-3701/two.rs b/src/tools/rustfmt/tests/source/issue-3701/two.rs
index 8e15c58b8b2..d7cb790a754 100644
--- a/src/tools/rustfmt/tests/source/issue-3701/two.rs
+++ b/src/tools/rustfmt/tests/source/issue-3701/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn build_sorted_static_get_entry_names(
     mut entries: Vec<(u8, &'static str)>,
diff --git a/src/tools/rustfmt/tests/source/issue-3805.rs b/src/tools/rustfmt/tests/source/issue-3805.rs
index a0289b57974..aadc4a9dddc 100644
--- a/src/tools/rustfmt/tests/source/issue-3805.rs
+++ b/src/tools/rustfmt/tests/source/issue-3805.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-format_macro_matchers: true
 
 // From original issue example - Line length 101
diff --git a/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs b/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs
index 7b505fda87c..8d009eabdec 100644
--- a/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs
+++ b/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs
@@ -1,5 +1,5 @@
 // rustfmt-hard_tabs: true
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 impl<Target: FromEvent<A> + FromEvent<B>, A: Widget2<Ctx = C>, B: Widget2<Ctx = C>, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter<Target, A, B>
 {
diff --git a/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs b/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs
index 39c8ef31292..9e283d3be0a 100644
--- a/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs
+++ b/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 impl<Target: FromEvent<A> + FromEvent<B>, A: Widget2<Ctx = C>, B: Widget2<Ctx = C>, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter<Target, A, B>
 {
diff --git a/src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs b/src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs
new file mode 100644
index 00000000000..bb4c9feae1d
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs
@@ -0,0 +1,3 @@
+// rustfmt-style_edition: 2015
+
+use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64};
diff --git a/src/tools/rustfmt/tests/source/issue-4530.rs b/src/tools/rustfmt/tests/source/issue-4530.rs
index 9d2882abb3c..6b92122f0c0 100644
--- a/src/tools/rustfmt/tests/source/issue-4530.rs
+++ b/src/tools/rustfmt/tests/source/issue-4530.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 fn main() {
     let [aaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccc, ddddddddddddddddddddddddd] = panic!();
 }
diff --git a/src/tools/rustfmt/tests/source/issue-4689/one.rs b/src/tools/rustfmt/tests/source/issue-4689/one.rs
index d048eb10fb1..bff090a3525 100644
--- a/src/tools/rustfmt/tests/source/issue-4689/one.rs
+++ b/src/tools/rustfmt/tests/source/issue-4689/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 // Based on the issue description
 pub trait PrettyPrinter<'tcx>:
diff --git a/src/tools/rustfmt/tests/source/issue-4689/two.rs b/src/tools/rustfmt/tests/source/issue-4689/two.rs
index ea7feda825d..217535c046e 100644
--- a/src/tools/rustfmt/tests/source/issue-4689/two.rs
+++ b/src/tools/rustfmt/tests/source/issue-4689/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 // Based on the issue description
 pub trait PrettyPrinter<'tcx>:
diff --git a/src/tools/rustfmt/tests/source/issue-5586.rs b/src/tools/rustfmt/tests/source/issue-5586.rs
index 9cf6c1d58dd..061ad4bdaa4 100644
--- a/src/tools/rustfmt/tests/source/issue-5586.rs
+++ b/src/tools/rustfmt/tests/source/issue-5586.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 fn main() {
     // sample 1
     {
diff --git a/src/tools/rustfmt/tests/source/issue-5655/one.rs b/src/tools/rustfmt/tests/source/issue-5655/one.rs
index 1758ec56f8b..62df2655c29 100644
--- a/src/tools/rustfmt/tests/source/issue-5655/one.rs
+++ b/src/tools/rustfmt/tests/source/issue-5655/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn foo<T>(_: T)
 where
diff --git a/src/tools/rustfmt/tests/source/issue-5655/two.rs b/src/tools/rustfmt/tests/source/issue-5655/two.rs
index e37ebbea8af..bfe1d3813bb 100644
--- a/src/tools/rustfmt/tests/source/issue-5655/two.rs
+++ b/src/tools/rustfmt/tests/source/issue-5655/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn foo<T>(_: T)
 where
diff --git a/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs b/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs
index 5847afd9560..e61d34604a1 100644
--- a/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs
+++ b/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs
@@ -1,7 +1,7 @@
 // rustfmt-comment_width: 120
 // rustfmt-wrap_comments: true
 // rustfmt-max_width: 120
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 /// This function is 120 columns wide and is left alone. This comment is 120 columns wide and the formatter is also fine
 fn my_super_cool_function_name(my_very_cool_argument_name: String, my_other_very_cool_argument_name: String) -> String {
diff --git a/src/tools/rustfmt/tests/source/issue-5987/two.rs b/src/tools/rustfmt/tests/source/issue-5987/two.rs
index e20026b5565..98ed35c4f9a 100644
--- a/src/tools/rustfmt/tests/source/issue-5987/two.rs
+++ b/src/tools/rustfmt/tests/source/issue-5987/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn main() {
     trace!(
diff --git a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs
index 2ac2e0361c3..bcae86aaff2 100644
--- a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs
+++ b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 pub fn main() {
 let a = Some(12);
diff --git a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs
index c1bf1ad4bf8..da612b213fc 100644
--- a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs
+++ b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 pub fn main() {
 let a = Some(12);
diff --git a/src/tools/rustfmt/tests/source/issue_5027.rs b/src/tools/rustfmt/tests/source/issue_5027.rs
index 67beeb23b71..a47d6df6f0f 100644
--- a/src/tools/rustfmt/tests/source/issue_5027.rs
+++ b/src/tools/rustfmt/tests/source/issue_5027.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 pub type Iter<'a, D> =                 impl       DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)>+ ExactSizeIterator+ 'a;
 
diff --git a/src/tools/rustfmt/tests/source/let_else_v2.rs b/src/tools/rustfmt/tests/source/let_else_v2.rs
index a420fbcf95b..23be32d629a 100644
--- a/src/tools/rustfmt/tests/source/let_else_v2.rs
+++ b/src/tools/rustfmt/tests/source/let_else_v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-single_line_let_else_max_width: 100
 
 fn issue5901() {
diff --git a/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs b/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs
index d6832c2af09..60083024810 100644
--- a/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs
+++ b/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 // Tests that a function which is almost short enough, but not quite, gets
 // formatted correctly.
 
diff --git a/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs b/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs
index f402a26e8b6..bce2c551c6a 100644
--- a/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs
+++ b/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // Tests that a function which is almost short enough, but not quite, gets
 // formatted correctly.
 
diff --git a/src/tools/rustfmt/tests/source/one_line_if_v1.rs b/src/tools/rustfmt/tests/source/one_line_if_v1.rs
index d3dcbe6787a..bf7bc75fb88 100644
--- a/src/tools/rustfmt/tests/source/one_line_if_v1.rs
+++ b/src/tools/rustfmt/tests/source/one_line_if_v1.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn plain_if(x: bool) -> u8 {
     if x {
diff --git a/src/tools/rustfmt/tests/source/one_line_if_v2.rs b/src/tools/rustfmt/tests/source/one_line_if_v2.rs
index 40c834959f9..f3c974b12db 100644
--- a/src/tools/rustfmt/tests/source/one_line_if_v2.rs
+++ b/src/tools/rustfmt/tests/source/one_line_if_v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn plain_if(x: bool) -> u8 {
     if x {
diff --git a/src/tools/rustfmt/tests/source/single-line-macro/v1.rs b/src/tools/rustfmt/tests/source/single-line-macro/v1.rs
index a3aa631ed4a..fea146d8d33 100644
--- a/src/tools/rustfmt/tests/source/single-line-macro/v1.rs
+++ b/src/tools/rustfmt/tests/source/single-line-macro/v1.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 // #2652
 // Preserve trailing comma inside macro, even if it looks an array.
diff --git a/src/tools/rustfmt/tests/source/single-line-macro/v2.rs b/src/tools/rustfmt/tests/source/single-line-macro/v2.rs
index 51a665f7560..d9fba64ac70 100644
--- a/src/tools/rustfmt/tests/source/single-line-macro/v2.rs
+++ b/src/tools/rustfmt/tests/source/single-line-macro/v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 // #2652
 // Preserve trailing comma inside macro, even if it looks an array.
diff --git a/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs b/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs
index 88249aa5fb9..c6d1b3f0ab7 100644
--- a/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs
+++ b/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-wrap_comments: true
 // rustfmt-hard_tabs: true
 
diff --git a/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs b/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs
index 7845f713b8a..431875989f3 100644
--- a/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs
+++ b/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-wrap_comments: true
 
 pub const IFF_MULTICAST: ::c_int = 0x0000000800; // Supports multicast
diff --git a/src/tools/rustfmt/tests/source/tuple_v2.rs b/src/tools/rustfmt/tests/source/tuple_v2.rs
index 9223033832b..6dc18e00d7e 100644
--- a/src/tools/rustfmt/tests/source/tuple_v2.rs
+++ b/src/tools/rustfmt/tests/source/tuple_v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn issue_4355() {
     let _ = ((1,),).0 .0;
diff --git a/src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs b/src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs
new file mode 100644
index 00000000000..722ebac47ac
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs
@@ -0,0 +1,31 @@
+type Foo // comment1
+    // interlinear1
+where // comment2
+    // interlinear2
+A: B, // comment3
+C: D, // comment4
+    // interlinear3
+= E; // comment5
+
+type Foo // comment6
+    // interlinear4
+where// comment7
+    // interlinear5
+A: B, // comment8
+C: D, // comment9
+    // interlinear6
+= E // comment10
+    // interlinear7
+where // comment11
+    // interlinear8
+F: G, // comment12
+H: I; // comment13
+
+type Foo // comment14
+    // interlinear9
+= E // comment15
+    // interlinear10
+where // comment16
+    // interlinear11
+F: G,// comment17
+H: I;// comment18
diff --git a/src/tools/rustfmt/tests/source/type-alias-where-clauses.rs b/src/tools/rustfmt/tests/source/type-alias-where-clauses.rs
new file mode 100644
index 00000000000..ad998caf308
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/type-alias-where-clauses.rs
@@ -0,0 +1,20 @@
+    type Foo
+    where
+    A: B,
+    C: D,
+    = E;
+
+    type Foo
+    where
+    A: B,
+    C: D,
+    = E
+    where
+    F: G,
+    H: I;
+
+    type Foo
+    = E
+    where
+    F: G,
+    H: I;
\ No newline at end of file
diff --git a/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs b/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs
index deffdbeeaaf..d638a6b4ef0 100644
--- a/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs
+++ b/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 fn main() {
     match a {
         _ =>
diff --git a/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs b/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs
index b0443411816..f6f4a60eec3 100644
--- a/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs
+++ b/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 fn main() {
     match a {
         _ =>
diff --git a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs
index 6b9489bef55..204dce6d655 100644
--- a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs
+++ b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 // rustfmt-error_on_line_overflow: false
 // rustfmt-indent_style: Block
 
diff --git a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs
index 4f4292e5f48..887e8328ccc 100644
--- a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs
+++ b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-error_on_line_overflow: false
 // rustfmt-indent_style: Block
 
diff --git a/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs
new file mode 100644
index 00000000000..05d4b8b6d33
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs
@@ -0,0 +1,135 @@
+// rustfmt-style_edition: 2015
+
+fn combine_blocklike() {
+    do_thing(|param| {
+        action();
+        foo(param)
+    });
+
+    do_thing(x, |param| {
+        action();
+        foo(param)
+    });
+
+    do_thing(
+        x,
+        // I'll be discussing the `action` with your para(m)legal counsel
+        |param| {
+            action();
+            foo(param)
+        },
+    );
+
+    do_thing(Bar {
+        x: value,
+        y: value2,
+    });
+
+    do_thing(
+        x,
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
+
+    do_thing(
+        x,
+        // Let me tell you about that one time at the `Bar`
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
+
+    do_thing(&[
+        value_with_longer_name,
+        value2_with_longer_name,
+        value3_with_longer_name,
+        value4_with_longer_name,
+    ]);
+
+    do_thing(
+        x,
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        // Just admit it; my list is longer than can be folded on to one line
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(vec![
+        value_with_longer_name,
+        value2_with_longer_name,
+        value3_with_longer_name,
+        value4_with_longer_name,
+    ]);
+
+    do_thing(
+        x,
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        // Just admit it; my list is longer than can be folded on to one line
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        (1, 2, 3, |param| {
+            action();
+            foo(param)
+        }),
+    );
+}
+
+fn combine_struct_sample() {
+    let identity = verify(
+        &ctx,
+        VerifyLogin {
+            type_: LoginType::Username,
+            username: args.username.clone(),
+            password: Some(args.password.clone()),
+            domain: None,
+        },
+    )?;
+}
+
+fn combine_macro_sample() {
+    rocket::ignite()
+        .mount(
+            "/",
+            routes![
+                http::auth::login,
+                http::auth::logout,
+                http::cors::options,
+                http::action::dance,
+                http::action::sleep,
+            ],
+        )
+        .launch();
+}
diff --git a/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs
new file mode 100644
index 00000000000..ecd2e8ca797
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs
@@ -0,0 +1,120 @@
+// rustfmt-style_edition: 2024
+
+fn combine_blocklike() {
+    do_thing(|param| {
+        action();
+        foo(param)
+    });
+
+    do_thing(x, |param| {
+        action();
+        foo(param)
+    });
+
+    do_thing(
+        x,
+        // I'll be discussing the `action` with your para(m)legal counsel
+        |param| {
+            action();
+            foo(param)
+        },
+    );
+
+    do_thing(Bar {
+        x: value,
+        y: value2,
+    });
+
+    do_thing(x, Bar {
+        x: value,
+        y: value2,
+    });
+
+    do_thing(
+        x,
+        // Let me tell you about that one time at the `Bar`
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
+
+    do_thing(&[
+        value_with_longer_name,
+        value2_with_longer_name,
+        value3_with_longer_name,
+        value4_with_longer_name,
+    ]);
+
+    do_thing(x, &[
+        value_with_longer_name,
+        value2_with_longer_name,
+        value3_with_longer_name,
+        value4_with_longer_name,
+    ]);
+
+    do_thing(
+        x,
+        // Just admit it; my list is longer than can be folded on to one line
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(vec![
+        value_with_longer_name,
+        value2_with_longer_name,
+        value3_with_longer_name,
+        value4_with_longer_name,
+    ]);
+
+    do_thing(x, vec![
+        value_with_longer_name,
+        value2_with_longer_name,
+        value3_with_longer_name,
+        value4_with_longer_name,
+    ]);
+
+    do_thing(
+        x,
+        // Just admit it; my list is longer than can be folded on to one line
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
+
+    do_thing(
+        x,
+        (1, 2, 3, |param| {
+            action();
+            foo(param)
+        }),
+    );
+}
+
+fn combine_struct_sample() {
+    let identity = verify(&ctx, VerifyLogin {
+        type_: LoginType::Username,
+        username: args.username.clone(),
+        password: Some(args.password.clone()),
+        domain: None,
+    })?;
+}
+
+fn combine_macro_sample() {
+    rocket::ignite()
+        .mount("/", routes![
+            http::auth::login,
+            http::auth::logout,
+            http::cors::options,
+            http::action::dance,
+            http::action::sleep,
+        ])
+        .launch();
+}
diff --git a/src/tools/rustfmt/tests/target/configs/version/mapped.rs b/src/tools/rustfmt/tests/target/configs/version/mapped.rs
new file mode 100644
index 00000000000..296dc559a93
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/configs/version/mapped.rs
@@ -0,0 +1,9 @@
+// rustfmt-version: Two
+fn main() {
+    let [
+        aaaaaaaaaaaaaaaaaaaaaaaaaa,
+        bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
+        cccccccccccccccccccccccccc,
+        ddddddddddddddddddddddddd,
+    ] = panic!();
+}
diff --git a/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs b/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs
index 013b2cd7216..5b704474d6d 100644
--- a/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs
+++ b/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs
@@ -1,5 +1,5 @@
 // rustfmt-fn_single_line: true
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 // Test single-line functions.
 
 fn foo_expr() { 1 }
diff --git a/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs b/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs
index b8053d4c2f5..aedd1d481b6 100644
--- a/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs
+++ b/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs
@@ -1,5 +1,5 @@
 // rustfmt-fn_single_line: true
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // Test single-line functions.
 
 fn foo_expr() { 1 }
diff --git a/src/tools/rustfmt/tests/target/issue-2179/one.rs b/src/tools/rustfmt/tests/target/issue-2179/one.rs
index 3f98acc8dcd..6593624e589 100644
--- a/src/tools/rustfmt/tests/target/issue-2179/one.rs
+++ b/src/tools/rustfmt/tests/target/issue-2179/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 // rustfmt-error_on_line_overflow: false
 
 fn issue_2179() {
diff --git a/src/tools/rustfmt/tests/target/issue-2179/two.rs b/src/tools/rustfmt/tests/target/issue-2179/two.rs
index 96531509ea2..088518705fe 100644
--- a/src/tools/rustfmt/tests/target/issue-2179/two.rs
+++ b/src/tools/rustfmt/tests/target/issue-2179/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-error_on_line_overflow: false
 
 fn issue_2179() {
diff --git a/src/tools/rustfmt/tests/target/issue-3132.rs b/src/tools/rustfmt/tests/target/issue-3132.rs
index 4dffe0ab836..4d61baa7507 100644
--- a/src/tools/rustfmt/tests/target/issue-3132.rs
+++ b/src/tools/rustfmt/tests/target/issue-3132.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn test() {
     /*
diff --git a/src/tools/rustfmt/tests/target/issue-3213/version_one.rs b/src/tools/rustfmt/tests/target/issue-3213/version_one.rs
index 307903b128b..a20915df761 100644
--- a/src/tools/rustfmt/tests/target/issue-3213/version_one.rs
+++ b/src/tools/rustfmt/tests/target/issue-3213/version_one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn foo() {
     match 0 {
diff --git a/src/tools/rustfmt/tests/target/issue-3213/version_two.rs b/src/tools/rustfmt/tests/target/issue-3213/version_two.rs
index de93d04ba95..fb609f6a67a 100644
--- a/src/tools/rustfmt/tests/target/issue-3213/version_two.rs
+++ b/src/tools/rustfmt/tests/target/issue-3213/version_two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn foo() {
     match 0 {
diff --git a/src/tools/rustfmt/tests/target/issue-3227/one.rs b/src/tools/rustfmt/tests/target/issue-3227/one.rs
index fcc8331000d..2922bfdfef7 100644
--- a/src/tools/rustfmt/tests/target/issue-3227/one.rs
+++ b/src/tools/rustfmt/tests/target/issue-3227/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn main() {
     thread::spawn(|| {
diff --git a/src/tools/rustfmt/tests/target/issue-3227/two.rs b/src/tools/rustfmt/tests/target/issue-3227/two.rs
index 374ab54305d..ae7eee47194 100644
--- a/src/tools/rustfmt/tests/target/issue-3227/two.rs
+++ b/src/tools/rustfmt/tests/target/issue-3227/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn main() {
     thread::spawn(|| {
diff --git a/src/tools/rustfmt/tests/target/issue-3270/one.rs b/src/tools/rustfmt/tests/target/issue-3270/one.rs
index 78de9473243..a31a0ff5742 100644
--- a/src/tools/rustfmt/tests/target/issue-3270/one.rs
+++ b/src/tools/rustfmt/tests/target/issue-3270/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 pub fn main() {
     /*   let s = String::from(
diff --git a/src/tools/rustfmt/tests/target/issue-3270/two.rs b/src/tools/rustfmt/tests/target/issue-3270/two.rs
index e48b5921329..8e26f8ac23d 100644
--- a/src/tools/rustfmt/tests/target/issue-3270/two.rs
+++ b/src/tools/rustfmt/tests/target/issue-3270/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 pub fn main() {
     /*   let s = String::from(
diff --git a/src/tools/rustfmt/tests/target/issue-3270/wrap.rs b/src/tools/rustfmt/tests/target/issue-3270/wrap.rs
index 7435c5f0866..967bfb5534c 100644
--- a/src/tools/rustfmt/tests/target/issue-3270/wrap.rs
+++ b/src/tools/rustfmt/tests/target/issue-3270/wrap.rs
@@ -1,5 +1,5 @@
 // rustfmt-wrap_comments: true
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 // check that a line below max_width does not get over the limit when wrapping
 // it in a block comment
diff --git a/src/tools/rustfmt/tests/target/issue-3272/v1.rs b/src/tools/rustfmt/tests/target/issue-3272/v1.rs
index aab201027d5..7cca7ea7896 100644
--- a/src/tools/rustfmt/tests/target/issue-3272/v1.rs
+++ b/src/tools/rustfmt/tests/target/issue-3272/v1.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn main() {
     assert!(HAYSTACK
diff --git a/src/tools/rustfmt/tests/target/issue-3272/v2.rs b/src/tools/rustfmt/tests/target/issue-3272/v2.rs
index a42a2fccd5b..ca7718c5a61 100644
--- a/src/tools/rustfmt/tests/target/issue-3272/v2.rs
+++ b/src/tools/rustfmt/tests/target/issue-3272/v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn main() {
     assert!(
diff --git a/src/tools/rustfmt/tests/target/issue-3278/version_one.rs b/src/tools/rustfmt/tests/target/issue-3278/version_one.rs
index 580679fbae3..718a32b4c7e 100644
--- a/src/tools/rustfmt/tests/target/issue-3278/version_one.rs
+++ b/src/tools/rustfmt/tests/target/issue-3278/version_one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 pub fn parse_conditional<'a, I: 'a>(
 ) -> impl Parser<Input = I, Output = Expr, PartialState = ()> + 'a
diff --git a/src/tools/rustfmt/tests/target/issue-3278/version_two.rs b/src/tools/rustfmt/tests/target/issue-3278/version_two.rs
index c17b1742d39..eb605e509f9 100644
--- a/src/tools/rustfmt/tests/target/issue-3278/version_two.rs
+++ b/src/tools/rustfmt/tests/target/issue-3278/version_two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 pub fn parse_conditional<'a, I: 'a>()
 -> impl Parser<Input = I, Output = Expr, PartialState = ()> + 'a
diff --git a/src/tools/rustfmt/tests/target/issue-3295/two.rs b/src/tools/rustfmt/tests/target/issue-3295/two.rs
index 3e669a0bb75..4f685172cfa 100644
--- a/src/tools/rustfmt/tests/target/issue-3295/two.rs
+++ b/src/tools/rustfmt/tests/target/issue-3295/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 pub enum TestEnum {
     a,
     b,
diff --git a/src/tools/rustfmt/tests/target/issue-3302.rs b/src/tools/rustfmt/tests/target/issue-3302.rs
index 146cb983819..504bfd987f5 100644
--- a/src/tools/rustfmt/tests/target/issue-3302.rs
+++ b/src/tools/rustfmt/tests/target/issue-3302.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 macro_rules! moo1 {
     () => {
diff --git a/src/tools/rustfmt/tests/target/issue-3614/version_one.rs b/src/tools/rustfmt/tests/target/issue-3614/version_one.rs
index 8ab28304732..4bd972aa87a 100644
--- a/src/tools/rustfmt/tests/target/issue-3614/version_one.rs
+++ b/src/tools/rustfmt/tests/target/issue-3614/version_one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn main() {
     let toto = || {
diff --git a/src/tools/rustfmt/tests/target/issue-3614/version_two.rs b/src/tools/rustfmt/tests/target/issue-3614/version_two.rs
index 5d6f8e7a313..7b9534caa02 100644
--- a/src/tools/rustfmt/tests/target/issue-3614/version_two.rs
+++ b/src/tools/rustfmt/tests/target/issue-3614/version_two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn main() {
     let toto = || {
diff --git a/src/tools/rustfmt/tests/target/issue-3701/one.rs b/src/tools/rustfmt/tests/target/issue-3701/one.rs
index 9d1ef9eed9a..b154724d341 100644
--- a/src/tools/rustfmt/tests/target/issue-3701/one.rs
+++ b/src/tools/rustfmt/tests/target/issue-3701/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn build_sorted_static_get_entry_names(
     mut entries: Vec<(u8, &'static str)>,
diff --git a/src/tools/rustfmt/tests/target/issue-3701/two.rs b/src/tools/rustfmt/tests/target/issue-3701/two.rs
index 62ffc9d823d..995763ef291 100644
--- a/src/tools/rustfmt/tests/target/issue-3701/two.rs
+++ b/src/tools/rustfmt/tests/target/issue-3701/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn build_sorted_static_get_entry_names(
     mut entries: Vec<(u8, &'static str)>,
diff --git a/src/tools/rustfmt/tests/target/issue-3805.rs b/src/tools/rustfmt/tests/target/issue-3805.rs
index a247a43fe6d..a1eda832d76 100644
--- a/src/tools/rustfmt/tests/target/issue-3805.rs
+++ b/src/tools/rustfmt/tests/target/issue-3805.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-format_macro_matchers: true
 
 // From original issue example - Line length 101
diff --git a/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs b/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs
index 084db3d1465..78a2f921735 100644
--- a/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs
+++ b/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs
@@ -1,5 +1,5 @@
 // rustfmt-hard_tabs: true
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 impl<
 	Target: FromEvent<A> + FromEvent<B>,
diff --git a/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs b/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs
index bc59b0baa56..c76dd4dafbb 100644
--- a/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs
+++ b/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 impl<
     Target: FromEvent<A> + FromEvent<B>,
diff --git a/src/tools/rustfmt/tests/target/issue-3882.rs b/src/tools/rustfmt/tests/target/issue-3882.rs
index 5eb442af974..8e617635b7c 100644
--- a/src/tools/rustfmt/tests/target/issue-3882.rs
+++ b/src/tools/rustfmt/tests/target/issue-3882.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 fn bar(_t: T, // bar
 ) {
 }
diff --git a/src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs
new file mode 100644
index 00000000000..1fc1613083e
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs
@@ -0,0 +1,3 @@
+// rustfmt-style_edition: 2015
+
+use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8};
diff --git a/src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs
new file mode 100644
index 00000000000..df85636ad57
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs
@@ -0,0 +1,3 @@
+// rustfmt-style_edition: 2024
+
+use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64};
diff --git a/src/tools/rustfmt/tests/target/issue-4530.rs b/src/tools/rustfmt/tests/target/issue-4530.rs
index 296dc559a93..9e9fbf95c2d 100644
--- a/src/tools/rustfmt/tests/target/issue-4530.rs
+++ b/src/tools/rustfmt/tests/target/issue-4530.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 fn main() {
     let [
         aaaaaaaaaaaaaaaaaaaaaaaaaa,
diff --git a/src/tools/rustfmt/tests/target/issue-4689/one.rs b/src/tools/rustfmt/tests/target/issue-4689/one.rs
index 7735e34f3b5..fb523f38501 100644
--- a/src/tools/rustfmt/tests/target/issue-4689/one.rs
+++ b/src/tools/rustfmt/tests/target/issue-4689/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 // Based on the issue description
 pub trait PrettyPrinter<'tcx>:
diff --git a/src/tools/rustfmt/tests/target/issue-4689/two.rs b/src/tools/rustfmt/tests/target/issue-4689/two.rs
index e3b5cd22810..30303490129 100644
--- a/src/tools/rustfmt/tests/target/issue-4689/two.rs
+++ b/src/tools/rustfmt/tests/target/issue-4689/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 // Based on the issue description
 pub trait PrettyPrinter<'tcx>:
diff --git a/src/tools/rustfmt/tests/target/issue-5586.rs b/src/tools/rustfmt/tests/target/issue-5586.rs
index 7033ae975b3..afe13d1fb1c 100644
--- a/src/tools/rustfmt/tests/target/issue-5586.rs
+++ b/src/tools/rustfmt/tests/target/issue-5586.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 fn main() {
     // sample 1
     {
diff --git a/src/tools/rustfmt/tests/target/issue-5655/one.rs b/src/tools/rustfmt/tests/target/issue-5655/one.rs
index 1758ec56f8b..62df2655c29 100644
--- a/src/tools/rustfmt/tests/target/issue-5655/one.rs
+++ b/src/tools/rustfmt/tests/target/issue-5655/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn foo<T>(_: T)
 where
diff --git a/src/tools/rustfmt/tests/target/issue-5655/two.rs b/src/tools/rustfmt/tests/target/issue-5655/two.rs
index 14fbc3d1321..4a7eb4f4bc2 100644
--- a/src/tools/rustfmt/tests/target/issue-5655/two.rs
+++ b/src/tools/rustfmt/tests/target/issue-5655/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn foo<T>(_: T)
 where
diff --git a/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs b/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs
index 9f294751108..461b5873a68 100644
--- a/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs
+++ b/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs
@@ -1,7 +1,7 @@
 // rustfmt-comment_width: 120
 // rustfmt-wrap_comments: true
 // rustfmt-max_width: 120
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 /// This function is 120 columns wide and is left alone. This comment is 120 columns wide and the formatter is also fine
 fn my_super_cool_function_name(my_very_cool_argument_name: String, my_other_very_cool_argument_name: String) -> String {
diff --git a/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs b/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs
index dd839dd4548..58bb8a1c2e3 100644
--- a/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs
+++ b/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs
@@ -1,7 +1,7 @@
 // rustfmt-comment_width: 120
 // rustfmt-wrap_comments: true
 // rustfmt-max_width: 120
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 /// This function is 120 columns wide and is left alone. This comment is 120 columns wide and the formatter is also fine
 fn my_super_cool_function_name(my_very_cool_argument_name: String, my_other_very_cool_argument_name: String) -> String {
diff --git a/src/tools/rustfmt/tests/target/issue-5987/one.rs b/src/tools/rustfmt/tests/target/issue-5987/one.rs
index 3c995ed28ba..17327feead5 100644
--- a/src/tools/rustfmt/tests/target/issue-5987/one.rs
+++ b/src/tools/rustfmt/tests/target/issue-5987/one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn main() {
     trace!(
diff --git a/src/tools/rustfmt/tests/target/issue-5987/two.rs b/src/tools/rustfmt/tests/target/issue-5987/two.rs
index 8fd92fc179e..41ecb13e3db 100644
--- a/src/tools/rustfmt/tests/target/issue-5987/two.rs
+++ b/src/tools/rustfmt/tests/target/issue-5987/two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn main() {
     trace!(
diff --git a/src/tools/rustfmt/tests/target/issue-6109.rs b/src/tools/rustfmt/tests/target/issue-6109.rs
new file mode 100644
index 00000000000..7d2bbf75691
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue-6109.rs
@@ -0,0 +1,7 @@
+/// Some doc comment with code snippet:
+///```
+/// '\u{1F}
+/// ```
+pub struct Code {}
+
+fn main() {}
diff --git a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs
index 75800012c63..d8e67655e4c 100644
--- a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs
+++ b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 pub fn main() {
     let a = Some(12);
diff --git a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs
index 5e4220e7306..9d0b12d686b 100644
--- a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs
+++ b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 pub fn main() {
     let a = Some(12);
diff --git a/src/tools/rustfmt/tests/target/issue_5027.rs b/src/tools/rustfmt/tests/target/issue_5027.rs
index 26d771720b6..1de3e0d4f15 100644
--- a/src/tools/rustfmt/tests/target/issue_5027.rs
+++ b/src/tools/rustfmt/tests/target/issue_5027.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 pub type Iter<'a, D> = impl DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)>
     + ExactSizeIterator
diff --git a/src/tools/rustfmt/tests/target/let_else_v2.rs b/src/tools/rustfmt/tests/target/let_else_v2.rs
index b25ac1609d8..6e886299cb0 100644
--- a/src/tools/rustfmt/tests/target/let_else_v2.rs
+++ b/src/tools/rustfmt/tests/target/let_else_v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-single_line_let_else_max_width: 100
 
 fn issue5901() {
diff --git a/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs b/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs
index 05f69953c26..60f672c1837 100644
--- a/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs
+++ b/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 // Tests that a function which is almost short enough, but not quite, gets
 // formatted correctly.
 
diff --git a/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs b/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs
index 32794bccde2..f6007398bcc 100644
--- a/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs
+++ b/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // Tests that a function which is almost short enough, but not quite, gets
 // formatted correctly.
 
diff --git a/src/tools/rustfmt/tests/target/one_line_if_v1.rs b/src/tools/rustfmt/tests/target/one_line_if_v1.rs
index b3c6c4cbeb2..5160ea0264c 100644
--- a/src/tools/rustfmt/tests/target/one_line_if_v1.rs
+++ b/src/tools/rustfmt/tests/target/one_line_if_v1.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 fn plain_if(x: bool) -> u8 {
     if x {
diff --git a/src/tools/rustfmt/tests/target/one_line_if_v2.rs b/src/tools/rustfmt/tests/target/one_line_if_v2.rs
index 81ca4c8b8b4..a9610ec9749 100644
--- a/src/tools/rustfmt/tests/target/one_line_if_v2.rs
+++ b/src/tools/rustfmt/tests/target/one_line_if_v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn plain_if(x: bool) -> u8 {
     if x { 0 } else { 1 }
diff --git a/src/tools/rustfmt/tests/target/single-line-macro/v1.rs b/src/tools/rustfmt/tests/target/single-line-macro/v1.rs
index a3aa631ed4a..fea146d8d33 100644
--- a/src/tools/rustfmt/tests/target/single-line-macro/v1.rs
+++ b/src/tools/rustfmt/tests/target/single-line-macro/v1.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: One
+// rustfmt-style_edition: 2015
 
 // #2652
 // Preserve trailing comma inside macro, even if it looks an array.
diff --git a/src/tools/rustfmt/tests/target/single-line-macro/v2.rs b/src/tools/rustfmt/tests/target/single-line-macro/v2.rs
index 9c6bcf33ad5..6fcacb70ba3 100644
--- a/src/tools/rustfmt/tests/target/single-line-macro/v2.rs
+++ b/src/tools/rustfmt/tests/target/single-line-macro/v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 // #2652
 // Preserve trailing comma inside macro, even if it looks an array.
diff --git a/src/tools/rustfmt/tests/target/style_edition/default.rs b/src/tools/rustfmt/tests/target/style_edition/default.rs
new file mode 100644
index 00000000000..17442df6c49
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/style_edition/default.rs
@@ -0,0 +1,10 @@
+fn build_sorted_static_get_entry_names(
+    mut entries: Vec<(u8, &'static str)>,
+) -> (impl Fn(
+    AlphabeticalTraversal,
+    Box<dyn dirents_sink::Sink<AlphabeticalTraversal>>,
+) -> BoxFuture<'static, Result<Box<dyn dirents_sink::Sealed>, Status>>
+        + Send
+        + Sync
+        + 'static) {
+}
diff --git a/src/tools/rustfmt/tests/target/style_edition/follows_edition.rs b/src/tools/rustfmt/tests/target/style_edition/follows_edition.rs
new file mode 100644
index 00000000000..c36a993d842
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/style_edition/follows_edition.rs
@@ -0,0 +1,14 @@
+// rustfmt-edition: 2024
+
+fn build_sorted_static_get_entry_names(
+    mut entries: Vec<(u8, &'static str)>,
+) -> (
+    impl Fn(
+        AlphabeticalTraversal,
+        Box<dyn dirents_sink::Sink<AlphabeticalTraversal>>,
+    ) -> BoxFuture<'static, Result<Box<dyn dirents_sink::Sealed>, Status>>
+    + Send
+    + Sync
+    + 'static
+) {
+}
diff --git a/src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs b/src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs
new file mode 100644
index 00000000000..6d0eaac8970
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs
@@ -0,0 +1,14 @@
+// rustfmt-edition: 2018
+// rustfmt-style_edition: 2024
+fn build_sorted_static_get_entry_names(
+    mut entries: Vec<(u8, &'static str)>,
+) -> (
+    impl Fn(
+        AlphabeticalTraversal,
+        Box<dyn dirents_sink::Sink<AlphabeticalTraversal>>,
+    ) -> BoxFuture<'static, Result<Box<dyn dirents_sink::Sealed>, Status>>
+    + Send
+    + Sync
+    + 'static
+) {
+}
diff --git a/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs b/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs
index 35e72f1affd..e7009ac00c0 100644
--- a/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs
+++ b/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-wrap_comments: true
 // rustfmt-hard_tabs: true
 
diff --git a/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs b/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs
index eba943042ad..34cfed1a229 100644
--- a/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs
+++ b/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 // rustfmt-wrap_comments: true
 
 pub const IFF_MULTICAST: ::c_int = 0x0000000800; // Supports multicast
diff --git a/src/tools/rustfmt/tests/target/tuple_v2.rs b/src/tools/rustfmt/tests/target/tuple_v2.rs
index ba653291c2f..fa508332be5 100644
--- a/src/tools/rustfmt/tests/target/tuple_v2.rs
+++ b/src/tools/rustfmt/tests/target/tuple_v2.rs
@@ -1,4 +1,4 @@
-// rustfmt-version: Two
+// rustfmt-style_edition: 2024
 
 fn issue_4355() {
     let _ = ((1,),).0.0;
diff --git a/src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs b/src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs
new file mode 100644
index 00000000000..25c38916376
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs
@@ -0,0 +1,39 @@
+type Foo
+// comment1
+// interlinear1
+where
+    // comment2
+    // interlinear2
+    A: B, // comment3
+    C: D, // comment4
+// interlinear3
+= E; // comment5
+
+type Foo
+// comment6
+// interlinear4
+where
+    // comment7
+    // interlinear5
+    A: B, // comment8
+    C: D, // comment9
+// interlinear6
+= E
+// comment10
+// interlinear7
+where
+    // comment11
+    // interlinear8
+    F: G, // comment12
+    H: I; // comment13
+
+type Foo // comment14
+    // interlinear9
+    = E
+// comment15
+// interlinear10
+where
+    // comment16
+    // interlinear11
+    F: G, // comment17
+    H: I; // comment18
diff --git a/src/tools/rustfmt/tests/target/type-alias-where-clauses.rs b/src/tools/rustfmt/tests/target/type-alias-where-clauses.rs
new file mode 100644
index 00000000000..96d2d63bbb8
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/type-alias-where-clauses.rs
@@ -0,0 +1,20 @@
+type Foo
+where
+    A: B,
+    C: D,
+= E;
+
+type Foo
+where
+    A: B,
+    C: D,
+= E
+where
+    F: G,
+    H: I;
+
+type Foo
+    = E
+where
+    F: G,
+    H: I;
diff --git a/tests/rustdoc-ui/doctest/doctest-output-include-fail.md b/tests/rustdoc-ui/doctest/doctest-output-include-fail.md
new file mode 100644
index 00000000000..a8e61238f31
--- /dev/null
+++ b/tests/rustdoc-ui/doctest/doctest-output-include-fail.md
@@ -0,0 +1,7 @@
+With a code sample, that has an error:
+
+```rust
+fn main() {
+    let x = 234 // no semicolon here! oh no!
+}
+```
diff --git a/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs b/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs
new file mode 100644
index 00000000000..4fc0674a0c9
--- /dev/null
+++ b/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs
@@ -0,0 +1,7 @@
+//@ compile-flags:--test --test-args=--test-threads=1
+//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR"
+//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME"
+//@ failure-status: 101
+
+// https://github.com/rust-lang/rust/issues/130470
+#![doc = include_str!("doctest-output-include-fail.md")]
diff --git a/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout b/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout
new file mode 100644
index 00000000000..22d15f8743c
--- /dev/null
+++ b/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout
@@ -0,0 +1,24 @@
+
+running 1 test
+test $DIR/doctest-output-include-fail.md - (line 3) ... FAILED
+
+failures:
+
+---- $DIR/doctest-output-include-fail.md - (line 3) stdout ----
+error: expected `;`, found `}`
+  --> $DIR/doctest-output-include-fail.md:5:16
+   |
+LL |     let x = 234 // no semicolon here! oh no!
+   |                ^ help: add `;` here
+LL | }
+   | - unexpected token
+
+error: aborting due to 1 previous error
+
+Couldn't compile the test.
+
+failures:
+    $DIR/doctest-output-include-fail.md - (line 3)
+
+test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+
diff --git a/tests/rustdoc-ui/doctest/doctest-output.stdout b/tests/rustdoc-ui/doctest/doctest-output.stdout
index 35b0e366fb5..c3b1570c43e 100644
--- a/tests/rustdoc-ui/doctest/doctest-output.stdout
+++ b/tests/rustdoc-ui/doctest/doctest-output.stdout
@@ -1,7 +1,7 @@
 
 running 3 tests
 test $DIR/doctest-output.rs - (line 8) ... ok
-test $DIR/doctest-output.rs - ExpandedStruct (line 24) ... ok
+test $DIR/doctest-output.rs - ExpandedStruct (line 25) ... ok
 test $DIR/doctest-output.rs - foo::bar (line 18) ... ok
 
 test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
diff --git a/tests/ui/macros/auxiliary/metavar_2018.rs b/tests/ui/macros/auxiliary/metavar_2018.rs
new file mode 100644
index 00000000000..7e8523a9edf
--- /dev/null
+++ b/tests/ui/macros/auxiliary/metavar_2018.rs
@@ -0,0 +1,14 @@
+//@ edition: 2018
+#[macro_export]
+macro_rules! make_matcher {
+    ($name:ident, $fragment_type:ident, $d:tt) => {
+        #[macro_export]
+        macro_rules! $name {
+            ($d _:$fragment_type) => { true };
+            (const { 0 }) => { false };
+            (A | B) => { false };
+        }
+    };
+}
+make_matcher!(is_expr_from_2018, expr, $);
+make_matcher!(is_pat_from_2018, pat, $);
diff --git a/tests/ui/macros/metavar_cross_edition_recursive_macros.rs b/tests/ui/macros/metavar_cross_edition_recursive_macros.rs
new file mode 100644
index 00000000000..3eec1208b89
--- /dev/null
+++ b/tests/ui/macros/metavar_cross_edition_recursive_macros.rs
@@ -0,0 +1,38 @@
+//@ compile-flags: --edition=2024 -Z unstable-options
+//@ aux-build: metavar_2018.rs
+//@ known-bug: #130484
+//@ run-pass
+
+// This test captures the behavior of macro-generating-macros with fragment
+// specifiers across edition boundaries.
+
+#![feature(expr_fragment_specifier_2024)]
+#![feature(macro_metavar_expr)]
+#![allow(incomplete_features)]
+
+extern crate metavar_2018;
+
+use metavar_2018::{is_expr_from_2018, is_pat_from_2018, make_matcher};
+
+make_matcher!(is_expr_from_2024, expr, $);
+make_matcher!(is_pat_from_2024, pat, $);
+
+fn main() {
+    // Check expr
+    let from_2018 = is_expr_from_2018!(const { 0 });
+    dbg!(from_2018);
+    let from_2024 = is_expr_from_2024!(const { 0 });
+    dbg!(from_2024);
+
+    assert!(!from_2018);
+    assert!(!from_2024); // from_2024 will be true once #130484 is fixed
+
+    // Check pat
+    let from_2018 = is_pat_from_2018!(A | B);
+    dbg!(from_2018);
+    let from_2024 = is_pat_from_2024!(A | B);
+    dbg!(from_2024);
+
+    assert!(!from_2018);
+    assert!(!from_2024); // from_2024 will be true once #130484 is fixed
+}
diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs
index 5ca9c8678b7..f49ce33841a 100644
--- a/tests/ui/structs-enums/type-sizes.rs
+++ b/tests/ui/structs-enums/type-sizes.rs
@@ -209,6 +209,23 @@ struct ReorderEndNiche {
     b: MiddleNiche4,
 }
 
+// We want that the niche selection doesn't depend on order of the fields. See issue #125630.
+pub enum NicheFieldOrder1 {
+    A {
+        x: NonZero<u64>,
+        y: [NonZero<u64>; 2],
+    },
+    B([u64; 2]),
+}
+
+pub enum NicheFieldOrder2 {
+    A {
+        y: [NonZero<u64>; 2],
+        x: NonZero<u64>,
+    },
+    B([u64; 2]),
+}
+
 
 // standins for std types which we want to be laid out in a reasonable way
 struct RawVecDummy {
@@ -260,6 +277,9 @@ pub fn main() {
                size_of::<EnumWithMaybeUninhabitedVariant<()>>());
     assert_eq!(size_of::<NicheFilledEnumWithAbsentVariant>(), size_of::<&'static ()>());
 
+    assert_eq!(size_of::<NicheFieldOrder1>(), 24);
+    assert_eq!(size_of::<NicheFieldOrder2>(), 24);
+
     assert_eq!(size_of::<Option<Option<(bool, &())>>>(), size_of::<(bool, &())>());
     assert_eq!(size_of::<Option<Option<(&(), bool)>>>(), size_of::<(bool, &())>());
     assert_eq!(size_of::<Option<Option2<bool, &()>>>(), size_of::<(bool, &())>());
diff --git a/tests/ui/traits/sized-coniductive.rs b/tests/ui/traits/sized-coniductive.rs
new file mode 100644
index 00000000000..5f63b166f98
--- /dev/null
+++ b/tests/ui/traits/sized-coniductive.rs
@@ -0,0 +1,14 @@
+//@ check-pass
+// https://github.com/rust-lang/rust/issues/129541
+
+#[derive(Clone)]
+struct Test {
+    field: std::borrow::Cow<'static, [Self]>,
+}
+
+#[derive(Clone)]
+struct Hello {
+    a: <[Hello] as std::borrow::ToOwned>::Owned,
+}
+
+fn main(){}