about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/bootstrap.py37
-rw-r--r--src/bootstrap/src/bin/rustdoc.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs44
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs37
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs19
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs2
-rw-r--r--src/bootstrap/src/core/builder.rs13
-rw-r--r--src/bootstrap/src/core/config/config.rs147
-rw-r--r--src/bootstrap/src/core/download.rs26
-rw-r--r--src/bootstrap/src/lib.rs117
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rwxr-xr-xsrc/ci/docker/scripts/rfl-build.sh4
-rw-r--r--src/doc/rustc/src/SUMMARY.md1
-rw-r--r--src/doc/rustc/src/platform-support.md2
-rw-r--r--src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md197
-rw-r--r--src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md14
-rw-r--r--src/doc/rustc/src/platform-support/wasm32-wasip1.md6
-rw-r--r--src/doc/rustc/src/platform-support/wasm32-wasip2.md6
-rw-r--r--src/librustdoc/Cargo.toml2
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/librustdoc/clean/types.rs7
-rw-r--r--src/librustdoc/doctest/rust.rs2
-rw-r--r--src/librustdoc/html/format.rs114
-rw-r--r--src/librustdoc/html/markdown.rs44
-rw-r--r--src/librustdoc/html/render/context.rs9
-rw-r--r--src/librustdoc/html/render/mod.rs2
-rw-r--r--src/librustdoc/html/render/ordered_json.rs83
-rw-r--r--src/librustdoc/html/render/ordered_json/tests.rs121
-rw-r--r--src/librustdoc/html/render/search_index.rs70
-rw-r--r--src/librustdoc/html/render/sorted_template.rs159
-rw-r--r--src/librustdoc/html/render/sorted_template/tests.rs149
-rw-r--r--src/librustdoc/html/render/write_shared.rs1470
-rw-r--r--src/librustdoc/html/render/write_shared/tests.rs207
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css13
-rw-r--r--src/librustdoc/html/static/js/search.js25
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs2
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs6
-rw-r--r--src/librustdoc/passes/strip_hidden.rs7
m---------src/llvm-project0
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/.cargo/config.toml6
-rw-r--r--src/tools/clippy/.github/deploy.sh1
-rw-r--r--src/tools/clippy/.github/workflows/clippy_bors.yml5
-rw-r--r--src/tools/clippy/CHANGELOG.md1
-rw-r--r--src/tools/clippy/Cargo.toml4
-rw-r--r--src/tools/clippy/book/src/development/adding_lints.md2
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md8
-rw-r--r--src/tools/clippy/clippy_config/Cargo.toml1
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs23
-rw-r--r--src/tools/clippy/clippy_config/src/lib.rs2
-rw-r--r--src/tools/clippy/clippy_config/src/msrvs.rs17
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs1
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml1
-rw-r--r--src/tools/clippy/clippy_lints/src/absolute_paths.rs95
-rw-r--r--src/tools/clippy/clippy_lints/src/approx_const.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/assigning_clones.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/cfg_not_test.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/checked_conversions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/cognitive_complexity.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/collapsible_if.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/create_dir.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/markdown.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs122
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs91
-rw-r--r--src/tools/clippy/clippy_lints/src/else_if_without_else.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_enum.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_clike.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/float_literal.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/from_str_radix_10.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs91
-rw-r--r--src/tools/clippy/clippy_lints/src/if_not_else.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_return.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/incompatible_msrv.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/indexing_slicing.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/infinite_iter.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/int_plus_one.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/item_name_repetitions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_statements.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_test_module.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/large_enum_variant.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_frames.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs48
-rw-r--r--src/tools/clippy/clippy_lints/src/literal_representation.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_float_methods.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_hash_one.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_range_patterns.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/min_ident_chars.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_key.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/mutex_atomic.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bool.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_continue.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_if.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_late_init.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs47
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/reference.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/regex.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_unit.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs49
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs1078
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/visibility.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs8
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml1
-rw-r--r--src/tools/clippy/clippy_utils/src/attrs.rs38
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs12
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs22
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs21
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs18
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs7
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs101
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs25
-rw-r--r--src/tools/clippy/declare_clippy_lint/src/lib.rs25
-rw-r--r--src/tools/clippy/lintcheck/src/json.rs39
-rw-r--r--src/tools/clippy/lintcheck/src/output.rs14
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/tests/compile-test.rs385
-rw-r--r--src/tools/clippy/tests/config-metadata.rs78
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr45
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr14
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.default.stderr80
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr71
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr98
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.rs178
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr14
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths_2015.rs16
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/allow_crates/clippy.toml3
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/allow_long/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/auxiliary/helper.rs11
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/default/clippy.toml0
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/absolute_paths/no_short/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs15
-rw-r--r--src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr16
-rw-r--r--src/tools/clippy/tests/ui/assigning_clones.fixed20
-rw-r--r--src/tools/clippy/tests/ui/assigning_clones.rs20
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr2
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr26
-rw-r--r--src/tools/clippy/tests/ui/collapsible_match.stderr2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3717.fixed11
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3717.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-3717.stderr2
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs17
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr10
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.fixed13
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.rs13
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr18
-rw-r--r--src/tools/clippy/tests/ui/double_must_use.rs8
-rw-r--r--src/tools/clippy/tests/ui/double_must_use.stderr16
-rw-r--r--src/tools/clippy/tests/ui/explicit_iter_loop.fixed12
-rw-r--r--src/tools/clippy/tests/ui/explicit_iter_loop.rs12
-rw-r--r--src/tools/clippy/tests/ui/floating_point_abs.fixed1
-rw-r--r--src/tools/clippy/tests/ui/floating_point_abs.rs1
-rw-r--r--src/tools/clippy/tests/ui/floating_point_abs.stderr16
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.fixed1
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.rs1
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.stderr26
-rw-r--r--src/tools/clippy/tests/ui/floating_point_rad.fixed1
-rw-r--r--src/tools/clippy/tests/ui/floating_point_rad.rs1
-rw-r--r--src/tools/clippy/tests/ui/floating_point_rad.stderr16
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed21
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs21
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr4
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs2
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr4
-rw-r--r--src/tools/clippy/tests/ui/string_slice.rs5
-rw-r--r--src/tools/clippy/tests/ui/string_slice.stderr14
-rw-r--r--src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.fixed9
-rw-r--r--src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.rs8
-rw-r--r--src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr20
-rw-r--r--src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs53
-rw-r--r--src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr22
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr566
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr35
-rw-r--r--src/tools/clippy/triagebot.toml1
-rw-r--r--src/tools/clippy/util/gh-pages/index.html393
-rw-r--r--src/tools/clippy/util/gh-pages/script.js100
-rw-r--r--src/tools/clippy/util/gh-pages/style.css398
-rw-r--r--src/tools/linkchecker/main.rs6
-rw-r--r--src/tools/lint-docs/src/lib.rs1
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/concurrency/thread.rs2
-rw-r--r--src/tools/miri/src/lib.rs1
-rw-r--r--src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr2
-rw-r--r--src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr2
-rw-r--r--src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr2
-rw-r--r--src/tools/miri/tests/fail/panic/bad_unwind.stderr2
-rw-r--r--src/tools/miri/tests/fail/panic/double_panic.stderr2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort1.stderr2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort2.stderr2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort3.stderr2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort4.stderr2
-rw-r--r--src/tools/miri/tests/fail/terminate-terminator.stderr2
-rw-r--r--src/tools/miri/tests/fail/unwind-action-terminate.stderr2
-rw-r--r--src/tools/miri/tests/panic/alloc_error_handler_hook.stderr2
-rw-r--r--src/tools/miri/tests/panic/alloc_error_handler_panic.stderr2
-rw-r--r--src/tools/miri/tests/panic/div-by-zero-2.stderr2
-rw-r--r--src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.stderr2
-rw-r--r--src/tools/miri/tests/panic/oob_subslice.stderr2
-rw-r--r--src/tools/miri/tests/panic/overflowing-lsh-neg.stderr2
-rw-r--r--src/tools/miri/tests/panic/overflowing-rsh-1.stderr2
-rw-r--r--src/tools/miri/tests/panic/overflowing-rsh-2.stderr2
-rw-r--r--src/tools/miri/tests/panic/panic2.stderr2
-rw-r--r--src/tools/miri/tests/panic/panic3.stderr2
-rw-r--r--src/tools/miri/tests/panic/panic4.stderr2
-rw-r--r--src/tools/miri/tests/panic/transmute_fat2.stderr2
-rw-r--r--src/tools/miri/tests/panic/unsupported_foreign_function.stderr2
-rw-r--r--src/tools/miri/tests/panic/unsupported_syscall.stderr2
-rw-r--r--src/tools/miri/tests/pass/panic/catch_panic.stderr2
-rw-r--r--src/tools/miri/tests/pass/panic/concurrent-panic.stderr2
-rw-r--r--src/tools/miri/tests/pass/panic/nested_panic_caught.stderr2
-rw-r--r--src/tools/miri/tests/pass/panic/thread_panic.stderr2
-rw-r--r--src/tools/run-make-support/Cargo.toml1
-rw-r--r--src/tools/run-make-support/src/env.rs8
-rw-r--r--src/tools/run-make-support/src/lib.rs3
-rw-r--r--src/tools/run-make-support/src/path_helpers.rs1
-rw-r--r--src/tools/run-make-support/src/run.rs2
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt2
-rw-r--r--src/tools/tidy/src/issues.txt3
311 files changed, 5341 insertions, 4003 deletions
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 4e8e0fd2532..71f69e03a9f 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -79,6 +79,11 @@ def get(base, url, path, checksums, verbose=False):
                 eprint("removing", temp_path)
             os.unlink(temp_path)
 
+def curl_version():
+    m = re.match(bytes("^curl ([0-9]+)\\.([0-9]+)", "utf8"), require(["curl", "-V"]))
+    if m is None:
+        return (0, 0)
+    return (int(m[1]), int(m[2]))
 
 def download(path, url, probably_big, verbose):
     for _ in range(4):
@@ -107,11 +112,15 @@ def _download(path, url, probably_big, verbose, exception):
         # If curl is not present on Win32, we should not sys.exit
         #   but raise `CalledProcessError` or `OSError` instead
         require(["curl", "--version"], exception=platform_is_win32())
-        run(["curl", option,
+        extra_flags = []
+        if curl_version() > (7, 70):
+            extra_flags = [ "--retry-all-errors" ]
+        run(["curl", option] + extra_flags + [
             "-L", # Follow redirect.
             "-y", "30", "-Y", "10",    # timeout if speed is < 10 bytes/sec for > 30 seconds
             "--connect-timeout", "30",  # timeout if cannot connect within 30 seconds
             "-o", path,
+            "--continue-at", "-",
             "--retry", "3", "-SRf", url],
             verbose=verbose,
             exception=True, # Will raise RuntimeError on failure
@@ -533,9 +542,13 @@ class RustBuild(object):
         bin_root = self.bin_root()
 
         key = self.stage0_compiler.date
-        if self.rustc().startswith(bin_root) and \
-                (not os.path.exists(self.rustc()) or
-                 self.program_out_of_date(self.rustc_stamp(), key)):
+        is_outdated = self.program_out_of_date(self.rustc_stamp(), key)
+        need_rustc = self.rustc().startswith(bin_root) and (not os.path.exists(self.rustc()) \
+            or is_outdated)
+        need_cargo = self.cargo().startswith(bin_root) and (not os.path.exists(self.cargo()) \
+            or is_outdated)
+
+        if need_rustc or need_cargo:
             if os.path.exists(bin_root):
                 # HACK: On Windows, we can't delete rust-analyzer-proc-macro-server while it's
                 # running. Kill it.
@@ -556,7 +569,6 @@ class RustBuild(object):
                     run_powershell([script])
                 shutil.rmtree(bin_root)
 
-            key = self.stage0_compiler.date
             cache_dst = (self.get_toml('bootstrap-cache-path', 'build') or
                 os.path.join(self.build_dir, "cache"))
 
@@ -568,11 +580,16 @@ class RustBuild(object):
 
             toolchain_suffix = "{}-{}{}".format(rustc_channel, self.build, tarball_suffix)
 
-            tarballs_to_download = [
-                ("rust-std-{}".format(toolchain_suffix), "rust-std-{}".format(self.build)),
-                ("rustc-{}".format(toolchain_suffix), "rustc"),
-                ("cargo-{}".format(toolchain_suffix), "cargo"),
-            ]
+            tarballs_to_download = []
+
+            if need_rustc:
+                tarballs_to_download.append(
+                    ("rust-std-{}".format(toolchain_suffix), "rust-std-{}".format(self.build))
+                )
+                tarballs_to_download.append(("rustc-{}".format(toolchain_suffix), "rustc"))
+
+            if need_cargo:
+                tarballs_to_download.append(("cargo-{}".format(toolchain_suffix), "cargo"))
 
             tarballs_download_info = [
                 DownloadInfo(
diff --git a/src/bootstrap/src/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs
index ba6b0c2dbda..a338b9c8080 100644
--- a/src/bootstrap/src/bin/rustdoc.rs
+++ b/src/bootstrap/src/bin/rustdoc.rs
@@ -59,8 +59,6 @@ fn main() {
     if stage == "0" {
         cmd.arg("--cfg=bootstrap");
     }
-    cmd.arg("-Zunstable-options");
-    cmd.arg("--check-cfg=cfg(bootstrap)");
 
     maybe_dump(format!("stage{stage}-rustdoc"), &cmd);
 
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 4353cfadd8d..edf18e2ebf3 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -199,16 +199,6 @@ impl Step for Std {
 
         builder.require_submodule("library/stdarch", None);
 
-        // Profiler information requires LLVM's compiler-rt
-        if builder.config.profiler {
-            builder.require_submodule(
-                "src/llvm-project",
-                Some(
-                    "The `build.profiler` config option requires `compiler-rt` sources from LLVM.",
-                ),
-            );
-        }
-
         let mut target_deps = builder.ensure(StartupObjects { compiler, target });
 
         let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
@@ -466,6 +456,29 @@ pub fn std_crates_for_run_make(run: &RunConfig<'_>) -> Vec<String> {
     }
 }
 
+/// Tries to find LLVM's `compiler-rt` source directory, for building `library/profiler_builtins`.
+///
+/// Normally it lives in the `src/llvm-project` submodule, but if we will be using a
+/// downloaded copy of CI LLVM, then we try to use the `compiler-rt` sources from
+/// there instead, which lets us avoid checking out the LLVM submodule.
+fn compiler_rt_for_profiler(builder: &Builder<'_>) -> PathBuf {
+    // Try to use `compiler-rt` sources from downloaded CI LLVM, if possible.
+    if builder.config.llvm_from_ci {
+        // CI LLVM might not have been downloaded yet, so try to download it now.
+        builder.config.maybe_download_ci_llvm();
+        let ci_llvm_compiler_rt = builder.config.ci_llvm_root().join("compiler-rt");
+        if ci_llvm_compiler_rt.exists() {
+            return ci_llvm_compiler_rt;
+        }
+    }
+
+    // Otherwise, fall back to requiring the LLVM submodule.
+    builder.require_submodule("src/llvm-project", {
+        Some("The `build.profiler` config option requires `compiler-rt` sources from LLVM.")
+    });
+    builder.src.join("src/llvm-project/compiler-rt")
+}
+
 /// Configure cargo to compile the standard library, adding appropriate env vars
 /// and such.
 pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, cargo: &mut Cargo) {
@@ -473,8 +486,15 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
         cargo.env("MACOSX_DEPLOYMENT_TARGET", target);
     }
 
+    // Paths needed by `library/profiler_builtins/build.rs`.
     if let Some(path) = builder.config.profiler_path(target) {
         cargo.env("LLVM_PROFILER_RT_LIB", path);
+    } else if builder.config.profiler_enabled(target) {
+        let compiler_rt = compiler_rt_for_profiler(builder);
+        // Currently this is separate from the env var used by `compiler_builtins`
+        // (below) so that adding support for CI LLVM here doesn't risk breaking
+        // the compiler builtins. But they could be unified if desired.
+        cargo.env("RUST_COMPILER_RT_FOR_PROFILER", compiler_rt);
     }
 
     // Determine if we're going to compile in optimized C intrinsics to
@@ -507,8 +527,8 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
         );
         let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt");
         assert!(compiler_builtins_root.exists());
-        // Note that `libprofiler_builtins/build.rs` also computes this so if
-        // you're changing something here please also change that.
+        // The path to `compiler-rt` is also used by `profiler_builtins` (above),
+        // so if you're changing something here please also change that as appropriate.
         cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root);
         " compiler-builtins-c"
     } else {
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 8d20b956213..4957de2e1b7 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -269,34 +269,45 @@ fn make_win_dist(
     let target_libs = find_files(&target_libs, &lib_path);
 
     // Copy runtime dlls next to rustc.exe
-    let dist_bin_dir = rust_root.join("bin/");
-    fs::create_dir_all(&dist_bin_dir).expect("creating dist_bin_dir failed");
-    for src in rustc_dlls {
-        builder.copy_link_to_folder(&src, &dist_bin_dir);
+    let rust_bin_dir = rust_root.join("bin/");
+    fs::create_dir_all(&rust_bin_dir).expect("creating rust_bin_dir failed");
+    for src in &rustc_dlls {
+        builder.copy_link_to_folder(src, &rust_bin_dir);
+    }
+
+    if builder.config.lld_enabled {
+        // rust-lld.exe also needs runtime dlls
+        let rust_target_bin_dir = rust_root.join("lib/rustlib").join(target).join("bin");
+        fs::create_dir_all(&rust_target_bin_dir).expect("creating rust_target_bin_dir failed");
+        for src in &rustc_dlls {
+            builder.copy_link_to_folder(src, &rust_target_bin_dir);
+        }
     }
 
     //Copy platform tools to platform-specific bin directory
-    let target_bin_dir =
-        plat_root.join("lib").join("rustlib").join(target).join("bin").join("self-contained");
-    fs::create_dir_all(&target_bin_dir).expect("creating target_bin_dir failed");
+    let plat_target_bin_self_contained_dir =
+        plat_root.join("lib/rustlib").join(target).join("bin/self-contained");
+    fs::create_dir_all(&plat_target_bin_self_contained_dir)
+        .expect("creating plat_target_bin_self_contained_dir failed");
     for src in target_tools {
-        builder.copy_link_to_folder(&src, &target_bin_dir);
+        builder.copy_link_to_folder(&src, &plat_target_bin_self_contained_dir);
     }
 
     // Warn windows-gnu users that the bundled GCC cannot compile C files
     builder.create(
-        &target_bin_dir.join("GCC-WARNING.txt"),
+        &plat_target_bin_self_contained_dir.join("GCC-WARNING.txt"),
         "gcc.exe contained in this folder cannot be used for compiling C files - it is only \
          used as a linker. In order to be able to compile projects containing C code use \
          the GCC provided by MinGW or Cygwin.",
     );
 
     //Copy platform libs to platform-specific lib directory
-    let target_lib_dir =
-        plat_root.join("lib").join("rustlib").join(target).join("lib").join("self-contained");
-    fs::create_dir_all(&target_lib_dir).expect("creating target_lib_dir failed");
+    let plat_target_lib_self_contained_dir =
+        plat_root.join("lib/rustlib").join(target).join("lib/self-contained");
+    fs::create_dir_all(&plat_target_lib_self_contained_dir)
+        .expect("creating plat_target_lib_self_contained_dir failed");
     for src in target_libs {
-        builder.copy_link_to_folder(&src, &target_lib_dir);
+        builder.copy_link_to_folder(&src, &plat_target_lib_self_contained_dir);
     }
 }
 
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 301633559fe..ffb617c642b 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -303,10 +303,11 @@ fn invoke_rustdoc(
         .arg(&out)
         .arg(&path)
         .arg("--markdown-css")
-        .arg("../rust.css");
+        .arg("../rust.css")
+        .arg("-Zunstable-options");
 
     if !builder.config.docs_minification {
-        cmd.arg("-Z").arg("unstable-options").arg("--disable-minification");
+        cmd.arg("--disable-minification");
     }
 
     cmd.run(builder);
@@ -376,8 +377,6 @@ impl Step for Standalone {
             }
 
             let mut cmd = builder.rustdoc_cmd(compiler);
-            // Needed for --index-page flag
-            cmd.arg("-Z").arg("unstable-options");
 
             cmd.arg("--html-after-content")
                 .arg(&footer)
@@ -386,6 +385,7 @@ impl Step for Standalone {
                 .arg("--html-in-header")
                 .arg(&favicon)
                 .arg("--markdown-no-toc")
+                .arg("-Zunstable-options")
                 .arg("--index-page")
                 .arg(builder.src.join("src/doc/index.md"))
                 .arg("--markdown-playground-url")
@@ -478,9 +478,6 @@ impl Step for Releases {
             mem::drop(tmpfile);
             let mut cmd = builder.rustdoc_cmd(compiler);
 
-            // Needed for --index-page flag
-            cmd.arg("-Z").arg("unstable-options");
-
             cmd.arg("--html-after-content")
                 .arg(&footer)
                 .arg("--html-before-content")
@@ -490,6 +487,7 @@ impl Step for Releases {
                 .arg("--markdown-no-toc")
                 .arg("--markdown-css")
                 .arg("rust.css")
+                .arg("-Zunstable-options")
                 .arg("--index-page")
                 .arg(builder.src.join("src/doc/index.md"))
                 .arg("--markdown-playground-url")
@@ -636,6 +634,8 @@ impl Step for Std {
         if !builder.config.docs_minification {
             extra_args.push("--disable-minification");
         }
+        // For `--index-page` and `--output-format=json`.
+        extra_args.push("-Zunstable-options");
 
         doc_std(builder, self.format, stage, target, &out, &extra_args, &crates);
 
@@ -715,8 +715,6 @@ fn doc_std(
         .arg("--target-dir")
         .arg(&*target_dir.to_string_lossy())
         .arg("-Zskip-rustdoc-fingerprint")
-        .rustdocflag("-Z")
-        .rustdocflag("unstable-options")
         .rustdocflag("--resource-suffix")
         .rustdocflag(&builder.version);
     for arg in extra_args {
@@ -822,7 +820,6 @@ impl Step for Rustc {
         // Since we always pass --document-private-items, there's no need to warn about linking to private items.
         cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
         cargo.rustdocflag("--enable-index-page");
-        cargo.rustdocflag("-Zunstable-options");
         cargo.rustdocflag("-Znormalize-docs");
         cargo.rustdocflag("--show-type-layout");
         // FIXME: `--generate-link-to-definition` tries to resolve cfged out code
@@ -830,7 +827,6 @@ impl Step for Rustc {
         // cargo.rustdocflag("--generate-link-to-definition");
 
         compile::rustc_cargo(builder, &mut cargo, target, &compiler);
-        cargo.arg("-Zunstable-options");
         cargo.arg("-Zskip-rustdoc-fingerprint");
 
         // Only include compiler crates, no dependencies of those, such as `libc`.
@@ -986,7 +982,6 @@ macro_rules! tool_doc {
                 cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
                 cargo.rustdocflag("--enable-index-page");
                 cargo.rustdocflag("--show-type-layout");
-                cargo.rustdocflag("-Zunstable-options");
                 // FIXME: `--generate-link-to-definition` tries to resolve cfged out code
                 // see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222
                 // cargo.rustdocflag("--generate-link-to-definition");
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index c5a1ab78801..e1eea31b3bb 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -110,7 +110,7 @@ pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> L
 
     // Initialize the llvm submodule if not initialized already.
     // If submodules are disabled, this does nothing.
-    builder.update_submodule("src/llvm-project");
+    builder.config.update_submodule("src/llvm-project");
 
     let root = "src/llvm-project/llvm";
     let out_dir = builder.llvm_out(target);
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index ff8ca2ad74a..3a1eb43b801 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -1096,7 +1096,7 @@ tool_extended!((self, builder),
     CargoClippy, "src/tools/clippy", "cargo-clippy", stable=true;
     Clippy, "src/tools/clippy", "clippy-driver", stable=true, add_bins_to_sysroot = ["clippy-driver", "cargo-clippy"];
     Miri, "src/tools/miri", "miri", stable=false, add_bins_to_sysroot = ["miri"];
-    CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", stable=true, add_bins_to_sysroot = ["cargo-miri"];
+    CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri", stable=false, add_bins_to_sysroot = ["cargo-miri"];
     Rls, "src/tools/rls", "rls", stable=true;
     Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"];
 );
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index a848163b272..ff0d1f3a725 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1612,7 +1612,6 @@ impl<'a> Builder<'a> {
             rustflags.arg("-Csymbol-mangling-version=v0");
         } else {
             rustflags.arg("-Csymbol-mangling-version=legacy");
-            rustflags.arg("-Zunstable-options");
         }
 
         // Enable compile-time checking of `cfg` names, values and Cargo `features`.
@@ -1860,16 +1859,7 @@ impl<'a> Builder<'a> {
             },
         );
 
-        let split_debuginfo = self.config.split_debuginfo(target);
-        let split_debuginfo_is_stable = target.contains("linux")
-            || target.contains("apple")
-            || (target.is_msvc() && split_debuginfo == SplitDebuginfo::Packed)
-            || (target.is_windows() && split_debuginfo == SplitDebuginfo::Off);
-
-        if !split_debuginfo_is_stable {
-            rustflags.arg("-Zunstable-options");
-        }
-        match split_debuginfo {
+        match self.config.split_debuginfo(target) {
             SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"),
             SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"),
             SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"),
@@ -2046,7 +2036,6 @@ impl<'a> Builder<'a> {
         }
 
         if mode == Mode::Rustc {
-            rustflags.arg("-Zunstable-options");
             rustflags.arg("-Wrustc::internal");
             // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all
             // of the individual lints are satisfied.
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index bdd9fd755aa..ce23b7735f8 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -14,7 +14,7 @@ use std::sync::OnceLock;
 use std::{cmp, env, fs};
 
 use build_helper::exit;
-use build_helper::git::GitConfig;
+use build_helper::git::{output_result, GitConfig};
 use serde::{Deserialize, Deserializer};
 use serde_derive::Deserialize;
 
@@ -2509,6 +2509,123 @@ impl Config {
         }
     }
 
+    /// Given a path to the directory of a submodule, update it.
+    ///
+    /// `relative_path` should be relative to the root of the git repository, not an absolute path.
+    ///
+    /// This *does not* update the submodule if `config.toml` explicitly says
+    /// not to, or if we're not in a git repository (like a plain source
+    /// tarball). Typically [`crate::Build::require_submodule`] should be
+    /// used instead to provide a nice error to the user if the submodule is
+    /// missing.
+    pub(crate) fn update_submodule(&self, relative_path: &str) {
+        if !self.submodules() {
+            return;
+        }
+
+        let absolute_path = self.src.join(relative_path);
+
+        // NOTE: The check for the empty directory is here because when running x.py the first time,
+        // the submodule won't be checked out. Check it out now so we can build it.
+        if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
+            && !helpers::dir_is_empty(&absolute_path)
+        {
+            return;
+        }
+
+        // Submodule updating actually happens during in the dry run mode. We need to make sure that
+        // all the git commands below are actually executed, because some follow-up code
+        // in bootstrap might depend on the submodules being checked out. Furthermore, not all
+        // the command executions below work with an empty output (produced during dry run).
+        // Therefore, all commands below are marked with `run_always()`, so that they also run in
+        // dry run mode.
+        let submodule_git = || {
+            let mut cmd = helpers::git(Some(&absolute_path));
+            cmd.run_always();
+            cmd
+        };
+
+        // Determine commit checked out in submodule.
+        let checked_out_hash = output(submodule_git().args(["rev-parse", "HEAD"]).as_command_mut());
+        let checked_out_hash = checked_out_hash.trim_end();
+        // Determine commit that the submodule *should* have.
+        let recorded = output(
+            helpers::git(Some(&self.src))
+                .run_always()
+                .args(["ls-tree", "HEAD"])
+                .arg(relative_path)
+                .as_command_mut(),
+        );
+
+        let actual_hash = recorded
+            .split_whitespace()
+            .nth(2)
+            .unwrap_or_else(|| panic!("unexpected output `{}`", recorded));
+
+        if actual_hash == checked_out_hash {
+            // already checked out
+            return;
+        }
+
+        println!("Updating submodule {relative_path}");
+        self.check_run(
+            helpers::git(Some(&self.src))
+                .run_always()
+                .args(["submodule", "-q", "sync"])
+                .arg(relative_path),
+        );
+
+        // Try passing `--progress` to start, then run git again without if that fails.
+        let update = |progress: bool| {
+            // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
+            // even though that has no relation to the upstream for the submodule.
+            let current_branch = output_result(
+                helpers::git(Some(&self.src))
+                    .allow_failure()
+                    .run_always()
+                    .args(["symbolic-ref", "--short", "HEAD"])
+                    .as_command_mut(),
+            )
+            .map(|b| b.trim().to_owned());
+
+            let mut git = helpers::git(Some(&self.src)).allow_failure();
+            git.run_always();
+            if let Ok(branch) = current_branch {
+                // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
+                // This syntax isn't accepted by `branch.{branch}`. Strip it.
+                let branch = branch.strip_prefix("heads/").unwrap_or(&branch);
+                git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
+            }
+            git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]);
+            if progress {
+                git.arg("--progress");
+            }
+            git.arg(relative_path);
+            git
+        };
+        if !self.check_run(&mut update(true)) {
+            self.check_run(&mut update(false));
+        }
+
+        // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
+        // diff-index reports the modifications through the exit status
+        let has_local_modifications = !self.check_run(submodule_git().allow_failure().args([
+            "diff-index",
+            "--quiet",
+            "HEAD",
+        ]));
+        if has_local_modifications {
+            self.check_run(submodule_git().args(["stash", "push"]));
+        }
+
+        self.check_run(submodule_git().args(["reset", "-q", "--hard"]));
+        self.check_run(submodule_git().args(["clean", "-qdfx"]));
+
+        if has_local_modifications {
+            self.check_run(submodule_git().args(["stash", "pop"]));
+        }
+    }
+
     #[cfg(feature = "bootstrap-self-test")]
     pub fn check_stage0_version(&self, _program_path: &Path, _component_name: &'static str) {}
 
@@ -2613,19 +2730,23 @@ impl Config {
         asserts: bool,
     ) -> bool {
         let if_unchanged = || {
-            // Git is needed to track modifications here, but tarball source is not available.
-            // If not modified here or built through tarball source, we maintain consistency
-            // with '"if available"'.
-            if !self.rust_info.is_from_tarball()
-                && self
-                    .last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
-                    .is_none()
-            {
-                // there are some untracked changes in the given paths.
-                false
-            } else {
-                llvm::is_ci_llvm_available(self, asserts)
+            if self.rust_info.is_from_tarball() {
+                // Git is needed for running "if-unchanged" logic.
+                println!(
+                    "WARNING: 'if-unchanged' has no effect on tarball sources; ignoring `download-ci-llvm`."
+                );
+                return false;
             }
+
+            self.update_submodule("src/llvm-project");
+
+            // Check for untracked changes in `src/llvm-project`.
+            let has_changes = self
+                .last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
+                .is_none();
+
+            // Return false if there are untracked changes, otherwise check if CI LLVM is available.
+            if has_changes { false } else { llvm::is_ci_llvm_available(self, asserts) }
         };
 
         match download_ci_llvm {
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index dd0309733ae..1e3f8da5258 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -22,6 +22,24 @@ fn try_run(config: &Config, cmd: &mut Command) -> Result<(), ()> {
     config.try_run(cmd)
 }
 
+fn extract_curl_version(out: &[u8]) -> semver::Version {
+    let out = String::from_utf8_lossy(out);
+    // The output should look like this: "curl <major>.<minor>.<patch> ..."
+    out.lines()
+        .next()
+        .and_then(|line| line.split(" ").nth(1))
+        .and_then(|version| semver::Version::parse(version).ok())
+        .unwrap_or(semver::Version::new(1, 0, 0))
+}
+
+fn curl_version() -> semver::Version {
+    let mut curl = Command::new("curl");
+    curl.arg("-V");
+    let Ok(out) = curl.output() else { return semver::Version::new(1, 0, 0) };
+    let out = out.stdout;
+    extract_curl_version(&out)
+}
+
 /// Generic helpers that are useful anywhere in bootstrap.
 impl Config {
     pub fn is_verbose(&self) -> bool {
@@ -56,7 +74,7 @@ impl Config {
     /// Returns false if do not execute at all, otherwise returns its
     /// `status.success()`.
     pub(crate) fn check_run(&self, cmd: &mut BootstrapCommand) -> bool {
-        if self.dry_run() {
+        if self.dry_run() && !cmd.run_always {
             return true;
         }
         self.verbose(|| println!("running: {cmd:?}"));
@@ -220,6 +238,8 @@ impl Config {
             "30", // timeout if cannot connect within 30 seconds
             "-o",
             tempfile.to_str().unwrap(),
+            "--continue-at",
+            "-",
             "--retry",
             "3",
             "-SRf",
@@ -230,6 +250,10 @@ impl Config {
         } else {
             curl.arg("--progress-bar");
         }
+        // --retry-all-errors was added in 7.71.0, don't use it if curl is old.
+        if curl_version() >= semver::Version::new(7, 71, 0) {
+            curl.arg("--retry-all-errors");
+        }
         curl.arg(url);
         if !self.check_run(&mut curl) {
             if self.build.contains("windows-msvc") {
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 784519a20a2..268392c5fb1 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -473,117 +473,6 @@ impl Build {
         build
     }
 
-    /// Given a path to the directory of a submodule, update it.
-    ///
-    /// `relative_path` should be relative to the root of the git repository, not an absolute path.
-    ///
-    /// This *does not* update the submodule if `config.toml` explicitly says
-    /// not to, or if we're not in a git repository (like a plain source
-    /// tarball). Typically [`Build::require_submodule`] should be
-    /// used instead to provide a nice error to the user if the submodule is
-    /// missing.
-    fn update_submodule(&self, relative_path: &str) {
-        if !self.config.submodules() {
-            return;
-        }
-
-        let absolute_path = self.config.src.join(relative_path);
-
-        // NOTE: The check for the empty directory is here because when running x.py the first time,
-        // the submodule won't be checked out. Check it out now so we can build it.
-        if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
-            && !dir_is_empty(&absolute_path)
-        {
-            return;
-        }
-
-        // Submodule updating actually happens during in the dry run mode. We need to make sure that
-        // all the git commands below are actually executed, because some follow-up code
-        // in bootstrap might depend on the submodules being checked out. Furthermore, not all
-        // the command executions below work with an empty output (produced during dry run).
-        // Therefore, all commands below are marked with `run_always()`, so that they also run in
-        // dry run mode.
-        let submodule_git = || {
-            let mut cmd = helpers::git(Some(&absolute_path));
-            cmd.run_always();
-            cmd
-        };
-
-        // Determine commit checked out in submodule.
-        let checked_out_hash =
-            submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(self).stdout();
-        let checked_out_hash = checked_out_hash.trim_end();
-        // Determine commit that the submodule *should* have.
-        let recorded = helpers::git(Some(&self.src))
-            .run_always()
-            .args(["ls-tree", "HEAD"])
-            .arg(relative_path)
-            .run_capture_stdout(self)
-            .stdout();
-        let actual_hash = recorded
-            .split_whitespace()
-            .nth(2)
-            .unwrap_or_else(|| panic!("unexpected output `{}`", recorded));
-
-        if actual_hash == checked_out_hash {
-            // already checked out
-            return;
-        }
-
-        println!("Updating submodule {relative_path}");
-        helpers::git(Some(&self.src))
-            .run_always()
-            .args(["submodule", "-q", "sync"])
-            .arg(relative_path)
-            .run(self);
-
-        // Try passing `--progress` to start, then run git again without if that fails.
-        let update = |progress: bool| {
-            // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
-            // even though that has no relation to the upstream for the submodule.
-            let current_branch = helpers::git(Some(&self.src))
-                .allow_failure()
-                .run_always()
-                .args(["symbolic-ref", "--short", "HEAD"])
-                .run_capture_stdout(self)
-                .stdout_if_ok()
-                .map(|s| s.trim().to_owned());
-
-            let mut git = helpers::git(Some(&self.src)).allow_failure();
-            git.run_always();
-            if let Some(branch) = current_branch {
-                // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
-                // This syntax isn't accepted by `branch.{branch}`. Strip it.
-                let branch = branch.strip_prefix("heads/").unwrap_or(&branch);
-                git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
-            }
-            git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]);
-            if progress {
-                git.arg("--progress");
-            }
-            git.arg(relative_path);
-            git
-        };
-        if !update(true).run(self) {
-            update(false).run(self);
-        }
-
-        // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
-        // diff-index reports the modifications through the exit status
-        let has_local_modifications =
-            !submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]).run(self);
-        if has_local_modifications {
-            submodule_git().args(["stash", "push"]).run(self);
-        }
-
-        submodule_git().args(["reset", "-q", "--hard"]).run(self);
-        submodule_git().args(["clean", "-qdfx"]).run(self);
-
-        if has_local_modifications {
-            submodule_git().args(["stash", "pop"]).run(self);
-        }
-    }
-
     /// Updates a submodule, and exits with a failure if submodule management
     /// is disabled and the submodule does not exist.
     ///
@@ -598,7 +487,7 @@ impl Build {
         if cfg!(test) && !self.config.submodules() {
             return;
         }
-        self.update_submodule(submodule);
+        self.config.update_submodule(submodule);
         let absolute_path = self.config.src.join(submodule);
         if dir_is_empty(&absolute_path) {
             let maybe_enable = if !self.config.submodules()
@@ -646,7 +535,7 @@ impl Build {
             let path = Path::new(submodule);
             // Don't update the submodule unless it's already been cloned.
             if GitInfo::new(false, path).is_managed_git_subrepository() {
-                self.update_submodule(submodule);
+                self.config.update_submodule(submodule);
             }
         }
     }
@@ -659,7 +548,7 @@ impl Build {
         }
 
         if GitInfo::new(false, Path::new(submodule)).is_managed_git_subrepository() {
-            self.update_submodule(submodule);
+            self.config.update_submodule(submodule);
         }
     }
 
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index c629f04c00e..51a25104e4c 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -230,4 +230,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Warning,
         summary: "./x test --rustc-args was renamed to --compiletest-rustc-args as it only applies there. ./x miri --rustc-args was also removed.",
     },
+    ChangeInfo {
+        change_id: 129295,
+        severity: ChangeSeverity::Info,
+        summary: "The `build.profiler` option now tries to use source code from `download-ci-llvm` if possible, instead of checking out the `src/llvm-project` submodule.",
+    },
 ];
diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh
index d690aac27fa..389abb2fdd3 100755
--- a/src/ci/docker/scripts/rfl-build.sh
+++ b/src/ci/docker/scripts/rfl-build.sh
@@ -2,7 +2,7 @@
 
 set -euo pipefail
 
-LINUX_VERSION=v6.11-rc1
+LINUX_VERSION=4c7864e81d8bbd51036dacf92fb0a400e13aaeee
 
 # Build rustc, rustdoc and cargo
 ../x.py build --stage 1 library rustdoc
@@ -28,7 +28,7 @@ rm -rf linux || true
 # Download Linux at a specific commit
 mkdir -p linux
 git -C linux init
-git -C linux remote add origin https://github.com/torvalds/linux.git
+git -C linux remote add origin https://github.com/Rust-for-Linux/linux.git
 git -C linux fetch --depth 1 origin ${LINUX_VERSION}
 git -C linux checkout FETCH_HEAD
 
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index cb0b2e63452..4e34e2667cf 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -81,6 +81,7 @@
     - [wasm32-wasip1](platform-support/wasm32-wasip1.md)
     - [wasm32-wasip1-threads](platform-support/wasm32-wasip1-threads.md)
     - [wasm32-wasip2](platform-support/wasm32-wasip2.md)
+    - [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md)
     - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md)
     - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md)
     - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 667758a1203..dd478e9df37 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -192,7 +192,7 @@ target | std | notes
 [`thumbv8m.main-none-eabi`](platform-support/thumbv8m.main-none-eabi.md) | * | Bare Armv8-M Mainline
 [`thumbv8m.main-none-eabihf`](platform-support/thumbv8m.main-none-eabi.md) | * | Bare Armv8-M Mainline, hardfloat
 `wasm32-unknown-emscripten` | ✓ | WebAssembly via Emscripten
-`wasm32-unknown-unknown` | ✓ | WebAssembly
+[`wasm32-unknown-unknown`](platform-support/wasm32-unknown-unknown.md) | ✓ | WebAssembly
 `wasm32-wasi` | ✓ | WebAssembly with WASI (undergoing a [rename to `wasm32-wasip1`][wasi-rename])
 [`wasm32-wasip1`](platform-support/wasm32-wasip1.md) | ✓ | WebAssembly with WASI
 [`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ | WebAssembly with WASI Preview 1 and threads
diff --git a/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md
new file mode 100644
index 00000000000..12b7817c382
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/wasm32-unknown-unknown.md
@@ -0,0 +1,197 @@
+# `wasm32-unknown-unknown`
+
+**Tier: 2**
+
+The `wasm32-unknown-unknown` target is a WebAssembly compilation target which
+does not import any functions from the host for the standard library. This is
+the "minimal" WebAssembly in the sense of making the fewest assumptions about
+the host environment. This target is often used when compiling to the web or
+JavaScript environments as there is no standard for what functions can be
+imported on the web. This target can also be useful for creating minimal or
+bare-bones WebAssembly binaries.
+
+The `wasm32-unknown-unknown` target has support for the Rust standard library
+but many parts of the standard library do not work and return errors. For
+example `println!` does nothing, `std::fs` always return errors, and
+`std::thread::spawn` will panic. There is no means by which this can be
+overridden. For a WebAssembly target that more fully supports the standard
+library see the [`wasm32-wasip1`](./wasm32-wasip1.md) or
+[`wasm32-wasip2`](./wasm32-wasip2.md) targets.
+
+The `wasm32-unknown-unknown` target has full support for the `core` and `alloc`
+crates. It additionally supports the `HashMap` type in the `std` crate, although
+hash maps are not randomized like they are on other platforms.
+
+One existing user of this target (please feel free to edit and expand this list
+too) is the [`wasm-bindgen` project](https://github.com/rustwasm/wasm-bindgen)
+which facilitates Rust code interoperating with JavaScript code. Note, though,
+that not all uses of `wasm32-unknown-unknown` are using JavaScript and the web.
+
+## Target maintainers
+
+When this target was added to the compiler, platform-specific documentation here
+was not maintained at that time. This means that the list below is not
+exhaustive, and there are more interested parties in this target. That being
+said, those interested in maintaining this target are:
+
+- Alex Crichton, https://github.com/alexcrichton
+
+## Requirements
+
+This target is cross-compiled. The target includes support for `std` itself,
+but as mentioned above many pieces of functionality that require an operating
+system do not work and will return errors.
+
+This target currently has no equivalent in C/C++. There is no C/C++ toolchain
+for this target. While interop is theoretically possible it's recommended to
+instead use one of:
+
+* `wasm32-unknown-emscripten` - for web-based use cases the Emscripten
+  toolchain is typically chosen for running C/C++.
+* [`wasm32-wasip1`](./wasm32-wasip1.md) - the wasi-sdk toolchain is used to
+  compile C/C++ on this target and can interop with Rust code. WASI works on
+  the web so far as there's no blocker, but an implementation of WASI APIs
+  must be either chosen or reimplemented.
+
+This target has no build requirements beyond what's in-tree in the Rust
+repository. Linking binaries requires LLD to be enabled for the `wasm-ld`
+driver. This target uses the `dlmalloc` crate as the default global allocator.
+
+## Building the target
+
+Building this target can be done by:
+
+* Configure the `wasm32-unknown-unknown` target to get built.
+* Configure LLD to be built.
+* Ensure the `WebAssembly` target backend is not disabled in LLVM.
+
+These are all controlled through `config.toml` options. It should be possible
+to build this target on any platform.
+
+## Building Rust programs
+
+Rust programs can be compiled by adding this target via rustup:
+
+```sh
+$ rustup target add wasm32-unknown-unknown
+```
+
+and then compiling with the target:
+
+```sh
+$ rustc foo.rs --target wasm32-unknown-unknown
+$ file foo.wasm
+```
+
+## Cross-compilation
+
+This target can be cross-compiled from any host.
+
+## Testing
+
+This target is not tested in CI for the rust-lang/rust repository. Many tests
+must be disabled to run on this target and failures are non-obvious because
+`println!` doesn't work in the standard library. It's recommended to test the
+`wasm32-wasip1` target instead for WebAssembly compatibility.
+
+## Conditionally compiling code
+
+It's recommended to conditionally compile code for this target with:
+
+```text
+#[cfg(all(target_family = "wasm", target_os = "unknown"))]
+```
+
+Note that there is no way to tell via `#[cfg]` whether code will be running on
+the web or not.
+
+## Enabled WebAssembly features
+
+WebAssembly is an evolving standard which adds new features such as new
+instructions over time. This target's default set of supported WebAssembly
+features will additionally change over time. The `wasm32-unknown-unknown` target
+inherits the default settings of LLVM which typically matches the default
+settings of Emscripten as well.
+
+Changes to WebAssembly go through a [proposals process][proposals] but reaching
+the final stage (stage 5) does not automatically mean that the feature will be
+enabled in LLVM and Rust by default. At this time the general guidance is that
+features must be present in most engines for a "good chunk of time" before
+they're enabled in LLVM by default. There is currently no exact number of
+months or engines that are required to enable features by default.
+
+[proposals]: https://github.com/WebAssembly/proposals
+
+As of the time of this writing the proposals that are enabled by default (the
+`generic` CPU in LLVM terminology) are:
+
+* `multivalue`
+* `mutable-globals`
+* `reference-types`
+* `sign-ext`
+
+If you're compiling WebAssembly code for an engine that does not support a
+feature in LLVM's default feature set then the feature must be disabled at
+compile time. Note, though, that enabled features may be used in the standard
+library or precompiled libraries shipped via rustup. This means that not only
+does your own code need to be compiled with the correct set of flags but the
+Rust standard library additionally must be recompiled.
+
+Compiling all code for the initial release of WebAssembly looks like:
+
+```sh
+$ export RUSTFLAGS=-Ctarget-cpu=mvp
+$ cargo +nightly build -Zbuild-std=panic_abort,std --target wasm32-unknown-unknown
+```
+
+Here the `mvp` "cpu" is a placeholder in LLVM for disabling all supported
+features by default. Cargo's `-Zbuild-std` feature, a Nightly Rust feature, is
+then used to recompile the standard library in addition to your own code. This
+will produce a binary that uses only the original WebAssembly features by
+default and no proposals since its inception.
+
+To enable individual features it can be done with `-Ctarget-feature=+foo`.
+Available features for Rust code itself are documented in the [reference] and
+can also be found through:
+
+```sh
+$ rustc -Ctarget-feature=help --target wasm32-unknown-unknown
+```
+
+You'll need to consult your WebAssembly engine's documentation to learn more
+about the supported WebAssembly features the engine has.
+
+[reference]: https://doc.rust-lang.org/reference/attributes/codegen.html#wasm32-or-wasm64
+
+Note that it is still possible for Rust crates and libraries to enable
+WebAssembly features on a per-function level. This means that the build
+command above may not be sufficient to disable all WebAssembly features. If the
+final binary still has SIMD instructions, for example, the function in question
+will need to be found and the crate in question will likely contain something
+like:
+
+```rust,ignore (not-always-compiled-to-wasm)
+#[target_feature(enable = "simd128")]
+fn foo() {
+    // ...
+}
+```
+
+In this situation there is no compiler flag to disable emission of SIMD
+instructions and the crate must instead be modified to not include this function
+at compile time either by default or through a Cargo feature. For crate authors
+it's recommended to avoid `#[target_feature(enable = "...")]` except where
+necessary and instead use:
+
+```rust,ignore (not-always-compiled-to-wasm)
+#[cfg(target_feature = "simd128")]
+fn foo() {
+    // ...
+}
+```
+
+That is to say instead of enabling target features it's recommended to
+conditionally compile code instead. This is notably different to the way native
+platforms such as x86\_64 work, and this is due to the fact that WebAssembly
+binaries must only contain code the engine understands. Native binaries work so
+long as the CPU doesn't execute unknown code dynamically at runtime.
diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md b/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md
index c3eda26ca8e..994c0f4bbb3 100644
--- a/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md
+++ b/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md
@@ -162,3 +162,17 @@ It's recommended to conditionally compile code for this target with:
 Prior to Rust 1.80 the `target_env = "p1"` key was not set. Currently the
 `target_feature = "atomics"` is Nightly-only. Note that the precise `#[cfg]`
 necessary to detect this target may change as the target becomes more stable.
+
+## Enabled WebAssembly features
+
+The default set of WebAssembly features enabled for compilation includes two
+more features in addition to that which
+[`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md) enables:
+
+* `bulk-memory`
+* `atomics`
+
+For more information about features see the documentation for
+[`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md), but note that the
+`mvp` CPU in LLVM does not support this target as it's required that
+`bulk-memory`, `atomics`, and `mutable-globals` are all enabled.
diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md
index fb70bbdc2b4..0e4def6768d 100644
--- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md
+++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md
@@ -132,3 +132,9 @@ It's recommended to conditionally compile code for this target with:
 
 Note that the `target_env = "p1"` condition first appeared in Rust 1.80. Prior
 to Rust 1.80 the `target_env` condition was not set.
+
+## Enabled WebAssembly features
+
+The default set of WebAssembly features enabled for compilation is currently the
+same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the
+documentation there for more information.
diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip2.md b/src/doc/rustc/src/platform-support/wasm32-wasip2.md
index 1e53fbc178e..bb2348b201e 100644
--- a/src/doc/rustc/src/platform-support/wasm32-wasip2.md
+++ b/src/doc/rustc/src/platform-support/wasm32-wasip2.md
@@ -61,3 +61,9 @@ It's recommended to conditionally compile code for this target with:
 ```text
 #[cfg(all(target_os = "wasi", target_env = "p2"))]
 ```
+
+## Enabled WebAssembly features
+
+The default set of WebAssembly features enabled for compilation is currently the
+same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the
+documentation there for more information.
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index b3fccbf6456..34332de80b3 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -12,7 +12,7 @@ rinja = { version = "0.3", default-features = false, features = ["config"] }
 base64 = "0.21.7"
 itertools = "0.12"
 indexmap = "2"
-minifier = "0.3.0"
+minifier = "0.3.1"
 pulldown-cmark-old = { version = "0.9.6", package = "pulldown-cmark", default-features = false }
 regex = "1"
 rustdoc-json-types = { path = "../rustdoc-json-types" }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index db81b4c4282..5260e363dd6 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -272,7 +272,7 @@ fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) ->
         | rbv::ResolvedArg::LateBound(_, _, did)
         | rbv::ResolvedArg::Free(_, did),
     ) = cx.tcx.named_bound_var(lifetime.hir_id)
-        && let Some(lt) = cx.args.get(&did).and_then(|arg| arg.as_lt())
+        && let Some(lt) = cx.args.get(&did.to_def_id()).and_then(|arg| arg.as_lt())
     {
         return lt.clone();
     }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index ff5c16f2b3e..63d71a73cdf 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -12,7 +12,7 @@ use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel, StableS
 use rustc_const_eval::const_eval::is_unstable_const_fn;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def::{CtorKind, DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{BodyId, Mutability};
 use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety;
@@ -89,6 +89,11 @@ impl ItemId {
     }
 
     #[inline]
+    pub(crate) fn as_local_def_id(self) -> Option<LocalDefId> {
+        self.as_def_id().and_then(|id| id.as_local())
+    }
+
+    #[inline]
     pub(crate) fn krate(self) -> CrateNum {
         match self {
             ItemId::Auto { for_: id, .. }
diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs
index abd66f15dc0..5c0898f28fc 100644
--- a/src/librustdoc/doctest/rust.rs
+++ b/src/librustdoc/doctest/rust.rs
@@ -136,7 +136,7 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> {
                 self.enable_per_target_ignores,
                 Some(&crate::html::markdown::ExtraInfo::new(
                     self.tcx,
-                    def_id.to_def_id(),
+                    def_id,
                     span_of_fragments(&attrs.doc_strings).unwrap_or(sp),
                 )),
             );
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 6357cfee141..5cbf8c5e19f 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -1288,56 +1288,90 @@ impl clean::Impl {
                 if self.is_negative_trait_impl() {
                     write!(f, "!")?;
                 }
-                ty.print(cx).fmt(f)?;
+                if self.kind.is_fake_variadic()
+                    && let generics = ty.generics()
+                    && let &[inner_type] = generics.as_ref().map_or(&[][..], |v| &v[..])
+                {
+                    let last = ty.last();
+                    if f.alternate() {
+                        write!(f, "{}<", last)?;
+                        self.print_type(inner_type, f, use_absolute, cx)?;
+                        write!(f, ">")?;
+                    } else {
+                        write!(f, "{}&lt;", anchor(ty.def_id(), last, cx).to_string())?;
+                        self.print_type(inner_type, f, use_absolute, cx)?;
+                        write!(f, "&gt;")?;
+                    }
+                } else {
+                    ty.print(cx).fmt(f)?;
+                }
                 write!(f, " for ")?;
             }
 
-            if let clean::Type::Tuple(types) = &self.for_
-                && let [clean::Type::Generic(name)] = &types[..]
-                && (self.kind.is_fake_variadic() || self.kind.is_auto())
-            {
-                // Hardcoded anchor library/core/src/primitive_docs.rs
-                // Link should match `# Trait implementations`
-                primitive_link_fragment(
-                    f,
-                    PrimitiveType::Tuple,
-                    format_args!("({name}₁, {name}₂, …, {name}ₙ)"),
-                    "#trait-implementations-1",
-                    cx,
-                )?;
-            } else if let clean::BareFunction(bare_fn) = &self.for_
-                && let [clean::Argument { type_: clean::Type::Generic(name), .. }] =
-                    &bare_fn.decl.inputs.values[..]
-                && (self.kind.is_fake_variadic() || self.kind.is_auto())
-            {
-                // Hardcoded anchor library/core/src/primitive_docs.rs
-                // Link should match `# Trait implementations`
-
-                print_higher_ranked_params_with_space(&bare_fn.generic_params, cx).fmt(f)?;
-                bare_fn.safety.print_with_space().fmt(f)?;
-                print_abi_with_space(bare_fn.abi).fmt(f)?;
-                let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" };
-                primitive_link_fragment(
-                    f,
-                    PrimitiveType::Tuple,
-                    format_args!("fn({name}₁, {name}₂, …, {name}ₙ{ellipsis})"),
-                    "#trait-implementations-1",
-                    cx,
-                )?;
-                // Write output.
-                if !bare_fn.decl.output.is_unit() {
-                    write!(f, " -> ")?;
-                    fmt_type(&bare_fn.decl.output, f, use_absolute, cx)?;
-                }
-            } else if let Some(ty) = self.kind.as_blanket_ty() {
+            if let Some(ty) = self.kind.as_blanket_ty() {
                 fmt_type(ty, f, use_absolute, cx)?;
             } else {
-                fmt_type(&self.for_, f, use_absolute, cx)?;
+                self.print_type(&self.for_, f, use_absolute, cx)?;
             }
 
             print_where_clause(&self.generics, cx, 0, Ending::Newline).fmt(f)
         })
     }
+    fn print_type<'a, 'tcx: 'a>(
+        &self,
+        type_: &clean::Type,
+        f: &mut fmt::Formatter<'_>,
+        use_absolute: bool,
+        cx: &'a Context<'tcx>,
+    ) -> Result<(), fmt::Error> {
+        if let clean::Type::Tuple(types) = type_
+            && let [clean::Type::Generic(name)] = &types[..]
+            && (self.kind.is_fake_variadic() || self.kind.is_auto())
+        {
+            // Hardcoded anchor library/core/src/primitive_docs.rs
+            // Link should match `# Trait implementations`
+            primitive_link_fragment(
+                f,
+                PrimitiveType::Tuple,
+                format_args!("({name}₁, {name}₂, …, {name}ₙ)"),
+                "#trait-implementations-1",
+                cx,
+            )?;
+        } else if let clean::Type::Array(ty, len) = type_
+            && let clean::Type::Generic(name) = &**ty
+            && &len[..] == "1"
+            && (self.kind.is_fake_variadic() || self.kind.is_auto())
+        {
+            primitive_link(f, PrimitiveType::Array, format_args!("[{name}; N]"), cx)?;
+        } else if let clean::BareFunction(bare_fn) = &type_
+            && let [clean::Argument { type_: clean::Type::Generic(name), .. }] =
+                &bare_fn.decl.inputs.values[..]
+            && (self.kind.is_fake_variadic() || self.kind.is_auto())
+        {
+            // Hardcoded anchor library/core/src/primitive_docs.rs
+            // Link should match `# Trait implementations`
+
+            print_higher_ranked_params_with_space(&bare_fn.generic_params, cx).fmt(f)?;
+            bare_fn.safety.print_with_space().fmt(f)?;
+            print_abi_with_space(bare_fn.abi).fmt(f)?;
+            let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" };
+            primitive_link_fragment(
+                f,
+                PrimitiveType::Tuple,
+                format_args!("fn({name}₁, {name}₂, …, {name}ₙ{ellipsis})"),
+                "#trait-implementations-1",
+                cx,
+            )?;
+            // Write output.
+            if !bare_fn.decl.output.is_unit() {
+                write!(f, " -> ")?;
+                fmt_type(&bare_fn.decl.output, f, use_absolute, cx)?;
+            }
+        } else {
+            fmt_type(&type_, f, use_absolute, cx)?;
+        }
+        Ok(())
+    }
 }
 
 impl clean::Arguments {
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 7bfe5d87d39..364d4e077b1 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -40,7 +40,7 @@ use pulldown_cmark::{
 };
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Diag, DiagMessage};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::TyCtxt;
 pub(crate) use rustc_resolve::rustdoc::main_body_opts;
 use rustc_resolve::rustdoc::may_be_doc_link;
@@ -818,27 +818,25 @@ pub(crate) fn find_codes<T: doctest::DocTestVisitor>(
 }
 
 pub(crate) struct ExtraInfo<'tcx> {
-    def_id: DefId,
+    def_id: LocalDefId,
     sp: Span,
     tcx: TyCtxt<'tcx>,
 }
 
 impl<'tcx> ExtraInfo<'tcx> {
-    pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: DefId, sp: Span) -> ExtraInfo<'tcx> {
+    pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId, sp: Span) -> ExtraInfo<'tcx> {
         ExtraInfo { def_id, sp, tcx }
     }
 
     fn error_invalid_codeblock_attr(&self, msg: impl Into<DiagMessage>) {
-        if let Some(def_id) = self.def_id.as_local() {
-            self.tcx.node_span_lint(
-                crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
-                self.tcx.local_def_id_to_hir_id(def_id),
-                self.sp,
-                |lint| {
-                    lint.primary_message(msg);
-                },
-            );
-        }
+        self.tcx.node_span_lint(
+            crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
+            self.tcx.local_def_id_to_hir_id(self.def_id),
+            self.sp,
+            |lint| {
+                lint.primary_message(msg);
+            },
+        );
     }
 
     fn error_invalid_codeblock_attr_with_help(
@@ -846,17 +844,15 @@ impl<'tcx> ExtraInfo<'tcx> {
         msg: impl Into<DiagMessage>,
         f: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
-        if let Some(def_id) = self.def_id.as_local() {
-            self.tcx.node_span_lint(
-                crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
-                self.tcx.local_def_id_to_hir_id(def_id),
-                self.sp,
-                |lint| {
-                    lint.primary_message(msg);
-                    f(lint);
-                },
-            );
-        }
+        self.tcx.node_span_lint(
+            crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
+            self.tcx.local_def_id_to_hir_id(self.def_id),
+            self.sp,
+            |lint| {
+                lint.primary_message(msg);
+                f(lint);
+            },
+        );
     }
 }
 
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 0334eacc161..0ed8921b1e8 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -14,7 +14,6 @@ use rustc_span::edition::Edition;
 use rustc_span::{sym, FileName, Symbol};
 
 use super::print_item::{full_path, item_path, print_item};
-use super::search_index::build_index;
 use super::sidebar::{print_sidebar, sidebar_module_like, Sidebar};
 use super::write_shared::write_shared;
 use super::{collect_spans_and_sources, scrape_examples_help, AllTypes, LinkFromSrc, StylePath};
@@ -573,13 +572,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         }
 
         if !no_emit_shared {
-            // Build our search index
-            let index = build_index(&krate, &mut Rc::get_mut(&mut cx.shared).unwrap().cache, tcx);
-
-            // Write shared runs within a flock; disable thread dispatching of IO temporarily.
-            Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
-            write_shared(&mut cx, &krate, index, &md_opts)?;
-            Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
+            write_shared(&mut cx, &krate, &md_opts, tcx)?;
         }
 
         Ok((cx, krate))
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 7ce637d3ab4..c1b2ee7d8ae 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -29,8 +29,10 @@ pub(crate) mod search_index;
 mod tests;
 
 mod context;
+mod ordered_json;
 mod print_item;
 pub(crate) mod sidebar;
+mod sorted_template;
 mod span_map;
 mod type_layout;
 mod write_shared;
diff --git a/src/librustdoc/html/render/ordered_json.rs b/src/librustdoc/html/render/ordered_json.rs
new file mode 100644
index 00000000000..7abe40eef3b
--- /dev/null
+++ b/src/librustdoc/html/render/ordered_json.rs
@@ -0,0 +1,83 @@
+use std::borrow::Borrow;
+use std::fmt;
+
+use itertools::Itertools as _;
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+
+/// Prerendered json.
+///
+/// Both the Display and serde_json::to_string implementations write the serialized json
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
+#[serde(from = "Value")]
+#[serde(into = "Value")]
+pub(crate) struct OrderedJson(String);
+
+impl OrderedJson {
+    /// If you pass in an array, it will not be sorted.
+    pub(crate) fn serialize<T: Serialize>(item: T) -> Result<Self, serde_json::Error> {
+        Ok(Self(serde_json::to_string(&item)?))
+    }
+
+    /// Serializes and sorts
+    pub(crate) fn array_sorted<T: Borrow<Self>, I: IntoIterator<Item = T>>(items: I) -> Self {
+        let items = items
+            .into_iter()
+            .sorted_unstable_by(|a, b| a.borrow().cmp(&b.borrow()))
+            .format_with(",", |item, f| f(item.borrow()));
+        Self(format!("[{}]", items))
+    }
+
+    pub(crate) fn array_unsorted<T: Borrow<Self>, I: IntoIterator<Item = T>>(items: I) -> Self {
+        let items = items.into_iter().format_with(",", |item, f| f(item.borrow()));
+        Self(format!("[{items}]"))
+    }
+}
+
+impl fmt::Display for OrderedJson {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl From<Value> for OrderedJson {
+    fn from(value: Value) -> Self {
+        let serialized =
+            serde_json::to_string(&value).expect("Serializing a Value to String should never fail");
+        Self(serialized)
+    }
+}
+
+impl From<OrderedJson> for Value {
+    fn from(json: OrderedJson) -> Self {
+        serde_json::from_str(&json.0).expect("OrderedJson should always store valid JSON")
+    }
+}
+
+/// For use in JSON.parse('{...}').
+///
+/// Assumes we are going to be wrapped in single quoted strings.
+///
+/// JSON.parse loads faster than raw JS source,
+/// so this is used for large objects.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub(crate) struct EscapedJson(OrderedJson);
+
+impl From<OrderedJson> for EscapedJson {
+    fn from(json: OrderedJson) -> Self {
+        Self(json)
+    }
+}
+
+impl fmt::Display for EscapedJson {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // All these `replace` calls are because we have to go through JS string
+        // for JSON content.
+        // We need to escape double quotes for the JSON
+        let json = self.0.0.replace('\\', r"\\").replace('\'', r"\'").replace("\\\"", "\\\\\"");
+        json.fmt(f)
+    }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/src/librustdoc/html/render/ordered_json/tests.rs b/src/librustdoc/html/render/ordered_json/tests.rs
new file mode 100644
index 00000000000..e0fe6446b9a
--- /dev/null
+++ b/src/librustdoc/html/render/ordered_json/tests.rs
@@ -0,0 +1,121 @@
+use super::super::ordered_json::*;
+
+fn check(json: OrderedJson, serialized: &str) {
+    assert_eq!(json.to_string(), serialized);
+    assert_eq!(serde_json::to_string(&json).unwrap(), serialized);
+
+    let json = json.to_string();
+    let json: OrderedJson = serde_json::from_str(&json).unwrap();
+
+    assert_eq!(json.to_string(), serialized);
+    assert_eq!(serde_json::to_string(&json).unwrap(), serialized);
+
+    let json = serde_json::to_string(&json).unwrap();
+    let json: OrderedJson = serde_json::from_str(&json).unwrap();
+
+    assert_eq!(json.to_string(), serialized);
+    assert_eq!(serde_json::to_string(&json).unwrap(), serialized);
+}
+
+// Make sure there is no extra level of string, plus number of escapes.
+#[test]
+fn escape_json_number() {
+    let json = OrderedJson::serialize(3).unwrap();
+    let json = EscapedJson::from(json);
+    assert_eq!(format!("{json}"), "3");
+}
+
+#[test]
+fn escape_json_single_quote() {
+    let json = OrderedJson::serialize("he's").unwrap();
+    let json = EscapedJson::from(json);
+    assert_eq!(format!("{json}"), r#""he\'s""#);
+}
+
+#[test]
+fn escape_json_array() {
+    let json = OrderedJson::serialize([1, 2, 3]).unwrap();
+    let json = EscapedJson::from(json);
+    assert_eq!(format!("{json}"), r#"[1,2,3]"#);
+}
+
+#[test]
+fn escape_json_string() {
+    let json = OrderedJson::serialize(r#"he"llo"#).unwrap();
+    let json = EscapedJson::from(json);
+    assert_eq!(format!("{json}"), r#""he\\\"llo""#);
+}
+
+#[test]
+fn escape_json_string_escaped() {
+    let json = OrderedJson::serialize(r#"he\"llo"#).unwrap();
+    let json = EscapedJson::from(json);
+    assert_eq!(format!("{json}"), r#""he\\\\\\\"llo""#);
+}
+
+#[test]
+fn escape_json_string_escaped_escaped() {
+    let json = OrderedJson::serialize(r#"he\\"llo"#).unwrap();
+    let json = EscapedJson::from(json);
+    assert_eq!(format!("{json}"), r#""he\\\\\\\\\\\"llo""#);
+}
+
+// Testing round trip + making sure there is no extra level of string
+#[test]
+fn number() {
+    let json = OrderedJson::serialize(3).unwrap();
+    let serialized = "3";
+    check(json, serialized);
+}
+
+#[test]
+fn boolean() {
+    let json = OrderedJson::serialize(true).unwrap();
+    let serialized = "true";
+    check(json, serialized);
+}
+
+#[test]
+fn string() {
+    let json = OrderedJson::serialize("he\"llo").unwrap();
+    let serialized = r#""he\"llo""#;
+    check(json, serialized);
+}
+
+#[test]
+fn serialize_array() {
+    let json = OrderedJson::serialize([3, 1, 2]).unwrap();
+    let serialized = "[3,1,2]";
+    check(json, serialized);
+}
+
+#[test]
+fn sorted_array() {
+    let items = ["c", "a", "b"];
+    let serialized = r#"["a","b","c"]"#;
+    let items: Vec<OrderedJson> =
+        items.into_iter().map(OrderedJson::serialize).collect::<Result<Vec<_>, _>>().unwrap();
+    let json = OrderedJson::array_sorted(items);
+    check(json, serialized);
+}
+
+#[test]
+fn nested_array() {
+    let a = OrderedJson::serialize(3).unwrap();
+    let b = OrderedJson::serialize(2).unwrap();
+    let c = OrderedJson::serialize(1).unwrap();
+    let d = OrderedJson::serialize([1, 3, 2]).unwrap();
+    let json = OrderedJson::array_sorted([a, b, c, d]);
+    let serialized = r#"[1,2,3,[1,3,2]]"#;
+    check(json, serialized);
+}
+
+#[test]
+fn array_unsorted() {
+    let items = ["c", "a", "b"];
+    let serialized = r#"["c","a","b"]"#;
+    let items: Vec<OrderedJson> =
+        items.into_iter().map(OrderedJson::serialize).collect::<Result<Vec<_>, _>>().unwrap();
+    let json = OrderedJson::array_unsorted(items);
+    check(json, serialized);
+}
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 8a2f31f7413..d7682bd1c19 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -18,6 +18,7 @@ use crate::formats::cache::{Cache, OrphanImplItem};
 use crate::formats::item_type::ItemType;
 use crate::html::format::join_with_double_colon;
 use crate::html::markdown::short_markdown_summary;
+use crate::html::render::ordered_json::OrderedJson;
 use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
 
 /// The serialized search description sharded version
@@ -46,7 +47,7 @@ use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, Re
 /// [2]: https://en.wikipedia.org/wiki/Sliding_window_protocol#Basic_concept
 /// [3]: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features
 pub(crate) struct SerializedSearchIndex {
-    pub(crate) index: String,
+    pub(crate) index: OrderedJson,
     pub(crate) desc: Vec<(usize, String)>,
 }
 
@@ -578,12 +579,14 @@ pub(crate) fn build_index<'tcx>(
             let mut names = Vec::with_capacity(self.items.len());
             let mut types = String::with_capacity(self.items.len());
             let mut full_paths = Vec::with_capacity(self.items.len());
-            let mut parents = Vec::with_capacity(self.items.len());
+            let mut parents = String::with_capacity(self.items.len());
+            let mut parents_backref_queue = VecDeque::new();
             let mut functions = String::with_capacity(self.items.len());
             let mut deprecated = Vec::with_capacity(self.items.len());
 
-            let mut backref_queue = VecDeque::new();
+            let mut type_backref_queue = VecDeque::new();
 
+            let mut last_name = None;
             for (index, item) in self.items.iter().enumerate() {
                 let n = item.ty as u8;
                 let c = char::try_from(n + b'A').expect("item types must fit in ASCII");
@@ -596,17 +599,39 @@ pub(crate) fn build_index<'tcx>(
                     "`{}` is missing idx",
                     item.name
                 );
-                // 0 is a sentinel, everything else is one-indexed
-                parents.push(item.parent_idx.map(|x| x + 1).unwrap_or(0));
+                assert!(
+                    parents_backref_queue.len() <= 16,
+                    "the string encoding only supports 16 slots of lookback"
+                );
+                let parent: i32 = item.parent_idx.map(|x| x + 1).unwrap_or(0).try_into().unwrap();
+                if let Some(idx) = parents_backref_queue.iter().position(|p: &i32| *p == parent) {
+                    parents.push(
+                        char::try_from('0' as u32 + u32::try_from(idx).unwrap())
+                            .expect("last possible value is '?'"),
+                    );
+                } else if parent == 0 {
+                    write_vlqhex_to_string(parent, &mut parents);
+                } else {
+                    parents_backref_queue.push_front(parent);
+                    write_vlqhex_to_string(parent, &mut parents);
+                    if parents_backref_queue.len() > 16 {
+                        parents_backref_queue.pop_back();
+                    }
+                }
 
-                names.push(item.name.as_str());
+                if Some(item.name.as_str()) == last_name {
+                    names.push("");
+                } else {
+                    names.push(item.name.as_str());
+                    last_name = Some(item.name.as_str());
+                }
 
                 if !item.path.is_empty() {
                     full_paths.push((index, &item.path));
                 }
 
                 match &item.search_type {
-                    Some(ty) => ty.write_to_string(&mut functions, &mut backref_queue),
+                    Some(ty) => ty.write_to_string(&mut functions, &mut type_backref_queue),
                     None => functions.push('`'),
                 }
 
@@ -683,24 +708,19 @@ pub(crate) fn build_index<'tcx>(
     // The index, which is actually used to search, is JSON
     // It uses `JSON.parse(..)` to actually load, since JSON
     // parses faster than the full JavaScript syntax.
-    let index = format!(
-        r#"["{}",{}]"#,
-        krate.name(tcx),
-        serde_json::to_string(&CrateData {
-            items: crate_items,
-            paths: crate_paths,
-            aliases: &aliases,
-            associated_item_disambiguators: &associated_item_disambiguators,
-            desc_index,
-            empty_desc,
-        })
-        .expect("failed serde conversion")
-        // All these `replace` calls are because we have to go through JS string for JSON content.
-        .replace('\\', r"\\")
-        .replace('\'', r"\'")
-        // We need to escape double quotes for the JSON.
-        .replace("\\\"", "\\\\\"")
-    );
+    let crate_name = krate.name(tcx);
+    let data = CrateData {
+        items: crate_items,
+        paths: crate_paths,
+        aliases: &aliases,
+        associated_item_disambiguators: &associated_item_disambiguators,
+        desc_index,
+        empty_desc,
+    };
+    let index = OrderedJson::array_unsorted([
+        OrderedJson::serialize(crate_name.as_str()).unwrap(),
+        OrderedJson::serialize(data).unwrap(),
+    ]);
     SerializedSearchIndex { index, desc }
 }
 
diff --git a/src/librustdoc/html/render/sorted_template.rs b/src/librustdoc/html/render/sorted_template.rs
new file mode 100644
index 00000000000..28f7766d7c7
--- /dev/null
+++ b/src/librustdoc/html/render/sorted_template.rs
@@ -0,0 +1,159 @@
+use std::collections::BTreeSet;
+use std::fmt::{self, Write as _};
+use std::marker::PhantomData;
+use std::str::FromStr;
+
+use itertools::{Itertools as _, Position};
+use serde::{Deserialize, Serialize};
+
+/// Append-only templates for sorted, deduplicated lists of items.
+///
+/// Last line of the rendered output is a comment encoding the next insertion point.
+#[derive(Debug, Clone)]
+pub(crate) struct SortedTemplate<F> {
+    format: PhantomData<F>,
+    before: String,
+    after: String,
+    fragments: BTreeSet<String>,
+}
+
+/// Written to last line of file to specify the location of each fragment
+#[derive(Serialize, Deserialize, Debug, Clone)]
+struct Offset {
+    /// Index of the first byte in the template
+    start: usize,
+    /// The length of each fragment in the encoded template, including the separator
+    fragment_lengths: Vec<usize>,
+}
+
+impl<F> SortedTemplate<F> {
+    /// Generate this template from arbitary text.
+    /// Will insert wherever the substring `delimiter` can be found.
+    /// Errors if it does not appear exactly once.
+    pub(crate) fn from_template(template: &str, delimiter: &str) -> Result<Self, Error> {
+        let mut split = template.split(delimiter);
+        let before = split.next().ok_or(Error("delimiter should appear at least once"))?;
+        let after = split.next().ok_or(Error("delimiter should appear at least once"))?;
+        // not `split_once` because we want to check for too many occurrences
+        if split.next().is_some() {
+            return Err(Error("delimiter should appear at most once"));
+        }
+        Ok(Self::from_before_after(before, after))
+    }
+
+    /// Template will insert fragments between `before` and `after`
+    pub(crate) fn from_before_after<S: ToString, T: ToString>(before: S, after: T) -> Self {
+        let before = before.to_string();
+        let after = after.to_string();
+        Self { format: PhantomData, before, after, fragments: Default::default() }
+    }
+}
+
+impl<F> SortedTemplate<F> {
+    /// Adds this text to the template
+    pub(crate) fn append(&mut self, insert: String) {
+        self.fragments.insert(insert);
+    }
+}
+
+impl<F: FileFormat> fmt::Display for SortedTemplate<F> {
+    fn fmt(&self, mut f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut fragment_lengths = Vec::default();
+        write!(f, "{}", self.before)?;
+        for (p, fragment) in self.fragments.iter().with_position() {
+            let mut f = DeltaWriter { inner: &mut f, delta: 0 };
+            let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR };
+            write!(f, "{}{}", sep, fragment)?;
+            fragment_lengths.push(f.delta);
+        }
+        let offset = Offset { start: self.before.len(), fragment_lengths };
+        let offset = serde_json::to_string(&offset).unwrap();
+        write!(f, "{}\n{}{}{}", self.after, F::COMMENT_START, offset, F::COMMENT_END)
+    }
+}
+
+impl<F: FileFormat> FromStr for SortedTemplate<F> {
+    type Err = Error;
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let (s, offset) = s
+            .rsplit_once("\n")
+            .ok_or(Error("invalid format: should have a newline on the last line"))?;
+        let offset = offset
+            .strip_prefix(F::COMMENT_START)
+            .ok_or(Error("last line expected to start with a comment"))?;
+        let offset = offset
+            .strip_suffix(F::COMMENT_END)
+            .ok_or(Error("last line expected to end with a comment"))?;
+        let offset: Offset = serde_json::from_str(&offset).map_err(|_| {
+            Error("could not find insertion location descriptor object on last line")
+        })?;
+        let (before, mut s) =
+            s.split_at_checked(offset.start).ok_or(Error("invalid start: out of bounds"))?;
+        let mut fragments = BTreeSet::default();
+        for (p, &index) in offset.fragment_lengths.iter().with_position() {
+            let (fragment, rest) =
+                s.split_at_checked(index).ok_or(Error("invalid fragment length: out of bounds"))?;
+            s = rest;
+            let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR };
+            let fragment = fragment
+                .strip_prefix(sep)
+                .ok_or(Error("invalid fragment length: expected to find separator here"))?;
+            fragments.insert(fragment.to_string());
+        }
+        Ok(Self {
+            format: PhantomData,
+            before: before.to_string(),
+            after: s.to_string(),
+            fragments,
+        })
+    }
+}
+
+pub(crate) trait FileFormat {
+    const COMMENT_START: &'static str;
+    const COMMENT_END: &'static str;
+    const SEPARATOR: &'static str;
+}
+
+#[derive(Debug, Clone)]
+pub(crate) struct Html;
+
+impl FileFormat for Html {
+    const COMMENT_START: &'static str = "<!--";
+    const COMMENT_END: &'static str = "-->";
+    const SEPARATOR: &'static str = "";
+}
+
+#[derive(Debug, Clone)]
+pub(crate) struct Js;
+
+impl FileFormat for Js {
+    const COMMENT_START: &'static str = "//";
+    const COMMENT_END: &'static str = "";
+    const SEPARATOR: &'static str = ",";
+}
+
+#[derive(Debug, Clone)]
+pub(crate) struct Error(&'static str);
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "invalid template: {}", self.0)
+    }
+}
+
+struct DeltaWriter<W> {
+    inner: W,
+    delta: usize,
+}
+
+impl<W: fmt::Write> fmt::Write for DeltaWriter<W> {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        self.inner.write_str(s)?;
+        self.delta += s.len();
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/src/librustdoc/html/render/sorted_template/tests.rs b/src/librustdoc/html/render/sorted_template/tests.rs
new file mode 100644
index 00000000000..db057463005
--- /dev/null
+++ b/src/librustdoc/html/render/sorted_template/tests.rs
@@ -0,0 +1,149 @@
+use std::str::FromStr;
+
+use super::super::sorted_template::*;
+
+fn is_comment_js(s: &str) -> bool {
+    s.starts_with("//")
+}
+
+fn is_comment_html(s: &str) -> bool {
+    // not correct but good enough for these tests
+    s.starts_with("<!--") && s.ends_with("-->")
+}
+
+#[test]
+fn html_from_empty() {
+    let inserts = ["<p>hello</p>", "<p>kind</p>", "<p>hello</p>", "<p>world</p>"];
+    let mut template = SortedTemplate::<Html>::from_before_after("", "");
+    for insert in inserts {
+        template.append(insert.to_string());
+    }
+    let template = format!("{template}");
+    let (template, end) = template.rsplit_once("\n").unwrap();
+    assert_eq!(template, "<p>hello</p><p>kind</p><p>world</p>");
+    assert!(is_comment_html(end));
+    assert!(!end.contains("\n"));
+}
+
+#[test]
+fn html_page() {
+    let inserts = ["<p>hello</p>", "<p>kind</p>", "<p>world</p>"];
+    let before = "<html><head></head><body>";
+    let after = "</body>";
+    let mut template = SortedTemplate::<Html>::from_before_after(before, after);
+    for insert in inserts {
+        template.append(insert.to_string());
+    }
+    let template = format!("{template}");
+    let (template, end) = template.rsplit_once("\n").unwrap();
+    assert_eq!(template, format!("{before}{}{after}", inserts.join("")));
+    assert!(is_comment_html(end));
+    assert!(!end.contains("\n"));
+}
+
+#[test]
+fn js_from_empty() {
+    let inserts = ["1", "2", "2", "2", "3", "1"];
+    let mut template = SortedTemplate::<Js>::from_before_after("", "");
+    for insert in inserts {
+        template.append(insert.to_string());
+    }
+    let template = format!("{template}");
+    let (template, end) = template.rsplit_once("\n").unwrap();
+    assert_eq!(template, "1,2,3");
+    assert!(is_comment_js(end));
+    assert!(!end.contains("\n"));
+}
+
+#[test]
+fn js_empty_array() {
+    let template = SortedTemplate::<Js>::from_before_after("[", "]");
+    let template = format!("{template}");
+    let (template, end) = template.rsplit_once("\n").unwrap();
+    assert_eq!(template, format!("[]"));
+    assert!(is_comment_js(end));
+    assert!(!end.contains("\n"));
+}
+
+#[test]
+fn js_number_array() {
+    let inserts = ["1", "2", "3"];
+    let mut template = SortedTemplate::<Js>::from_before_after("[", "]");
+    for insert in inserts {
+        template.append(insert.to_string());
+    }
+    let template = format!("{template}");
+    let (template, end) = template.rsplit_once("\n").unwrap();
+    assert_eq!(template, format!("[1,2,3]"));
+    assert!(is_comment_js(end));
+    assert!(!end.contains("\n"));
+}
+
+#[test]
+fn magic_js_number_array() {
+    let inserts = ["1", "1"];
+    let mut template = SortedTemplate::<Js>::from_template("[#]", "#").unwrap();
+    for insert in inserts {
+        template.append(insert.to_string());
+    }
+    let template = format!("{template}");
+    let (template, end) = template.rsplit_once("\n").unwrap();
+    assert_eq!(template, format!("[1]"));
+    assert!(is_comment_js(end));
+    assert!(!end.contains("\n"));
+}
+
+#[test]
+fn round_trip_js() {
+    let inserts = ["1", "2", "3"];
+    let mut template = SortedTemplate::<Js>::from_before_after("[", "]");
+    for insert in inserts {
+        template.append(insert.to_string());
+    }
+    let template1 = format!("{template}");
+    let mut template = SortedTemplate::<Js>::from_str(&template1).unwrap();
+    assert_eq!(template1, format!("{template}"));
+    template.append("4".to_string());
+    let template = format!("{template}");
+    let (template, end) = template.rsplit_once("\n").unwrap();
+    assert_eq!(template, "[1,2,3,4]");
+    assert!(is_comment_js(end));
+}
+
+#[test]
+fn round_trip_html() {
+    let inserts = ["<p>hello</p>", "<p>kind</p>", "<p>world</p>", "<p>kind</p>"];
+    let before = "<html><head></head><body>";
+    let after = "</body>";
+    let mut template = SortedTemplate::<Html>::from_before_after(before, after);
+    template.append(inserts[0].to_string());
+    template.append(inserts[1].to_string());
+    let template = format!("{template}");
+    let mut template = SortedTemplate::<Html>::from_str(&template).unwrap();
+    template.append(inserts[2].to_string());
+    let template = format!("{template}");
+    let (template, end) = template.rsplit_once("\n").unwrap();
+    assert_eq!(template, format!("{before}<p>hello</p><p>kind</p><p>world</p>{after}"));
+    assert!(is_comment_html(end));
+}
+
+#[test]
+fn blank_js() {
+    let inserts = ["1", "2", "3"];
+    let template = SortedTemplate::<Js>::from_before_after("", "");
+    let template = format!("{template}");
+    let (t, _) = template.rsplit_once("\n").unwrap();
+    assert_eq!(t, "");
+    let mut template = SortedTemplate::<Js>::from_str(&template).unwrap();
+    for insert in inserts {
+        template.append(insert.to_string());
+    }
+    let template1 = format!("{template}");
+    let mut template = SortedTemplate::<Js>::from_str(&template1).unwrap();
+    assert_eq!(template1, format!("{template}"));
+    template.append("4".to_string());
+    let template = format!("{template}");
+    let (template, end) = template.rsplit_once("\n").unwrap();
+    assert_eq!(template, "1,2,3,4");
+    assert!(is_comment_js(end));
+}
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 8fd56eae37f..a18b7a252a4 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -1,19 +1,41 @@
+//! Rustdoc writes aut two kinds of shared files:
+//!  - Static files, which are embedded in the rustdoc binary and are written with a
+//!    filename that includes a hash of their contents. These will always have a new
+//!    URL if the contents change, so they are safe to cache with the
+//!    `Cache-Control: immutable` directive. They are written under the static.files/
+//!    directory and are written when --emit-type is empty (default) or contains
+//!    "toolchain-specific". If using the --static-root-path flag, it should point
+//!    to a URL path prefix where each of these filenames can be fetched.
+//!  - Invocation specific files. These are generated based on the crate(s) being
+//!    documented. Their filenames need to be predictable without knowing their
+//!    contents, so they do not include a hash in their filename and are not safe to
+//!    cache with `Cache-Control: immutable`. They include the contents of the
+//!    --resource-suffix flag and are emitted when --emit-type is empty (default)
+//!    or contains "invocation-specific".
+
 use std::cell::RefCell;
-use std::fs::{self, File};
-use std::io::prelude::*;
-use std::io::{self, BufReader};
-use std::path::{Component, Path};
+use std::ffi::OsString;
+use std::fs::File;
+use std::io::{self, BufWriter, Write as _};
+use std::iter::once;
+use std::marker::PhantomData;
+use std::path::{Component, Path, PathBuf};
 use std::rc::{Rc, Weak};
+use std::str::FromStr;
+use std::{fmt, fs};
 
 use indexmap::IndexMap;
 use itertools::Itertools;
+use regex::Regex;
 use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::DefId;
 use rustc_span::Symbol;
+use serde::de::DeserializeOwned;
 use serde::ser::SerializeSeq;
-use serde::{Serialize, Serializer};
+use serde::{Deserialize, Serialize, Serializer};
 
 use super::{collect_paths_for_type, ensure_trailing_slash, Context, RenderMode};
 use crate::clean::{Crate, Item, ItemId, ItemKind};
@@ -24,53 +46,83 @@ use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::formats::Impl;
 use crate::html::format::Buffer;
-use crate::html::render::search_index::SerializedSearchIndex;
+use crate::html::layout;
+use crate::html::render::ordered_json::{EscapedJson, OrderedJson};
+use crate::html::render::search_index::{build_index, SerializedSearchIndex};
+use crate::html::render::sorted_template::{self, FileFormat, SortedTemplate};
 use crate::html::render::{AssocItemLink, ImplRenderingParameters};
-use crate::html::{layout, static_files};
+use crate::html::static_files::{self, suffix_path};
 use crate::visit::DocVisitor;
 use crate::{try_err, try_none};
 
-/// Rustdoc writes out two kinds of shared files:
-///  - Static files, which are embedded in the rustdoc binary and are written with a
-///    filename that includes a hash of their contents. These will always have a new
-///    URL if the contents change, so they are safe to cache with the
-///    `Cache-Control: immutable` directive. They are written under the static.files/
-///    directory and are written when --emit-type is empty (default) or contains
-///    "toolchain-specific". If using the --static-root-path flag, it should point
-///    to a URL path prefix where each of these filenames can be fetched.
-///  - Invocation specific files. These are generated based on the crate(s) being
-///    documented. Their filenames need to be predictable without knowing their
-///    contents, so they do not include a hash in their filename and are not safe to
-///    cache with `Cache-Control: immutable`. They include the contents of the
-///    --resource-suffix flag and are emitted when --emit-type is empty (default)
-///    or contains "invocation-specific".
-pub(super) fn write_shared(
+/// Write cross-crate information files, static files, invocation-specific files, etc. to disk
+pub(crate) fn write_shared(
     cx: &mut Context<'_>,
     krate: &Crate,
-    search_index: SerializedSearchIndex,
-    options: &RenderOptions,
+    opt: &RenderOptions,
+    tcx: TyCtxt<'_>,
 ) -> Result<(), Error> {
-    // Write out the shared files. Note that these are shared among all rustdoc
-    // docs placed in the output directory, so this needs to be a synchronized
-    // operation with respect to all other rustdocs running around.
+    // NOTE(EtomicBomb): I don't think we need sync here because no read-after-write?
+    Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
     let lock_file = cx.dst.join(".lock");
+    // Write shared runs within a flock; disable thread dispatching of IO temporarily.
     let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);
 
-    // InvocationSpecific resources should always be dynamic.
-    let write_invocation_specific = |p: &str, make_content: &dyn Fn() -> Result<Vec<u8>, Error>| {
-        let content = make_content()?;
-        if options.emit.is_empty() || options.emit.contains(&EmitType::InvocationSpecific) {
-            let output_filename = static_files::suffix_path(p, &cx.shared.resource_suffix);
-            cx.shared.fs.write(cx.dst.join(output_filename), content)
-        } else {
-            Ok(())
-        }
+    let SerializedSearchIndex { index, desc } =
+        build_index(&krate, &mut Rc::get_mut(&mut cx.shared).unwrap().cache, tcx);
+    write_search_desc(cx, &krate, &desc)?; // does not need to be merged; written unconditionally
+
+    let crate_name = krate.name(cx.tcx());
+    let crate_name = crate_name.as_str(); // rand
+    let crate_name_json = OrderedJson::serialize(crate_name).unwrap(); // "rand"
+    let external_crates = hack_get_external_crate_names(&cx.dst)?;
+    let info = CrateInfo {
+        src_files_js: SourcesPart::get(cx, &crate_name_json)?,
+        search_index_js: SearchIndexPart::get(index, &cx.shared.resource_suffix)?,
+        all_crates: AllCratesPart::get(crate_name_json.clone())?,
+        crates_index: CratesIndexPart::get(&crate_name, &external_crates)?,
+        trait_impl: TraitAliasPart::get(cx, &crate_name_json)?,
+        type_impl: TypeAliasPart::get(cx, krate, &crate_name_json)?,
     };
 
-    cx.shared
-        .fs
-        .create_dir_all(cx.dst.join("static.files"))
-        .map_err(|e| PathError::new(e, "static.files"))?;
+    let crates = vec![info]; // we have info from just one crate. rest will found in out dir
+
+    write_static_files(cx, &opt)?;
+    let dst = &cx.dst;
+    if opt.emit.is_empty() || opt.emit.contains(&EmitType::InvocationSpecific) {
+        if cx.include_sources {
+            write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, &crates)?;
+        }
+        write_rendered_cci::<SearchIndexPart, _>(SearchIndexPart::blank, dst, &crates)?;
+        write_rendered_cci::<AllCratesPart, _>(AllCratesPart::blank, dst, &crates)?;
+    }
+    write_rendered_cci::<TraitAliasPart, _>(TraitAliasPart::blank, dst, &crates)?;
+    write_rendered_cci::<TypeAliasPart, _>(TypeAliasPart::blank, dst, &crates)?;
+    match &opt.index_page {
+        Some(index_page) if opt.enable_index_page => {
+            let mut md_opts = opt.clone();
+            md_opts.output = cx.dst.clone();
+            md_opts.external_html = cx.shared.layout.external_html.clone();
+            try_err!(
+                crate::markdown::render(&index_page, md_opts, cx.shared.edition()),
+                &index_page
+            );
+        }
+        None if opt.enable_index_page => {
+            write_rendered_cci::<CratesIndexPart, _>(|| CratesIndexPart::blank(cx), dst, &crates)?;
+        }
+        _ => {} // they don't want an index page
+    }
+
+    Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
+    Ok(())
+}
+
+/// Writes the static files, the style files, and the css extensions
+fn write_static_files(cx: &mut Context<'_>, options: &RenderOptions) -> Result<(), Error> {
+    let static_dir = cx.dst.join("static.files");
+
+    cx.shared.fs.create_dir_all(&static_dir).map_err(|e| PathError::new(e, "static.files"))?;
 
     // Handle added third-party themes
     for entry in &cx.shared.style_files {
@@ -97,680 +149,786 @@ pub(super) fn write_shared(
     }
 
     if options.emit.is_empty() || options.emit.contains(&EmitType::Toolchain) {
-        let static_dir = cx.dst.join(Path::new("static.files"));
         static_files::for_each(|f: &static_files::StaticFile| {
             let filename = static_dir.join(f.output_filename());
             cx.shared.fs.write(filename, f.minified())
         })?;
     }
 
-    /// Read a file and return all lines that match the `"{crate}":{data},` format,
-    /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
-    ///
-    /// This forms the payload of files that look like this:
-    ///
-    /// ```javascript
-    /// var data = {
-    /// "{crate1}":{data},
-    /// "{crate2}":{data}
-    /// };
-    /// use_data(data);
-    /// ```
-    ///
-    /// The file needs to be formatted so that *only crate data lines start with `"`*.
-    fn collect(path: &Path, krate: &str) -> io::Result<(Vec<String>, Vec<String>)> {
-        let mut ret = Vec::new();
-        let mut krates = Vec::new();
-
-        if path.exists() {
-            let prefix = format!("\"{krate}\"");
-            for line in BufReader::new(File::open(path)?).lines() {
-                let line = line?;
-                if !line.starts_with('"') {
-                    continue;
-                }
-                if line.starts_with(&prefix) {
-                    continue;
-                }
-                if line.ends_with(',') {
-                    ret.push(line[..line.len() - 1].to_string());
-                } else {
-                    // No comma (it's the case for the last added crate line)
-                    ret.push(line.to_string());
-                }
-                krates.push(
-                    line.split('"')
-                        .find(|s| !s.is_empty())
-                        .map(|s| s.to_owned())
-                        .unwrap_or_else(String::new),
-                );
-            }
-        }
-        Ok((ret, krates))
-    }
-
-    /// Read a file and return all lines that match the <code>"{crate}":{data},\ </code> format,
-    /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
-    ///
-    /// This forms the payload of files that look like this:
-    ///
-    /// ```javascript
-    /// var data = JSON.parse('{\
-    /// "{crate1}":{data},\
-    /// "{crate2}":{data}\
-    /// }');
-    /// use_data(data);
-    /// ```
-    ///
-    /// The file needs to be formatted so that *only crate data lines start with `"`*.
-    fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec<String>, Vec<String>)> {
-        let mut ret = Vec::new();
-        let mut krates = Vec::new();
-
-        if path.exists() {
-            let prefix = format!("[\"{krate}\"");
-            for line in BufReader::new(File::open(path)?).lines() {
-                let line = line?;
-                if !line.starts_with("[\"") {
-                    continue;
-                }
-                if line.starts_with(&prefix) {
-                    continue;
-                }
-                if line.ends_with("],\\") {
-                    ret.push(line[..line.len() - 2].to_string());
-                } else {
-                    // Ends with "\\" (it's the case for the last added crate line)
-                    ret.push(line[..line.len() - 1].to_string());
-                }
-                krates.push(
-                    line[1..] // We skip the `[` parent at the beginning of the line.
-                        .split('"')
-                        .find(|s| !s.is_empty())
-                        .map(|s| s.to_owned())
-                        .unwrap_or_else(String::new),
-                );
-            }
-        }
-        Ok((ret, krates))
+    Ok(())
+}
+
+/// Write the search description shards to disk
+fn write_search_desc(
+    cx: &mut Context<'_>,
+    krate: &Crate,
+    search_desc: &[(usize, String)],
+) -> Result<(), Error> {
+    let crate_name = krate.name(cx.tcx()).to_string();
+    let encoded_crate_name = OrderedJson::serialize(&crate_name).unwrap();
+    let path = PathBuf::from_iter([&cx.dst, Path::new("search.desc"), Path::new(&crate_name)]);
+    if path.exists() {
+        try_err!(fs::remove_dir_all(&path), &path);
+    }
+    for (i, (_, part)) in search_desc.iter().enumerate() {
+        let filename = static_files::suffix_path(
+            &format!("{crate_name}-desc-{i}-.js"),
+            &cx.shared.resource_suffix,
+        );
+        let path = path.join(filename);
+        let part = OrderedJson::serialize(&part).unwrap();
+        let part = format!("searchState.loadedDescShard({encoded_crate_name}, {i}, {part})");
+        create_parents(&path)?;
+        try_err!(fs::write(&path, part), &path);
     }
+    Ok(())
+}
 
-    use std::ffi::OsString;
+/// Contains pre-rendered contents to insert into the CCI template
+#[derive(Serialize, Deserialize, Clone, Debug)]
+struct CrateInfo {
+    src_files_js: PartsAndLocations<SourcesPart>,
+    search_index_js: PartsAndLocations<SearchIndexPart>,
+    all_crates: PartsAndLocations<AllCratesPart>,
+    crates_index: PartsAndLocations<CratesIndexPart>,
+    trait_impl: PartsAndLocations<TraitAliasPart>,
+    type_impl: PartsAndLocations<TypeAliasPart>,
+}
+
+/// Paths (relative to the doc root) and their pre-merge contents
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(transparent)]
+struct PartsAndLocations<P> {
+    parts: Vec<(PathBuf, P)>,
+}
 
-    #[derive(Debug, Default)]
-    struct Hierarchy {
-        parent: Weak<Self>,
-        elem: OsString,
-        children: RefCell<FxHashMap<OsString, Rc<Self>>>,
-        elems: RefCell<FxHashSet<OsString>>,
+impl<P> Default for PartsAndLocations<P> {
+    fn default() -> Self {
+        Self { parts: Vec::default() }
     }
+}
 
-    impl Hierarchy {
-        fn with_parent(elem: OsString, parent: &Rc<Self>) -> Self {
-            Self { elem, parent: Rc::downgrade(parent), ..Self::default() }
-        }
+impl<T, U> PartsAndLocations<Part<T, U>> {
+    fn push(&mut self, path: PathBuf, item: U) {
+        self.parts.push((path, Part { _artifact: PhantomData, item }));
+    }
 
-        fn to_json_string(&self) -> String {
-            let borrow = self.children.borrow();
-            let mut subs: Vec<_> = borrow.values().collect();
-            subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem));
-            let mut files = self
-                .elems
-                .borrow()
-                .iter()
-                .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion")))
-                .collect::<Vec<_>>();
-            files.sort_unstable();
-            let subs = subs.iter().map(|s| s.to_json_string()).collect::<Vec<_>>().join(",");
-            let dirs = if subs.is_empty() && files.is_empty() {
-                String::new()
-            } else {
-                format!(",[{subs}]")
-            };
-            let files = files.join(",");
-            let files = if files.is_empty() { String::new() } else { format!(",[{files}]") };
-            format!(
-                "[\"{name}\"{dirs}{files}]",
-                name = self.elem.to_str().expect("invalid osstring conversion"),
-                dirs = dirs,
-                files = files
-            )
-        }
+    /// Singleton part, one file
+    fn with(path: PathBuf, part: U) -> Self {
+        let mut ret = Self::default();
+        ret.push(path, part);
+        ret
+    }
+}
 
-        fn add_path(self: &Rc<Self>, path: &Path) {
-            let mut h = Rc::clone(&self);
-            let mut elems = path
-                .components()
-                .filter_map(|s| match s {
-                    Component::Normal(s) => Some(s.to_owned()),
-                    Component::ParentDir => Some(OsString::from("..")),
-                    _ => None,
-                })
-                .peekable();
-            loop {
-                let cur_elem = elems.next().expect("empty file path");
-                if cur_elem == ".." {
-                    if let Some(parent) = h.parent.upgrade() {
-                        h = parent;
-                    }
-                    continue;
-                }
-                if elems.peek().is_none() {
-                    h.elems.borrow_mut().insert(cur_elem);
-                    break;
-                } else {
-                    let entry = Rc::clone(
-                        h.children
-                            .borrow_mut()
-                            .entry(cur_elem.clone())
-                            .or_insert_with(|| Rc::new(Self::with_parent(cur_elem, &h))),
-                    );
-                    h = entry;
-                }
-            }
+/// A piece of one of the shared artifacts for documentation (search index, sources, alias list, etc.)
+///
+/// Merged at a user specified time and written to the `doc/` directory
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(transparent)]
+struct Part<T, U> {
+    #[serde(skip)]
+    _artifact: PhantomData<T>,
+    item: U,
+}
+
+impl<T, U: fmt::Display> fmt::Display for Part<T, U> {
+    /// Writes serialized JSON
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.item)
+    }
+}
+
+/// Wrapper trait for `Part<T, U>`
+trait CciPart: Sized + fmt::Display + DeserializeOwned + 'static {
+    /// Identifies the file format of the cross-crate information
+    type FileFormat: sorted_template::FileFormat;
+    fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self>;
+}
+
+#[derive(Serialize, Deserialize, Clone, Default, Debug)]
+struct SearchIndex;
+type SearchIndexPart = Part<SearchIndex, EscapedJson>;
+impl CciPart for SearchIndexPart {
+    type FileFormat = sorted_template::Js;
+    fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
+        &crate_info.search_index_js
+    }
+}
+
+impl SearchIndexPart {
+    fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
+        SortedTemplate::from_before_after(
+            r"var searchIndex = new Map(JSON.parse('[",
+            r"]'));
+if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
+else if (window.initSearch) window.initSearch(searchIndex);",
+        )
+    }
+
+    fn get(
+        search_index: OrderedJson,
+        resource_suffix: &str,
+    ) -> Result<PartsAndLocations<Self>, Error> {
+        let path = suffix_path("search-index.js", resource_suffix);
+        let search_index = EscapedJson::from(search_index);
+        Ok(PartsAndLocations::with(path, search_index))
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone, Default, Debug)]
+struct AllCrates;
+type AllCratesPart = Part<AllCrates, OrderedJson>;
+impl CciPart for AllCratesPart {
+    type FileFormat = sorted_template::Js;
+    fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
+        &crate_info.all_crates
+    }
+}
+
+impl AllCratesPart {
+    fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
+        SortedTemplate::from_before_after("window.ALL_CRATES = [", "];")
+    }
+
+    fn get(crate_name_json: OrderedJson) -> Result<PartsAndLocations<Self>, Error> {
+        // external hack_get_external_crate_names not needed here, because
+        // there's no way that we write the search index but not crates.js
+        let path = PathBuf::from("crates.js");
+        Ok(PartsAndLocations::with(path, crate_name_json))
+    }
+}
+
+/// Reads `crates.js`, which seems like the best
+/// place to obtain the list of externally documented crates if the index
+/// page was disabled when documenting the deps.
+///
+/// This is to match the current behavior of rustdoc, which allows you to get all crates
+/// on the index page, even if --enable-index-page is only passed to the last crate.
+fn hack_get_external_crate_names(doc_root: &Path) -> Result<Vec<String>, Error> {
+    let path = doc_root.join("crates.js");
+    let Ok(content) = fs::read_to_string(&path) else {
+        // they didn't emit invocation specific, so we just say there were no crates
+        return Ok(Vec::default());
+    };
+    // this is only run once so it's fine not to cache it
+    // !dot_matches_new_line: all crates on same line. greedy: match last bracket
+    let regex = Regex::new(r"\[.*\]").unwrap();
+    let Some(content) = regex.find(&content) else {
+        return Err(Error::new("could not find crates list in crates.js", path));
+    };
+    let content: Vec<String> = try_err!(serde_json::from_str(content.as_str()), &path);
+    Ok(content)
+}
+
+#[derive(Serialize, Deserialize, Clone, Default, Debug)]
+struct CratesIndex;
+type CratesIndexPart = Part<CratesIndex, String>;
+impl CciPart for CratesIndexPart {
+    type FileFormat = sorted_template::Html;
+    fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
+        &crate_info.crates_index
+    }
+}
+
+impl CratesIndexPart {
+    fn blank(cx: &Context<'_>) -> SortedTemplate<<Self as CciPart>::FileFormat> {
+        let page = layout::Page {
+            title: "Index of crates",
+            css_class: "mod sys",
+            root_path: "./",
+            static_root_path: cx.shared.static_root_path.as_deref(),
+            description: "List of crates",
+            resource_suffix: &cx.shared.resource_suffix,
+            rust_logo: true,
+        };
+        let layout = &cx.shared.layout;
+        let style_files = &cx.shared.style_files;
+        const DELIMITER: &str = "\u{FFFC}"; // users are being naughty if they have this
+        let content =
+            format!("<h1>List of all crates</h1><ul class=\"all-items\">{DELIMITER}</ul>");
+        let template = layout::render(layout, &page, "", content, &style_files);
+        match SortedTemplate::from_template(&template, DELIMITER) {
+            Ok(template) => template,
+            Err(e) => panic!(
+                "Object Replacement Character (U+FFFC) should not appear in the --index-page: {e}"
+            ),
         }
     }
 
-    if cx.include_sources {
-        let hierarchy = Rc::new(Hierarchy::default());
-        for source in cx
-            .shared
-            .local_sources
-            .iter()
-            .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok())
-        {
-            hierarchy.add_path(source);
+    /// Might return parts that are duplicate with ones in prexisting index.html
+    fn get(crate_name: &str, external_crates: &[String]) -> Result<PartsAndLocations<Self>, Error> {
+        let mut ret = PartsAndLocations::default();
+        let path = PathBuf::from("index.html");
+        for crate_name in external_crates.iter().map(|s| s.as_str()).chain(once(crate_name)) {
+            let part = format!(
+                "<li><a href=\"{trailing_slash}index.html\">{crate_name}</a></li>",
+                trailing_slash = ensure_trailing_slash(crate_name),
+            );
+            ret.push(path.clone(), part);
         }
-        let hierarchy = Rc::try_unwrap(hierarchy).unwrap();
-        let dst = cx.dst.join(&format!("src-files{}.js", cx.shared.resource_suffix));
-        let make_sources = || {
-            let (mut all_sources, _krates) =
-                try_err!(collect_json(&dst, krate.name(cx.tcx()).as_str()), &dst);
-            all_sources.push(format!(
-                r#"["{}",{}]"#,
-                &krate.name(cx.tcx()),
-                hierarchy
-                    .to_json_string()
-                    // All these `replace` calls are because we have to go through JS string for JSON content.
-                    .replace('\\', r"\\")
-                    .replace('\'', r"\'")
-                    // We need to escape double quotes for the JSON.
-                    .replace("\\\"", "\\\\\"")
-            ));
-            all_sources.sort();
-            // This needs to be `var`, not `const`.
-            // This variable needs declared in the current global scope so that if
-            // src-script.js loads first, it can pick it up.
-            let mut v = String::from("var srcIndex = new Map(JSON.parse('[\\\n");
-            v.push_str(&all_sources.join(",\\\n"));
-            v.push_str("\\\n]'));\ncreateSrcSidebar();\n");
-            Ok(v.into_bytes())
-        };
-        write_invocation_specific("src-files.js", &make_sources)?;
+        Ok(ret)
     }
+}
 
-    // Update the search index and crate list.
-    let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix));
-    let (mut all_indexes, mut krates) =
-        try_err!(collect_json(&dst, krate.name(cx.tcx()).as_str()), &dst);
-    all_indexes.push(search_index.index);
-    krates.push(krate.name(cx.tcx()).to_string());
-    krates.sort();
+#[derive(Serialize, Deserialize, Clone, Default, Debug)]
+struct Sources;
+type SourcesPart = Part<Sources, EscapedJson>;
+impl CciPart for SourcesPart {
+    type FileFormat = sorted_template::Js;
+    fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
+        &crate_info.src_files_js
+    }
+}
 
-    // Sort the indexes by crate so the file will be generated identically even
-    // with rustdoc running in parallel.
-    all_indexes.sort();
-    write_invocation_specific("search-index.js", &|| {
+impl SourcesPart {
+    fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
         // This needs to be `var`, not `const`.
         // This variable needs declared in the current global scope so that if
-        // search.js loads first, it can pick it up.
-        let mut v = String::from("var searchIndex = new Map(JSON.parse('[\\\n");
-        v.push_str(&all_indexes.join(",\\\n"));
-        v.push_str(
-            r#"\
-]'));
-if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
-else if (window.initSearch) window.initSearch(searchIndex);
-"#,
-        );
-        Ok(v.into_bytes())
-    })?;
-
-    let search_desc_dir = cx.dst.join(format!("search.desc/{krate}", krate = krate.name(cx.tcx())));
-    if Path::new(&search_desc_dir).exists() {
-        try_err!(std::fs::remove_dir_all(&search_desc_dir), &search_desc_dir);
-    }
-    try_err!(std::fs::create_dir_all(&search_desc_dir), &search_desc_dir);
-    let kratename = krate.name(cx.tcx()).to_string();
-    for (i, (_, data)) in search_index.desc.into_iter().enumerate() {
-        let output_filename = static_files::suffix_path(
-            &format!("{kratename}-desc-{i}-.js"),
-            &cx.shared.resource_suffix,
-        );
-        let path = search_desc_dir.join(output_filename);
-        try_err!(
-            std::fs::write(
-                &path,
-                &format!(
-                    r##"searchState.loadedDescShard({kratename}, {i}, {data})"##,
-                    kratename = serde_json::to_string(&kratename).unwrap(),
-                    data = serde_json::to_string(&data).unwrap(),
-                )
-                .into_bytes()
-            ),
-            &path
-        );
+        // src-script.js loads first, it can pick it up.
+        SortedTemplate::from_before_after(
+            r"var srcIndex = new Map(JSON.parse('[",
+            r"]'));
+createSrcSidebar();",
+        )
     }
 
-    write_invocation_specific("crates.js", &|| {
-        let krates = krates.iter().map(|k| format!("\"{k}\"")).join(",");
-        Ok(format!("window.ALL_CRATES = [{krates}];").into_bytes())
-    })?;
+    fn get(cx: &Context<'_>, crate_name: &OrderedJson) -> Result<PartsAndLocations<Self>, Error> {
+        let hierarchy = Rc::new(Hierarchy::default());
+        cx.shared
+            .local_sources
+            .iter()
+            .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok())
+            .for_each(|source| hierarchy.add_path(source));
+        let path = suffix_path("src-files.js", &cx.shared.resource_suffix);
+        let hierarchy = hierarchy.to_json_string();
+        let part = OrderedJson::array_unsorted([crate_name, &hierarchy]);
+        let part = EscapedJson::from(part);
+        Ok(PartsAndLocations::with(path, part))
+    }
+}
 
-    if options.enable_index_page {
-        if let Some(index_page) = options.index_page.clone() {
-            let mut md_opts = options.clone();
-            md_opts.output = cx.dst.clone();
-            md_opts.external_html = (*cx.shared).layout.external_html.clone();
+/// Source files directory tree
+#[derive(Debug, Default)]
+struct Hierarchy {
+    parent: Weak<Self>,
+    elem: OsString,
+    children: RefCell<FxHashMap<OsString, Rc<Self>>>,
+    elems: RefCell<FxHashSet<OsString>>,
+}
 
-            crate::markdown::render(&index_page, md_opts, cx.shared.edition())
-                .map_err(|e| Error::new(e, &index_page))?;
-        } else {
-            let shared = Rc::clone(&cx.shared);
-            let dst = cx.dst.join("index.html");
-            let page = layout::Page {
-                title: "Index of crates",
-                css_class: "mod sys",
-                root_path: "./",
-                static_root_path: shared.static_root_path.as_deref(),
-                description: "List of crates",
-                resource_suffix: &shared.resource_suffix,
-                rust_logo: true,
-            };
+impl Hierarchy {
+    fn with_parent(elem: OsString, parent: &Rc<Self>) -> Self {
+        Self { elem, parent: Rc::downgrade(parent), ..Self::default() }
+    }
 
-            let content = format!(
-                "<h1>List of all crates</h1><ul class=\"all-items\">{}</ul>",
-                krates.iter().format_with("", |k, f| {
-                    f(&format_args!(
-                        "<li><a href=\"{trailing_slash}index.html\">{k}</a></li>",
-                        trailing_slash = ensure_trailing_slash(k),
-                    ))
-                })
-            );
-            let v = layout::render(&shared.layout, &page, "", content, &shared.style_files);
-            shared.fs.write(dst, v)?;
+    fn to_json_string(&self) -> OrderedJson {
+        let subs = self.children.borrow();
+        let files = self.elems.borrow();
+        let name = OrderedJson::serialize(self.elem.to_str().expect("invalid osstring conversion"))
+            .unwrap();
+        let mut out = Vec::from([name]);
+        if !subs.is_empty() || !files.is_empty() {
+            let subs = subs.iter().map(|(_, s)| s.to_json_string());
+            out.push(OrderedJson::array_sorted(subs));
+        }
+        if !files.is_empty() {
+            let files = files
+                .iter()
+                .map(|s| OrderedJson::serialize(s.to_str().expect("invalid osstring")).unwrap());
+            out.push(OrderedJson::array_sorted(files));
         }
+        OrderedJson::array_unsorted(out)
     }
 
-    let cloned_shared = Rc::clone(&cx.shared);
-    let cache = &cloned_shared.cache;
-
-    // Collect the list of aliased types and their aliases.
-    // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
-    //
-    // The clean AST has type aliases that point at their types, but
-    // this visitor works to reverse that: `aliased_types` is a map
-    // from target to the aliases that reference it, and each one
-    // will generate one file.
-    struct TypeImplCollector<'cx, 'cache> {
-        // Map from DefId-of-aliased-type to its data.
-        aliased_types: IndexMap<DefId, AliasedType<'cache>>,
-        visited_aliases: FxHashSet<DefId>,
-        cache: &'cache Cache,
-        cx: &'cache mut Context<'cx>,
-    }
-    // Data for an aliased type.
-    //
-    // In the final file, the format will be roughly:
-    //
-    // ```json
-    // // type.impl/CRATE/TYPENAME.js
-    // JSONP(
-    // "CRATE": [
-    //   ["IMPL1 HTML", "ALIAS1", "ALIAS2", ...],
-    //   ["IMPL2 HTML", "ALIAS3", "ALIAS4", ...],
-    //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ struct AliasedType
-    //   ...
-    // ]
-    // )
-    // ```
-    struct AliasedType<'cache> {
-        // This is used to generate the actual filename of this aliased type.
-        target_fqp: &'cache [Symbol],
-        target_type: ItemType,
-        // This is the data stored inside the file.
-        // ItemId is used to deduplicate impls.
-        impl_: IndexMap<ItemId, AliasedTypeImpl<'cache>>,
-    }
-    // The `impl_` contains data that's used to figure out if an alias will work,
-    // and to generate the HTML at the end.
-    //
-    // The `type_aliases` list is built up with each type alias that matches.
-    struct AliasedTypeImpl<'cache> {
-        impl_: &'cache Impl,
-        type_aliases: Vec<(&'cache [Symbol], Item)>,
-    }
-    impl<'cx, 'cache> DocVisitor for TypeImplCollector<'cx, 'cache> {
-        fn visit_item(&mut self, it: &Item) {
-            self.visit_item_recur(it);
-            let cache = self.cache;
-            let ItemKind::TypeAliasItem(ref t) = *it.kind else { return };
-            let Some(self_did) = it.item_id.as_def_id() else { return };
-            if !self.visited_aliases.insert(self_did) {
-                return;
-            }
-            let Some(target_did) = t.type_.def_id(cache) else { return };
-            let get_extern = { || cache.external_paths.get(&target_did) };
-            let Some(&(ref target_fqp, target_type)) =
-                cache.paths.get(&target_did).or_else(get_extern)
-            else {
-                return;
-            };
-            let aliased_type = self.aliased_types.entry(target_did).or_insert_with(|| {
-                let impl_ = cache
-                    .impls
-                    .get(&target_did)
-                    .map(|v| &v[..])
-                    .unwrap_or_default()
-                    .iter()
-                    .map(|impl_| {
-                        (
-                            impl_.impl_item.item_id,
-                            AliasedTypeImpl { impl_, type_aliases: Vec::new() },
-                        )
-                    })
-                    .collect();
-                AliasedType { target_fqp: &target_fqp[..], target_type, impl_ }
-            });
-            let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) };
-            let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else {
-                return;
-            };
-            let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder();
-            // Exclude impls that are directly on this type. They're already in the HTML.
-            // Some inlining scenarios can cause there to be two versions of the same
-            // impl: one on the type alias and one on the underlying target type.
-            let mut seen_impls: FxHashSet<ItemId> = cache
-                .impls
-                .get(&self_did)
-                .map(|s| &s[..])
-                .unwrap_or_default()
-                .iter()
-                .map(|i| i.impl_item.item_id)
-                .collect();
-            for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ {
-                // Only include this impl if it actually unifies with this alias.
-                // Synthetic impls are not included; those are also included in the HTML.
-                //
-                // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this
-                // to use type unification.
-                // Be aware of `tests/rustdoc/type-alias/deeply-nested-112515.rs` which might regress.
-                let Some(impl_did) = impl_item_id.as_def_id() else { continue };
-                let for_ty = self.cx.tcx().type_of(impl_did).skip_binder();
-                let reject_cx = DeepRejectCtxt::new(self.cx.tcx(), TreatParams::AsCandidateKey);
-                if !reject_cx.types_may_unify(aliased_ty, for_ty) {
-                    continue;
-                }
-                // Avoid duplicates
-                if !seen_impls.insert(*impl_item_id) {
-                    continue;
+    fn add_path(self: &Rc<Self>, path: &Path) {
+        let mut h = Rc::clone(&self);
+        let mut elems = path
+            .components()
+            .filter_map(|s| match s {
+                Component::Normal(s) => Some(s.to_owned()),
+                Component::ParentDir => Some(OsString::from("..")),
+                _ => None,
+            })
+            .peekable();
+        loop {
+            let cur_elem = elems.next().expect("empty file path");
+            if cur_elem == ".." {
+                if let Some(parent) = h.parent.upgrade() {
+                    h = parent;
                 }
-                // This impl was not found in the set of rejected impls
-                aliased_type_impl.type_aliases.push((&self_fqp[..], it.clone()));
+                continue;
             }
-        }
-    }
-    let mut type_impl_collector = TypeImplCollector {
-        aliased_types: IndexMap::default(),
-        visited_aliases: FxHashSet::default(),
-        cache,
-        cx,
-    };
-    DocVisitor::visit_crate(&mut type_impl_collector, &krate);
-    // Final serialized form of the alias impl
-    struct AliasSerializableImpl {
-        text: String,
-        trait_: Option<String>,
-        aliases: Vec<String>,
-    }
-    impl Serialize for AliasSerializableImpl {
-        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-        where
-            S: Serializer,
-        {
-            let mut seq = serializer.serialize_seq(None)?;
-            seq.serialize_element(&self.text)?;
-            if let Some(trait_) = &self.trait_ {
-                seq.serialize_element(trait_)?;
+            if elems.peek().is_none() {
+                h.elems.borrow_mut().insert(cur_elem);
+                break;
             } else {
-                seq.serialize_element(&0)?;
-            }
-            for type_ in &self.aliases {
-                seq.serialize_element(type_)?;
+                let entry = Rc::clone(
+                    h.children
+                        .borrow_mut()
+                        .entry(cur_elem.clone())
+                        .or_insert_with(|| Rc::new(Self::with_parent(cur_elem, &h))),
+                );
+                h = entry;
             }
-            seq.end()
         }
     }
-    let cx = type_impl_collector.cx;
-    let dst = cx.dst.join("type.impl");
-    let aliased_types = type_impl_collector.aliased_types;
-    for aliased_type in aliased_types.values() {
-        let impls = aliased_type
-            .impl_
-            .values()
-            .flat_map(|AliasedTypeImpl { impl_, type_aliases }| {
-                let mut ret = Vec::new();
-                let trait_ = impl_
-                    .inner_impl()
-                    .trait_
-                    .as_ref()
-                    .map(|trait_| format!("{:#}", trait_.print(cx)));
-                // render_impl will filter out "impossible-to-call" methods
-                // to make that functionality work here, it needs to be called with
-                // each type alias, and if it gives a different result, split the impl
-                for &(type_alias_fqp, ref type_alias_item) in type_aliases {
-                    let mut buf = Buffer::html();
-                    cx.id_map = Default::default();
-                    cx.deref_id_map = Default::default();
-                    let target_did = impl_
+}
+
+#[derive(Serialize, Deserialize, Clone, Default, Debug)]
+struct TypeAlias;
+type TypeAliasPart = Part<TypeAlias, OrderedJson>;
+impl CciPart for TypeAliasPart {
+    type FileFormat = sorted_template::Js;
+    fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
+        &crate_info.type_impl
+    }
+}
+
+impl TypeAliasPart {
+    fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
+        SortedTemplate::from_before_after(
+            r"(function() {
+    var type_impls = Object.fromEntries([",
+            r"]);
+    if (window.register_type_impls) {
+        window.register_type_impls(type_impls);
+    } else {
+        window.pending_type_impls = type_impls;
+    }
+})()",
+        )
+    }
+
+    fn get(
+        cx: &mut Context<'_>,
+        krate: &Crate,
+        crate_name_json: &OrderedJson,
+    ) -> Result<PartsAndLocations<Self>, Error> {
+        let cache = &Rc::clone(&cx.shared).cache;
+        let mut path_parts = PartsAndLocations::default();
+
+        let mut type_impl_collector = TypeImplCollector {
+            aliased_types: IndexMap::default(),
+            visited_aliases: FxHashSet::default(),
+            cache,
+            cx,
+        };
+        DocVisitor::visit_crate(&mut type_impl_collector, &krate);
+        let cx = type_impl_collector.cx;
+        let aliased_types = type_impl_collector.aliased_types;
+        for aliased_type in aliased_types.values() {
+            let impls = aliased_type
+                .impl_
+                .values()
+                .flat_map(|AliasedTypeImpl { impl_, type_aliases }| {
+                    let mut ret = Vec::new();
+                    let trait_ = impl_
                         .inner_impl()
                         .trait_
                         .as_ref()
-                        .map(|trait_| trait_.def_id())
-                        .or_else(|| impl_.inner_impl().for_.def_id(cache));
-                    let provided_methods;
-                    let assoc_link = if let Some(target_did) = target_did {
-                        provided_methods = impl_.inner_impl().provided_trait_methods(cx.tcx());
-                        AssocItemLink::GotoSource(ItemId::DefId(target_did), &provided_methods)
-                    } else {
-                        AssocItemLink::Anchor(None)
-                    };
-                    super::render_impl(
-                        &mut buf,
-                        cx,
-                        *impl_,
-                        &type_alias_item,
-                        assoc_link,
-                        RenderMode::Normal,
-                        None,
-                        &[],
-                        ImplRenderingParameters {
-                            show_def_docs: true,
-                            show_default_items: true,
-                            show_non_assoc_items: true,
-                            toggle_open_by_default: true,
-                        },
-                    );
-                    let text = buf.into_inner();
-                    let type_alias_fqp = (*type_alias_fqp).iter().join("::");
-                    if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) {
-                        ret.last_mut()
-                            .expect("already established that ret.last() is Some()")
-                            .aliases
-                            .push(type_alias_fqp);
+                        .map(|trait_| format!("{:#}", trait_.print(cx)));
+                    // render_impl will filter out "impossible-to-call" methods
+                    // to make that functionality work here, it needs to be called with
+                    // each type alias, and if it gives a different result, split the impl
+                    for &(type_alias_fqp, ref type_alias_item) in type_aliases {
+                        let mut buf = Buffer::html();
+                        cx.id_map = Default::default();
+                        cx.deref_id_map = Default::default();
+                        let target_did = impl_
+                            .inner_impl()
+                            .trait_
+                            .as_ref()
+                            .map(|trait_| trait_.def_id())
+                            .or_else(|| impl_.inner_impl().for_.def_id(cache));
+                        let provided_methods;
+                        let assoc_link = if let Some(target_did) = target_did {
+                            provided_methods = impl_.inner_impl().provided_trait_methods(cx.tcx());
+                            AssocItemLink::GotoSource(ItemId::DefId(target_did), &provided_methods)
+                        } else {
+                            AssocItemLink::Anchor(None)
+                        };
+                        super::render_impl(
+                            &mut buf,
+                            cx,
+                            *impl_,
+                            &type_alias_item,
+                            assoc_link,
+                            RenderMode::Normal,
+                            None,
+                            &[],
+                            ImplRenderingParameters {
+                                show_def_docs: true,
+                                show_default_items: true,
+                                show_non_assoc_items: true,
+                                toggle_open_by_default: true,
+                            },
+                        );
+                        let text = buf.into_inner();
+                        let type_alias_fqp = (*type_alias_fqp).iter().join("::");
+                        if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) {
+                            ret.last_mut()
+                                .expect("already established that ret.last() is Some()")
+                                .aliases
+                                .push(type_alias_fqp);
+                        } else {
+                            ret.push(AliasSerializableImpl {
+                                text,
+                                trait_: trait_.clone(),
+                                aliases: vec![type_alias_fqp],
+                            })
+                        }
+                    }
+                    ret
+                })
+                .collect::<Vec<_>>();
+
+            let mut path = PathBuf::from("type.impl");
+            for component in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] {
+                path.push(component.as_str());
+            }
+            let aliased_item_type = aliased_type.target_type;
+            path.push(&format!(
+                "{aliased_item_type}.{}.js",
+                aliased_type.target_fqp[aliased_type.target_fqp.len() - 1]
+            ));
+
+            let part = OrderedJson::array_sorted(
+                impls.iter().map(OrderedJson::serialize).collect::<Result<Vec<_>, _>>().unwrap(),
+            );
+            path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
+        }
+        Ok(path_parts)
+    }
+}
+
+#[derive(Serialize, Deserialize, Clone, Default, Debug)]
+struct TraitAlias;
+type TraitAliasPart = Part<TraitAlias, OrderedJson>;
+impl CciPart for TraitAliasPart {
+    type FileFormat = sorted_template::Js;
+    fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
+        &crate_info.trait_impl
+    }
+}
+
+impl TraitAliasPart {
+    fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
+        SortedTemplate::from_before_after(
+            r"(function() {
+    var implementors = Object.fromEntries([",
+            r"]);
+    if (window.register_implementors) {
+        window.register_implementors(implementors);
+    } else {
+        window.pending_implementors = implementors;
+    }
+})()",
+        )
+    }
+
+    fn get(
+        cx: &mut Context<'_>,
+        crate_name_json: &OrderedJson,
+    ) -> Result<PartsAndLocations<Self>, Error> {
+        let cache = &cx.shared.cache;
+        let mut path_parts = PartsAndLocations::default();
+        // Update the list of all implementors for traits
+        // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
+        for (&did, imps) in &cache.implementors {
+            // Private modules can leak through to this phase of rustdoc, which
+            // could contain implementations for otherwise private types. In some
+            // rare cases we could find an implementation for an item which wasn't
+            // indexed, so we just skip this step in that case.
+            //
+            // FIXME: this is a vague explanation for why this can't be a `get`, in
+            //        theory it should be...
+            let (remote_path, remote_item_type) = match cache.exact_paths.get(&did) {
+                Some(p) => match cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) {
+                    Some((_, t)) => (p, t),
+                    None => continue,
+                },
+                None => match cache.external_paths.get(&did) {
+                    Some((p, t)) => (p, t),
+                    None => continue,
+                },
+            };
+
+            let implementors = imps
+                .iter()
+                .filter_map(|imp| {
+                    // If the trait and implementation are in the same crate, then
+                    // there's no need to emit information about it (there's inlining
+                    // going on). If they're in different crates then the crate defining
+                    // the trait will be interested in our implementation.
+                    //
+                    // If the implementation is from another crate then that crate
+                    // should add it.
+                    if imp.impl_item.item_id.krate() == did.krate
+                        || !imp.impl_item.item_id.is_local()
+                    {
+                        None
                     } else {
-                        ret.push(AliasSerializableImpl {
-                            text,
-                            trait_: trait_.clone(),
-                            aliases: vec![type_alias_fqp],
+                        Some(Implementor {
+                            text: imp.inner_impl().print(false, cx).to_string(),
+                            synthetic: imp.inner_impl().kind.is_auto(),
+                            types: collect_paths_for_type(imp.inner_impl().for_.clone(), cache),
                         })
                     }
-                }
-                ret
-            })
-            .collect::<Vec<_>>();
+                })
+                .collect::<Vec<_>>();
 
-        // FIXME: this fixes only rustdoc part of instability of trait impls
-        // for js files, see #120371
-        // Manually collect to string and sort to make list not depend on order
-        let mut impls = impls
-            .iter()
-            .map(|i| serde_json::to_string(i).expect("failed serde conversion"))
-            .collect::<Vec<_>>();
-        impls.sort();
+            // Only create a js file if we have impls to add to it. If the trait is
+            // documented locally though we always create the file to avoid dead
+            // links.
+            if implementors.is_empty() && !cache.paths.contains_key(&did) {
+                continue;
+            }
 
-        let impls = format!(r#""{}":[{}]"#, krate.name(cx.tcx()), impls.join(","));
+            let mut path = PathBuf::from("trait.impl");
+            for component in &remote_path[..remote_path.len() - 1] {
+                path.push(component.as_str());
+            }
+            path.push(&format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
 
-        let mut mydst = dst.clone();
-        for part in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] {
-            mydst.push(part.to_string());
+            let part = OrderedJson::array_sorted(
+                implementors
+                    .iter()
+                    .map(OrderedJson::serialize)
+                    .collect::<Result<Vec<_>, _>>()
+                    .unwrap(),
+            );
+            path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
         }
-        cx.shared.ensure_dir(&mydst)?;
-        let aliased_item_type = aliased_type.target_type;
-        mydst.push(&format!(
-            "{aliased_item_type}.{}.js",
-            aliased_type.target_fqp[aliased_type.target_fqp.len() - 1]
-        ));
-
-        let (mut all_impls, _) = try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst);
-        all_impls.push(impls);
-        // Sort the implementors by crate so the file will be generated
-        // identically even with rustdoc running in parallel.
-        all_impls.sort();
-
-        let mut v = String::from("(function() {var type_impls = {\n");
-        v.push_str(&all_impls.join(",\n"));
-        v.push_str("\n};");
-        v.push_str(
-            "if (window.register_type_impls) {\
-                 window.register_type_impls(type_impls);\
-             } else {\
-                 window.pending_type_impls = type_impls;\
-             }",
-        );
-        v.push_str("})()");
-        cx.shared.fs.write(mydst, v)?;
-    }
-
-    // Update the list of all implementors for traits
-    // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
-    let dst = cx.dst.join("trait.impl");
-    for (&did, imps) in &cache.implementors {
-        // Private modules can leak through to this phase of rustdoc, which
-        // could contain implementations for otherwise private types. In some
-        // rare cases we could find an implementation for an item which wasn't
-        // indexed, so we just skip this step in that case.
-        //
-        // FIXME: this is a vague explanation for why this can't be a `get`, in
-        //        theory it should be...
-        let (remote_path, remote_item_type) = match cache.exact_paths.get(&did) {
-            Some(p) => match cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) {
-                Some((_, t)) => (p, t),
-                None => continue,
-            },
-            None => match cache.external_paths.get(&did) {
-                Some((p, t)) => (p, t),
-                None => continue,
-            },
-        };
+        Ok(path_parts)
+    }
+}
 
-        struct Implementor {
-            text: String,
-            synthetic: bool,
-            types: Vec<String>,
+struct Implementor {
+    text: String,
+    synthetic: bool,
+    types: Vec<String>,
+}
+
+impl Serialize for Implementor {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut seq = serializer.serialize_seq(None)?;
+        seq.serialize_element(&self.text)?;
+        if self.synthetic {
+            seq.serialize_element(&1)?;
+            seq.serialize_element(&self.types)?;
         }
+        seq.end()
+    }
+}
 
-        impl Serialize for Implementor {
-            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-            where
-                S: Serializer,
-            {
-                let mut seq = serializer.serialize_seq(None)?;
-                seq.serialize_element(&self.text)?;
-                if self.synthetic {
-                    seq.serialize_element(&1)?;
-                    seq.serialize_element(&self.types)?;
-                }
-                seq.end()
+/// Collect the list of aliased types and their aliases.
+/// <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
+///
+/// The clean AST has type aliases that point at their types, but
+/// this visitor works to reverse that: `aliased_types` is a map
+/// from target to the aliases that reference it, and each one
+/// will generate one file.
+struct TypeImplCollector<'cx, 'cache> {
+    /// Map from DefId-of-aliased-type to its data.
+    aliased_types: IndexMap<DefId, AliasedType<'cache>>,
+    visited_aliases: FxHashSet<DefId>,
+    cache: &'cache Cache,
+    cx: &'cache mut Context<'cx>,
+}
+
+/// Data for an aliased type.
+///
+/// In the final file, the format will be roughly:
+///
+/// ```json
+/// // type.impl/CRATE/TYPENAME.js
+/// JSONP(
+/// "CRATE": [
+///   ["IMPL1 HTML", "ALIAS1", "ALIAS2", ...],
+///   ["IMPL2 HTML", "ALIAS3", "ALIAS4", ...],
+///    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ struct AliasedType
+///   ...
+/// ]
+/// )
+/// ```
+struct AliasedType<'cache> {
+    /// This is used to generate the actual filename of this aliased type.
+    target_fqp: &'cache [Symbol],
+    target_type: ItemType,
+    /// This is the data stored inside the file.
+    /// ItemId is used to deduplicate impls.
+    impl_: IndexMap<ItemId, AliasedTypeImpl<'cache>>,
+}
+
+/// The `impl_` contains data that's used to figure out if an alias will work,
+/// and to generate the HTML at the end.
+///
+/// The `type_aliases` list is built up with each type alias that matches.
+struct AliasedTypeImpl<'cache> {
+    impl_: &'cache Impl,
+    type_aliases: Vec<(&'cache [Symbol], Item)>,
+}
+
+impl<'cx, 'cache> DocVisitor for TypeImplCollector<'cx, 'cache> {
+    fn visit_item(&mut self, it: &Item) {
+        self.visit_item_recur(it);
+        let cache = self.cache;
+        let ItemKind::TypeAliasItem(ref t) = *it.kind else { return };
+        let Some(self_did) = it.item_id.as_def_id() else { return };
+        if !self.visited_aliases.insert(self_did) {
+            return;
+        }
+        let Some(target_did) = t.type_.def_id(cache) else { return };
+        let get_extern = { || cache.external_paths.get(&target_did) };
+        let Some(&(ref target_fqp, target_type)) = cache.paths.get(&target_did).or_else(get_extern)
+        else {
+            return;
+        };
+        let aliased_type = self.aliased_types.entry(target_did).or_insert_with(|| {
+            let impl_ = cache
+                .impls
+                .get(&target_did)
+                .map(|v| &v[..])
+                .unwrap_or_default()
+                .iter()
+                .map(|impl_| {
+                    (impl_.impl_item.item_id, AliasedTypeImpl { impl_, type_aliases: Vec::new() })
+                })
+                .collect();
+            AliasedType { target_fqp: &target_fqp[..], target_type, impl_ }
+        });
+        let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) };
+        let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else {
+            return;
+        };
+        let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder();
+        // Exclude impls that are directly on this type. They're already in the HTML.
+        // Some inlining scenarios can cause there to be two versions of the same
+        // impl: one on the type alias and one on the underlying target type.
+        let mut seen_impls: FxHashSet<ItemId> = cache
+            .impls
+            .get(&self_did)
+            .map(|s| &s[..])
+            .unwrap_or_default()
+            .iter()
+            .map(|i| i.impl_item.item_id)
+            .collect();
+        for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ {
+            // Only include this impl if it actually unifies with this alias.
+            // Synthetic impls are not included; those are also included in the HTML.
+            //
+            // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this
+            // to use type unification.
+            // Be aware of `tests/rustdoc/type-alias/deeply-nested-112515.rs` which might regress.
+            let Some(impl_did) = impl_item_id.as_def_id() else { continue };
+            let for_ty = self.cx.tcx().type_of(impl_did).skip_binder();
+            let reject_cx = DeepRejectCtxt::new(self.cx.tcx(), TreatParams::AsCandidateKey);
+            if !reject_cx.types_may_unify(aliased_ty, for_ty) {
+                continue;
+            }
+            // Avoid duplicates
+            if !seen_impls.insert(*impl_item_id) {
+                continue;
             }
+            // This impl was not found in the set of rejected impls
+            aliased_type_impl.type_aliases.push((&self_fqp[..], it.clone()));
         }
+    }
+}
 
-        let implementors = imps
-            .iter()
-            .filter_map(|imp| {
-                // If the trait and implementation are in the same crate, then
-                // there's no need to emit information about it (there's inlining
-                // going on). If they're in different crates then the crate defining
-                // the trait will be interested in our implementation.
-                //
-                // If the implementation is from another crate then that crate
-                // should add it.
-                if imp.impl_item.item_id.krate() == did.krate || !imp.impl_item.item_id.is_local() {
-                    None
-                } else {
-                    Some(Implementor {
-                        text: imp.inner_impl().print(false, cx).to_string(),
-                        synthetic: imp.inner_impl().kind.is_auto(),
-                        types: collect_paths_for_type(imp.inner_impl().for_.clone(), cache),
-                    })
-                }
-            })
-            .collect::<Vec<_>>();
+/// Final serialized form of the alias impl
+struct AliasSerializableImpl {
+    text: String,
+    trait_: Option<String>,
+    aliases: Vec<String>,
+}
 
-        // Only create a js file if we have impls to add to it. If the trait is
-        // documented locally though we always create the file to avoid dead
-        // links.
-        if implementors.is_empty() && !cache.paths.contains_key(&did) {
-            continue;
+impl Serialize for AliasSerializableImpl {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut seq = serializer.serialize_seq(None)?;
+        seq.serialize_element(&self.text)?;
+        if let Some(trait_) = &self.trait_ {
+            seq.serialize_element(trait_)?;
+        } else {
+            seq.serialize_element(&0)?;
+        }
+        for type_ in &self.aliases {
+            seq.serialize_element(type_)?;
         }
+        seq.end()
+    }
+}
 
-        // FIXME: this fixes only rustdoc part of instability of trait impls
-        // for js files, see #120371
-        // Manually collect to string and sort to make list not depend on order
-        let mut implementors = implementors
-            .iter()
-            .map(|i| serde_json::to_string(i).expect("failed serde conversion"))
-            .collect::<Vec<_>>();
-        implementors.sort();
+fn get_path_parts<T: CciPart>(
+    dst: &Path,
+    crates_info: &[CrateInfo],
+) -> FxHashMap<PathBuf, Vec<String>> {
+    let mut templates: FxHashMap<PathBuf, Vec<String>> = FxHashMap::default();
+    crates_info
+        .iter()
+        .map(|crate_info| T::from_crate_info(crate_info).parts.iter())
+        .flatten()
+        .for_each(|(path, part)| {
+            let path = dst.join(&path);
+            let part = part.to_string();
+            templates.entry(path).or_default().push(part);
+        });
+    templates
+}
 
-        let implementors = format!(r#""{}":[{}]"#, krate.name(cx.tcx()), implementors.join(","));
+/// Create all parents
+fn create_parents(path: &Path) -> Result<(), Error> {
+    let parent = path.parent().expect("should not have an empty path here");
+    try_err!(fs::create_dir_all(parent), parent);
+    Ok(())
+}
+
+/// Returns a blank template unless we could find one to append to
+fn read_template_or_blank<F, T: FileFormat>(
+    mut make_blank: F,
+    path: &Path,
+) -> Result<SortedTemplate<T>, Error>
+where
+    F: FnMut() -> SortedTemplate<T>,
+{
+    match fs::read_to_string(&path) {
+        Ok(template) => Ok(try_err!(SortedTemplate::from_str(&template), &path)),
+        Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(make_blank()),
+        Err(e) => Err(Error::new(e, &path)),
+    }
+}
 
-        let mut mydst = dst.clone();
-        for part in &remote_path[..remote_path.len() - 1] {
-            mydst.push(part.to_string());
+/// info from this crate and the --include-info-json'd crates
+fn write_rendered_cci<T: CciPart, F>(
+    mut make_blank: F,
+    dst: &Path,
+    crates_info: &[CrateInfo],
+) -> Result<(), Error>
+where
+    F: FnMut() -> SortedTemplate<T::FileFormat>,
+{
+    // write the merged cci to disk
+    for (path, parts) in get_path_parts::<T>(dst, crates_info) {
+        create_parents(&path)?;
+        // read previous rendered cci from storage, append to them
+        let mut template = read_template_or_blank::<_, T::FileFormat>(&mut make_blank, &path)?;
+        for part in parts {
+            template.append(part);
         }
-        cx.shared.ensure_dir(&mydst)?;
-        mydst.push(&format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
-
-        let (mut all_implementors, _) =
-            try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst);
-        all_implementors.push(implementors);
-        // Sort the implementors by crate so the file will be generated
-        // identically even with rustdoc running in parallel.
-        all_implementors.sort();
-
-        let mut v = String::from("(function() {var implementors = {\n");
-        v.push_str(&all_implementors.join(",\n"));
-        v.push_str("\n};");
-        v.push_str(
-            "if (window.register_implementors) {\
-                 window.register_implementors(implementors);\
-             } else {\
-                 window.pending_implementors = implementors;\
-             }",
-        );
-        v.push_str("})()");
-        cx.shared.fs.write(mydst, v)?;
+        let file = try_err!(File::create(&path), &path);
+        let mut file = BufWriter::new(file);
+        try_err!(write!(file, "{template}"), &path);
+        try_err!(file.flush(), &path);
     }
     Ok(())
 }
+
+#[cfg(test)]
+mod tests;
diff --git a/src/librustdoc/html/render/write_shared/tests.rs b/src/librustdoc/html/render/write_shared/tests.rs
new file mode 100644
index 00000000000..4d1874b7df5
--- /dev/null
+++ b/src/librustdoc/html/render/write_shared/tests.rs
@@ -0,0 +1,207 @@
+use crate::html::render::ordered_json::{EscapedJson, OrderedJson};
+use crate::html::render::sorted_template::{Html, SortedTemplate};
+use crate::html::render::write_shared::*;
+
+#[test]
+fn hack_external_crate_names() {
+    let path = tempfile::TempDir::new().unwrap();
+    let path = path.path();
+    let crates = hack_get_external_crate_names(&path).unwrap();
+    assert!(crates.is_empty());
+    fs::write(path.join("crates.js"), r#"window.ALL_CRATES = ["a","b","c"];"#).unwrap();
+    let crates = hack_get_external_crate_names(&path).unwrap();
+    assert_eq!(crates, ["a".to_string(), "b".to_string(), "c".to_string()]);
+}
+
+fn but_last_line(s: &str) -> &str {
+    let (before, _) = s.rsplit_once("\n").unwrap();
+    before
+}
+
+#[test]
+fn sources_template() {
+    let mut template = SourcesPart::blank();
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r"var srcIndex = new Map(JSON.parse('[]'));
+createSrcSidebar();"
+    );
+    template.append(EscapedJson::from(OrderedJson::serialize("u").unwrap()).to_string());
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r#"var srcIndex = new Map(JSON.parse('["u"]'));
+createSrcSidebar();"#
+    );
+    template.append(EscapedJson::from(OrderedJson::serialize("v").unwrap()).to_string());
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r#"var srcIndex = new Map(JSON.parse('["u","v"]'));
+createSrcSidebar();"#
+    );
+}
+
+#[test]
+fn sources_parts() {
+    let parts =
+        SearchIndexPart::get(OrderedJson::serialize(["foo", "bar"]).unwrap(), "suffix").unwrap();
+    assert_eq!(&parts.parts[0].0, Path::new("search-indexsuffix.js"));
+    assert_eq!(&parts.parts[0].1.to_string(), r#"["foo","bar"]"#);
+}
+
+#[test]
+fn all_crates_template() {
+    let mut template = AllCratesPart::blank();
+    assert_eq!(but_last_line(&template.to_string()), r"window.ALL_CRATES = [];");
+    template.append(EscapedJson::from(OrderedJson::serialize("b").unwrap()).to_string());
+    assert_eq!(but_last_line(&template.to_string()), r#"window.ALL_CRATES = ["b"];"#);
+    template.append(EscapedJson::from(OrderedJson::serialize("a").unwrap()).to_string());
+    assert_eq!(but_last_line(&template.to_string()), r#"window.ALL_CRATES = ["a","b"];"#);
+}
+
+#[test]
+fn all_crates_parts() {
+    let parts = AllCratesPart::get(OrderedJson::serialize("crate").unwrap()).unwrap();
+    assert_eq!(&parts.parts[0].0, Path::new("crates.js"));
+    assert_eq!(&parts.parts[0].1.to_string(), r#""crate""#);
+}
+
+#[test]
+fn search_index_template() {
+    let mut template = SearchIndexPart::blank();
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r"var searchIndex = new Map(JSON.parse('[]'));
+if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
+else if (window.initSearch) window.initSearch(searchIndex);"
+    );
+    template.append(EscapedJson::from(OrderedJson::serialize([1, 2]).unwrap()).to_string());
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r"var searchIndex = new Map(JSON.parse('[[1,2]]'));
+if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
+else if (window.initSearch) window.initSearch(searchIndex);"
+    );
+    template.append(EscapedJson::from(OrderedJson::serialize([4, 3]).unwrap()).to_string());
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r"var searchIndex = new Map(JSON.parse('[[1,2],[4,3]]'));
+if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
+else if (window.initSearch) window.initSearch(searchIndex);"
+    );
+}
+
+#[test]
+fn crates_index_part() {
+    let external_crates = ["bar".to_string(), "baz".to_string()];
+    let mut parts = CratesIndexPart::get("foo", &external_crates).unwrap();
+    parts.parts.sort_by(|a, b| a.1.to_string().cmp(&b.1.to_string()));
+
+    assert_eq!(&parts.parts[0].0, Path::new("index.html"));
+    assert_eq!(&parts.parts[0].1.to_string(), r#"<li><a href="bar/index.html">bar</a></li>"#);
+
+    assert_eq!(&parts.parts[1].0, Path::new("index.html"));
+    assert_eq!(&parts.parts[1].1.to_string(), r#"<li><a href="baz/index.html">baz</a></li>"#);
+
+    assert_eq!(&parts.parts[2].0, Path::new("index.html"));
+    assert_eq!(&parts.parts[2].1.to_string(), r#"<li><a href="foo/index.html">foo</a></li>"#);
+}
+
+#[test]
+fn trait_alias_template() {
+    let mut template = TraitAliasPart::blank();
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r#"(function() {
+    var implementors = Object.fromEntries([]);
+    if (window.register_implementors) {
+        window.register_implementors(implementors);
+    } else {
+        window.pending_implementors = implementors;
+    }
+})()"#,
+    );
+    template.append(OrderedJson::serialize(["a"]).unwrap().to_string());
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r#"(function() {
+    var implementors = Object.fromEntries([["a"]]);
+    if (window.register_implementors) {
+        window.register_implementors(implementors);
+    } else {
+        window.pending_implementors = implementors;
+    }
+})()"#,
+    );
+    template.append(OrderedJson::serialize(["b"]).unwrap().to_string());
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r#"(function() {
+    var implementors = Object.fromEntries([["a"],["b"]]);
+    if (window.register_implementors) {
+        window.register_implementors(implementors);
+    } else {
+        window.pending_implementors = implementors;
+    }
+})()"#,
+    );
+}
+
+#[test]
+fn type_alias_template() {
+    let mut template = TypeAliasPart::blank();
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r#"(function() {
+    var type_impls = Object.fromEntries([]);
+    if (window.register_type_impls) {
+        window.register_type_impls(type_impls);
+    } else {
+        window.pending_type_impls = type_impls;
+    }
+})()"#,
+    );
+    template.append(OrderedJson::serialize(["a"]).unwrap().to_string());
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r#"(function() {
+    var type_impls = Object.fromEntries([["a"]]);
+    if (window.register_type_impls) {
+        window.register_type_impls(type_impls);
+    } else {
+        window.pending_type_impls = type_impls;
+    }
+})()"#,
+    );
+    template.append(OrderedJson::serialize(["b"]).unwrap().to_string());
+    assert_eq!(
+        but_last_line(&template.to_string()),
+        r#"(function() {
+    var type_impls = Object.fromEntries([["a"],["b"]]);
+    if (window.register_type_impls) {
+        window.register_type_impls(type_impls);
+    } else {
+        window.pending_type_impls = type_impls;
+    }
+})()"#,
+    );
+}
+
+#[test]
+fn read_template_test() {
+    let path = tempfile::TempDir::new().unwrap();
+    let path = path.path().join("file.html");
+    let make_blank = || SortedTemplate::<Html>::from_before_after("<div>", "</div>");
+
+    let template = read_template_or_blank(make_blank, &path).unwrap();
+    assert_eq!(but_last_line(&template.to_string()), "<div></div>");
+    fs::write(&path, template.to_string()).unwrap();
+    let mut template = read_template_or_blank(make_blank, &path).unwrap();
+    template.append("<img/>".to_string());
+    fs::write(&path, template.to_string()).unwrap();
+    let mut template = read_template_or_blank(make_blank, &path).unwrap();
+    template.append("<br/>".to_string());
+    fs::write(&path, template.to_string()).unwrap();
+    let template = read_template_or_blank(make_blank, &path).unwrap();
+
+    assert_eq!(but_last_line(&template.to_string()), "<div><br/><img/></div>");
+}
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index eb5a5d935e2..28df8d3f011 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1547,10 +1547,23 @@ instead, we check that it's not a "finger" cursor.
 	margin-left: 24px;
 }
 
+@keyframes targetfadein {
+	from {
+		background-color: var(--main-background-color);
+	}
+	10% {
+		background-color: var(--target-border-color);
+	}
+	to {
+		background-color: var(--target-background-color);
+	}
+}
+
 :target {
 	padding-right: 3px;
 	background-color: var(--target-background-color);
 	border-right: 3px solid var(--target-border-color);
+	animation: 0.65s cubic-bezier(0, 0, 0.1, 1.0) 0.1s targetfadein;
 }
 
 .code-header a.tooltip {
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 3eb27ea087c..be0ec425946 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1393,6 +1393,7 @@ function initSearch(rawSearchIndex) {
          */
         async function sortResults(results, isType, preferredCrate) {
             const userQuery = parsedQuery.userQuery;
+            const casedUserQuery = parsedQuery.original;
             const result_list = [];
             for (const result of results.values()) {
                 result.item = searchIndex[result.id];
@@ -1403,6 +1404,13 @@ function initSearch(rawSearchIndex) {
             result_list.sort((aaa, bbb) => {
                 let a, b;
 
+                // sort by exact case-sensitive match
+                a = (aaa.item.name !== casedUserQuery);
+                b = (bbb.item.name !== casedUserQuery);
+                if (a !== b) {
+                    return a - b;
+                }
+
                 // sort by exact match with regard to the last word (mismatch goes later)
                 a = (aaa.word !== userQuery);
                 b = (bbb.word !== userQuery);
@@ -3546,7 +3554,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             // Used to de-duplicate inlined and re-exported stuff
             const itemReexports = new Map(crateCorpus.r);
             // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
-            const itemParentIdxs = crateCorpus.i;
+            const itemParentIdxDecoder = new VlqHexDecoder(crateCorpus.i, noop => noop);
             // a map Number, string for impl disambiguators
             const implDisambiguator = new Map(crateCorpus.b);
             // an array of [(Number) item type,
@@ -3593,6 +3601,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             // faster analysis operations
             lastPath = "";
             len = itemTypes.length;
+            let lastName = "";
+            let lastWord = "";
             for (let i = 0; i < len; ++i) {
                 const bitIndex = i + 1;
                 if (descIndex >= descShard.len &&
@@ -3608,10 +3618,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                     descIndex = 0;
                     descShardList.push(descShard);
                 }
-                let word = "";
-                if (typeof itemNames[i] === "string") {
-                    word = itemNames[i].toLowerCase();
-                }
+                const name = itemNames[i] === "" ? lastName : itemNames[i];
+                const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase();
                 const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
                 const type = itemFunctionDecoder.next();
                 if (type !== null) {
@@ -3633,15 +3641,16 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 }
                 // This object should have exactly the same set of fields as the "crateRow"
                 // object defined above.
+                const itemParentIdx = itemParentIdxDecoder.next();
                 const row = {
                     crate,
                     ty: itemTypes.charCodeAt(i) - 65, // 65 = "A"
-                    name: itemNames[i],
+                    name,
                     path,
                     descShard,
                     descIndex,
                     exactPath: itemReexports.has(i) ? itemPaths.get(itemReexports.get(i)) : path,
-                    parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
+                    parent: itemParentIdx > 0 ? paths[itemParentIdx - 1] : undefined,
                     type,
                     id,
                     word,
@@ -3655,6 +3664,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 if (!searchIndexEmptyDesc.get(crate).contains(bitIndex)) {
                     descIndex += 1;
                 }
+                lastName = name;
+                lastWord = word;
             }
 
             if (aliases) {
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 5b96529fed7..ab03f620230 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -53,7 +53,7 @@ fn filter_assoc_items_by_name_and_namespace<'a>(
     assoc_items_of: DefId,
     ident: Ident,
     ns: Namespace,
-) -> impl Iterator<Item = &ty::AssocItem> + 'a {
+) -> impl Iterator<Item = &'a ty::AssocItem> + 'a {
     tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name).filter(move |item| {
         item.kind.namespace() == ns && tcx.hygienic_eq(ident, item.ident(tcx), assoc_items_of)
     })
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index ef05befdddc..977c0953336 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -16,9 +16,11 @@ use crate::core::DocContext;
 use crate::html::markdown::{self, RustCodeBlock};
 
 pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
-    if let Some(dox) = &item.opt_doc_value() {
+    if let Some(def_id) = item.item_id.as_local_def_id()
+        && let Some(dox) = &item.opt_doc_value()
+    {
         let sp = item.attr_span(cx.tcx);
-        let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp);
+        let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, def_id, sp);
         for code_block in markdown::rust_code_blocks(dox, &extra) {
             check_rust_syntax(cx, item, dox, code_block);
         }
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index faf42b3aab1..23e298571d5 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -2,7 +2,7 @@
 
 use std::mem;
 
-use rustc_hir::def_id::LocalDefId;
+use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::sym;
 
@@ -145,8 +145,9 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
                 let old = mem::replace(&mut self.update_retained, false);
                 let ret = self.set_is_in_hidden_item_and_fold(true, i);
                 self.update_retained = old;
-                if ret.is_crate() {
-                    // We don't strip the crate, even if it has `#[doc(hidden)]`.
+                if ret.item_id == clean::ItemId::DefId(CRATE_DEF_ID.into()) {
+                    // We don't strip the current crate, even if it has `#[doc(hidden)]`.
+                    debug!("strip_hidden: Not strippping local crate");
                     Some(ret)
                 } else {
                     Some(strip_item(ret))
diff --git a/src/llvm-project b/src/llvm-project
-Subproject ccf4c38bdd73f1a37ec266c73bdaef80e39f8cf
+Subproject 2b259b3c201f939f2ed8d2fb0a0e9b37dd2d132
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject ba8b39413c74d08494f94a7542fe79aa636e166
+Subproject 8f40fc59fb0c8df91c97405785197f3c630304e
diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml
index ce07290d1e1..d9c635df5dc 100644
--- a/src/tools/clippy/.cargo/config.toml
+++ b/src/tools/clippy/.cargo/config.toml
@@ -1,10 +1,10 @@
 [alias]
+bless = "test --config env.RUSTC_BLESS='1'"
 uitest = "test --test compile-test"
-uibless = "test --test compile-test -- -- --bless"
-bless = "test -- -- --bless"
+uibless = "bless --test compile-test"
 dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
 lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml  -- "
-collect-metadata = "test --test dogfood --features internal -- collect_metadata"
+collect-metadata = "test --test compile-test --config env.COLLECT_METADATA='1'"
 
 [build]
 # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh
index 5a59f94ec91..d937661c0f8 100644
--- a/src/tools/clippy/.github/deploy.sh
+++ b/src/tools/clippy/.github/deploy.sh
@@ -10,6 +10,7 @@ mkdir out/master/
 cp util/gh-pages/index.html out/master
 cp util/gh-pages/script.js out/master
 cp util/gh-pages/lints.json out/master
+cp util/gh-pages/style.css out/master
 
 if [[ -n $TAG_NAME ]]; then
   echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it"
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 10e18e84c89..2aa13313fa5 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -136,11 +136,6 @@ jobs:
     - name: Test metadata collection
       run: cargo collect-metadata
 
-    - name: Test lint_configuration.md is up-to-date
-      run: |
-        echo "run \`cargo collect-metadata\` if this fails"
-        git update-index --refresh
-
   integration_build:
     needs: changelog
     runs-on: ubuntu-latest
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index fddc2fd994e..9bc4ad9698d 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -5914,6 +5914,7 @@ Released 2018-09-13
 [`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
 [`to_string_trait_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_trait_impl
 [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
+[`too_long_first_doc_paragraph`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_long_first_doc_paragraph
 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 78409c7a09e..b48b881097f 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -30,8 +30,11 @@ color-print = "0.3.4"
 anstream = "0.6.0"
 
 [dev-dependencies]
+cargo_metadata = "0.18.1"
 ui_test = "0.25"
 regex = "1.5.5"
+serde = { version = "1.0.145", features = ["derive"] }
+serde_json = "1.0.122"
 toml = "0.7.3"
 walkdir = "2.3"
 filetime = "0.2.9"
@@ -41,7 +44,6 @@ itertools = "0.12"
 clippy_utils = { path = "clippy_utils" }
 if_chain = "1.0"
 quote = "1.0.25"
-serde = { version = "1.0.145", features = ["derive"] }
 syn = { version = "2.0", features = ["full"] }
 futures = "0.3"
 parking_lot = "0.12"
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index a71d94daca7..963e02e5c16 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -739,7 +739,7 @@ for some users. Adding a configuration is done in the following steps:
 
 5. Update [Lint Configuration](../lint_configuration.md)
 
-   Run `cargo collect-metadata` to generate documentation changes for the book.
+   Run `cargo bless --test config-metadata` to generate documentation changes for the book.
 
 [`clippy_config::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_config/src/conf.rs
 [`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index e3d550b1466..78348797588 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -1,5 +1,5 @@
 <!--
-This file is generated by `cargo collect-metadata`.
+This file is generated by `cargo bless --test config-metadata`.
 Please use that command to update the file and do not edit it by hand.
 -->
 
@@ -199,7 +199,7 @@ Allowed names below the minimum allowed characters. The value `".."` can be used
 the list to indicate, that the configured values should be appended to the default
 configuration of Clippy. By default, any configuration will replace the default value.
 
-**Default Value:** `["j", "z", "i", "y", "n", "x", "w"]`
+**Default Value:** `["i", "j", "x", "y", "z", "w", "n"]`
 
 ---
 **Affected lints:**
@@ -455,7 +455,7 @@ default configuration of Clippy. By default, any configuration will replace the
 * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
 * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
 
-**Default Value:** `["TiB", "CoreGraphics", "CoffeeScript", "TeX", "Direct2D", "PiB", "DirectX", "NetBSD", "OAuth", "NaN", "OpenType", "WebGL2", "WebTransport", "JavaScript", "OpenSSL", "OpenSSH", "EiB", "PureScript", "OpenAL", "MiB", "WebAssembly", "MinGW", "CoreFoundation", "WebGPU", "ClojureScript", "CamelCase", "OpenDNS", "NaNs", "OpenMP", "GitLab", "KiB", "sRGB", "CoreText", "macOS", "TypeScript", "GiB", "OpenExr", "YCbCr", "OpenTelemetry", "OpenBSD", "FreeBSD", "GPLv2", "PostScript", "WebP", "LaTeX", "TensorFlow", "AccessKit", "TrueType", "OpenStreetMap", "OpenGL", "DevOps", "OCaml", "WebRTC", "WebGL", "BibLaTeX", "GitHub", "GraphQL", "iOS", "Direct3D", "BibTeX", "DirectWrite", "GPLv3", "IPv6", "WebSocket", "IPv4", "ECMAScript"]`
+**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "AccessKit", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
 
 ---
 **Affected lints:**
@@ -949,5 +949,3 @@ Whether to also emit warnings for unsafe blocks with metavariable expansions in
 ---
 **Affected lints:**
 * [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe)
-
-
diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml
index d5b28e25323..5c4e0761dbc 100644
--- a/src/tools/clippy/clippy_config/Cargo.toml
+++ b/src/tools/clippy/clippy_config/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2021"
 
 [dependencies]
 itertools = "0.12"
-rustc-semver = "1.1"
 serde = { version = "1.0", features = ["derive"] }
 toml = "0.7.3"
 
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index 4c2a8255d6b..a6f1b958bfb 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -1,7 +1,6 @@
 use crate::msrvs::Msrv;
 use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename};
 use crate::ClippyConfiguration;
-use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_session::Session;
 use rustc_span::edit_distance::edit_distance;
@@ -218,7 +217,7 @@ macro_rules! define_Conf {
 define_Conf! {
     /// Which crates to allow absolute paths from
     #[lints(absolute_paths)]
-    absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default(),
+    absolute_paths_allowed_crates: Vec<String> = Vec::new(),
     /// The maximum number of segments a path can have before being linted, anything above this will
     /// be linted.
     #[lints(absolute_paths)]
@@ -280,12 +279,12 @@ define_Conf! {
     allowed_dotfiles: Vec<String> = Vec::default(),
     /// A list of crate names to allow duplicates of
     #[lints(multiple_crate_versions)]
-    allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default(),
+    allowed_duplicate_crates: Vec<String> = Vec::new(),
     /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of
     /// the list to indicate, that the configured values should be appended to the default
     /// configuration of Clippy. By default, any configuration will replace the default value.
     #[lints(min_ident_chars)]
-    allowed_idents_below_min_chars: FxHashSet<String> =
+    allowed_idents_below_min_chars: Vec<String> =
         DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(),
     /// List of prefixes to allow when determining whether an item's name ends with the module's name.
     /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`),
@@ -323,7 +322,7 @@ define_Conf! {
     /// 2. Paths with any segment that containing the word 'prelude'
     /// are already allowed by default.
     #[lints(wildcard_imports)]
-    allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default(),
+    allowed_wildcard_imports: Vec<String> = Vec::new(),
     /// Suppress checking of the passed type names in all types of operations.
     ///
     /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead.
@@ -355,7 +354,7 @@ define_Conf! {
     /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
     /// ```
     #[lints(arithmetic_side_effects)]
-    arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default(),
+    arithmetic_side_effects_allowed_binary: Vec<(String, String)> = <_>::default(),
     /// Suppress checking of the passed type names in unary operations like "negation" (`-`).
     ///
     /// #### Example
@@ -431,7 +430,7 @@ define_Conf! {
     /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
     /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
     #[lints(doc_markdown)]
-    doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(),
+    doc_valid_idents: Vec<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(),
     /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
     #[lints(non_send_fields_in_send_ty)]
     enable_raw_pointer_heuristic_for_send: bool = true,
@@ -706,12 +705,12 @@ fn deserialize(file: &SourceFile) -> TryConf {
                 DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS,
             );
             // TODO: THIS SHOULD BE TESTED, this comment will be gone soon
-            if conf.conf.allowed_idents_below_min_chars.contains("..") {
+            if conf.conf.allowed_idents_below_min_chars.iter().any(|e| e == "..") {
                 conf.conf
                     .allowed_idents_below_min_chars
                     .extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string));
             }
-            if conf.conf.doc_valid_idents.contains("..") {
+            if conf.conf.doc_valid_idents.iter().any(|e| e == "..") {
                 conf.conf
                     .doc_valid_idents
                     .extend(DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string));
@@ -890,14 +889,14 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
 
 #[cfg(test)]
 mod tests {
-    use rustc_data_structures::fx::{FxHashMap, FxHashSet};
     use serde::de::IgnoredAny;
+    use std::collections::{HashMap, HashSet};
     use std::fs;
     use walkdir::WalkDir;
 
     #[test]
     fn configs_are_tested() {
-        let mut names: FxHashSet<String> = crate::get_configuration_metadata()
+        let mut names: HashSet<String> = crate::get_configuration_metadata()
             .into_iter()
             .map(|meta| meta.name.replace('_', "-"))
             .collect();
@@ -910,7 +909,7 @@ mod tests {
         for entry in toml_files {
             let file = fs::read_to_string(entry.path()).unwrap();
             #[allow(clippy::zero_sized_map_values)]
-            if let Ok(map) = toml::from_str::<FxHashMap<String, IgnoredAny>>(&file) {
+            if let Ok(map) = toml::from_str::<HashMap<String, IgnoredAny>>(&file) {
                 for name in map.keys() {
                     names.remove(name.as_str());
                 }
diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs
index d2246f12029..ac838cc81d2 100644
--- a/src/tools/clippy/clippy_config/src/lib.rs
+++ b/src/tools/clippy/clippy_config/src/lib.rs
@@ -14,7 +14,7 @@
 )]
 
 extern crate rustc_ast;
-extern crate rustc_data_structures;
+extern crate rustc_attr;
 #[allow(unused_extern_crates)]
 extern crate rustc_driver;
 extern crate rustc_errors;
diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs
index fc56ac51796..0e8215aa8e3 100644
--- a/src/tools/clippy/clippy_config/src/msrvs.rs
+++ b/src/tools/clippy/clippy_config/src/msrvs.rs
@@ -1,6 +1,6 @@
 use rustc_ast::Attribute;
-use rustc_semver::RustcVersion;
-use rustc_session::Session;
+use rustc_attr::parse_version;
+use rustc_session::{RustcVersion, Session};
 use rustc_span::{sym, Symbol};
 use serde::Deserialize;
 use std::fmt;
@@ -10,7 +10,7 @@ macro_rules! msrv_aliases {
         $($name:ident),* $(,)?
     })*) => {
         $($(
-        pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
+        pub const $name: RustcVersion = RustcVersion { major: $major, minor :$minor, patch: $patch };
         )*)*
     };
 }
@@ -18,6 +18,7 @@ macro_rules! msrv_aliases {
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
     1,81,0  { LINT_REASONS_STABILIZATION }
+    1,80,0 { BOX_INTO_ITER}
     1,77,0 { C_STR_LITERALS }
     1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
     1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
@@ -81,9 +82,9 @@ impl<'de> Deserialize<'de> for Msrv {
         D: serde::Deserializer<'de>,
     {
         let v = String::deserialize(deserializer)?;
-        RustcVersion::parse(&v)
+        parse_version(Symbol::intern(&v))
             .map(|v| Msrv { stack: vec![v] })
-            .map_err(|_| serde::de::Error::custom("not a valid Rust version"))
+            .ok_or_else(|| serde::de::Error::custom("not a valid Rust version"))
     }
 }
 
@@ -95,7 +96,7 @@ impl Msrv {
     pub fn read_cargo(&mut self, sess: &Session) {
         let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
             .ok()
-            .and_then(|v| RustcVersion::parse(&v).ok());
+            .and_then(|v| parse_version(Symbol::intern(&v)));
 
         match (self.current(), cargo_msrv) {
             (None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv],
@@ -115,7 +116,7 @@ impl Msrv {
     }
 
     pub fn meets(&self, required: RustcVersion) -> bool {
-        self.current().map_or(true, |version| version.meets(required))
+        self.current().map_or(true, |msrv| msrv >= required)
     }
 
     fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
@@ -131,7 +132,7 @@ impl Msrv {
             }
 
             if let Some(msrv) = msrv_attr.value_str() {
-                if let Ok(version) = RustcVersion::parse(msrv.as_str()) {
+                if let Some(version) = parse_version(msrv) {
                     return Some(version);
                 }
 
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index 755b04b0b23..fc15913354c 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -1,3 +1,4 @@
+#![feature(rustc_private)]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 15578d69c3a..8dbda1c634c 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -604,7 +604,7 @@ fn gen_declared_lints<'a>(
     details.sort_unstable();
 
     let mut output = GENERATED_FILE_COMMENT.to_string();
-    output.push_str("pub(crate) static LINTS: &[&crate::LintInfo] = &[\n");
+    output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n");
 
     for (is_public, module_name, lint_name) in details {
         if !is_public {
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 99ed93468a0..fbd4566da58 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -25,7 +25,6 @@ regex = { version = "1.5", optional = true }
 unicode-normalization = "0.1"
 unicode-script = { version = "0.5", default-features = false }
 semver = "1.0"
-rustc-semver = "1.1"
 url = "2.2"
 
 [dev-dependencies]
diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
index c0a9d888e0b..c72b3f1318c 100644
--- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::is_from_proc_macro;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
@@ -8,6 +8,7 @@ use rustc_hir::{HirId, ItemKind, Node, Path};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
+use rustc_span::Symbol;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -24,6 +25,13 @@ declare_clippy_lint! {
     /// Note: One exception to this is code from macro expansion - this does not lint such cases, as
     /// using absolute paths is the proper way of referencing items in one.
     ///
+    /// ### Known issues
+    ///
+    /// There are currently a few cases which are not caught by this lint:
+    /// * Macro calls. e.g. `path::to::macro!()`
+    /// * Derive macros. e.g. `#[derive(path::to::macro)]`
+    /// * Attribute macros. e.g. `#[path::to::macro]`
+    ///
     /// ### Example
     /// ```no_run
     /// let x = std::f64::consts::PI;
@@ -48,63 +56,66 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
 
 pub struct AbsolutePaths {
     pub absolute_paths_max_segments: u64,
-    pub absolute_paths_allowed_crates: &'static FxHashSet<String>,
+    pub absolute_paths_allowed_crates: FxHashSet<Symbol>,
 }
 
 impl AbsolutePaths {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
             absolute_paths_max_segments: conf.absolute_paths_max_segments,
-            absolute_paths_allowed_crates: &conf.absolute_paths_allowed_crates,
+            absolute_paths_allowed_crates: conf
+                .absolute_paths_allowed_crates
+                .iter()
+                .map(|x| Symbol::intern(x))
+                .collect(),
         }
     }
 }
 
-impl LateLintPass<'_> for AbsolutePaths {
+impl<'tcx> LateLintPass<'tcx> for AbsolutePaths {
     // We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath`
     // we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't
     // a `Use`
-    #[expect(clippy::cast_possible_truncation)]
-    fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) {
-        let Self {
-            absolute_paths_max_segments,
-            absolute_paths_allowed_crates,
-        } = self;
-
-        if !path.span.from_expansion()
-            && let node = cx.tcx.hir_node(hir_id)
-            && !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(_, _)))
-            && let [first, rest @ ..] = path.segments
-            // Handle `::std`
-            && let (segment, len) = if first.ident.name == kw::PathRoot {
-                // Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1`
-                // is fine here for the same reason
-                (&rest[0], path.segments.len() - 1)
-            } else {
-                (first, path.segments.len())
-            }
-            && len > *absolute_paths_max_segments as usize
-            && let Some(segment_snippet) = snippet_opt(cx, segment.ident.span)
-            && segment_snippet == segment.ident.as_str()
-        {
-            let is_abs_external =
-                matches!(segment.res, Res::Def(DefKind::Mod, DefId { index, .. }) if index == CRATE_DEF_INDEX);
-            let is_abs_crate = segment.ident.name == kw::Crate;
-
-            if is_abs_external && absolute_paths_allowed_crates.contains(segment.ident.name.as_str())
-                || is_abs_crate && absolute_paths_allowed_crates.contains("crate")
+    fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, hir_id: HirId) {
+        let segments = match path.segments {
+            [] | [_] => return,
+            // Don't count enum variants and trait items as part of the length.
+            [rest @ .., _]
+                if let [.., s] = rest
+                    && matches!(s.res, Res::Def(DefKind::Enum | DefKind::Trait | DefKind::TraitAlias, _)) =>
+            {
+                rest
+            },
+            path => path,
+        };
+        if let [s1, s2, ..] = segments
+            && let has_root = s1.ident.name == kw::PathRoot
+            && let first = if has_root { s2 } else { s1 }
+            && let len = segments.len() - usize::from(has_root)
+            && len as u64 > self.absolute_paths_max_segments
+            && let crate_name = if let Res::Def(DefKind::Mod, DefId { index, .. }) = first.res
+                && index == CRATE_DEF_INDEX
             {
+                // `other_crate::foo` or `::other_crate::foo`
+                first.ident.name
+            } else if first.ident.name == kw::Crate || has_root {
+                // `::foo` or `crate::foo`
+                kw::Crate
+            } else {
                 return;
             }
-
-            if is_abs_external || is_abs_crate {
-                span_lint(
-                    cx,
-                    ABSOLUTE_PATHS,
-                    path.span,
-                    "consider bringing this path into scope with the `use` keyword",
-                );
-            }
+            && !path.span.from_expansion()
+            && let node = cx.tcx.hir_node(hir_id)
+            && !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(..)))
+            && !self.absolute_paths_allowed_crates.contains(&crate_name)
+            && !is_from_proc_macro(cx, path)
+        {
+            span_lint(
+                cx,
+                ABSOLUTE_PATHS,
+                path.span,
+                "consider bringing this path into scope with the `use` keyword",
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs
index 3b4cc113480..56f5e903dc3 100644
--- a/src/tools/clippy/clippy_lints/src/approx_const.rs
+++ b/src/tools/clippy/clippy_lints/src/approx_const.rs
@@ -4,8 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
-use rustc_session::impl_lint_pass;
+use rustc_session::{impl_lint_pass, RustcVersion};
 use rustc_span::symbol;
 use std::f64::consts as f64;
 
diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
index 6e336efbb90..55645d04eef 100644
--- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs
+++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
@@ -3,7 +3,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{is_diag_trait_item, last_path_segment, local_is_initialized, path_to_local};
+use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -118,6 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
                 }
             )
             && !clone_source_borrows_from_dest(cx, lhs, rhs.span)
+            && !is_in_test(cx.tcx, e.hir_id)
         {
             span_lint_and_then(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs
index ca43e76ac57..7ff644b4c44 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs
@@ -1,6 +1,6 @@
 use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::{is_present_in_source, snippet_opt, without_block_comments};
+use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt};
 use rustc_ast::{AttrKind, AttrStyle};
 use rustc_lint::EarlyContext;
 use rustc_span::Span;
@@ -26,7 +26,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
                 item.span.parent(),
             );
 
-            if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) {
+            if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) {
                 let lines = snippet.split('\n').collect::<Vec<_>>();
                 let lines = without_block_comments(lines);
 
diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
index 8f430ae601a..3b14e9aee7f 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
@@ -1,5 +1,3 @@
-//! checks for attributes
-
 mod allow_attributes;
 mod allow_attributes_without_reason;
 mod blanket_clippy_restriction_lints;
@@ -310,8 +308,8 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// #[allow(unused_mut)]
     /// fn foo() -> usize {
-    ///    let mut a = Vec::new();
-    ///    a.len()
+    ///     let mut a = Vec::new();
+    ///     a.len()
     /// }
     /// ```
     /// Use instead:
diff --git a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs
index 3fde7061585..877025cce4c 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs
@@ -1,6 +1,6 @@
 use super::{Attribute, NON_MINIMAL_CFG};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::{MetaItemKind, NestedMetaItem};
 use rustc_errors::Applicability;
 use rustc_lint::EarlyContext;
@@ -29,8 +29,13 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
                         meta.span,
                         "unneeded sub `cfg` when there is only one condition",
                         |diag| {
-                            if let Some(snippet) = snippet_opt(cx, list[0].span()) {
-                                diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect);
+                            if let Some(snippet) = list[0].span().get_source_text(cx) {
+                                diag.span_suggestion(
+                                    meta.span,
+                                    "try",
+                                    snippet.to_owned(),
+                                    Applicability::MaybeIncorrect,
+                                );
                             }
                         },
                     );
diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs
index 486e7c6ec4f..478ba7a187b 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs
@@ -1,6 +1,7 @@
 use super::{Attribute, UNNECESSARY_CLIPPY_CFG};
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
+use itertools::Itertools;
 use rustc_ast::AttrStyle;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, Level};
@@ -31,7 +32,7 @@ pub(super) fn check(
             return;
         }
         if nb_items == clippy_lints.len() {
-            if let Some(snippet) = snippet_opt(cx, behind_cfg_attr.span) {
+            if let Some(snippet) = behind_cfg_attr.span.get_source_text(cx) {
                 span_lint_and_sugg(
                     cx,
                     UNNECESSARY_CLIPPY_CFG,
@@ -47,11 +48,7 @@ pub(super) fn check(
                 );
             }
         } else {
-            let snippet = clippy_lints
-                .iter()
-                .filter_map(|sp| snippet_opt(cx, *sp))
-                .collect::<Vec<_>>()
-                .join(",");
+            let snippet = clippy_lints.iter().filter_map(|sp| sp.get_source_text(cx)).join(",");
             span_lint_and_note(
                 cx,
                 UNNECESSARY_CLIPPY_CFG,
diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
index f0868231d01..67ba605a59f 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
@@ -1,7 +1,7 @@
 use super::utils::{extract_clippy_lint, is_lint_level, is_word};
 use super::{Attribute, USELESS_ATTRIBUTE};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{first_line_of_span, snippet_opt};
+use clippy_utils::source::{first_line_of_span, SpanRangeExt};
 use rustc_ast::NestedMetaItem;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
@@ -69,14 +69,14 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
                 }
                 let line_span = first_line_of_span(cx, attr.span);
 
-                if let Some(mut sugg) = snippet_opt(cx, line_span) {
-                    if sugg.contains("#[") {
+                if let Some(src) = line_span.get_source_text(cx) {
+                    if src.contains("#[") {
+                        #[expect(clippy::collapsible_span_lint_calls)]
                         span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
-                            sugg = sugg.replacen("#[", "#![", 1);
                             diag.span_suggestion(
                                 line_span,
                                 "if you just forgot a `!`, use",
-                                sugg,
+                                src.replacen("#[", "#![", 1),
                                 Applicability::MaybeIncorrect,
                             );
                         });
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index a2f48c18170..e933fdf1d6e 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::eq_expr_value;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -134,28 +134,30 @@ fn check_inverted_bool_in_condition(
 
     let suggestion = match (left.kind, right.kind) {
         (ExprKind::Unary(UnOp::Not, left_sub), ExprKind::Unary(UnOp::Not, right_sub)) => {
-            let Some(left) = snippet_opt(cx, left_sub.span) else {
+            let Some(left) = left_sub.span.get_source_text(cx) else {
                 return;
             };
-            let Some(right) = snippet_opt(cx, right_sub.span) else {
+            let Some(right) = right_sub.span.get_source_text(cx) else {
                 return;
             };
             let Some(op) = bin_op_eq_str(op) else { return };
             format!("{left} {op} {right}")
         },
         (ExprKind::Unary(UnOp::Not, left_sub), _) => {
-            let Some(left) = snippet_opt(cx, left_sub.span) else {
+            let Some(left) = left_sub.span.get_source_text(cx) else {
                 return;
             };
-            let Some(right) = snippet_opt(cx, right.span) else {
+            let Some(right) = right.span.get_source_text(cx) else {
                 return;
             };
             let Some(op) = inverted_bin_op_eq_str(op) else { return };
             format!("{left} {op} {right}")
         },
         (_, ExprKind::Unary(UnOp::Not, right_sub)) => {
-            let Some(left) = snippet_opt(cx, left.span) else { return };
-            let Some(right) = snippet_opt(cx, right_sub.span) else {
+            let Some(left) = left.span.get_source_text(cx) else {
+                return;
+            };
+            let Some(right) = right_sub.span.get_source_text(cx) else {
                 return;
             };
             let Some(op) = inverted_bin_op_eq_str(op) else { return };
@@ -313,8 +315,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
                         self.output.push_str(&str);
                     } else {
                         self.output.push('!');
-                        let snip = snippet_opt(self.cx, terminal.span)?;
-                        self.output.push_str(&snip);
+                        self.output.push_str(&terminal.span.get_source_text(self.cx)?);
                     }
                 },
                 True | False | Not(_) => {
@@ -345,8 +346,12 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
                 }
             },
             &Term(n) => {
-                let snip = snippet_opt(self.cx, self.terminals[n as usize].span.source_callsite())?;
-                self.output.push_str(&snip);
+                self.output.push_str(
+                    &self.terminals[n as usize]
+                        .span
+                        .source_callsite()
+                        .get_source_text(self.cx)?,
+                );
             },
         }
         Some(())
@@ -370,8 +375,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
                 _ => None,
             }
             .and_then(|op| {
-                let lhs_snippet = snippet_opt(cx, lhs.span)?;
-                let rhs_snippet = snippet_opt(cx, rhs.span)?;
+                let lhs_snippet = lhs.span.get_source_text(cx)?;
+                let rhs_snippet = rhs.span.get_source_text(cx)?;
 
                 if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) {
                     if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) {
@@ -399,7 +404,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
                     let path: &str = path.ident.name.as_str();
                     a == path
                 })
-                .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?)))
+                .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", receiver.span.get_source_text(cx)?)))
         },
         _ => None,
     }
diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
index bd123a725a7..cba8224b84c 100644
--- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
@@ -1,6 +1,6 @@
 use crate::reference::DEREF_ADDROF;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed};
 use rustc_errors::Applicability;
@@ -73,6 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
                 }
             })
             && !is_from_proc_macro(cx, e)
+            && let Some(deref_text) = deref_target.span.get_source_text(cx)
         {
             span_lint_and_then(
                 cx,
@@ -83,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
                     diag.span_suggestion(
                         e.span,
                         "if you would like to reborrow, try removing `&*`",
-                        snippet_opt(cx, deref_target.span).unwrap(),
+                        deref_text.as_str(),
                         Applicability::MachineApplicable,
                     );
 
@@ -98,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
                     diag.span_suggestion(
                         e.span,
                         "if you would like to deref, try using `&**`",
-                        format!("&**{}", &snippet_opt(cx, deref_target.span).unwrap()),
+                        format!("&**{deref_text}"),
                         Applicability::MaybeIncorrect,
                     );
                 },
diff --git a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
index 3af2d8c0256..fed0aa8b275 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
@@ -1,5 +1,3 @@
-//! lint on missing cargo common metadata
-
 use cargo_metadata::Metadata;
 use clippy_utils::diagnostics::span_lint;
 use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/cargo/mod.rs b/src/tools/clippy/clippy_lints/src/cargo/mod.rs
index 312ad4c2990..96a2b161464 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/mod.rs
@@ -205,7 +205,7 @@ declare_clippy_lint! {
 }
 
 pub struct Cargo {
-    allowed_duplicate_crates: &'static FxHashSet<String>,
+    allowed_duplicate_crates: FxHashSet<String>,
     ignore_publish: bool,
 }
 
@@ -221,7 +221,7 @@ impl_lint_pass!(Cargo => [
 impl Cargo {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
-            allowed_duplicate_crates: &conf.allowed_duplicate_crates,
+            allowed_duplicate_crates: conf.allowed_duplicate_crates.iter().cloned().collect(),
             ignore_publish: conf.cargo_ignore_publish,
         }
     }
@@ -263,7 +263,7 @@ impl LateLintPass<'_> for Cargo {
         {
             match MetadataCommand::new().exec() {
                 Ok(metadata) => {
-                    multiple_crate_versions::check(cx, &metadata, self.allowed_duplicate_crates);
+                    multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates);
                 },
                 Err(e) => {
                     for lint in WITH_DEPS_LINTS {
diff --git a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs
index 2769463c8a5..44cd1f7192f 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs
@@ -1,5 +1,3 @@
-//! lint on multiple versions of a crate being used
-
 use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId};
 use clippy_utils::diagnostics::span_lint;
 use itertools::Itertools;
diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
index f05fd3fcde5..15ecba20a0b 100644
--- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity()
         && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
         && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
-        && let Some(recv) = snippet_opt(cx, receiver.span)
+        && let Some(recv) = receiver.span.get_source_text(cx)
     {
         // `as_mut_ptr` might not exist
         let applicability = Applicability::MaybeIncorrect;
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
index bd3acc06f4b..346aed7e9f1 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
@@ -1,7 +1,7 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_const_context;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_isize_or_usize;
 use rustc_errors::Applicability;
@@ -34,7 +34,7 @@ pub(super) fn check(
             diag.help("an `as` cast can become silently lossy if the types change in the future");
             let mut applicability = Applicability::MachineApplicable;
             let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "<from>", &mut applicability);
-            let Some(ty) = snippet_opt(cx, hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt())) else {
+            let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_source_text(cx) else {
                 return;
             };
             match cast_to_hir.kind {
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
index 102fe25fc67..5708aae3f3e 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -4,7 +4,7 @@ use clippy_utils::expr_or_init;
 use clippy_utils::source::snippet;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
-use rustc_errors::{Applicability, Diag, SuggestionStyle};
+use rustc_errors::{Applicability, Diag};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -190,12 +190,10 @@ fn offer_suggestion(
         format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, ".."))
     };
 
-    diag.span_suggestion_with_style(
+    diag.span_suggestion_verbose(
         expr.span,
         "... or use `try_from` and handle the error accordingly",
         suggestion,
         Applicability::Unspecified,
-        // always show the suggestion in a separate line
-        SuggestionStyle::ShowAlways,
     );
 }
diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
index 5dc6df1e907..b22e8f4ee89 100644
--- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
@@ -24,12 +24,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             expr.span,
             format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
             |diag| {
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     expr.span,
                     "did you mean to invoke the function?",
                     format!("{from_snippet}() as {cast_to}"),
                     applicability,
-                    SuggestionStyle::ShowAlways,
                 );
             },
         );
diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
index fb0b0cba6a6..566adc83d69 100644
--- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::numeric_literal::NumericLiteral;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::{snippet_opt, SpanRangeExt};
 use clippy_utils::visitors::{for_each_expr_without_closures, Visitable};
 use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
 use rustc_ast::{LitFloatType, LitIntType, LitKind};
@@ -104,7 +104,7 @@ pub(super) fn check<'tcx>(
         let literal_str = &cast_str;
 
         if let LitKind::Int(n, _) = lit.node
-            && let Some(src) = snippet_opt(cx, cast_expr.span)
+            && let Some(src) = cast_expr.span.get_source_text(cx)
             && cast_to.is_floating_point()
             && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
             && let from_nbits = 128 - n.get().leading_zeros()
@@ -131,7 +131,7 @@ pub(super) fn check<'tcx>(
             | LitKind::Float(_, LitFloatType::Suffixed(_))
                 if cast_from.kind() == cast_to.kind() =>
             {
-                if let Some(src) = snippet_opt(cx, cast_expr.span) {
+                if let Some(src) = cast_expr.span.get_source_text(cx) {
                     if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
                         lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
                         return true;
@@ -253,7 +253,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
             let res = cx.qpath_res(&qpath, expr.hir_id);
             // Function call
             if let Res::Def(DefKind::Fn, def_id) = res {
-                let Some(snippet) = snippet_opt(cx, cx.tcx.def_span(def_id)) else {
+                let Some(snippet) = cx.tcx.def_span(def_id).get_source_text(cx) else {
                     return ControlFlow::Continue(());
                 };
                 // This is the worst part of this entire function. This is the only way I know of to
diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs
index 3c1c7d2dc3a..c5c4a28646d 100644
--- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability, Ty, TyKind};
@@ -20,7 +20,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>
 
         let sugg = if let TyKind::Infer = mut_ty.ty.kind {
             format!("{std_or_core}::{sugg_fn}()")
-        } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
+        } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) {
             format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
         } else {
             return;
diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
index b54f392bf2f..d820c1e0720 100644
--- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
+++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
@@ -5,7 +5,7 @@ use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#{cfg(not(test))]`)
+    /// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#[cfg(not(test))]`)
     ///
     /// ### Why is this bad?
     /// This may give the false impression that a codebase has 100% coverage, yet actually has untested code.
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index 1711565fca8..dd7c34d1e46 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -1,5 +1,3 @@
-//! lint on manually implemented checked conversions that could be transformed into `try_from`
-
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index 5fa0522e4e5..0099eefbc8d 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -1,5 +1,3 @@
-//! calculate cognitive complexity and warn about overly complex functions
-
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::{IntoSpan, SpanRangeExt};
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
index f311c052ad6..e73bfc6ebf7 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
@@ -1,17 +1,3 @@
-//! Checks for if expressions that contain only an if expression.
-//!
-//! For example, the lint would catch:
-//!
-//! ```rust,ignore
-//! if x {
-//!     if y {
-//!         println!("Hello world");
-//!     }
-//! }
-//! ```
-//!
-//! This lint is **warn** by default
-
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
 use clippy_utils::sugg::Sugg;
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
index eebda3ff76f..c6847411c75 100644
--- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
 use clippy_utils::visitors::{for_each_expr, Visitable};
 use clippy_utils::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
@@ -7,7 +7,6 @@ use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
-use rustc_span::Symbol;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -44,24 +43,11 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
 
-// Add `String` here when it is added to diagnostic items
-static COLLECTIONS: [Symbol; 9] = [
-    sym::BTreeMap,
-    sym::BTreeSet,
-    sym::BinaryHeap,
-    sym::HashMap,
-    sym::HashSet,
-    sym::LinkedList,
-    sym::Option,
-    sym::Vec,
-    sym::VecDeque,
-];
-
 impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
         // Look for local variables whose type is a container. Search surrounding block for read access.
         if let PatKind::Binding(_, local_id, _, _) = local.pat.kind
-            && match_acceptable_type(cx, local, &COLLECTIONS)
+            && match_acceptable_type(cx, local)
             && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
             && has_no_read_access(cx, local_id, enclosing_block)
         {
@@ -70,11 +56,22 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
     }
 }
 
-fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: &[Symbol]) -> bool {
+fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool {
     let ty = cx.typeck_results().pat_ty(local.pat);
-    collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
-    // String type is a lang item but not a diagnostic item for now so we need a separate check
-        || is_type_lang_item(cx, ty, LangItem::String)
+    matches!(
+        get_type_diagnostic_name(cx, ty),
+        Some(
+            sym::BTreeMap
+                | sym::BTreeSet
+                | sym::BinaryHeap
+                | sym::HashMap
+                | sym::HashSet
+                | sym::LinkedList
+                | sym::Option
+                | sym::Vec
+                | sym::VecDeque
+        )
+    ) || is_type_lang_item(cx, ty, LangItem::String)
 }
 
 fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs
index b49a977dbea..24570d8f440 100644
--- a/src/tools/clippy/clippy_lints/src/create_dir.rs
+++ b/src/tools/clippy/clippy_lints/src/create_dir.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -46,7 +46,7 @@ impl LateLintPass<'_> for CreateDir {
                 "calling `std::fs::create_dir` where there may be a better way",
                 |diag| {
                     let mut app = Applicability::MaybeIncorrect;
-                    diag.span_suggestion_with_style(
+                    diag.span_suggestion_verbose(
                         expr.span,
                         "consider calling `std::fs::create_dir_all` instead",
                         format!(
@@ -54,7 +54,6 @@ impl LateLintPass<'_> for CreateDir {
                             snippet_with_applicability(cx, arg.span, "..", &mut app)
                         ),
                         app,
-                        SuggestionStyle::ShowAlways,
                     );
                 },
             );
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 3fb083dd833..60e51713173 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -2,7 +2,7 @@
 // Use that command to update this file and do not edit by hand.
 // Manual edits will be overwritten.
 
-pub(crate) static LINTS: &[&crate::LintInfo] = &[
+pub static LINTS: &[&crate::LintInfo] = &[
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO,
     #[cfg(feature = "internal")]
@@ -22,8 +22,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO,
     #[cfg(feature = "internal")]
-    crate::utils::internal_lints::metadata_collector::METADATA_COLLECTOR_INFO,
-    #[cfg(feature = "internal")]
     crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO,
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO,
@@ -146,6 +144,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::doc::NEEDLESS_DOCTEST_MAIN_INFO,
     crate::doc::SUSPICIOUS_DOC_COMMENTS_INFO,
     crate::doc::TEST_ATTR_IN_DOCTEST_INFO,
+    crate::doc::TOO_LONG_FIRST_DOC_PARAGRAPH_INFO,
     crate::doc::UNNECESSARY_SAFETY_DOC_INFO,
     crate::double_parens::DOUBLE_PARENS_INFO,
     crate::drop_forget_ref::DROP_NON_DROP_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
index bd1cc46e185..771bcac2441 100644
--- a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use itertools::Itertools;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_lint::LateContext;
 use rustc_span::{BytePos, Span};
 use std::ops::Range;
@@ -59,12 +59,11 @@ pub(super) fn check(
                 && (doc_comment == "///" || doc_comment == "//!")
             {
                 // suggest filling in a blank line
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     line_break_span.shrink_to_lo(),
                     "if this should be its own paragraph, add a blank doc comment line",
                     format!("\n{doc_comment}"),
                     Applicability::MaybeIncorrect,
-                    SuggestionStyle::ShowAlways,
                 );
                 if ccount > 0 || blockquote_level > 0 {
                     diag.help("if this not intended to be a quote at all, escape it with `\\>`");
@@ -79,12 +78,11 @@ pub(super) fn check(
             if ccount == 0 && blockquote_level == 0 {
                 // simpler suggestion style for indentation
                 let indent = list_indentation - lcount;
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     span.shrink_to_hi(),
                     "indent this line",
                     std::iter::repeat(" ").take(indent).join(""),
                     Applicability::MaybeIncorrect,
-                    SuggestionStyle::ShowAlways,
                 );
                 diag.help("if this is supposed to be its own paragraph, add a blank line");
                 return;
@@ -107,12 +105,11 @@ pub(super) fn check(
                     suggested.push_str(text);
                 }
             }
-            diag.span_suggestion_with_style(
+            diag.span_suggestion_verbose(
                 span,
                 "add markers to start of line",
                 suggested,
                 Applicability::MachineApplicable,
-                SuggestionStyle::ShowAlways,
             );
             diag.help("if this not intended to be a quote at all, escape it with `\\>`");
         });
diff --git a/src/tools/clippy/clippy_lints/src/doc/markdown.rs b/src/tools/clippy/clippy_lints/src/doc/markdown.rs
index 41c0bcd55ad..8cdaba88e50 100644
--- a/src/tools/clippy/clippy_lints/src/doc/markdown.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/markdown.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_lint::LateContext;
 use rustc_span::{BytePos, Pos, Span};
 use url::Url;
@@ -92,6 +92,10 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
             && matches!(prefix.chars().last(), Some('S' | 'X'))
         {
             prefix
+        } else if let Some(prefix) = s.strip_suffix("ified")
+            && prefix.chars().all(|c| c.is_ascii_uppercase())
+        {
+            prefix
         } else {
             s.strip_suffix('s').unwrap_or(s)
         };
@@ -133,24 +137,15 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
     }
 
     if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") {
-        let mut applicability = Applicability::MachineApplicable;
-
         span_lint_and_then(
             cx,
             DOC_MARKDOWN,
             span,
             "item in documentation is missing backticks",
             |diag| {
+                let mut applicability = Applicability::MachineApplicable;
                 let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
-                diag.span_suggestion_with_style(
-                    span,
-                    "try",
-                    format!("`{snippet}`"),
-                    applicability,
-                    // always show the suggestion in a separate line, since the
-                    // inline presentation adds another pair of backticks
-                    SuggestionStyle::ShowAlways,
-                );
+                diag.span_suggestion_verbose(span, "try", format!("`{snippet}`"), applicability);
             },
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index d7b3a7c74f3..790579b21c9 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -1,4 +1,6 @@
 mod lazy_continuation;
+mod too_long_first_doc_paragraph;
+
 use clippy_config::Conf;
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
@@ -309,7 +311,7 @@ declare_clippy_lint! {
     /// ### Known problems
     /// Inner doc comments can only appear before items, so there are certain cases where the suggestion
     /// made by this lint is not valid code. For example:
-    /// ```rs
+    /// ```rust
     /// fn foo() {}
     /// ///!
     /// fn bar() {}
@@ -422,15 +424,47 @@ declare_clippy_lint! {
     "require every line of a paragraph to be indented and marked"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks if the first line in the documentation of items listed in module page is too long.
+    ///
+    /// ### Why is this bad?
+    /// Documentation will show the first paragraph of the doscstring in the summary page of a
+    /// module, so having a nice, short summary in the first paragraph is part of writing good docs.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// /// A very short summary.
+    /// /// A much longer explanation that goes into a lot more detail about
+    /// /// how the thing works, possibly with doclinks and so one,
+    /// /// and probably spanning a many rows.
+    /// struct Foo {}
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// /// A very short summary.
+    /// ///
+    /// /// A much longer explanation that goes into a lot more detail about
+    /// /// how the thing works, possibly with doclinks and so one,
+    /// /// and probably spanning a many rows.
+    /// struct Foo {}
+    /// ```
+    #[clippy::version = "1.81.0"]
+    pub TOO_LONG_FIRST_DOC_PARAGRAPH,
+    style,
+    "ensure that the first line of a documentation paragraph isn't too long"
+}
+
+#[derive(Clone)]
 pub struct Documentation {
-    valid_idents: &'static FxHashSet<String>,
+    valid_idents: FxHashSet<String>,
     check_private_items: bool,
 }
 
 impl Documentation {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
-            valid_idents: &conf.doc_valid_idents,
+            valid_idents: conf.doc_valid_idents.iter().cloned().collect(),
             check_private_items: conf.check_private_items,
         }
     }
@@ -448,48 +482,60 @@ impl_lint_pass!(Documentation => [
     SUSPICIOUS_DOC_COMMENTS,
     EMPTY_DOCS,
     DOC_LAZY_CONTINUATION,
+    TOO_LONG_FIRST_DOC_PARAGRAPH,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Documentation {
     fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
-        let Some(headers) = check_attrs(cx, self.valid_idents, attrs) else {
+        let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
             return;
         };
 
         match cx.tcx.hir_node(cx.last_node_with_lint_attrs) {
-            Node::Item(item) => match item.kind {
-                ItemKind::Fn(sig, _, body_id) => {
-                    if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
-                        let body = cx.tcx.hir().body(body_id);
-
-                        let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
-                        missing_headers::check(
+            Node::Item(item) => {
+                too_long_first_doc_paragraph::check(
+                    cx,
+                    item,
+                    attrs,
+                    headers.first_paragraph_len,
+                    self.check_private_items,
+                );
+                match item.kind {
+                    ItemKind::Fn(sig, _, body_id) => {
+                        if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())
+                            || in_external_macro(cx.tcx.sess, item.span))
+                        {
+                            let body = cx.tcx.hir().body(body_id);
+
+                            let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
+                            missing_headers::check(
+                                cx,
+                                item.owner_id,
+                                sig,
+                                headers,
+                                Some(body_id),
+                                panic_info,
+                                self.check_private_items,
+                            );
+                        }
+                    },
+                    ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
+                        (false, Safety::Unsafe) => span_lint(
                             cx,
-                            item.owner_id,
-                            sig,
-                            headers,
-                            Some(body_id),
-                            panic_info,
-                            self.check_private_items,
-                        );
-                    }
-                },
-                ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
-                    (false, Safety::Unsafe) => span_lint(
-                        cx,
-                        MISSING_SAFETY_DOC,
-                        cx.tcx.def_span(item.owner_id),
-                        "docs for unsafe trait missing `# Safety` section",
-                    ),
-                    (true, Safety::Safe) => span_lint(
-                        cx,
-                        UNNECESSARY_SAFETY_DOC,
-                        cx.tcx.def_span(item.owner_id),
-                        "docs for safe trait have unnecessary `# Safety` section",
-                    ),
+                            MISSING_SAFETY_DOC,
+                            cx.tcx.def_span(item.owner_id),
+                            "docs for unsafe trait missing `# Safety` section",
+                        ),
+                        (true, Safety::Safe) => span_lint(
+                            cx,
+                            UNNECESSARY_SAFETY_DOC,
+                            cx.tcx.def_span(item.owner_id),
+                            "docs for safe trait have unnecessary `# Safety` section",
+                        ),
+                        _ => (),
+                    },
                     _ => (),
-                },
-                _ => (),
+                }
             },
             Node::TraitItem(trait_item) => {
                 if let TraitItemKind::Fn(sig, ..) = trait_item.kind
@@ -547,6 +593,7 @@ struct DocHeaders {
     safety: bool,
     errors: bool,
     panics: bool,
+    first_paragraph_len: usize,
 }
 
 /// Does some pre-processing on raw, desugared `#[doc]` attributes such as parsing them and
@@ -653,6 +700,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     let mut paragraph_range = 0..0;
     let mut code_level = 0;
     let mut blockquote_level = 0;
+    let mut is_first_paragraph = true;
 
     let mut containers = Vec::new();
 
@@ -720,6 +768,10 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                 }
                 ticks_unbalanced = false;
                 paragraph_range = range;
+                if is_first_paragraph {
+                    headers.first_paragraph_len = doc[paragraph_range.clone()].chars().count();
+                    is_first_paragraph = false;
+                }
             },
             End(TagEnd::Heading(_) | TagEnd::Paragraph | TagEnd::Item) => {
                 if let End(TagEnd::Heading(_)) = event {
diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
new file mode 100644
index 00000000000..7bb3bb12f2c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
@@ -0,0 +1,91 @@
+use rustc_ast::ast::Attribute;
+use rustc_errors::Applicability;
+use rustc_hir::{Item, ItemKind};
+use rustc_lint::LateContext;
+
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::source::snippet_opt;
+
+use super::TOO_LONG_FIRST_DOC_PARAGRAPH;
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    item: &Item<'_>,
+    attrs: &[Attribute],
+    mut first_paragraph_len: usize,
+    check_private_items: bool,
+) {
+    if !check_private_items && !cx.effective_visibilities.is_exported(item.owner_id.def_id) {
+        return;
+    }
+    if first_paragraph_len <= 200
+        || !matches!(
+            item.kind,
+            // This is the list of items which can be documented AND are displayed on the module
+            // page. So associated items or impl blocks are not part of this list.
+            ItemKind::Static(..)
+                | ItemKind::Const(..)
+                | ItemKind::Fn(..)
+                | ItemKind::Macro(..)
+                | ItemKind::Mod(..)
+                | ItemKind::TyAlias(..)
+                | ItemKind::Enum(..)
+                | ItemKind::Struct(..)
+                | ItemKind::Union(..)
+                | ItemKind::Trait(..)
+                | ItemKind::TraitAlias(..)
+        )
+    {
+        return;
+    }
+
+    let mut spans = Vec::new();
+    let mut should_suggest_empty_doc = false;
+
+    for attr in attrs {
+        if let Some(doc) = attr.doc_str() {
+            spans.push(attr.span);
+            let doc = doc.as_str();
+            let doc = doc.trim();
+            if spans.len() == 1 {
+                // We make this suggestion only if the first doc line ends with a punctuation
+                // because it might just need to add an empty line with `///`.
+                should_suggest_empty_doc = doc.ends_with('.') || doc.ends_with('!') || doc.ends_with('?');
+            }
+            let len = doc.chars().count();
+            if len >= first_paragraph_len {
+                break;
+            }
+            first_paragraph_len -= len;
+        }
+    }
+
+    let &[first_span, .., last_span] = spans.as_slice() else {
+        return;
+    };
+    if is_from_proc_macro(cx, item) {
+        return;
+    }
+
+    span_lint_and_then(
+        cx,
+        TOO_LONG_FIRST_DOC_PARAGRAPH,
+        first_span.with_hi(last_span.lo()),
+        "first doc comment paragraph is too long",
+        |diag| {
+            if should_suggest_empty_doc
+                && let Some(second_span) = spans.get(1)
+                && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
+                && let Some(snippet) = snippet_opt(cx, new_span)
+            {
+                diag.span_suggestion(
+                    new_span,
+                    "add an empty line",
+                    format!("{snippet}///\n"),
+                    Applicability::MachineApplicable,
+                );
+            }
+        },
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
index 02f9c2c3648..5315f55ba38 100644
--- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
+++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
@@ -1,5 +1,3 @@
-//! Lint on if expressions with an else if, but without a final else branch.
-
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs
index 1869faab1d3..f4c55738cb8 100644
--- a/src/tools/clippy/clippy_lints/src/empty_enum.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs
@@ -1,5 +1,3 @@
-//! lint when there is an enum with no variants
-
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs
index e54cd248ead..4755cefe784 100644
--- a/src/tools/clippy/clippy_lints/src/enum_clike.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs
@@ -1,6 +1,3 @@
-//! lint on C-like enums that are `repr(isize/usize)` and have values that
-//! don't fit into an `i32`
-
 use clippy_utils::consts::{mir_to_const, Constant};
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{Item, ItemKind};
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index a7e831fdc42..cabc6592258 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::type_diagnostic_name;
+use clippy_utils::ty::get_type_diagnostic_name;
 use clippy_utils::usage::{local_used_after_expr, local_used_in};
 use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id};
 use rustc_errors::Applicability;
@@ -139,7 +139,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
         {
             let callee_ty_raw = typeck.expr_ty(callee);
             let callee_ty = callee_ty_raw.peel_refs();
-            if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
+            if matches!(get_type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
                 || !check_inputs(typeck, body.params, None, args)
             {
                 return;
diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs
index f095c1add91..012ad8e1a22 100644
--- a/src/tools/clippy/clippy_lints/src/float_literal.rs
+++ b/src/tools/clippy/clippy_lints/src/float_literal.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::numeric_literal;
 use rustc_ast::ast::{self, LitFloatType, LitKind};
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, FloatTy};
@@ -117,12 +117,11 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
                             if type_suffix.is_none() {
                                 float_str.push_str(".0");
                             }
-                            diag.span_suggestion_with_style(
+                            diag.span_suggestion_verbose(
                                 expr.span,
                                 "consider changing the type or replacing it with",
                                 numeric_literal::format(&float_str, type_suffix, true),
                                 Applicability::MachineApplicable,
-                                SuggestionStyle::ShowAlways,
                             );
                         },
                     );
@@ -134,12 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
                     expr.span,
                     "float has excessive precision",
                     |diag| {
-                        diag.span_suggestion_with_style(
+                        diag.span_suggestion_verbose(
                             expr.span,
                             "consider changing the type or truncating it to",
                             numeric_literal::format(&float_str, type_suffix, true),
                             Applicability::MachineApplicable,
-                            SuggestionStyle::ShowAlways,
                         );
                     },
                 );
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index 0b248f784b7..4dedff69b9a 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage};
-use clippy_utils::source::{snippet_opt, snippet_with_context};
+use clippy_utils::source::{snippet_with_context, SpanRangeExt};
 use clippy_utils::sugg::Sugg;
 use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
 use rustc_errors::Applicability;
@@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
                 ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
                 ([], [_]) => {
                     // Simulate macro expansion, converting {{ and }} to { and }.
-                    let Some(snippet) = snippet_opt(cx, format_args.span) else {
+                    let Some(snippet) = format_args.span.get_source_text(cx) else {
                         return;
                     };
                     let s_expand = snippet.replace("{{", "{").replace("}}", "}");
@@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
                     span_useless_format(cx, call_site, sugg, applicability);
                 },
                 ([arg], [piece]) => {
-                    if let Ok(value) = find_format_arg_expr(expr, arg)
+                    if let Some(value) = find_format_arg_expr(expr, arg)
                         && let FormatArgsPiece::Placeholder(placeholder) = piece
                         && placeholder.format_trait == FormatTrait::Display
                         && placeholder.format_options == FormatOptions::default()
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index a31d5cb6ec7..020c0c5440d 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -7,7 +7,7 @@ use clippy_utils::macros::{
     find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro,
     is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall,
 };
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
 use itertools::Itertools;
 use rustc_ast::{
@@ -224,13 +224,11 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
             if let FormatArgsPiece::Placeholder(placeholder) = piece
                 && let Ok(index) = placeholder.argument.index
                 && let Some(arg) = self.format_args.arguments.all_args().get(index)
+                && let Some(arg_expr) = find_format_arg_expr(self.expr, arg)
             {
-                let arg_expr = find_format_arg_expr(self.expr, arg);
-
                 self.check_unused_format_specifier(placeholder, arg_expr);
 
-                if let Ok(arg_expr) = arg_expr
-                    && placeholder.format_trait == FormatTrait::Display
+                if placeholder.format_trait == FormatTrait::Display
                     && placeholder.format_options == FormatOptions::default()
                     && !self.is_aliased(index)
                 {
@@ -242,28 +240,13 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
         }
     }
 
-    fn check_unused_format_specifier(
-        &self,
-        placeholder: &FormatPlaceholder,
-        arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>,
-    ) {
-        let ty_or_ast_expr = arg_expr.map(|expr| self.cx.typeck_results().expr_ty(expr).peel_refs());
-
-        let is_format_args = match ty_or_ast_expr {
-            Ok(ty) => is_type_lang_item(self.cx, ty, LangItem::FormatArguments),
-            Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)),
-        };
-
+    fn check_unused_format_specifier(&self, placeholder: &FormatPlaceholder, arg: &Expr<'_>) {
         let options = &placeholder.format_options;
 
-        let arg_span = match arg_expr {
-            Ok(expr) => expr.span,
-            Err(expr) => expr.span,
-        };
-
         if let Some(placeholder_span) = placeholder.span
-            && is_format_args
             && *options != FormatOptions::default()
+            && let ty = self.cx.typeck_results().expr_ty(arg).peel_refs()
+            && is_type_lang_item(self.cx, ty, LangItem::FormatArguments)
         {
             span_lint_and_then(
                 self.cx,
@@ -274,7 +257,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
                     let mut suggest_format = |spec| {
                         let message = format!("for the {spec} to apply consider using `format!()`");
 
-                        if let Some(mac_call) = matching_root_macro_call(self.cx, arg_span, sym::format_args_macro) {
+                        if let Some(mac_call) = matching_root_macro_call(self.cx, arg.span, sym::format_args_macro) {
                             diag.span_suggestion(
                                 self.cx.sess().source_map().span_until_char(mac_call.span, '!'),
                                 message,
@@ -424,7 +407,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
                 count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
             && implements_trait(cx, target, display_trait_id, &[])
             && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
-            && let Some(receiver_snippet) = snippet_opt(cx, receiver.span.source_callsite())
+            && let Some(receiver_snippet) = receiver.span.source_callsite().get_source_text(cx)
         {
             let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
             if n_needed_derefs == 0 && !needs_ref {
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index e6f27cb82d1..7cd12ac7db8 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -196,7 +196,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> {
                     && trait_name == self.format_trait_impl.name
                     && let Ok(index) = placeholder.argument.index
                     && let Some(arg) = format_args.arguments.all_args().get(index)
-                    && let Ok(arg_expr) = find_format_arg_expr(self.expr, arg)
+                    && let Some(arg_expr) = find_format_arg_expr(self.expr, arg)
                 {
                     self.check_format_arg_self(arg_expr);
                 }
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index 1c1d8b57bc4..b84bf7c821c 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -3,7 +3,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::span_is_local;
 use clippy_utils::path_def_id;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_path, Visitor};
 use rustc_hir::{
@@ -178,8 +178,8 @@ fn convert_to_from(
         return None;
     };
 
-    let from = snippet_opt(cx, self_ty.span)?;
-    let into = snippet_opt(cx, target_ty.span)?;
+    let from = self_ty.span.get_source_text(cx)?;
+    let into = target_ty.span.get_source_text(cx)?;
 
     let mut suggestions = vec![
         // impl Into<T> for U  ->  impl From<T> for U
@@ -187,10 +187,10 @@ fn convert_to_from(
         (into_trait_seg.ident.span, String::from("From")),
         // impl Into<T> for U  ->  impl Into<U> for U
         //           ~                       ~
-        (target_ty.span, from.clone()),
+        (target_ty.span, from.to_owned()),
         // impl Into<T> for U  ->  impl Into<T> for T
         //                  ~                       ~
-        (self_ty.span, into),
+        (self_ty.span, into.to_owned()),
         // fn into(self) -> T  ->  fn from(self) -> T
         //    ~~~~                    ~~~~
         (impl_item.ident.span, String::from("from")),
@@ -223,7 +223,7 @@ fn convert_to_from(
     }
 
     for span in finder.upper {
-        suggestions.push((span, from.clone()));
+        suggestions.push((span, from.to_owned()));
     }
     for span in finder.lower {
         suggestions.push((span, String::from("val")));
diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
index 6ab7bbc2dfc..e5fa67d6964 100644
--- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
+++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
@@ -22,8 +22,8 @@ declare_clippy_lint! {
     ///
     /// ### Known problems
     ///
-    /// This lint may suggest using (&<expression>).parse() instead of <expression>.parse() directly
-    /// in some cases, which is correct but adds unnecessary complexity to the code.
+    /// This lint may suggest using `(&<expression>).parse()` instead of `<expression>.parse()`
+    /// directly in some cases, which is correct but adds unnecessary complexity to the code.
     ///
     /// ### Example
     /// ```ignore
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index cf85c74e688..05e341e06fd 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_test;
 
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind};
@@ -18,20 +19,18 @@ fn report(cx: &LateContext<'_>, param: &GenericParam<'_>, generics: &Generics<'_
         |diag| {
             if let Some(gen_span) = generics.span_for_param_suggestion() {
                 // If there's already a generic param with the same bound, do not lint **this** suggestion.
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     gen_span,
                     "add a type parameter",
                     format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
-                    rustc_errors::Applicability::HasPlaceholders,
-                    rustc_errors::SuggestionStyle::ShowAlways,
+                    Applicability::HasPlaceholders,
                 );
             } else {
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     generics.span,
                     "add a type parameter",
                     format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
-                    rustc_errors::Applicability::HasPlaceholders,
-                    rustc_errors::SuggestionStyle::ShowAlways,
+                    Applicability::HasPlaceholders,
                 );
             }
         },
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index b179d7b5249..93828121047 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -8,11 +8,11 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{sym, Span};
 
 use clippy_utils::attrs::is_proc_macro;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_must_use_ty;
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{return_ty, trait_ref_of_method};
@@ -129,9 +129,9 @@ fn check_needless_must_use(
             cx,
             DOUBLE_MUST_USE,
             fn_header_span,
-            "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
+            "this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`",
             None,
-            "either add some descriptive text or remove the attribute",
+            "either add some descriptive message or remove the attribute",
         );
     }
 }
@@ -155,7 +155,7 @@ fn check_must_use_candidate<'tcx>(
         return;
     }
     span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
-        if let Some(snippet) = snippet_opt(cx, fn_span) {
+        if let Some(snippet) = fn_span.get_source_text(cx) {
             diag.span_suggestion(
                 fn_span,
                 "add the attribute",
@@ -193,17 +193,13 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet)
     }
 }
 
-static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc];
-
 fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool {
     match *ty.kind() {
         // primitive types are never mutable
         ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
         ty::Adt(adt, args) => {
             tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env)
-                || KNOWN_WRAPPER_TYS
-                    .iter()
-                    .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
+                || matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc))
                     && args.types().any(|ty| is_mutable_ty(cx, ty, tys))
         },
         ty::Tuple(args) => args.iter().any(|ty| is_mutable_ty(cx, ty, tys)),
diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
index 586ca58d60d..0f5ce340c44 100644
--- a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
@@ -1,12 +1,11 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::SpanRangeExt;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_span::Span;
 
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::snippet_opt;
-
 use super::TOO_MANY_LINES;
 
 pub(super) fn check_fn(
@@ -22,57 +21,57 @@ pub(super) fn check_fn(
         return;
     }
 
-    let Some(code_snippet) = snippet_opt(cx, body.value.span) else {
-        return;
-    };
     let mut line_count: u64 = 0;
-    let mut in_comment = false;
-    let mut code_in_line;
+    let too_many = body.value.span.check_source_text(cx, |src| {
+        let mut in_comment = false;
+        let mut code_in_line;
 
-    let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
-        && code_snippet.as_bytes().first().copied() == Some(b'{')
-        && code_snippet.as_bytes().last().copied() == Some(b'}')
-    {
-        // Removing the braces from the enclosing block
-        &code_snippet[1..code_snippet.len() - 1]
-    } else {
-        &code_snippet
-    }
-    .trim() // Remove leading and trailing blank lines
-    .lines();
+        let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
+            && src.as_bytes().first().copied() == Some(b'{')
+            && src.as_bytes().last().copied() == Some(b'}')
+        {
+            // Removing the braces from the enclosing block
+            &src[1..src.len() - 1]
+        } else {
+            src
+        }
+        .trim() // Remove leading and trailing blank lines
+        .lines();
 
-    for mut line in function_lines {
-        code_in_line = false;
-        loop {
-            line = line.trim_start();
-            if line.is_empty() {
-                break;
-            }
-            if in_comment {
-                if let Some(i) = line.find("*/") {
-                    line = &line[i + 2..];
-                    in_comment = false;
-                    continue;
+        for mut line in function_lines {
+            code_in_line = false;
+            loop {
+                line = line.trim_start();
+                if line.is_empty() {
+                    break;
                 }
-            } else {
-                let multi_idx = line.find("/*").unwrap_or(line.len());
-                let single_idx = line.find("//").unwrap_or(line.len());
-                code_in_line |= multi_idx > 0 && single_idx > 0;
-                // Implies multi_idx is below line.len()
-                if multi_idx < single_idx {
-                    line = &line[multi_idx + 2..];
-                    in_comment = true;
-                    continue;
+                if in_comment {
+                    if let Some(i) = line.find("*/") {
+                        line = &line[i + 2..];
+                        in_comment = false;
+                        continue;
+                    }
+                } else {
+                    let multi_idx = line.find("/*").unwrap_or(line.len());
+                    let single_idx = line.find("//").unwrap_or(line.len());
+                    code_in_line |= multi_idx > 0 && single_idx > 0;
+                    // Implies multi_idx is below line.len()
+                    if multi_idx < single_idx {
+                        line = &line[multi_idx + 2..];
+                        in_comment = true;
+                        continue;
+                    }
                 }
+                break;
+            }
+            if code_in_line {
+                line_count += 1;
             }
-            break;
-        }
-        if code_in_line {
-            line_count += 1;
         }
-    }
+        line_count > too_many_lines_threshold
+    });
 
-    if line_count > too_many_lines_threshold {
+    if too_many {
         span_lint(
             cx,
             TOO_MANY_LINES,
diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs
index 0ebd8d0c237..120c5396a1c 100644
--- a/src/tools/clippy/clippy_lints/src/if_not_else.rs
+++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs
@@ -1,6 +1,3 @@
-//! lint on if branches that could be swapped so no `!` operation is necessary
-//! on the condition
-
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_else_clause;
diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs
index b926e1e62ba..ba06567b957 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_return.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context, wal
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro};
 use core::ops::ControlFlow;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -54,13 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
         |diag| {
             let mut app = Applicability::MachineApplicable;
             let snip = snippet_with_applicability(cx, span, "..", &mut app);
-            diag.span_suggestion_with_style(
-                span,
-                "add `return` as shown",
-                format!("return {snip}"),
-                app,
-                SuggestionStyle::ShowAlways,
-            );
+            diag.span_suggestion_verbose(span, "add `return` as shown", format!("return {snip}"), app);
         },
     );
 }
@@ -75,12 +69,11 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp
         |diag| {
             let mut app = Applicability::MachineApplicable;
             let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
-            diag.span_suggestion_with_style(
+            diag.span_suggestion_verbose(
                 break_span,
                 "change `break` to `return` as shown",
                 format!("return {snip}"),
                 app,
-                SuggestionStyle::ShowAlways,
             );
         },
     );
diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
index 0ef5b803a89..2ad045e1268 100644
--- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
+++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
@@ -7,8 +7,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TyCtxt;
-use rustc_semver::RustcVersion;
-use rustc_session::impl_lint_pass;
+use rustc_session::{impl_lint_pass, RustcVersion};
 use rustc_span::def_id::DefId;
 use rustc_span::{ExpnKind, Span};
 
@@ -65,18 +64,18 @@ impl IncompatibleMsrv {
                 StabilityLevel::Stable {
                     since: StableSince::Version(version),
                     ..
-                } => Some(RustcVersion::new(
-                    version.major.into(),
-                    version.minor.into(),
-                    version.patch.into(),
-                )),
+                } => Some(version),
                 _ => None,
             }) {
             version
         } else if let Some(parent_def_id) = tcx.opt_parent(def_id) {
             self.get_def_id_version(tcx, parent_def_id)
         } else {
-            RustcVersion::new(1, 0, 0)
+            RustcVersion {
+                major: 1,
+                minor: 0,
+                patch: 0,
+            }
         };
         self.is_above_msrv.insert(def_id, version);
         version
diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
index 5b0aadf35c6..d386bfca6ba 100644
--- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::fulfill_or_allowed;
 use clippy_utils::source::snippet;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
@@ -71,6 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
             && let ty = cx.typeck_results().expr_ty(expr)
             && let Some(adt_def) = ty.ty_adt_def()
             && adt_def.is_struct()
+            && let Some(local_def_id) = adt_def.did().as_local()
+            && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
             && let Some(variant) = adt_def.variants().iter().next()
         {
             let mut def_order_map = FxHashMap::default();
@@ -103,15 +106,17 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
                 snippet(cx, qpath.span(), ".."),
             );
 
-            span_lint_and_sugg(
-                cx,
-                INCONSISTENT_STRUCT_CONSTRUCTOR,
-                expr.span,
-                "struct constructor field order is inconsistent with struct definition field order",
-                "try",
-                sugg,
-                Applicability::MachineApplicable,
-            );
+            if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) {
+                span_lint_and_sugg(
+                    cx,
+                    INCONSISTENT_STRUCT_CONSTRUCTOR,
+                    expr.span,
+                    "struct constructor field order is inconsistent with struct definition field order",
+                    "try",
+                    sugg,
+                    Applicability::MachineApplicable,
+                );
+            }
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
index 3ac50b8f1fb..22e9674714f 100644
--- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
@@ -1,5 +1,3 @@
-//! lint on indexing and slicing operations
-
 use clippy_config::Conf;
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
index 676d50c4951..71c317552ee 100644
--- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::higher;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
 use rustc_hir::{BorrowKind, Closure, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -210,18 +210,6 @@ const COMPLETING_METHODS: [(&str, usize); 12] = [
     ("product", 0),
 ];
 
-/// the paths of types that are known to be infinitely allocating
-const INFINITE_COLLECTORS: &[Symbol] = &[
-    sym::BinaryHeap,
-    sym::BTreeMap,
-    sym::BTreeSet,
-    sym::HashMap,
-    sym::HashSet,
-    sym::LinkedList,
-    sym::Vec,
-    sym::VecDeque,
-];
-
 fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
     match expr.kind {
         ExprKind::MethodCall(method, receiver, args, _) => {
@@ -248,10 +236,19 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
                 }
             } else if method.ident.name == sym!(collect) {
                 let ty = cx.typeck_results().expr_ty(expr);
-                if INFINITE_COLLECTORS
-                    .iter()
-                    .any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item))
-                {
+                if matches!(
+                    get_type_diagnostic_name(cx, ty),
+                    Some(
+                        sym::BinaryHeap
+                            | sym::BTreeMap
+                            | sym::BTreeSet
+                            | sym::HashMap
+                            | sym::HashSet
+                            | sym::LinkedList
+                            | sym::Vec
+                            | sym::VecDeque,
+                    )
+                ) {
                     return is_infinite(cx, receiver);
                 }
             }
diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
index 9eed7aa9243..d39f910f993 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
@@ -1,5 +1,3 @@
-//! lint on inherent implementations
-
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_lint_allowed;
 use rustc_data_structures::fx::FxHashMap;
diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
index 5657c58bb0a..1b900f6be8e 100644
--- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
+++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
@@ -1,5 +1,3 @@
-//! checks for `#[inline]` on trait methods without bodies
-
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sugg::DiagExt;
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/int_plus_one.rs b/src/tools/clippy/clippy_lints/src/int_plus_one.rs
index b8e0eef7c7e..fc575bff7e6 100644
--- a/src/tools/clippy/clippy_lints/src/int_plus_one.rs
+++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs
@@ -1,7 +1,5 @@
-//! lint on blocks unnecessarily using >= with a + 1 or - 1
-
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind};
 use rustc_ast::token;
 use rustc_errors::Applicability;
@@ -132,8 +130,8 @@ impl IntPlusOne {
             BinOpKind::Le => "<",
             _ => return None,
         };
-        if let Some(snippet) = snippet_opt(cx, node.span) {
-            if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
+        if let Some(snippet) = node.span.get_source_text(cx) {
+            if let Some(other_side_snippet) = other_side.span.get_source_text(cx) {
                 let rec = match side {
                     Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
                     Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
index 4d44bae02b8..74bf82f58bd 100644
--- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
+++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
@@ -1,5 +1,3 @@
-//! lint on enum variants that are prefixed or suffixed by the same characters
-
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
 use clippy_utils::is_bool;
diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
index a88d8e24fda..4f066113aea 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
@@ -1,5 +1,3 @@
-//! lint when items are used after statements
-
 use clippy_utils::diagnostics::span_lint_hir;
 use rustc_hir::{Block, ItemKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
index 3614fb8cc96..8caa484bb99 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro};
 use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::{HirId, Item, ItemKind, Mod};
@@ -93,11 +93,14 @@ impl LateLintPass<'_> for ItemsAfterTestModule {
                     if let Some(prev) = mod_pos.checked_sub(1)
                         && let prev = cx.tcx.hir().item(module.item_ids[prev])
                         && let items_span = last.span.with_lo(test_mod.span.hi())
-                        && let Some(items) = snippet_opt(cx, items_span)
+                        && let Some(items) = items_span.get_source_text(cx)
                     {
                         diag.multipart_suggestion_with_style(
                             "move the items to before the test module was defined",
-                            vec![(prev.span.shrink_to_hi(), items), (items_span, String::new())],
+                            vec![
+                                (prev.span.shrink_to_hi(), items.to_owned()),
+                                (items_span, String::new()),
+                            ],
                             Applicability::MachineApplicable,
                             SuggestionStyle::HideCodeAlways,
                         );
diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
index 225d79aa71d..2f027c11707 100644
--- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
+++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
@@ -1,5 +1,3 @@
-//! lint when there is a large size difference between variants on an enum
-
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
index 4abf7edc9b4..d2bdf194ada 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
@@ -3,7 +3,7 @@ use std::{fmt, ops};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::fn_has_unsatisfiable_preds;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl};
@@ -186,7 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames {
                         // TODO: Is there a cleaner, robust way to ask this question?
                         // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data",
                         // and that doesn't get us the true name in scope rather than the span text either.
-                        if let Some(name) = snippet_opt(cx, local_span)
+                        if let Some(name) = local_span.get_source_text(cx)
                             && is_ident(&name)
                         {
                             // If the local is an ordinary named variable,
diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
index 752e1326e3e..ccab1e27d3b 100644
--- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
@@ -3,7 +3,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::{get_parent_expr, is_from_proc_macro};
 use hir::def_id::DefId;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -143,12 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
             && !is_from_proc_macro(cx, expr)
         {
             span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| {
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     span,
                     "use the associated constant instead",
                     sugg,
                     Applicability::MaybeIncorrect,
-                    SuggestionStyle::ShowAlways,
                 );
             });
         }
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 4c737371bd2..0bc7fc2dd9d 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet_opt, snippet_with_context};
+use clippy_utils::source::{snippet_with_context, SpanRangeExt};
 use clippy_utils::sugg::{has_enclosing_paren, Sugg};
 use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
 use rustc_ast::ast::LitKind;
@@ -216,7 +216,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
 }
 
 fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span {
-    let Some(snippet) = snippet_opt(cx, span) else {
+    let Some(snippet) = span.get_source_text(cx) else {
         return span;
     };
     if has_enclosing_paren(snippet) {
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index ce13a9afef5..2ac06b360be 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -66,8 +66,8 @@ extern crate declare_clippy_lint;
 #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 mod utils;
 
-mod declared_lints;
-mod deprecated_lints;
+pub mod declared_lints;
+pub mod deprecated_lints;
 
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
 mod absolute_paths;
@@ -440,7 +440,7 @@ impl RegistrationGroups {
     }
 }
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 pub(crate) enum LintCategory {
     Cargo,
     Complexity,
@@ -479,11 +479,39 @@ impl LintCategory {
     }
 }
 
-pub(crate) struct LintInfo {
+pub struct LintInfo {
     /// Double reference to maintain pointer equality
-    lint: &'static &'static Lint,
+    pub lint: &'static &'static Lint,
     category: LintCategory,
-    explanation: &'static str,
+    pub explanation: &'static str,
+    /// e.g. `clippy_lints/src/absolute_paths.rs#43`
+    pub location: &'static str,
+    pub version: Option<&'static str>,
+}
+
+impl LintInfo {
+    /// Returns the lint name in lowercase without the `clippy::` prefix
+    #[allow(clippy::missing_panics_doc)]
+    pub fn name_lower(&self) -> String {
+        self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase()
+    }
+
+    /// Returns the name of the lint's category in lowercase (`style`, `pedantic`)
+    pub fn category_str(&self) -> &'static str {
+        match self.category {
+            Cargo => "cargo",
+            Complexity => "complexity",
+            Correctness => "correctness",
+            Nursery => "nursery",
+            Pedantic => "pedantic",
+            Perf => "perf",
+            Restriction => "restriction",
+            Style => "style",
+            Suspicious => "suspicious",
+            #[cfg(feature = "internal")]
+            Internal => "internal",
+        }
+    }
 }
 
 pub fn explain(name: &str) -> i32 {
@@ -538,14 +566,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         store.register_removed(name, reason);
     }
 
-    #[cfg(feature = "internal")]
-    {
-        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
-            store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
-            return;
-        }
-    }
-
     let format_args_storage = FormatArgsStorage::default();
     let format_args = format_args_storage.clone();
     store.register_early_pass(move || {
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index 259e4d6c08f..81f2a03fb55 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -1,10 +1,7 @@
-//! Lints concerned with the grouping of digits with underscores in integral or
-//! floating-point literal expressions.
-
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::numeric_literal::{NumericLiteral, Radix};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::ast::{Expr, ExprKind, LitKind};
 use rustc_ast::token;
 use rustc_errors::Applicability;
@@ -228,7 +225,7 @@ impl LiteralDigitGrouping {
     }
 
     fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
-        if let Some(src) = snippet_opt(cx, span)
+        if let Some(src) = span.get_source_text(cx)
             && let Ok(lit_kind) = LitKind::from_token_lit(lit)
             && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
         {
@@ -442,7 +439,7 @@ impl DecimalLiteralRepresentation {
         // Lint integral literals.
         if let Ok(lit_kind) = LitKind::from_token_lit(lit)
             && let LitKind::Int(val, _) = lit_kind
-            && let Some(src) = snippet_opt(cx, span)
+            && let Some(src) = span.get_source_text(cx)
             && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
             && num_lit.radix == Radix::Decimal
             && val >= u128::from(self.threshold)
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
index eea5f2a94ea..b134af500f5 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -3,7 +3,7 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{
-    implements_trait, implements_trait_with_env, is_copy, make_normalized_projection,
+    implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection,
     make_normalized_projection_with_regions, normalize_with_regions,
 };
 use rustc_errors::Applicability;
@@ -20,9 +20,10 @@ pub(super) fn check(
     msrv: &Msrv,
     enforce_iter_loop_reborrow: bool,
 ) {
-    let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow) else {
+    let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow, msrv) else {
         return;
     };
+
     if let ty::Array(_, count) = *ty.peel_refs().kind() {
         if !ty.is_ref() {
             if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
@@ -109,6 +110,7 @@ fn is_ref_iterable<'tcx>(
     self_arg: &Expr<'_>,
     call_expr: &Expr<'_>,
     enforce_iter_loop_reborrow: bool,
+    msrv: &Msrv,
 ) -> Option<(AdjustKind, Ty<'tcx>)> {
     let typeck = cx.typeck_results();
     if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
@@ -128,6 +130,12 @@ fn is_ref_iterable<'tcx>(
         let self_ty = typeck.expr_ty(self_arg);
         let self_is_copy = is_copy(cx, self_ty);
 
+        if !msrv.meets(msrvs::BOX_INTO_ITER)
+            && is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox)
+        {
+            return None;
+        }
+
         if adjustments.is_empty() && self_is_copy {
             // Exact type match, already checked earlier
             return Some((AdjustKind::None, self_ty));
diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
index fed58f7ff14..e215097142b 100644
--- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
@@ -122,8 +122,23 @@ struct BodyVisitor<'a, 'tcx> {
     /// within a relevant macro.
     macro_unsafe_blocks: Vec<HirId>,
     /// When this is >0, it means that the node currently being visited is "within" a
-    /// macro definition. This is not necessary for correctness, it merely helps reduce the number
-    /// of spans we need to insert into the map, since only spans from macros are relevant.
+    /// macro definition.
+    /// This is used to detect if an expression represents a metavariable.
+    ///
+    /// For example, the following pre-expansion code that we want to lint
+    /// ```ignore
+    /// macro_rules! m { ($e:expr) => { unsafe { $e; } } }
+    /// m!(1);
+    /// ```
+    /// would look like this post-expansion code:
+    /// ```ignore
+    /// unsafe { /* macro */
+    ///     1 /* root */; /* macro */
+    /// }
+    /// ```
+    /// Visiting the block and the statement will increment the `expn_depth` so that it is >0,
+    /// and visiting the expression with a root context while `expn_depth > 0` tells us
+    /// that it must be a metavariable.
     expn_depth: u32,
     cx: &'a LateContext<'tcx>,
     lint: &'a mut ExprMetavarsInUnsafe,
@@ -157,7 +172,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> {
             && (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id))
         {
             self.macro_unsafe_blocks.push(block.hir_id);
+            self.expn_depth += 1;
             walk_block(self, block);
+            self.expn_depth -= 1;
             self.macro_unsafe_blocks.pop();
         } else if ctxt.is_root() && self.expn_depth > 0 {
             let unsafe_block = self.macro_unsafe_blocks.last().copied();
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 25c7e5d38b3..61723aec590 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
+use clippy_utils::source::{position_before_rarrow, snippet_block, SpanRangeExt};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
@@ -68,8 +68,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
                 header_span,
                 "this function can be simplified using the `async fn` syntax",
                 |diag| {
-                    if let Some(vis_snip) = snippet_opt(cx, *vis_span)
-                        && let Some(header_snip) = snippet_opt(cx, header_span)
+                    if let Some(vis_snip) = vis_span.get_source_text(cx)
+                        && let Some(header_snip) = header_span.get_source_text(cx)
                         && let Some(ret_pos) = position_before_rarrow(&header_snip)
                         && let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output)
                     {
@@ -190,6 +190,6 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
         Some((sugg, String::new()))
     } else {
         let sugg = "return the output of the future directly";
-        snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {snip}")))
+        output.span.get_source_text(cx).map(|src| (sugg, format!(" -> {src}")))
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
index 6bdc79129a9..9d3ddab60bb 100644
--- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
@@ -1,6 +1,6 @@
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_from_proc_macro, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Constness, Expr, ExprKind};
@@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
             // case somebody does that for some reason
             && (is_infinity(&const_1) && is_neg_infinity(&const_2)
                 || is_neg_infinity(&const_1) && is_infinity(&const_2))
-            && let Some(local_snippet) = snippet_opt(cx, first.span)
+            && let Some(local_snippet) = first.span.get_source_text(cx)
         {
             let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
                 (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite,
diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
index 1c568b1b74f..11716a539ab 100644
--- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
@@ -1,7 +1,7 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::visitors::{is_local_used, local_used_once};
 use clippy_utils::{is_trait_method, path_to_local_id};
 use rustc_errors::Applicability;
@@ -107,8 +107,8 @@ impl LateLintPass<'_> for ManualHashOne {
                 finish_expr.span,
                 "manual implementation of `BuildHasher::hash_one`",
                 |diag| {
-                    if let Some(build_hasher) = snippet_opt(cx, build_hasher.span)
-                        && let Some(hashed_value) = snippet_opt(cx, hashed_value.span)
+                    if let Some(build_hasher) = build_hasher.span.get_source_text(cx)
+                        && let Some(hashed_value) = hashed_value.span.get_source_text(cx)
                     {
                         diag.multipart_suggestion(
                             "try",
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 28cfe22835a..6b1d90483cc 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::is_doc_hidden;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::ast::{self, VisibilityKind};
 use rustc_ast::attr;
 use rustc_data_structures::fx::FxHashSet;
@@ -124,7 +124,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
                     |diag| {
                         if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
                             && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
-                            && let Some(snippet) = snippet_opt(cx, header_span)
+                            && let Some(snippet) = header_span.get_source_text(cx)
                         {
                             diag.span_suggestion(
                                 header_span,
@@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
                     let header_span = cx.sess().source_map().span_until_char(enum_span, '{');
-                    if let Some(snippet) = snippet_opt(cx, header_span) {
+                    if let Some(snippet) = header_span.get_source_text(cx) {
                         diag.span_suggestion(
                             header_span,
                             "add the attribute",
diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
index 07d4abbf5cd..9afb2b5bc84 100644
--- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -143,8 +143,8 @@ impl LateLintPass<'_> for ManualRangePatterns {
                 pat.span,
                 "this OR pattern can be rewritten using a range",
                 |diag| {
-                    if let Some(min) = snippet_opt(cx, min.span)
-                        && let Some(max) = snippet_opt(cx, max.span)
+                    if let Some(min) = min.span.get_source_text(cx)
+                        && let Some(max) = max.span.get_source_text(cx)
                     {
                         diag.span_suggestion(
                             pat.span,
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index 09f6362b4dd..d4e53f8f74b 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -2,14 +2,13 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
 use clippy_utils::{match_def_path, paths, SpanlessEq};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::ExprKind::Assign;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
@@ -21,16 +20,6 @@ const ACCEPTABLE_METHODS: [&[&str]; 5] = [
     &paths::SLICE_INTO,
     &paths::VEC_DEQUE_ITER,
 ];
-const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 7] = [
-    (sym::BinaryHeap, Some(msrvs::BINARY_HEAP_RETAIN)),
-    (sym::BTreeSet, Some(msrvs::BTREE_SET_RETAIN)),
-    (sym::BTreeMap, Some(msrvs::BTREE_MAP_RETAIN)),
-    (sym::HashSet, Some(msrvs::HASH_SET_RETAIN)),
-    (sym::HashMap, Some(msrvs::HASH_MAP_RETAIN)),
-    (sym::Vec, None),
-    (sym::VecDeque, None),
-];
-const MAP_TYPES: [rustc_span::Symbol; 2] = [sym::BTreeMap, sym::HashMap];
 
 declare_clippy_lint! {
     /// ### What it does
@@ -265,16 +254,22 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo
 }
 
 fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool {
-    let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
-    ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
-        is_type_diagnostic_item(cx, expr_ty, *ty)
-            && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv))
-    })
+    let ty = cx.typeck_results().expr_ty(expr).peel_refs();
+    let required = match get_type_diagnostic_name(cx, ty) {
+        Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN,
+        Some(sym::BTreeSet) => msrvs::BTREE_SET_RETAIN,
+        Some(sym::BTreeMap) => msrvs::BTREE_MAP_RETAIN,
+        Some(sym::HashSet) => msrvs::HASH_SET_RETAIN,
+        Some(sym::HashMap) => msrvs::HASH_MAP_RETAIN,
+        Some(sym::Vec | sym::VecDeque) => return true,
+        _ => return false,
+    };
+    msrv.meets(required)
 }
 
 fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
-    let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
-    MAP_TYPES.iter().any(|ty| is_type_diagnostic_item(cx, expr_ty, *ty))
+    let ty = cx.typeck_results().expr_ty(expr).peel_refs();
+    matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap))
 }
 
 fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) {
diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
index 90cfdecc199..b666e77c09e 100644
--- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
@@ -96,7 +96,7 @@ fn check_arm<'tcx>(
         // collapsing patterns need an explicit field name in struct pattern matching
         // ex: Struct {x: Some(1)}
         let replace_msg = if is_innermost_parent_pat_struct {
-            format!(", prefixed by {}:", snippet(cx, binding_span, "their field name"))
+            format!(", prefixed by `{}`:", snippet(cx, binding_span, "their field name"))
         } else {
             String::new()
         };
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
index 2d4c8daf5cb..d5d83df9347 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
@@ -1,6 +1,6 @@
 use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::contains_return_break_continue_macro;
 use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg};
@@ -67,11 +67,11 @@ fn check_and_lint<'tcx>(
         && path_to_local_id(peel_blocks(then_expr), binding_hir_id)
         && cx.typeck_results().expr_adjustments(then_expr).is_empty()
         && let Some(ty_name) = find_type_name(cx, ty)
-        && let Some(or_body_snippet) = snippet_opt(cx, else_expr.span)
+        && let Some(or_body_snippet) = else_expr.span.get_source_text(cx)
         && let Some(indent) = indent_of(cx, expr.span)
         && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some()
     {
-        lint(cx, expr, let_expr, ty_name, or_body_snippet, indent);
+        lint(cx, expr, let_expr, ty_name, &or_body_snippet, indent);
     }
 }
 
@@ -110,7 +110,7 @@ fn lint<'tcx>(
     expr: &Expr<'tcx>,
     scrutinee: &'tcx Expr<'_>,
     ty_name: &str,
-    or_body_snippet: String,
+    or_body_snippet: &str,
     indent: usize,
 ) {
     let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent));
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index 24dea03601c..b6930f7b9d1 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -22,7 +22,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
 /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
 /// match arms.
 fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
-    if let Some(ff) = span.get_source_text(cx)
+    if let Some(ff) = span.get_source_range(cx)
         && let Some(text) = ff.as_str()
     {
         text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")
diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
index a5df863d800..740cce0a6c2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
 use clippy_utils::ty::is_type_lang_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -49,10 +49,12 @@ pub(super) fn check<'tcx>(
             "case-sensitive file extension comparison",
             |diag| {
                 diag.help("consider using a case-insensitive comparison instead");
-                if let Some(mut recv_source) = snippet_opt(cx, recv.span) {
-                    if !cx.typeck_results().expr_ty(recv).is_ref() {
-                        recv_source = format!("&{recv_source}");
-                    }
+                if let Some(recv_source) = recv.span.get_source_text(cx) {
+                    let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() {
+                        recv_source.to_owned()
+                    } else {
+                        format!("&{recv_source}")
+                    };
 
                     let suggestion_source = reindent_multiline(
                         format!(
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
index 2e43d19a699..4fbf661727d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
@@ -1,7 +1,7 @@
 use super::FILTER_MAP_BOOL_THEN;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::paths::BOOL_THEN;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_copy;
 use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks};
 use rustc_errors::Applicability;
@@ -42,9 +42,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
             .iter()
             .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
             .count()
-        && let Some(param_snippet) = snippet_opt(cx, param.span)
-        && let Some(filter) = snippet_opt(cx, recv.span)
-        && let Some(map) = snippet_opt(cx, then_body.span)
+        && let Some(param_snippet) = param.span.get_source_text(cx)
+        && let Some(filter) = recv.span.get_source_text(cx)
+        && let Some(map) = then_body.span.get_source_text(cx)
     {
         span_lint_and_sugg(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
index 917a8e33eb9..f4840785584 100644
--- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{is_path_diagnostic_item, sugg};
 use rustc_errors::Applicability;
@@ -39,7 +39,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) ->
     }
 
     let call_site = expr.span.source_callsite();
-    if let Some(snippet) = snippet_opt(cx, call_site)
+    if let Some(snippet) = call_site.get_source_text(cx)
         && let snippet_split = snippet.split("::").collect::<Vec<_>>()
         && let Some((_, elements)) = snippet_split.split_last()
     {
diff --git a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
index c6285c87a26..9daad1a8a94 100644
--- a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
@@ -74,7 +74,7 @@ pub(super) fn check<'tcx>(
                 "&"
             };
 
-            diag.span_suggestion_with_style(
+            diag.span_suggestion_verbose(
                 span,
                 "using `[]` is clearer and more concise",
                 format!(
@@ -82,7 +82,6 @@ pub(super) fn check<'tcx>(
                     snippet_with_applicability(cx, recv.span, "..", &mut applicability)
                 ),
                 applicability,
-                rustc_errors::SuggestionStyle::ShowAlways,
             );
         },
     );
diff --git a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
index 22d896433f0..40b48ccca5d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -1,5 +1,3 @@
-//! Lint for `c.is_digit(10)`
-
 use super::IS_DIGIT_ASCII_RADIX;
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, FullInt};
diff --git a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs
index aa1ec60d434..2dad7fcf3c1 100644
--- a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::expr_or_init;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a
             join_arg.span,
             "argument to `Path::join` starts with a path separator",
             |diag| {
-                let arg_str = snippet_opt(cx, spanned.span).unwrap_or_else(|| "..".to_string());
+                let arg_str = snippet(cx, spanned.span, "..");
 
                 let no_separator = if sym_str.starts_with('/') {
                     arg_str.replacen('/', "", 1)
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
index b1af0083e65..b1a8e1e5e47 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
 use rustc_errors::Applicability;
@@ -23,11 +23,11 @@ pub(super) fn check<'tcx>(
         && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind
         && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr)
         && is_ok_wrapping(cx, map_expr)
-        && let Some(recv_snippet) = snippet_opt(cx, recv.span)
-        && let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span)
+        && let Some(recv_snippet) = recv.span.get_source_text(cx)
+        && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx)
         && let Some(indent) = indent_of(cx, expr.span)
     {
-        let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
+        let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str().into(), true, Some(indent + 4));
         span_lint_and_sugg(
             cx,
             MANUAL_OK_OR,
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
index f93edded729..11fba35c16e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{is_from_proc_macro, is_trait_method};
 use rustc_errors::Applicability;
@@ -31,13 +31,15 @@ pub(super) fn check<'tcx>(
         && let Res::Def(DefKind::Ctor(_, _), _) = cx.qpath_res(&qpath, path.hir_id)
         && let ExprKind::Closure(closure) = acc.kind
         && !is_from_proc_macro(cx, expr)
-        && let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| snippet_opt(cx, fn_arg_span))
+        && let Some(args_snip) = closure
+            .fn_arg_span
+            .and_then(|fn_arg_span| fn_arg_span.get_source_text(cx))
     {
         let init_snip = rest
             .is_empty()
             .then_some(first.span)
-            .and_then(|span| snippet_opt(cx, span))
-            .unwrap_or("...".to_owned());
+            .and_then(|span| span.get_source_text(cx))
+            .map_or_else(|| "...".to_owned(), |src| src.to_owned());
 
         span_lint_and_sugg(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 1d7b10fe8f0..d7126990edb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -2702,10 +2702,10 @@ declare_clippy_lint! {
     ///             }
     ///         })
     /// }
-    ///  ```
+    /// ```
     ///
-    ///  After:
-    ///  ```rust
+    /// After:
+    /// ```rust
     /// use std::{fmt, num::ParseIntError};
     ///
     /// #[derive(Debug)]
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs
index e3d78207715..332da722a37 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs
@@ -7,7 +7,7 @@ use rustc_span::Span;
 use super::utils::get_last_chain_binding_hir_id;
 use super::NEEDLESS_CHARACTER_ITERATION;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
 
 fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
@@ -35,7 +35,7 @@ fn handle_expr(
                 && path_to_local_id(receiver, first_param)
                 && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
                 && *char_arg_ty.kind() == ty::Char
-                && let Some(snippet) = snippet_opt(cx, before_chars)
+                && let Some(snippet) = before_chars.get_source_text(cx)
             {
                 span_lint_and_sugg(
                     cx,
@@ -79,7 +79,7 @@ fn handle_expr(
                 && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
                 && match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
                 && path_to_local_id(peels_expr_ref(arg), first_param)
-                && let Some(snippet) = snippet_opt(cx, before_chars)
+                && let Some(snippet) = before_chars.get_source_text(cx)
             {
                 span_lint_and_sugg(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index 46b457daf70..f61923e5bf5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -2,7 +2,7 @@ use super::NEEDLESS_COLLECT;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection};
+use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection};
 use clippy_utils::{
     can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id,
     CaptureKind,
@@ -88,9 +88,10 @@ pub(super) fn check<'tcx>(
         Node::LetStmt(l) => {
             if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind
                 && let ty = cx.typeck_results().expr_ty(collect_expr)
-                && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
-                    .into_iter()
-                    .any(|item| is_type_diagnostic_item(cx, ty, item))
+                && matches!(
+                    get_type_diagnostic_name(cx, ty),
+                    Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList)
+                )
                 && let iter_ty = cx.typeck_results().expr_ty(iter_expr)
                 && let Some(block) = get_enclosing_block(cx, l.hir_id)
                 && let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs
index eaae8613d44..9f714fdd47b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::path_res;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::local_used_after_expr;
 use rustc_errors::Applicability;
@@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name
             expr.span,
             "derefed type is same as origin",
             "try",
-            snippet_opt(cx, recv.span).unwrap(),
+            recv.span.get_source_text(cx).unwrap().to_owned(),
             Applicability::MachineApplicable,
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
index 5f6f027a3b5..cc0d432b799 100644
--- a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local};
 use itertools::Itertools;
 use rustc_ast::LitKind;
@@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(
             _ => return,
         }
         && !is_from_proc_macro(cx, expr)
-        && let Some(scrutinee_snip) = snippet_opt(cx, scrutinee.span)
+        && let Some(scrutinee_snip) = scrutinee.span.get_source_text(cx)
     {
         // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then
         // something like `r"\"` will become `'\'`, which is of course invalid
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs
index f6184222d8e..64eb8411795 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_type_diagnostic_item;
 
 use rustc_errors::Applicability;
@@ -38,11 +38,11 @@ pub(super) fn check(
         return;
     };
     let both_calls_span = get_call_span.with_hi(call_span.hi());
-    if let Some(snippet) = snippet_opt(cx, both_calls_span)
-        && let Some(arg_snippet) = snippet_opt(cx, arg.span)
+    if let Some(snippet) = both_calls_span.get_source_text(cx)
+        && let Some(arg_snippet) = arg.span.get_source_text(cx)
     {
         let generics_snippet = if let Some(generics) = path.args
-            && let Some(generics_snippet) = snippet_opt(cx, generics.span_ext)
+            && let Some(generics_snippet) = generics.span_ext.get_source_text(cx)
         {
             format!("::{generics_snippet}")
         } else {
@@ -63,7 +63,7 @@ pub(super) fn check(
                 suggestion,
                 Applicability::MaybeIncorrect,
             );
-        } else if let Some(caller_snippet) = snippet_opt(cx, get_caller.span) {
+        } else if let Some(caller_snippet) = get_caller.span.get_source_text(cx) {
             let full_span = get_caller.span.with_hi(call_span.hi());
 
             span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
index 70885e46e95..4d18bc7ac77 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
@@ -1,7 +1,7 @@
 use super::utils::clone_or_copy_needed;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::ForLoop;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
@@ -40,7 +40,7 @@ pub fn check_for_loop_iter(
         && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent)
         && let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body)
         && !clone_or_copy_needed
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         // Issue 12098
         // https://github.com/rust-lang/rust-clippy/issues/12098
@@ -100,7 +100,7 @@ pub fn check_for_loop_iter(
             && implements_trait(cx, collection_ty, into_iterator_trait_id, &[])
             && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item")
             && iter_item_ty == into_iter_item_ty
-            && let Some(collection_snippet) = snippet_opt(cx, collection.span)
+            && let Some(collection_snippet) = collection.span.get_source_text(cx)
         {
             collection_snippet
         } else {
@@ -122,7 +122,7 @@ pub fn check_for_loop_iter(
                 } else {
                     Applicability::MachineApplicable
                 };
-                diag.span_suggestion(expr.span, "use", snippet, applicability);
+                diag.span_suggestion(expr.span, "use", snippet.to_owned(), applicability);
                 if !references_to_binding.is_empty() {
                     diag.multipart_suggestion(
                         "remove any references to the binding",
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index 4429f032605..b84594c0da1 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -64,9 +64,9 @@ pub(super) fn check<'tcx>(
                 // but prefer to avoid changing the signature of the function itself.
                 if let hir::ExprKind::MethodCall(.., span) = expr.kind {
                     span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
-                        diag.span_suggestion(
+                        diag.span_suggestion_verbose(
                             span,
-                            format!("use `{simplify_using}(..)` instead"),
+                            format!("use `{simplify_using}` instead"),
                             format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")),
                             applicability,
                         );
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index fed2b128dcf..69c5bc57e29 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -2,7 +2,7 @@ use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::visitors::find_all_ret_expressions;
 use clippy_utils::{
@@ -133,7 +133,7 @@ fn check_addr_of_expr(
         && (*referent_ty != receiver_ty
             || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty))
             || is_cow_into_owned(cx, method_name, method_def_id))
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
             span_lint_and_sugg(
@@ -167,7 +167,7 @@ fn check_addr_of_expr(
                     parent.span,
                     format!("unnecessary use of `{method_name}`"),
                     "use",
-                    receiver_snippet,
+                    receiver_snippet.to_owned(),
                     Applicability::MachineApplicable,
                 );
             } else {
@@ -217,7 +217,7 @@ fn check_into_iter_call_arg(
         && let parent_ty = cx.typeck_results().expr_ty(parent)
         && implements_trait(cx, parent_ty, iterator_trait_id, &[])
         && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty)
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
             return true;
@@ -309,8 +309,8 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb
     if let Some(parent) = get_parent_expr(cx, expr)
         && let Some((fn_name, argument_expr)) = get_fn_name_and_arg(cx, parent)
         && fn_name.as_str() == "split"
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
-        && let Some(arg_snippet) = snippet_opt(cx, argument_expr.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
+        && let Some(arg_snippet) = argument_expr.span.get_source_text(cx)
     {
         // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x`
         // implements `AsRef<str>` but does not implement `Deref<Target = str>`. In this case, we have to
@@ -405,7 +405,7 @@ fn check_other_call_arg<'tcx>(
             None
         }
         && can_change_type(cx, maybe_arg, receiver_ty)
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         span_lint_and_sugg(
             cx,
@@ -695,7 +695,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx
         && let arg_ty = arg_ty.peel_refs()
         // For now we limit this lint to `String` and `Vec`.
         && (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty))
-        && let Some(snippet) = snippet_opt(cx, caller.span)
+        && let Some(snippet) = caller.span.get_source_text(cx)
     {
         span_lint_and_sugg(
             cx,
@@ -706,7 +706,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx
             if original_arg_ty.is_array() {
                 format!("{snippet}.as_slice()")
             } else {
-                snippet
+                snippet.to_owned()
             },
             Applicability::MaybeIncorrect,
         );
diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
index 3004d9c4233..ee5177d1ffa 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
@@ -81,8 +81,14 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
         let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) {
             // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`.
             // Fallback to `..` if we fail getting either snippet.
-            Some(ty_span) => snippet_opt(cx, elem.span)
-                .and_then(|binding_name| snippet_opt(cx, ty_span).map(|ty_name| format!("{binding_name}: {ty_name}")))
+            Some(ty_span) => elem
+                .span
+                .get_source_text(cx)
+                .and_then(|binding_name| {
+                    ty_span
+                        .get_source_text(cx)
+                        .map(|ty_name| format!("{binding_name}: {ty_name}"))
+                })
                 .unwrap_or_else(|| "..".to_string()),
             // Otherwise, we have no explicit type. We can replace with the binding name of the element.
             None => snippet(cx, elem.span, "..").into_owned(),
diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
index 53fa444e93c..c83e5198c27 100644
--- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
+++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
@@ -41,14 +41,14 @@ declare_clippy_lint! {
 impl_lint_pass!(MinIdentChars => [MIN_IDENT_CHARS]);
 
 pub struct MinIdentChars {
-    allowed_idents_below_min_chars: &'static FxHashSet<String>,
+    allowed_idents_below_min_chars: FxHashSet<String>,
     min_ident_chars_threshold: u64,
 }
 
 impl MinIdentChars {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
-            allowed_idents_below_min_chars: &conf.allowed_idents_below_min_chars,
+            allowed_idents_below_min_chars: conf.allowed_idents_below_min_chars.iter().cloned().collect(),
             min_ident_chars_threshold: conf.min_ident_chars_threshold,
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs
index 6db03adf44a..33ab94e00a5 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
+use itertools::Itertools;
 use rustc_ast::ast::{Pat, PatKind};
 use rustc_lint::EarlyContext;
 
@@ -45,19 +46,6 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
                             "you matched a field with a wildcard pattern, consider using `..` instead",
                         );
                     } else {
-                        let mut normal = vec![];
-
-                        for field in pfields {
-                            match field.pat.kind {
-                                PatKind::Wild => {},
-                                _ => {
-                                    if let Some(n) = snippet_opt(cx, field.span) {
-                                        normal.push(n);
-                                    }
-                                },
-                            }
-                        }
-
                         #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
                         span_lint_and_then(
                             cx,
@@ -65,7 +53,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
                             field.span,
                             "you matched a field with a wildcard pattern, consider using `..` instead",
                             |diag| {
-                                diag.help(format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")));
+                                diag.help(format!(
+                                    "try with `{type_name} {{ {}, .. }}`",
+                                    pfields
+                                        .iter()
+                                        .filter_map(|f| match f.pat.kind {
+                                            PatKind::Wild => None,
+                                            _ => f.span.get_source_text(cx),
+                                        })
+                                        .format(", "),
+                                ));
                             },
                         );
                     }
diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
index cdf6645b3df..59f29d242ab 100644
--- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
 use clippy_utils::def_path_def_ids;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::DefIdMap;
@@ -73,7 +73,7 @@ impl LateLintPass<'_> for ImportRename {
                     && let Some(name) = self.renames.get(&id)
                     // Remove semicolon since it is not present for nested imports
                     && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';')
-                    && let Some(snip) = snippet_opt(cx, span_without_semi)
+                    && let Some(snip) = span_without_semi.get_source_text(cx)
                     && let Some(import) = match snip.split_once(" as ") {
                         None => Some(snip.as_str()),
                         Some((import, rename)) => {
diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs
index 83af9979b9e..f52b3a6a5a1 100644
--- a/src/tools/clippy/clippy_lints/src/mut_key.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_key.rs
@@ -125,14 +125,12 @@ impl<'tcx> MutableKeyType<'tcx> {
     // generics (because the compiler cannot ensure immutability for unknown types).
     fn check_ty_(&mut self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
         let ty = ty.peel_refs();
-        if let ty::Adt(def, args) = ty.kind() {
-            let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
-                .iter()
-                .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
-            if !is_keyed_type {
-                return;
-            }
-
+        if let ty::Adt(def, args) = ty.kind()
+            && matches!(
+                cx.tcx.get_diagnostic_name(def.did()),
+                Some(sym::HashMap | sym::BTreeMap | sym::HashSet | sym::BTreeSet)
+            )
+        {
             let subst_ty = args.type_at(0);
             if self.interior_mut.is_interior_mut_ty(cx, subst_ty) {
                 span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
index 853e476a006..38841496458 100644
--- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
+++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
@@ -1,7 +1,3 @@
-//! Checks for usage of mutex where an atomic value could be used
-//!
-//! This lint is **allow** by default
-
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir::Expr;
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index 9cb4fa41c73..df155a7a412 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -1,7 +1,3 @@
-//! Checks for needless boolean results of if-else expressions
-//!
-//! This lint is **warn** by default
-
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs
index b97cb4579ca..ce97370d4d9 100644
--- a/src/tools/clippy/clippy_lints/src/needless_continue.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs
@@ -1,38 +1,3 @@
-//! Checks for continue statements in loops that are redundant.
-//!
-//! For example, the lint would catch
-//!
-//! ```rust
-//! let mut a = 1;
-//! let x = true;
-//!
-//! while a < 5 {
-//!     a = 6;
-//!     if x {
-//!         // ...
-//!     } else {
-//!         continue;
-//!     }
-//!     println!("Hello, world");
-//! }
-//! ```
-//!
-//! And suggest something like this:
-//!
-//! ```rust
-//! let mut a = 1;
-//! let x = true;
-//!
-//! while a < 5 {
-//!     a = 6;
-//!     if x {
-//!         // ...
-//!         println!("Hello, world");
-//!     }
-//! }
-//! ```
-//!
-//! This lint is **warn** by default.
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::{indent_of, snippet, snippet_block};
 use rustc_ast::ast;
diff --git a/src/tools/clippy/clippy_lints/src/needless_if.rs b/src/tools/clippy/clippy_lints/src/needless_if.rs
index 1d6233d432a..8e14fbf2f80 100644
--- a/src/tools/clippy/clippy_lints/src/needless_if.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_if.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher::If;
 use clippy_utils::is_from_proc_macro;
-use clippy_utils::source::{snippet_opt, SpanRangeExt};
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::{ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -57,7 +57,7 @@ impl LateLintPass<'_> for NeedlessIf {
                 src.bytes()
                     .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace())
             })
-            && let Some(cond_snippet) = snippet_opt(cx, cond.span)
+            && let Some(cond_snippet) = cond.span.get_source_text(cx)
             && !is_from_proc_macro(cx, expr)
         {
             span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
index 4bfc30fa5cf..46cdb82130f 100644
--- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::path_to_local;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::{SourceText, SpanRangeExt};
 use clippy_utils::ty::needs_ordered_drop;
 use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used};
 use core::ops::ControlFlow;
@@ -236,7 +236,7 @@ fn first_usage<'tcx>(
         })
 }
 
-fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option<String> {
+fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option<SourceText> {
     let span = local.span.with_hi(match local.ty {
         // let <pat>: <ty>;
         // ~~~~~~~~~~~~~~~
@@ -246,7 +246,7 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) ->
         None => local.pat.span.hi(),
     });
 
-    snippet_opt(cx, span)
+    span.get_source_text(cx)
 }
 
 fn check<'tcx>(
@@ -275,7 +275,10 @@ fn check<'tcx>(
                 |diag| {
                     diag.multipart_suggestion(
                         format!("move the declaration `{binding_name}` here"),
-                        vec![(local_stmt.span, String::new()), (assign.lhs_span, let_snippet)],
+                        vec![
+                            (local_stmt.span, String::new()),
+                            (assign.lhs_span, let_snippet.to_owned()),
+                        ],
                         Applicability::MachineApplicable,
                     );
                 },
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index a0bbf6b14b2..addb4b1aee8 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_self;
 use clippy_utils::ptr::get_spans;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::ty::{
     implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item,
 };
@@ -242,8 +242,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                         for (span, suggestion) in clone_spans {
                             diag.span_suggestion(
                                 span,
-                                snippet_opt(cx, span)
-                                    .map_or("change the call to".into(), |x| format!("change `{x}` to")),
+                                span.get_source_text(cx)
+                                    .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
                                 suggestion,
                                 Applicability::Unspecified,
                             );
@@ -267,8 +267,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                             for (span, suggestion) in clone_spans {
                                 diag.span_suggestion(
                                     span,
-                                    snippet_opt(cx, span)
-                                        .map_or("change the call to".into(), |x| format!("change `{x}` to")),
+                                    span.get_source_text(cx)
+                                        .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
                                     suggestion,
                                     Applicability::Unspecified,
                                 );
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 8232e69db39..b181791699a 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::has_drop;
 use clippy_utils::{
     in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks,
@@ -268,34 +268,31 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
         && reduced.iter().all(|e| e.span.ctxt() == ctxt)
     {
         if let ExprKind::Index(..) = &expr.kind {
-            if is_inside_always_const_context(cx.tcx, expr.hir_id) {
-                return;
+            if !is_inside_always_const_context(cx.tcx, expr.hir_id)
+                && let [arr, func] = &*reduced
+                && let Some(arr) = arr.span.get_source_text(cx)
+                && let Some(func) = func.span.get_source_text(cx)
+            {
+                span_lint_hir_and_then(
+                    cx,
+                    UNNECESSARY_OPERATION,
+                    expr.hir_id,
+                    stmt.span,
+                    "unnecessary operation",
+                    |diag| {
+                        diag.span_suggestion(
+                            stmt.span,
+                            "statement can be written as",
+                            format!("assert!({arr}.len() > {func});"),
+                            Applicability::MaybeIncorrect,
+                        );
+                    },
+                );
             }
-            let snippet =
-                if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
-                    format!("assert!({arr}.len() > {func});")
-                } else {
-                    return;
-                };
-            span_lint_hir_and_then(
-                cx,
-                UNNECESSARY_OPERATION,
-                expr.hir_id,
-                stmt.span,
-                "unnecessary operation",
-                |diag| {
-                    diag.span_suggestion(
-                        stmt.span,
-                        "statement can be written as",
-                        snippet,
-                        Applicability::MaybeIncorrect,
-                    );
-                },
-            );
         } else {
             let mut snippet = String::new();
             for e in reduced {
-                if let Some(snip) = snippet_opt(cx, e.span) {
+                if let Some(snip) = e.span.get_source_text(cx) {
                     snippet.push_str(&snip);
                     snippet.push(';');
                 } else {
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 6915cd40615..25f547f1a43 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -1,7 +1,3 @@
-//! Checks for usage of const which the type is not `Freeze` (`Cell`-free).
-//!
-//! This lint is **warn** by default.
-
 use std::ptr;
 
 use clippy_config::Conf;
@@ -188,7 +184,7 @@ impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTE
 impl<'tcx> NonCopyConst<'tcx> {
     pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self {
         Self {
-            interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability),
+            interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability),
         }
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
index 2aaf1e6ff46..51ba29d7389 100644
--- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
+++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
@@ -1,7 +1,7 @@
 use clippy_config::types::MacroMatcher;
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::{SourceText, SpanRangeExt};
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
@@ -34,7 +34,7 @@ declare_clippy_lint! {
 }
 
 /// The (callsite span, (open brace, close brace), source snippet)
-type MacroInfo = (Span, (char, char), String);
+type MacroInfo = (Span, (char, char), SourceText);
 
 pub struct MacroBraces {
     macro_braces: FxHashMap<String, (char, char)>,
@@ -94,7 +94,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace
     if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind
         && let name = mac_name.as_str()
         && let Some(&braces) = mac_braces.macro_braces.get(name)
-        && let Some(snip) = snippet_opt(cx, span_call_site)
+        && let Some(snip) = span_call_site.get_source_text(cx)
         // we must check only invocation sites
         // https://github.com/rust-lang/rust-clippy/issues/7422
         && snip.starts_with(&format!("{name}!"))
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index bc71a4790b9..8b9f899d82d 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -35,7 +35,7 @@ impl ArithmeticSideEffects {
             ("f64", FxHashSet::from_iter(["f64"])),
             ("std::string::String", FxHashSet::from_iter(["str"])),
         ]);
-        for [lhs, rhs] in &conf.arithmetic_side_effects_allowed_binary {
+        for (lhs, rhs) in &conf.arithmetic_side_effects_allowed_binary {
             allowed_binary.entry(lhs).or_default().insert(rhs);
         }
         for s in &conf.arithmetic_side_effects_allowed {
diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
index 641d881d974..11c97b4ef00 100644
--- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method};
@@ -46,8 +46,8 @@ pub(super) fn check<'tcx>(
                     expr.span,
                     "manual implementation of an assign operation",
                     |diag| {
-                        if let (Some(snip_a), Some(snip_r)) =
-                            (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
+                        if let Some(snip_a) = assignee.span.get_source_text(cx)
+                            && let Some(snip_r) = rhs.span.get_source_text(cx)
                         {
                             diag.span_suggestion(
                                 expr.span,
diff --git a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs
index 311cbd050a1..8daedd1c901 100644
--- a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{eq_expr_value, sugg};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -42,7 +42,9 @@ fn lint_misrefactored_assign_op(
         expr.span,
         "variable appears on both sides of an assignment operation",
         |diag| {
-            if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
+            if let Some(snip_a) = assignee.span.get_source_text(cx)
+                && let Some(snip_r) = rhs_other.span.get_source_text(cx)
+            {
                 let a = &sugg::Sugg::hir(cx, assignee, "..");
                 let r = &sugg::Sugg::hir(cx, rhs, "..");
                 let long = format!("{snip_a} = {}", sugg::make_binop(op, a, r));
diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs
index 9baecff801f..9e8a821c3f4 100644
--- a/src/tools/clippy/clippy_lints/src/operators/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs
@@ -80,7 +80,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// // `n` can be any number, including `i32::MAX`.
     /// fn foo(n: i32) -> i32 {
-    ///   n + 1
+    ///     n + 1
     /// }
     /// ```
     ///
diff --git a/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs b/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs
index ab5fb178700..9d8e833ef6d 100644
--- a/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -24,8 +24,8 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp
             e.span,
             "use of bitwise operator instead of lazy operator between booleans",
             |diag| {
-                if let Some(lhs_snip) = snippet_opt(cx, lhs.span)
-                    && let Some(rhs_snip) = snippet_opt(cx, rhs.span)
+                if let Some(lhs_snip) = lhs.span.get_source_text(cx)
+                    && let Some(rhs_snip) = rhs.span.get_source_text(cx)
                 {
                     let sugg = format!("{lhs_snip} {op_str} {rhs_snip}");
                     diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
diff --git a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs
index 607930561e0..861564d5456 100644
--- a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::std_or_core;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
@@ -22,8 +22,8 @@ pub(super) fn check<'tcx>(
 
         if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left)
             && let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right)
-            && let Some(left_snip) = snippet_opt(cx, left_var.span)
-            && let Some(right_snip) = snippet_opt(cx, right_var.span)
+            && let Some(left_snip) = left_var.span.get_source_text(cx)
+            && let Some(right_snip) = right_var.span.get_source_text(cx)
         {
             let Some(top_crate) = std_or_core(cx) else { return };
             span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
index 18bfb588a11..d7fa48c1e38 100644
--- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::path_to_local_id;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_ast::{LitKind, StrStyle};
 use rustc_errors::Applicability;
@@ -74,7 +74,7 @@ impl<'tcx> PathbufPushSearcher<'tcx> {
             && let Some(arg) = self.arg
             && let ExprKind::Lit(x) = arg.kind
             && let LitKind::Str(_, StrStyle::Cooked) = x.node
-            && let Some(s) = snippet_opt(cx, arg.span)
+            && let Some(s) = arg.span.get_source_text(cx)
         {
             Some(format!(" = PathBuf::from({s});"))
         } else {
@@ -84,8 +84,8 @@ impl<'tcx> PathbufPushSearcher<'tcx> {
 
     fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option<String> {
         let arg = self.arg?;
-        let arg_str = snippet_opt(cx, arg.span)?;
-        let init_val = snippet_opt(cx, self.init_val.span)?;
+        let arg_str = arg.span.get_source_text(cx)?;
+        let init_val = self.init_val.span.get_source_text(cx)?;
         Some(format!(" = {init_val}.join({arg_str});"))
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 02c05e0aaf9..125f694996c 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -1,7 +1,5 @@
-//! Checks for usage of  `&Vec[_]` and `&String`.
-
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::expr_sig;
 use clippy_utils::visitors::contains_unsafe_block;
 use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
@@ -243,7 +241,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
                         .chain(result.replacements.iter().map(|r| {
                             (
                                 r.expr_span,
-                                format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
+                                format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement),
                             )
                         }))
                         .collect(),
@@ -372,7 +370,7 @@ impl fmt::Display for DerefTyDisplay<'_, '_> {
             DerefTy::Path => f.write_str("Path"),
             DerefTy::Slice(hir_ty, ty) => {
                 f.write_char('[')?;
-                match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
+                match hir_ty.and_then(|s| s.get_source_text(self.0)) {
                     Some(s) => f.write_str(&s)?,
                     None => ty.fmt(f)?,
                 }
@@ -413,6 +411,7 @@ impl<'tcx> DerefTy<'tcx> {
     }
 }
 
+#[expect(clippy::too_many_lines)]
 fn check_fn_args<'cx, 'tcx: 'cx>(
     cx: &'cx LateContext<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
@@ -488,8 +487,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                                 return None;
                             }
 
-                            let ty_name = snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
-
                             span_lint_hir_and_then(
                                 cx,
                                 PTR_ARG,
@@ -500,7 +497,10 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                                     diag.span_suggestion(
                                         hir_ty.span,
                                         "change this to",
-                                        format!("&{}{ty_name}", mutability.prefix_str()),
+                                        match ty.span().get_source_text(cx) {
+                                            Some(s) => format!("&{}{s}", mutability.prefix_str()),
+                                            None => format!("&{}{}", mutability.prefix_str(), args.type_at(1)),
+                                        },
                                         Applicability::Unspecified,
                                     );
                                 },
diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
index 7c82895d609..87a52cb2186 100644
--- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -120,8 +120,8 @@ fn build_suggestion(
     receiver_expr: &Expr<'_>,
     cast_lhs_expr: &Expr<'_>,
 ) -> Option<String> {
-    let receiver = snippet_opt(cx, receiver_expr.span)?;
-    let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?;
+    let receiver = receiver_expr.span.get_source_text(cx)?;
+    let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?;
     Some(format!("{receiver}.{}({cast_lhs})", method.suggestion()))
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index a1231c082e6..bfdc1cbeed7 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth};
 use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
 use rustc_errors::Applicability;
@@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
                 .assert_crate_local()
                 .lint_root;
 
-            if let Some(snip) = snippet_opt(cx, span)
+            if let Some(snip) = span.get_source_text(cx)
                 && let Some(dot) = snip.rfind('.')
             {
                 let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap()));
diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs
index 8f32cf5f2a1..2b4ef21fc48 100644
--- a/src/tools/clippy/clippy_lints/src/reference.rs
+++ b/src/tools/clippy/clippy_lints/src/reference.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::source::{snippet_with_applicability, SpanRangeExt};
 use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::BytePos;
+use rustc_span::{BytePos, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -56,11 +56,11 @@ impl EarlyLintPass for DerefAddrOf {
         {
             let mut applicability = Applicability::MachineApplicable;
             let sugg = if e.span.from_expansion() {
-                if let Some(macro_source) = snippet_opt(cx, e.span) {
+                if let Some(macro_source) = e.span.get_source_text(cx) {
                     // Remove leading whitespace from the given span
                     // e.g: ` $visitor` turns into `$visitor`
-                    let trim_leading_whitespaces = |span| {
-                        snippet_opt(cx, span)
+                    let trim_leading_whitespaces = |span: Span| {
+                        span.get_source_text(cx)
                             .and_then(|snip| {
                                 #[expect(clippy::cast_possible_truncation)]
                                 snip.find(|c: char| !c.is_whitespace())
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index 95014b23043..f6ef02b7c23 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -2,7 +2,7 @@ use std::fmt::Display;
 
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{def_path_def_ids, path_def_id, paths};
 use rustc_ast::ast::{LitKind, StrStyle};
 use rustc_hir::def_id::DefIdMap;
@@ -122,7 +122,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: &regex_syntax::Error, unescape
     };
 
     if let Some((primary, auxiliary, kind)) = parts
-        && let Some(literal_snippet) = snippet_opt(cx, base)
+        && let Some(literal_snippet) = base.get_source_text(cx)
         && let Some(inner) = literal_snippet.get(offset as usize..)
         // Only convert to native rustc spans if the parsed regex matches the
         // source snippet exactly, to ensure the span offsets are correct
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index 13016cdadb0..5ce091b7a7a 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
-use clippy_utils::source::{snippet_opt, snippet_with_context};
+use clippy_utils::source::{snippet_with_context, SpanRangeExt};
 use clippy_utils::sugg::has_enclosing_paren;
 use clippy_utils::visitors::{for_each_expr, Descend};
 use clippy_utils::{
@@ -250,20 +250,25 @@ impl<'tcx> LateLintPass<'tcx> for Return {
                 |err| {
                     err.span_label(local.span, "unnecessary `let` binding");
 
-                    if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
-                        if binary_expr_needs_parentheses(initexpr) {
-                            if !has_enclosing_paren(&snippet) {
-                                snippet = format!("({snippet})");
+                    if let Some(src) = initexpr.span.get_source_text(cx) {
+                        let sugg = if binary_expr_needs_parentheses(initexpr) {
+                            if has_enclosing_paren(&src) {
+                                src.to_owned()
+                            } else {
+                                format!("({src})")
                             }
                         } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
-                            if !has_enclosing_paren(&snippet) {
-                                snippet = format!("({snippet})");
+                            if has_enclosing_paren(&src) {
+                                format!("{src} as _")
+                            } else {
+                                format!("({src}) as _")
                             }
-                            snippet.push_str(" as _");
-                        }
+                        } else {
+                            src.to_owned()
+                        };
                         err.multipart_suggestion(
                             "return the expression directly",
-                            vec![(local.span, String::new()), (retexpr.span, snippet)],
+                            vec![(local.span, String::new()), (retexpr.span, sugg)],
                             Applicability::MachineApplicable,
                         );
                     } else {
@@ -394,7 +399,7 @@ fn check_final_expr<'tcx>(
 
             // Returns may be used to turn an expression into a statement in rustc's AST.
             // This allows the addition of attributes, like `#[allow]` (See: clippy#9361)
-            // `#[expect(clippy::needless_return)]` needs to be handled separatly to
+            // `#[expect(clippy::needless_return)]` needs to be handled separately to
             // actually fulfill the expectation (clippy::#12998)
             match cx.tcx.hir().attrs(expr.hir_id) {
                 [] => {},
diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
index e0558429638..44e585953bf 100644
--- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::get_trait_def_id;
 use clippy_utils::higher::VecArgs;
 use clippy_utils::macros::root_macro_call_first_node;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use rustc_ast::{LitIntType, LitKind, UintTy};
 use rustc_errors::Applicability;
@@ -92,12 +92,12 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
 
         if matches!(lang_item, LangItem::Range)
             && let ty = cx.typeck_results().expr_ty(start.expr)
-            && let Some(snippet) = snippet_opt(cx, span)
+            && let Some(snippet) = span.get_source_text(cx)
             // `is_from_proc_macro` will skip any `vec![]`. Let's not!
             && snippet.starts_with(suggested_type.starts_with())
             && snippet.ends_with(suggested_type.ends_with())
-            && let Some(start_snippet) = snippet_opt(cx, start.span)
-            && let Some(end_snippet) = snippet_opt(cx, end.span)
+            && let Some(start_snippet) = start.span.get_source_text(cx)
+            && let Some(end_snippet) = end.span.get_source_text(cx)
         {
             let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"])
                 && implements_trait(cx, ty, step_def_id, &[])
diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
index 01f0e3cfadb..7750d8909d3 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
@@ -1,6 +1,3 @@
-//! Lint on use of `size_of` or `size_of_val` of T in an expression
-//! expecting a count of T
-
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index 974e21df817..44283a49e84 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -9,7 +9,6 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{HirId, Path, PathSegment};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
 use rustc_span::{sym, Span};
@@ -185,9 +184,7 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool {
             } = stability.level
         {
             let stable = match since {
-                StableSince::Version(v) => {
-                    msrv.meets(RustcVersion::new(v.major.into(), v.minor.into(), v.patch.into()))
-                },
+                StableSince::Version(v) => msrv.meets(v),
                 StableSince::Current => msrv.current().is_none(),
                 StableSince::Err => false,
             };
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index cfc387886dc..6ca4ca000e9 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd {
                 }
             },
             ExprKind::Index(target, _idx, _) => {
-                let e_ty = cx.typeck_results().expr_ty(target).peel_refs();
+                let e_ty = cx.typeck_results().expr_ty_adjusted(target).peel_refs();
                 if e_ty.is_str() || is_type_lang_item(cx, e_ty, LangItem::String) {
                     span_lint(
                         cx,
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 59b74122f30..2e87d36df31 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -1,7 +1,7 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
+use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt};
 use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash};
 use core::hash::{Hash, Hasher};
 use itertools::Itertools;
@@ -206,8 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
 
                 let fixed_trait_snippet = unique_traits
                     .iter()
-                    .filter_map(|b| snippet_opt(cx, b.span))
-                    .collect::<Vec<_>>()
+                    .filter_map(|b| b.span.get_source_text(cx))
                     .join(" + ");
 
                 span_lint_and_sugg(
@@ -462,9 +461,8 @@ fn rollup_traits(
 
         let traits = comparable_bounds
             .iter()
-            .filter_map(|&(_, span)| snippet_opt(cx, span))
-            .collect::<Vec<_>>();
-        let traits = traits.join(" + ");
+            .filter_map(|&(_, span)| span.get_source_text(cx))
+            .join(" + ");
 
         span_lint_and_sugg(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
index afc53e6f32d..978d49f09c3 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SourceText, SpanRangeExt};
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind};
 use rustc_lint::LateContext;
@@ -79,7 +79,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
                         && block.expr.is_none()
                         && let Some(last_stmt) = block.stmts.iter().last()
                         && let StmtKind::Semi(last_expr) = last_stmt.kind
-                        && let Some(snip) = snippet_opt(cx, last_expr.span)
+                        && let Some(snip) = last_expr.span.get_source_text(cx)
                     {
                         Some((last_stmt.span, snip))
                     } else {
@@ -90,24 +90,24 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
                     db.span_suggestion(
                         span,
                         "remove the semicolon from the last statement in the block",
-                        sugg,
+                        sugg.as_str(),
                         Applicability::MaybeIncorrect,
                     );
                     or = "or ";
                     applicability = Applicability::MaybeIncorrect;
                 });
 
-            let arg_snippets: Vec<String> = args_to_recover
+            let arg_snippets: Vec<_> = args_to_recover
                 .iter()
-                .filter_map(|arg| snippet_opt(cx, arg.span))
+                .filter_map(|arg| arg.span.get_source_text(cx))
                 .collect();
-            let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover
+            let arg_snippets_without_empty_blocks: Vec<_> = args_to_recover
                 .iter()
                 .filter(|arg| !is_empty_block(arg))
-                .filter_map(|arg| snippet_opt(cx, arg.span))
+                .filter_map(|arg| arg.span.get_source_text(cx))
                 .collect();
 
-            if let Some(call_snippet) = snippet_opt(cx, expr.span) {
+            if let Some(call_snippet) = expr.span.get_source_text(cx) {
                 let sugg = fmt_stmts_and_call(
                     cx,
                     expr,
@@ -161,8 +161,8 @@ fn fmt_stmts_and_call(
     cx: &LateContext<'_>,
     call_expr: &Expr<'_>,
     call_snippet: &str,
-    args_snippets: &[impl AsRef<str>],
-    non_empty_block_args_snippets: &[impl AsRef<str>],
+    args_snippets: &[SourceText],
+    non_empty_block_args_snippets: &[SourceText],
 ) -> String {
     let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
     let call_snippet_with_replacements = args_snippets
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index 448946bd66d..af7abd009d2 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -34,11 +34,18 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// use std::io;
     /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
-    ///     // must be `w.write_all(b"foo")?;`
     ///     w.write(b"foo")?;
     ///     Ok(())
     /// }
     /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// use std::io;
+    /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
+    ///     w.write_all(b"foo")?;
+    ///     Ok(())
+    /// }
+    /// ```
     #[clippy::version = "pre 1.29.0"]
     pub UNUSED_IO_AMOUNT,
     correctness,
@@ -235,7 +242,7 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
 
 /// If `expr` is an (e).await, return the inner expression "e" that's being
 /// waited on.  Otherwise return None.
-fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &hir::Expr<'a> {
+fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
     if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind {
         if let ExprKind::Call(func, [ref arg_0, ..]) = expr.kind {
             if matches!(
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
index b70aa768b46..65f431d338b 100644
--- a/src/tools/clippy/clippy_lints/src/unused_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{position_before_rarrow, snippet_opt};
+use clippy_utils::source::{position_before_rarrow, SpanRangeExt};
 use rustc_ast::visit::FnKind;
 use rustc_ast::{ast, ClosureBinder};
 use rustc_errors::Applicability;
@@ -128,15 +128,17 @@ fn is_unit_expr(expr: &ast::Expr) -> bool {
 
 fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
     let (ret_span, appl) =
-        snippet_opt(cx, span.with_hi(ty.span.hi())).map_or((ty.span, Applicability::MaybeIncorrect), |fn_source| {
-            position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
-                (
-                    #[expect(clippy::cast_possible_truncation)]
-                    ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
-                    Applicability::MachineApplicable,
-                )
-            })
-        });
+        span.with_hi(ty.span.hi())
+            .get_source_text(cx)
+            .map_or((ty.span, Applicability::MaybeIncorrect), |src| {
+                position_before_rarrow(&src).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
+                    (
+                        #[expect(clippy::cast_possible_truncation)]
+                        ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
+                        Applicability::MachineApplicable,
+                    )
+                })
+            });
     span_lint_and_sugg(
         cx,
         UNUSED_UNIT,
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index c0d9bcdd259..0b4ea0752b6 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -89,15 +89,15 @@ enum UnwrappableKind {
 impl UnwrappableKind {
     fn success_variant_pattern(self) -> &'static str {
         match self {
-            UnwrappableKind::Option => "Some(..)",
-            UnwrappableKind::Result => "Ok(..)",
+            UnwrappableKind::Option => "Some(<item>)",
+            UnwrappableKind::Result => "Ok(<item>)",
         }
     }
 
     fn error_variant_pattern(self) -> &'static str {
         match self {
             UnwrappableKind::Option => "None",
-            UnwrappableKind::Result => "Err(..)",
+            UnwrappableKind::Result => "Err(<item>)",
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 93785b45c27..5da48f4f7fb 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -229,7 +229,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity()
             && same_type_and_consts(ty, impl_ty)
             // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that
-            // the lifetime parameters of `ty` are ellided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in
+            // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in
             // which case we must still trigger the lint.
             && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty))
         {
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 316c1f32d3a..0cce45290cf 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -1,6 +1,3 @@
-//! A group of attributes that can be attached to Rust code in order
-//! to generate a clippy lint detecting said code automatically.
-
 use clippy_utils::{get_attr, higher};
 use rustc_ast::ast::{LitFloatType, LitKind};
 use rustc_ast::LitIntType;
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
index 5acfd35fd6a..f50ce6c99de 100644
--- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -1,5 +1,5 @@
 use clippy_utils::macros::FormatArgsStorage;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use itertools::Itertools;
 use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
 use rustc_data_structures::fx::FxHashMap;
@@ -75,38 +75,21 @@ fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool {
 
     // `format!("{} {} {c}", "one", "two", c = "three")`
     //                     ^^     ^^     ^^^^^^
-    let between_spans = once(args.span)
+    !once(args.span)
         .chain(argument_span)
         .tuple_windows()
-        .map(|(start, end)| start.between(end));
-
-    for between_span in between_spans {
-        let mut seen_comma = false;
-
-        let Some(snippet) = snippet_opt(cx, between_span) else {
-            return true;
-        };
-        for token in tokenize(&snippet) {
-            match token.kind {
-                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
-                TokenKind::Comma if !seen_comma => seen_comma = true,
-                // named arguments, `start_val, name = end_val`
-                //                            ^^^^^^^^^ between_span
-                TokenKind::Ident | TokenKind::Eq if seen_comma => {},
-                // An unexpected token usually indicates that we crossed a macro boundary
-                //
-                // `println!(some_proc_macro!("input {}"), a)`
-                //                                      ^^^ between_span
-                // `println!("{}", val!(x))`
-                //               ^^^^^^^ between_span
-                _ => return true,
-            }
-        }
-
-        if !seen_comma {
-            return true;
-        }
-    }
-
-    false
+        .map(|(start, end)| start.between(end))
+        .all(|sp| {
+            sp.check_source_text(cx, |src| {
+                // text should be either `, name` or `, name =`
+                let mut iter = tokenize(src).filter(|t| {
+                    !matches!(
+                        t.kind,
+                        TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
+                    )
+                });
+                iter.next().is_some_and(|t| matches!(t.kind, TokenKind::Comma))
+                    && iter.all(|t| matches!(t.kind, TokenKind::Ident | TokenKind::Eq))
+            })
+        })
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index 1d294c2944b..f662c7651f6 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -3,7 +3,6 @@ pub mod collapsible_calls;
 pub mod interning_defined_symbol;
 pub mod invalid_paths;
 pub mod lint_without_lint_pass;
-pub mod metadata_collector;
 pub mod msrv_attr_impl;
 pub mod outer_expn_data_pass;
 pub mod produce_ice;
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index df342e48d63..20526113d69 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -9,7 +9,6 @@ use rustc_hir::intravisit::Visitor;
 use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
-use rustc_semver::RustcVersion;
 use rustc_session::impl_lint_pass;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Symbol;
@@ -92,7 +91,12 @@ pub struct LintWithoutLintPass {
     registered_lints: FxHashSet<Symbol>,
 }
 
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
+impl_lint_pass!(LintWithoutLintPass => [
+    DEFAULT_LINT,
+    LINT_WITHOUT_LINT_PASS,
+    INVALID_CLIPPY_VERSION_ATTRIBUTE,
+    MISSING_CLIPPY_VERSION_ATTRIBUTE,
+]);
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -220,7 +224,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'
             return;
         }
 
-        if RustcVersion::parse(value.as_str()).is_err() {
+        if rustc_attr::parse_version(value).is_none() {
             span_lint_and_help(
                 cx,
                 INVALID_CLIPPY_VERSION_ATTRIBUTE,
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
deleted file mode 100644
index 57f45aa3e48..00000000000
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ /dev/null
@@ -1,1078 +0,0 @@
-//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
-//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
-//!
-//! This module and therefore the entire lint is guarded by a feature flag called `internal`
-//!
-//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
-//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
-//! a simple mistake)
-
-use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type};
-use clippy_config::{get_configuration_metadata, ClippyConfiguration};
-
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
-use clippy_utils::{last_path_segment, match_function_call, match_path, paths};
-use itertools::Itertools;
-use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::def::DefKind;
-use rustc_hir::intravisit::Visitor;
-use rustc_hir::{self as hir, intravisit, Closure, ExprKind, Item, ItemKind, Mutability, QPath};
-use rustc_lint::{unerased_lint_store, CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
-use rustc_middle::hir::nested_filter;
-use rustc_session::impl_lint_pass;
-use rustc_span::symbol::Ident;
-use rustc_span::{sym, Loc, Span, Symbol};
-use serde::ser::SerializeStruct;
-use serde::{Serialize, Serializer};
-use std::collections::{BTreeSet, BinaryHeap};
-use std::fmt::Write as _;
-use std::fs::{self, File};
-use std::io::prelude::*;
-use std::path::{Path, PathBuf};
-use std::process::Command;
-use std::{env, fmt};
-
-/// This is the json output file of the lint collector.
-const JSON_OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
-/// This is the markdown output file of the lint collector.
-const MARKDOWN_OUTPUT_FILE: &str = "../book/src/lint_configuration.md";
-/// These groups will be ignored by the lint group matcher. This is useful for collections like
-/// `clippy::all`
-const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
-/// Lints within this group will be excluded from the collection. These groups
-/// have to be defined without the `clippy::` prefix.
-const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
-/// Collected deprecated lint will be assigned to this group in the JSON output
-const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
-/// This is the lint level for deprecated lints that will be displayed in the lint list
-const DEPRECATED_LINT_LEVEL: &str = "none";
-/// This array holds Clippy's lint groups with their corresponding default lint level. The
-/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
-const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
-    ("correctness", "deny"),
-    ("suspicious", "warn"),
-    ("restriction", "allow"),
-    ("style", "warn"),
-    ("pedantic", "allow"),
-    ("complexity", "warn"),
-    ("perf", "warn"),
-    ("cargo", "allow"),
-    ("nursery", "allow"),
-];
-/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
-/// to only keep the actual lint group in the output.
-const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
-const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
-    &["clippy_utils", "diagnostics", "span_lint"],
-    &["clippy_utils", "diagnostics", "span_lint_and_help"],
-    &["clippy_utils", "diagnostics", "span_lint_and_note"],
-    &["clippy_utils", "diagnostics", "span_lint_hir"],
-    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
-    &["clippy_utils", "diagnostics", "span_lint_and_then"],
-    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
-];
-const SUGGESTION_DIAG_METHODS: [(&str, bool); 9] = [
-    ("span_suggestion", false),
-    ("span_suggestion_short", false),
-    ("span_suggestion_verbose", false),
-    ("span_suggestion_hidden", false),
-    ("tool_only_span_suggestion", false),
-    ("multipart_suggestion", true),
-    ("multipart_suggestions", true),
-    ("tool_only_multipart_suggestion", true),
-    ("span_suggestions", true),
-];
-
-/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
-const APPLICABILITY_NAME_INDEX: usize = 2;
-/// This applicability will be set for unresolved applicability values.
-const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
-/// The version that will be displayed if none has been defined
-const VERSION_DEFAULT_STR: &str = "Unknown";
-
-const CHANGELOG_PATH: &str = "../CHANGELOG.md";
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Collects metadata about clippy lints for the website.
-    ///
-    /// This lint will be used to report problems of syntax parsing. You should hopefully never
-    /// see this but never say never I guess ^^
-    ///
-    /// ### Why is this bad?
-    /// This is not a bad thing but definitely a hacky way to do it. See
-    /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
-    /// about the implementation.
-    ///
-    /// ### Known problems
-    /// Hopefully none. It would be pretty uncool to have a problem here :)
-    ///
-    /// ### Example output
-    /// ```json,ignore
-    /// {
-    ///     "id": "metadata_collector",
-    ///     "id_span": {
-    ///         "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
-    ///         "line": 1
-    ///     },
-    ///     "group": "clippy::internal",
-    ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
-    /// }
-    /// ```
-    #[clippy::version = "1.56.0"]
-    pub METADATA_COLLECTOR,
-    internal,
-    "A busy bee collection metadata about lints"
-}
-
-impl_lint_pass!(MetadataCollector => [METADATA_COLLECTOR]);
-
-#[allow(clippy::module_name_repetitions)]
-#[derive(Debug, Clone)]
-pub struct MetadataCollector {
-    /// All collected lints
-    ///
-    /// We use a Heap here to have the lints added in alphabetic order in the export
-    lints: BinaryHeap<LintMetadata>,
-    applicability_info: FxHashMap<String, ApplicabilityInfo>,
-    config: Vec<ClippyConfiguration>,
-    clippy_project_root: PathBuf,
-}
-
-impl MetadataCollector {
-    pub fn new() -> Self {
-        Self {
-            lints: BinaryHeap::<LintMetadata>::default(),
-            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
-            config: get_configuration_metadata(),
-            clippy_project_root: env::current_dir()
-                .expect("failed to get current dir")
-                .ancestors()
-                .nth(1)
-                .expect("failed to get project root")
-                .to_path_buf(),
-        }
-    }
-
-    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
-        self.config
-            .iter()
-            .filter(|config| config.lints.iter().any(|&lint| lint == lint_name))
-            .map(ToString::to_string)
-            .reduce(|acc, x| acc + "\n\n" + &x)
-            .map(|configurations| {
-                format!(
-                    r#"
-### Configuration
-This lint has the following configuration variables:
-
-{configurations}
-"#
-                )
-            })
-    }
-
-    fn configs_to_markdown(&self, map_fn: fn(&ClippyConfiguration) -> String) -> String {
-        self.config
-            .iter()
-            .filter(|config| config.deprecation_reason.is_none())
-            .filter(|config| !config.lints.is_empty())
-            .map(map_fn)
-            .join("\n")
-    }
-
-    fn get_markdown_docs(&self) -> String {
-        format!(
-            r#"# Lint Configuration Options
-
-The following list shows each configuration option, along with a description, its default value, an example
-and lints affected.
-
----
-
-{}"#,
-            self.configs_to_markdown(ClippyConfiguration::to_markdown_paragraph),
-        )
-    }
-}
-
-impl Drop for MetadataCollector {
-    /// You might ask: How hacky is this?
-    /// My answer:     YES
-    fn drop(&mut self) {
-        // The metadata collector gets dropped twice, this makes sure that we only write
-        // when the list is full
-        if self.lints.is_empty() {
-            return;
-        }
-
-        let mut applicability_info = std::mem::take(&mut self.applicability_info);
-
-        // Add deprecated lints
-        self.lints.extend(
-            crate::deprecated_lints::DEPRECATED
-                .iter()
-                .zip(crate::deprecated_lints::DEPRECATED_VERSION)
-                .filter_map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)),
-        );
-        // Mapping the final data
-        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
-        for x in &mut lints {
-            x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default());
-            replace_produces(&x.id, &mut x.docs, &self.clippy_project_root);
-        }
-
-        collect_renames(&mut lints);
-
-        // Outputting json
-        fs::write(JSON_OUTPUT_FILE, serde_json::to_string_pretty(&lints).unwrap()).unwrap();
-
-        // Outputting markdown
-        let mut file = File::create(MARKDOWN_OUTPUT_FILE).unwrap();
-        writeln!(
-            file,
-            "<!--
-This file is generated by `cargo collect-metadata`.
-Please use that command to update the file and do not edit it by hand.
--->
-
-{}",
-            self.get_markdown_docs(),
-        )
-        .unwrap();
-
-        // Write configuration links to CHANGELOG.md
-        let changelog = fs::read_to_string(CHANGELOG_PATH).unwrap();
-        let mut changelog_file = File::create(CHANGELOG_PATH).unwrap();
-        let position = changelog
-            .find("<!-- begin autogenerated links to configuration documentation -->")
-            .unwrap();
-        writeln!(
-            changelog_file,
-            "{}<!-- begin autogenerated links to configuration documentation -->\n{}\n<!-- end autogenerated links to configuration documentation -->",
-            &changelog[..position],
-            self.configs_to_markdown(ClippyConfiguration::to_markdown_link)
-        )
-        .unwrap();
-    }
-}
-
-#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
-struct LintMetadata {
-    id: String,
-    id_span: Option<SerializableSpan>,
-    group: String,
-    level: String,
-    docs: String,
-    version: String,
-    /// This field is only used in the output and will only be
-    /// mapped shortly before the actual output.
-    applicability: Option<ApplicabilityInfo>,
-    /// All the past names of lints which have been renamed.
-    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
-    former_ids: BTreeSet<String>,
-}
-
-impl LintMetadata {
-    fn new(
-        id: String,
-        id_span: SerializableSpan,
-        group: String,
-        level: &'static str,
-        version: String,
-        docs: String,
-    ) -> Self {
-        Self {
-            id,
-            id_span: Some(id_span),
-            group,
-            level: level.to_string(),
-            version,
-            docs,
-            applicability: None,
-            former_ids: BTreeSet::new(),
-        }
-    }
-
-    fn new_deprecated(name: &str, reason: &str, version: &str) -> Option<Self> {
-        // The reason starts with a lowercase letter and end without a period.
-        // This needs to be fixed for the website.
-        let mut reason = reason.to_owned();
-        if let Some(reason) = reason.get_mut(0..1) {
-            reason.make_ascii_uppercase();
-        }
-        name.strip_prefix("clippy::").map(|name| Self {
-            id: name.into(),
-            id_span: None,
-            group: DEPRECATED_LINT_GROUP_STR.into(),
-            level: DEPRECATED_LINT_LEVEL.into(),
-            version: version.into(),
-            docs: format!(
-                "### What it does\n\n\
-                Nothing. This lint has been deprecated\n\n\
-                ### Deprecation reason\n\n{reason}.\n",
-            ),
-            applicability: None,
-            former_ids: BTreeSet::new(),
-        })
-    }
-}
-
-fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) {
-    let mut doc_lines = docs.lines().map(ToString::to_string).collect::<Vec<_>>();
-    let mut lines = doc_lines.iter_mut();
-
-    'outer: loop {
-        // Find the start of the example
-
-        // ```rust
-        loop {
-            match lines.next() {
-                Some(line) if line.trim_start().starts_with("```rust") => {
-                    if line.contains("ignore") || line.contains("no_run") {
-                        // A {{produces}} marker may have been put on a ignored code block by mistake,
-                        // just seek to the end of the code block and continue checking.
-                        if lines.any(|line| line.trim_start().starts_with("```")) {
-                            continue;
-                        }
-
-                        panic!("lint `{lint_name}` has an unterminated code block")
-                    }
-
-                    break;
-                },
-                Some(line) if line.trim_start() == "{{produces}}" => {
-                    panic!("lint `{lint_name}` has marker {{{{produces}}}} with an ignored or missing code block")
-                },
-                Some(line) => {
-                    let line = line.trim();
-                    // These are the two most common markers of the corrections section
-                    if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") {
-                        break 'outer;
-                    }
-                },
-                None => break 'outer,
-            }
-        }
-
-        // Collect the example
-        let mut example = Vec::new();
-        loop {
-            match lines.next() {
-                Some(line) if line.trim_start() == "```" => break,
-                Some(line) => example.push(line),
-                None => panic!("lint `{lint_name}` has an unterminated code block"),
-            }
-        }
-
-        // Find the {{produces}} and attempt to generate the output
-        loop {
-            match lines.next() {
-                Some(line) if line.is_empty() => {},
-                Some(line) if line.trim() == "{{produces}}" => {
-                    let output = get_lint_output(lint_name, &example, clippy_project_root);
-                    line.replace_range(
-                        ..,
-                        &format!(
-                            "<details>\
-                            <summary>Produces</summary>\n\
-                            \n\
-                            ```text\n\
-                            {output}\n\
-                            ```\n\
-                        </details>"
-                        ),
-                    );
-
-                    break;
-                },
-                // No {{produces}}, we can move on to the next example
-                Some(_) => break,
-                None => break 'outer,
-            }
-        }
-    }
-
-    *docs = cleanup_docs(&doc_lines);
-}
-
-fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String {
-    let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}"));
-    let file = dir.path().join("lint_example.rs");
-
-    let mut source = String::new();
-    let unhidden = example
-        .iter()
-        .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line));
-
-    // Get any attributes
-    let mut lines = unhidden.peekable();
-    while let Some(line) = lines.peek() {
-        if line.starts_with("#!") {
-            source.push_str(line);
-            source.push('\n');
-            lines.next();
-        } else {
-            break;
-        }
-    }
-
-    let needs_main = !example.iter().any(|line| line.contains("fn main"));
-    if needs_main {
-        source.push_str("fn main() {\n");
-    }
-
-    for line in lines {
-        source.push_str(line);
-        source.push('\n');
-    }
-
-    if needs_main {
-        source.push_str("}\n");
-    }
-
-    if let Err(e) = fs::write(&file, &source) {
-        panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy());
-    }
-
-    let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}");
-
-    let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into()));
-
-    cmd.current_dir(clippy_project_root)
-        .env("CARGO_INCREMENTAL", "0")
-        .env("CLIPPY_ARGS", "")
-        .env("CLIPPY_DISABLE_DOCS_LINKS", "1")
-        // We need to disable this to enable all lints
-        .env("ENABLE_METADATA_COLLECTION", "0")
-        .args(["run", "--bin", "clippy-driver"])
-        .args(["--target-dir", "./clippy_lints/target"])
-        .args(["--", "--error-format=json"])
-        .args(["--edition", "2021"])
-        .arg("-Cdebuginfo=0")
-        .args(["-A", "clippy::all"])
-        .args(["-W", &prefixed_name])
-        .args(["-L", "./target/debug"])
-        .args(["-Z", "no-codegen"]);
-
-    let output = cmd
-        .arg(file.as_path())
-        .output()
-        .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}"));
-
-    let tmp_file_path = file.to_string_lossy();
-    let stderr = std::str::from_utf8(&output.stderr).unwrap();
-    let msgs = stderr
-        .lines()
-        .filter(|line| line.starts_with('{'))
-        .map(|line| serde_json::from_str(line).unwrap())
-        .collect::<Vec<serde_json::Value>>();
-
-    let mut rendered = String::new();
-    let iter = msgs
-        .iter()
-        .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name));
-
-    for message in iter {
-        let rendered_part = message["rendered"].as_str().expect("rendered field should exist");
-        rendered.push_str(rendered_part);
-    }
-
-    if rendered.is_empty() {
-        let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect();
-        let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect();
-        panic!(
-            "did not find lint `{lint_name}` in output of example, got:\n{}\n{}",
-            non_json.join("\n"),
-            rendered.join("\n")
-        );
-    }
-
-    // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :)
-    rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs")
-}
-
-#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
-struct SerializableSpan {
-    path: String,
-    line: usize,
-}
-
-impl fmt::Display for SerializableSpan {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
-    }
-}
-
-impl SerializableSpan {
-    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
-        Self::from_span(cx, item.ident.span)
-    }
-
-    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
-        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
-
-        Self {
-            path: format!("{}", loc.file.name.prefer_remapped_unconditionaly()),
-            line: loc.line,
-        }
-    }
-}
-
-#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
-struct ApplicabilityInfo {
-    /// Indicates if any of the lint emissions uses multiple spans. This is related to
-    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
-    /// currently not be applied automatically.
-    is_multi_part_suggestion: bool,
-    applicability: Option<usize>,
-}
-
-impl Serialize for ApplicabilityInfo {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
-        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
-        if let Some(index) = self.applicability {
-            s.serialize_field(
-                "applicability",
-                &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
-            )?;
-        } else {
-            s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
-        }
-        s.end()
-    }
-}
-
-// ==================================================================
-// Lint pass
-// ==================================================================
-impl<'hir> LateLintPass<'hir> for MetadataCollector {
-    /// Collecting lint declarations like:
-    /// ```rust, ignore
-    /// declare_clippy_lint! {
-    ///     /// ### What it does
-    ///     /// Something IDK.
-    ///     pub SOME_LINT,
-    ///     internal,
-    ///     "Who am I?"
-    /// }
-    /// ```
-    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
-        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
-            // Normal lint
-            if is_lint_ref_type(cx, ty)
-                // item validation
-                // disallow check
-                && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase()
-                // metadata extraction
-                && let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item)
-                && let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item)
-            {
-                if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
-                    raw_docs.push_str(&configuration_section);
-                }
-                let version = get_lint_version(cx, item);
-
-                self.lints.push(LintMetadata::new(
-                    lint_name,
-                    SerializableSpan::from_item(cx, item),
-                    group,
-                    level,
-                    version,
-                    raw_docs,
-                ));
-            }
-        }
-    }
-
-    /// Collecting constant applicability from the actual lint emissions
-    ///
-    /// Example:
-    /// ```rust, ignore
-    /// span_lint_and_sugg(
-    ///     cx,
-    ///     SOME_LINT,
-    ///     item.span,
-    ///     "Le lint message",
-    ///     "Here comes help:",
-    ///     "#![allow(clippy::all)]",
-    ///     Applicability::MachineApplicable, // <-- Extracts this constant value
-    /// );
-    /// ```
-    fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
-        if let Some(args) = match_lint_emission(cx, expr) {
-            let emission_info = extract_emission_info(cx, args);
-            if emission_info.is_empty() {
-                // See:
-                // - src/misc.rs:734:9
-                // - src/methods/mod.rs:3545:13
-                // - src/methods/mod.rs:3496:13
-                // We are basically unable to resolve the lint name itself.
-                return;
-            }
-
-            for (lint_name, applicability, is_multi_part) in emission_info {
-                let app_info = self.applicability_info.entry(lint_name).or_default();
-                app_info.applicability = applicability;
-                app_info.is_multi_part_suggestion = is_multi_part;
-            }
-        }
-    }
-}
-
-// ==================================================================
-// Lint definition extraction
-// ==================================================================
-fn sym_to_string(sym: Symbol) -> String {
-    sym.as_str().to_string()
-}
-
-fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
-    extract_attr_docs(cx, item).or_else(|| {
-        lint_collection_error_item(cx, item, "could not collect the lint documentation");
-        None
-    })
-}
-
-/// This function collects all documentation that has been added to an item using
-/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
-///
-/// ```ignore
-/// #[doc = r"Hello world!"]
-/// #[doc = r"=^.^="]
-/// struct SomeItem {}
-/// ```
-///
-/// Would result in `Hello world!\n=^.^=\n`
-fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
-    let attrs = cx.tcx.hir().attrs(item.hir_id());
-    let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
-
-    if let Some(line) = lines.next() {
-        let raw_docs = lines.fold(String::from(line.as_str()) + "\n", |s, line| s + line.as_str() + "\n");
-        return Some(raw_docs);
-    }
-
-    None
-}
-
-/// This function may modify the doc comment to ensure that the string can be displayed using a
-/// markdown viewer in Clippy's lint list. The following modifications could be applied:
-/// * Removal of leading space after a new line. (Important to display tables)
-/// * Ensures that code blocks only contain language information
-fn cleanup_docs(docs_collection: &Vec<String>) -> String {
-    let mut in_code_block = false;
-    let mut is_code_block_rust = false;
-
-    let mut docs = String::new();
-    for line in docs_collection {
-        // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :)
-        if is_code_block_rust && line.trim_start().starts_with("# ") {
-            continue;
-        }
-
-        // The line should be represented in the lint list, even if it's just an empty line
-        docs.push('\n');
-        if let Some(info) = line.trim_start().strip_prefix("```") {
-            in_code_block = !in_code_block;
-            is_code_block_rust = false;
-            if in_code_block {
-                let lang = info
-                    .trim()
-                    .split(',')
-                    // remove rustdoc directives
-                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
-                    // if no language is present, fill in "rust"
-                    .unwrap_or("rust");
-                let len_diff = line.len() - line.trim_start().len();
-                if len_diff != 0 {
-                    // We put back the indentation.
-                    docs.push_str(&line[..len_diff]);
-                }
-                docs.push_str("```");
-                docs.push_str(lang);
-
-                is_code_block_rust = lang == "rust";
-                continue;
-            }
-        }
-        // This removes the leading space that the macro translation introduces
-        if let Some(stripped_doc) = line.strip_prefix(' ') {
-            docs.push_str(stripped_doc);
-        } else if !line.is_empty() {
-            docs.push_str(line);
-        }
-    }
-
-    docs
-}
-
-fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
-    extract_clippy_version_value(cx, item).map_or_else(
-        || VERSION_DEFAULT_STR.to_string(),
-        |version| version.as_str().to_string(),
-    )
-}
-
-fn get_lint_group_and_level_or_lint(
-    cx: &LateContext<'_>,
-    lint_name: &str,
-    item: &Item<'_>,
-) -> Option<(String, &'static str)> {
-    let result = unerased_lint_store(cx.tcx.sess).check_lint_name(
-        lint_name,
-        Some(sym::clippy),
-        &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
-    );
-    if let CheckLintNameResult::Tool(lint_lst, None) = result {
-        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
-            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
-                return None;
-            }
-
-            if let Some(level) = get_lint_level_from_group(&group) {
-                Some((group, level))
-            } else {
-                lint_collection_error_item(
-                    cx,
-                    item,
-                    &format!("Unable to determine lint level for found group `{group}`"),
-                );
-                None
-            }
-        } else {
-            lint_collection_error_item(cx, item, "Unable to determine lint group");
-            None
-        }
-    } else {
-        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
-        None
-    }
-}
-
-fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
-    for (group_name, lints, _) in unerased_lint_store(cx.tcx.sess).get_lint_groups() {
-        if IGNORED_LINT_GROUPS.contains(&group_name) {
-            continue;
-        }
-
-        if lints.iter().any(|group_lint| *group_lint == lint_id) {
-            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
-            return Some((*group).to_string());
-        }
-    }
-
-    None
-}
-
-fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
-    DEFAULT_LINT_LEVELS
-        .iter()
-        .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
-}
-
-fn collect_renames(lints: &mut Vec<LintMetadata>) {
-    for lint in lints {
-        let mut collected = String::new();
-        let mut names = vec![lint.id.clone()];
-
-        loop {
-            if let Some(lint_name) = names.pop() {
-                for (k, v) in crate::deprecated_lints::RENAMED {
-                    if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX)
-                        && name == lint_name
-                        && let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX)
-                    {
-                        lint.former_ids.insert(past_name.to_owned());
-                        writeln!(collected, "* `{past_name}`").unwrap();
-                        names.push(past_name.to_string());
-                    }
-                }
-
-                continue;
-            }
-
-            break;
-        }
-
-        if !collected.is_empty() {
-            write!(
-                &mut lint.docs,
-                r#"
-### Past names
-
-{collected}
-"#
-            )
-            .unwrap();
-        }
-    }
-}
-
-// ==================================================================
-// Lint emission
-// ==================================================================
-fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
-    span_lint(
-        cx,
-        METADATA_COLLECTOR,
-        item.ident.span,
-        format!("metadata collection error for `{}`: {message}", item.ident.name),
-    );
-}
-
-// ==================================================================
-// Applicability
-// ==================================================================
-/// This function checks if a given expression is equal to a simple lint emission function call.
-/// It will return the function arguments if the emission matched any function.
-fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
-    LINT_EMISSION_FUNCTIONS
-        .iter()
-        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
-}
-
-fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
-    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
-}
-
-fn extract_emission_info<'hir>(
-    cx: &LateContext<'hir>,
-    args: &'hir [hir::Expr<'hir>],
-) -> Vec<(String, Option<usize>, bool)> {
-    let mut lints = Vec::new();
-    let mut applicability = None;
-    let mut multi_part = false;
-
-    for arg in args {
-        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
-
-        if match_type(cx, arg_ty, &paths::LINT) {
-            // If we found the lint arg, extract the lint name
-            let mut resolved_lints = resolve_lints(cx, arg);
-            lints.append(&mut resolved_lints);
-        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
-            applicability = resolve_applicability(cx, arg);
-        } else if arg_ty.is_closure() {
-            multi_part |= check_is_multi_part(cx, arg);
-            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
-        }
-    }
-
-    lints
-        .into_iter()
-        .map(|lint_name| (lint_name, applicability, multi_part))
-        .collect()
-}
-
-/// Resolves the possible lints that this expression could reference
-fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
-    let mut resolver = LintResolver::new(cx);
-    resolver.visit_expr(expr);
-    resolver.lints
-}
-
-/// This function tries to resolve the linked applicability to the given expression.
-fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
-    let mut resolver = ApplicabilityResolver::new(cx);
-    resolver.visit_expr(expr);
-    resolver.complete()
-}
-
-fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
-    if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind {
-        let mut scanner = IsMultiSpanScanner::new(cx);
-        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
-        return scanner.is_multi_part();
-    } else if let Some(local) = get_parent_local(cx, closure_expr) {
-        if let Some(local_init) = local.init {
-            return check_is_multi_part(cx, local_init);
-        }
-    }
-
-    false
-}
-
-struct LintResolver<'a, 'hir> {
-    cx: &'a LateContext<'hir>,
-    lints: Vec<String>,
-}
-
-impl<'a, 'hir> LintResolver<'a, 'hir> {
-    fn new(cx: &'a LateContext<'hir>) -> Self {
-        Self {
-            cx,
-            lints: Vec::<String>::default(),
-        }
-    }
-}
-
-impl<'a, 'hir> Visitor<'hir> for LintResolver<'a, 'hir> {
-    type NestedFilter = nested_filter::All;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
-    }
-
-    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
-        if let ExprKind::Path(qpath) = &expr.kind
-            && let QPath::Resolved(_, path) = qpath
-            && let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr))
-            && match_type(self.cx, expr_ty, &paths::LINT)
-        {
-            if let hir::def::Res::Def(DefKind::Static { .. }, _) = path.res {
-                let lint_name = last_path_segment(qpath).ident.name;
-                self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
-            } else if let Some(local) = get_parent_local(self.cx, expr) {
-                if let Some(local_init) = local.init {
-                    intravisit::walk_expr(self, local_init);
-                }
-            }
-        }
-
-        intravisit::walk_expr(self, expr);
-    }
-}
-
-/// This visitor finds the highest applicability value in the visited expressions
-struct ApplicabilityResolver<'a, 'hir> {
-    cx: &'a LateContext<'hir>,
-    /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES`
-    applicability_index: Option<usize>,
-}
-
-impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
-    fn new(cx: &'a LateContext<'hir>) -> Self {
-        Self {
-            cx,
-            applicability_index: None,
-        }
-    }
-
-    fn add_new_index(&mut self, new_index: usize) {
-        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
-    }
-
-    fn complete(self) -> Option<usize> {
-        self.applicability_index
-    }
-}
-
-impl<'a, 'hir> Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
-    type NestedFilter = nested_filter::All;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
-    }
-
-    fn visit_path(&mut self, path: &hir::Path<'hir>, _id: hir::HirId) {
-        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
-            if match_path(path, enum_value) {
-                self.add_new_index(index);
-                return;
-            }
-        }
-    }
-
-    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
-        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
-
-        if match_type(self.cx, expr_ty, &paths::APPLICABILITY)
-            && let Some(local) = get_parent_local(self.cx, expr)
-            && let Some(local_init) = local.init
-        {
-            intravisit::walk_expr(self, local_init);
-        };
-
-        intravisit::walk_expr(self, expr);
-    }
-}
-
-/// This returns the parent local node if the expression is a reference one
-fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::LetStmt<'hir>> {
-    if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
-        if let hir::def::Res::Local(local_hir) = path.res {
-            return get_parent_local_hir_id(cx, local_hir);
-        }
-    }
-
-    None
-}
-
-fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::LetStmt<'hir>> {
-    match cx.tcx.parent_hir_node(hir_id) {
-        hir::Node::LetStmt(local) => Some(local),
-        hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id),
-        _ => None,
-    }
-}
-
-/// This visitor finds the highest applicability value in the visited expressions
-struct IsMultiSpanScanner<'a, 'hir> {
-    cx: &'a LateContext<'hir>,
-    suggestion_count: usize,
-}
-
-impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
-    fn new(cx: &'a LateContext<'hir>) -> Self {
-        Self {
-            cx,
-            suggestion_count: 0,
-        }
-    }
-
-    /// Add a new single expression suggestion to the counter
-    fn add_single_span_suggestion(&mut self) {
-        self.suggestion_count += 1;
-    }
-
-    /// Signals that a suggestion with possible multiple spans was found
-    fn add_multi_part_suggestion(&mut self) {
-        self.suggestion_count += 2;
-    }
-
-    /// Checks if the suggestions include multiple spans
-    fn is_multi_part(&self) -> bool {
-        self.suggestion_count > 1
-    }
-}
-
-impl<'a, 'hir> Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
-    type NestedFilter = nested_filter::All;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
-    }
-
-    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
-        // Early return if the lint is already multi span
-        if self.is_multi_part() {
-            return;
-        }
-
-        if let ExprKind::MethodCall(path, recv, _, _arg_span) = &expr.kind {
-            let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv));
-            if match_type(self.cx, self_ty, &paths::DIAG) {
-                let called_method = path.ident.name.as_str().to_string();
-                for (method_name, is_multi_part) in &SUGGESTION_DIAG_METHODS {
-                    if *method_name == called_method {
-                        if *is_multi_part {
-                            self.add_multi_part_suggestion();
-                        } else {
-                            self.add_single_span_suggestion();
-                        }
-                        break;
-                    }
-                }
-            }
-        }
-
-        intravisit::walk_expr(self, expr);
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index 228db14d1b7..d3e49bff422 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -5,7 +5,7 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_copy;
 use clippy_utils::visitors::for_each_local_use_after_expr;
 use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method};
@@ -214,9 +214,11 @@ impl SuggestedType {
     }
 
     fn snippet(self, cx: &LateContext<'_>, args_span: Option<Span>, len_span: Option<Span>) -> String {
-        let maybe_args = args_span.and_then(|sp| snippet_opt(cx, sp)).unwrap_or_default();
+        let maybe_args = args_span
+            .and_then(|sp| sp.get_source_text(cx))
+            .map_or(String::new(), |x| x.to_owned());
         let maybe_len = len_span
-            .and_then(|sp| snippet_opt(cx, sp).map(|s| format!("; {s}")))
+            .and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}")))
             .unwrap_or_default();
 
         match self {
diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs
index 63f3a5d7f83..7a85196ceca 100644
--- a/src/tools/clippy/clippy_lints/src/visibility.rs
+++ b/src/tools/clippy/clippy_lints/src/visibility.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::ast::{Item, VisibilityKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
@@ -82,9 +82,7 @@ impl EarlyLintPass for Visibility {
         if !in_external_macro(cx.sess(), item.span)
             && let VisibilityKind::Restricted { path, shorthand, .. } = &item.vis.kind
         {
-            if **path == kw::SelfLower
-                && let Some(false) = is_from_proc_macro(cx, item.vis.span)
-            {
+            if **path == kw::SelfLower && !is_from_proc_macro(cx, item.vis.span) {
                 span_lint_and_then(
                     cx,
                     NEEDLESS_PUB_SELF,
@@ -104,7 +102,7 @@ impl EarlyLintPass for Visibility {
             if (**path == kw::Super || **path == kw::SelfLower || **path == kw::Crate)
                 && !*shorthand
                 && let [.., last] = &*path.segments
-                && let Some(false) = is_from_proc_macro(cx, item.vis.span)
+                && !is_from_proc_macro(cx, item.vis.span)
             {
                 #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
                 span_lint_and_then(
@@ -125,7 +123,7 @@ impl EarlyLintPass for Visibility {
 
             if *shorthand
                 && let [.., last] = &*path.segments
-                && let Some(false) = is_from_proc_macro(cx, item.vis.span)
+                && !is_from_proc_macro(cx, item.vis.span)
             {
                 #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
                 span_lint_and_then(
@@ -147,6 +145,6 @@ impl EarlyLintPass for Visibility {
     }
 }
 
-fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> Option<bool> {
-    snippet_opt(cx, span).map(|s| !s.starts_with("pub"))
+fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> bool {
+    !span.check_source_text(cx, |src| src.starts_with("pub"))
 }
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index c4d64ee4609..7d9e58ad2f8 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -100,14 +100,14 @@ declare_clippy_lint! {
 
 pub struct WildcardImports {
     warn_on_all: bool,
-    allowed_segments: &'static FxHashSet<String>,
+    allowed_segments: FxHashSet<String>,
 }
 
 impl WildcardImports {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
             warn_on_all: conf.warn_on_all_wildcard_imports,
-            allowed_segments: &conf.allowed_wildcard_imports,
+            allowed_segments: conf.allowed_wildcard_imports.iter().cloned().collect(),
         }
     }
 }
@@ -181,7 +181,7 @@ impl WildcardImports {
     fn check_exceptions(&self, cx: &LateContext<'_>, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
         item.span.from_expansion()
             || is_prelude_import(segments)
-            || is_allowed_via_config(segments, self.allowed_segments)
+            || is_allowed_via_config(segments, &self.allowed_segments)
             || (is_super_only_import(segments) && is_in_test(cx.tcx, item.hir_id()))
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 54e7e92f0c4..ec5a5896fb2 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -2,7 +2,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::is_in_test;
 use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall};
-use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
+use clippy_utils::source::{expand_past_previous_comma, SpanRangeExt};
 use rustc_ast::token::LitKind;
 use rustc_ast::{
     FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder,
@@ -397,7 +397,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma
             format!("using `{name}!()` with a format string that ends in a single newline"),
             |diag| {
                 let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
-                let Some(format_snippet) = snippet_opt(cx, format_string_span) else {
+                let Some(format_snippet) = format_string_span.get_source_text(cx) else {
                     return;
                 };
 
@@ -492,7 +492,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
             && let Some(arg) = format_args.arguments.by_index(index)
             && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
             && !arg.expr.span.from_expansion()
-            && let Some(value_string) = snippet_opt(cx, arg.expr.span)
+            && let Some(value_string) = arg.expr.span.get_source_text(cx)
         {
             let (replacement, replace_raw) = match lit.kind {
                 LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
@@ -515,7 +515,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
                 _ => continue,
             };
 
-            let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else {
+            let Some(format_string_snippet) = format_args.span.get_source_text(cx) else {
                 continue;
             };
             let format_string_is_raw = format_string_snippet.starts_with('r');
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 9fefd94d339..629ce9d04d4 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -8,7 +8,6 @@ publish = false
 clippy_config = { path = "../clippy_config" }
 arrayvec = { version = "0.7", default-features = false }
 itertools = "0.12"
-rustc-semver = "1.1"
 # FIXME(f16_f128): remove when no longer needed for parsing
 rustc_apfloat = "0.2.0"
 
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
index d2200bcf710..42c8b218d14 100644
--- a/src/tools/clippy/clippy_utils/src/attrs.rs
+++ b/src/tools/clippy/clippy_utils/src/attrs.rs
@@ -7,7 +7,7 @@ use rustc_session::Session;
 use rustc_span::{sym, Span};
 use std::str::FromStr;
 
-use crate::source::snippet_opt;
+use crate::source::SpanRangeExt;
 use crate::tokenize_with_text;
 
 /// Deprecation status of attributes known by Clippy.
@@ -179,25 +179,23 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
 
 /// Checks if the given span contains a `#[cfg(..)]` attribute
 pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
-    let Some(snip) = snippet_opt(cx, s) else {
-        // Assume true. This would require either an invalid span, or one which crosses file boundaries.
-        return true;
-    };
-    let mut iter = tokenize_with_text(&snip);
+    s.check_source_text(cx, |src| {
+        let mut iter = tokenize_with_text(src);
 
-    // Search for the token sequence [`#`, `[`, `cfg`]
-    while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
-        let mut iter = iter.by_ref().skip_while(|(t, _)| {
-            matches!(
-                t,
-                TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
-            )
-        });
-        if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
-            && matches!(iter.next(), Some((TokenKind::Ident, "cfg")))
-        {
-            return true;
+        // Search for the token sequence [`#`, `[`, `cfg`]
+        while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
+            let mut iter = iter.by_ref().skip_while(|(t, _)| {
+                matches!(
+                    t,
+                    TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
+                )
+            });
+            if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
+                && matches!(iter.next(), Some((TokenKind::Ident, "cfg")))
+            {
+                return true;
+            }
         }
-    }
-    false
+        false
+    })
 }
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index 7f2234b310b..2bd6837d973 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -123,16 +123,14 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
 
 fn path_search_pat(path: &Path<'_>) -> (Pat, Pat) {
     let (head, tail) = match path.segments {
-        [head, .., tail] => (head, tail),
-        [p] => (p, p),
         [] => return (Pat::Str(""), Pat::Str("")),
+        [p] => (Pat::Sym(p.ident.name), p),
+        // QPath::Resolved can have a path that looks like `<Foo as Bar>::baz` where
+        // the path (`Bar::baz`) has it's span covering the whole QPath.
+        [.., tail] => (Pat::Str(""), tail),
     };
     (
-        if head.ident.name == kw::PathRoot {
-            Pat::Str("::")
-        } else {
-            Pat::Sym(head.ident.name)
-        },
+        head,
         if tail.args.is_some() {
             Pat::Str(">")
         } else {
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index e907e4058e5..760d5bc95f7 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -687,7 +687,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
                 if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
                     && let expr_lo = expr_span.lo()
                     && expr_lo >= span.lo
-                    && let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx)
+                    && let Some(src) = (span.lo..expr_lo).get_source_range(&self.tcx)
                     && let Some(src) = src.as_str()
                 {
                     use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index f325e4eaf15..f61ef9ac1b0 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -1,6 +1,6 @@
 use crate::consts::ConstEvalCtxt;
 use crate::macros::macro_backtrace;
-use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt};
+use crate::source::{walk_span_to_context, SpanRange, SpanRangeExt};
 use crate::tokenize_with_text;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
@@ -588,10 +588,9 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
             // block with an empty span.
             ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
             // `{}` => `()`
-            ([], None) => match snippet_opt(cx, block.span) {
-                // Don't reduce if there are any tokens contained in the braces
-                Some(snip)
-                    if tokenize(&snip)
+            ([], None)
+                if block.span.check_source_text(cx, |src| {
+                    tokenize(src)
                         .map(|t| t.kind)
                         .filter(|t| {
                             !matches!(
@@ -599,11 +598,10 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
                                 TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
                             )
                         })
-                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
-                {
-                    kind
-                },
-                _ => &ExprKind::Tup(&[]),
+                        .eq([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied())
+                }) =>
+            {
+                &ExprKind::Tup(&[])
             },
             ([], Some(expr)) => match expr.kind {
                 // `{ return .. }` => `return ..`
@@ -1191,9 +1189,9 @@ fn eq_span_tokens(
     pred: impl Fn(TokenKind) -> bool,
 ) -> bool {
     fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool {
-        if let Some(lsrc) = left.get_source_text(cx)
+        if let Some(lsrc) = left.get_source_range(cx)
             && let Some(lsrc) = lsrc.as_str()
-            && let Some(rsrc) = right.get_source_text(cx)
+            && let Some(rsrc) = right.get_source_range(cx)
             && let Some(rsrc) = rsrc.as_str()
         {
             let pred = |t: &(_, _)| pred(t.0);
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 28755ae0710..5db14872c36 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -149,6 +149,7 @@ macro_rules! extract_msrv_attr {
 
 /// If the given expression is a local binding, find the initializer expression.
 /// If that initializer expression is another local binding, find its initializer again.
+///
 /// This process repeats as long as possible (but usually no more than once). Initializer
 /// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
 /// instead.
@@ -179,6 +180,7 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr
 }
 
 /// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
+///
 /// By only considering immutable bindings, we guarantee that the returned expression represents the
 /// value of the binding wherever it is referenced.
 ///
@@ -431,12 +433,12 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tc
         })
 }
 
-/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
+/// THIS METHOD IS DEPRECATED. Matches a `QPath` against a slice of segment string literals.
+///
+/// This method is deprecated and will eventually be removed since it does not match against the
 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 /// `QPath::Resolved.1.res.opt_def_id()`.
 ///
-/// Matches a `QPath` against a slice of segment string literals.
-///
 /// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
 /// `rustc_hir::QPath`.
 ///
@@ -485,12 +487,12 @@ pub fn is_path_diagnostic_item<'tcx>(
     path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
 }
 
-/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
+/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals.
+///
+/// This method is deprecated and will eventually be removed since it does not match against the
 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 /// `QPath::Resolved.1.res.opt_def_id()`.
 ///
-/// Matches a `Path` against a slice of segment string literals.
-///
 /// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
 /// `rustc_hir::Path`.
 ///
@@ -905,6 +907,7 @@ pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) ->
 }
 
 /// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
+///
 /// It doesn't cover all cases, for example indirect function calls (some of std
 /// functions are supported) but it is the best we have.
 pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
@@ -1061,6 +1064,7 @@ impl std::ops::BitOrAssign for CaptureKind {
 }
 
 /// Given an expression referencing a local, determines how it would be captured in a closure.
+///
 /// Note as this will walk up to parent expressions until the capture can be determined it should
 /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 /// function argument (other than a receiver).
@@ -2365,8 +2369,9 @@ pub fn fn_def_id_with_node_args<'tcx>(
 }
 
 /// Returns `Option<String>` where String is a textual representation of the type encapsulated in
-/// the slice iff the given expression is a slice of primitives (as defined in the
-/// `is_recursively_primitive_type` function) and `None` otherwise.
+/// the slice iff the given expression is a slice of primitives.
+///
+/// (As defined in the `is_recursively_primitive_type` function.) Returns `None` otherwise.
 pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
     let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
     let expr_kind = expr_type.kind();
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 455239cc37f..1d7479bff82 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -150,10 +150,11 @@ pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) ->
 }
 
 /// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
-/// macro call site (i.e. the parent of the macro expansion). This generally means that `node`
-/// is the outermost node of an entire macro expansion, but there are some caveats noted below.
-/// This is useful for finding macro calls while visiting the HIR without processing the macro call
-/// at every node within its expansion.
+/// macro call site (i.e. the parent of the macro expansion).
+///
+/// This generally means that `node` is the outermost node of an entire macro expansion, but there
+/// are some caveats noted below. This is useful for finding macro calls while visiting the HIR
+/// without processing the macro call at every node within its expansion.
 ///
 /// If you already have immediate access to the parent node, it is simpler to
 /// just check the context of that span directly (e.g. `parent.span.from_expansion()`).
@@ -426,12 +427,8 @@ impl FormatArgsStorage {
     }
 }
 
-/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
-/// it cannot be found it will return the [`rustc_ast::Expr`].
-pub fn find_format_arg_expr<'hir, 'ast>(
-    start: &'hir Expr<'hir>,
-    target: &'ast FormatArgument,
-) -> Result<&'hir Expr<'hir>, &'ast rustc_ast::Expr> {
+/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value
+pub fn find_format_arg_expr<'hir>(start: &'hir Expr<'hir>, target: &FormatArgument) -> Option<&'hir Expr<'hir>> {
     let SpanData {
         lo,
         hi,
@@ -449,7 +446,6 @@ pub fn find_format_arg_expr<'hir, 'ast>(
             ControlFlow::Continue(())
         }
     })
-    .ok_or(&target.expr)
 }
 
 /// Span of the `:` and format specifiers
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 95fbf0b2ea2..d9befb3c157 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -18,7 +18,6 @@ use rustc_middle::mir::{
 use rustc_middle::traits::{BuiltinImplSource, ImplSource, ObligationCause};
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::{self, GenericArgKind, TraitRef, Ty, TyCtxt};
-use rustc_semver::RustcVersion;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext};
@@ -391,11 +390,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
                     StableSince::Err => return false,
                 };
 
-                msrv.meets(RustcVersion::new(
-                    u32::from(const_stab_rust_version.major),
-                    u32::from(const_stab_rust_version.minor),
-                    u32::from(const_stab_rust_version.patch),
-                ))
+                msrv.meets(const_stab_rust_version)
             } else {
                 // Unstable const fn with the feature enabled.
                 msrv.current().is_none()
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 96dd3c55d37..482e1e0147b 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -16,7 +16,7 @@ use rustc_span::{
 };
 use std::borrow::Cow;
 use std::fmt;
-use std::ops::Range;
+use std::ops::{Deref, Index, Range};
 
 pub trait HasSession {
     fn sess(&self) -> &Session;
@@ -94,10 +94,16 @@ impl IntoSpan for Range<BytePos> {
 }
 
 pub trait SpanRangeExt: SpanRange {
+    /// Attempts to get a handle to the source text. Returns `None` if either the span is malformed,
+    /// or the source text is not accessible.
+    fn get_source_text(self, cx: &impl HasSession) -> Option<SourceText> {
+        get_source_range(cx.sess().source_map(), self.into_range()).and_then(SourceText::new)
+    }
+
     /// Gets the source file, and range in the file, of the given span. Returns `None` if the span
     /// extends through multiple files, or is malformed.
-    fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> {
-        get_source_text(cx.sess().source_map(), self.into_range())
+    fn get_source_range(self, cx: &impl HasSession) -> Option<SourceFileRange> {
+        get_source_range(cx.sess().source_map(), self.into_range())
     }
 
     /// Calls the given function with the source text referenced and returns the value. Returns
@@ -144,32 +150,70 @@ pub trait SpanRangeExt: SpanRange {
     fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> {
         trim_start(cx.sess().source_map(), self.into_range())
     }
+}
+impl<T: SpanRange> SpanRangeExt for T {}
 
-    /// Writes the referenced source text to the given writer. Will return `Err` if the source text
-    /// could not be retrieved.
-    fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result {
-        write_source_text_to(cx.sess().source_map(), self.into_range(), dst)
+/// Handle to a range of text in a source file.
+pub struct SourceText(SourceFileRange);
+impl SourceText {
+    /// Takes ownership of the source file handle if the source text is accessible.
+    pub fn new(text: SourceFileRange) -> Option<Self> {
+        if text.as_str().is_some() {
+            Some(Self(text))
+        } else {
+            None
+        }
     }
 
-    /// Extracts the referenced source text as an owned string.
-    fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> {
-        self.with_source_text(cx, ToOwned::to_owned)
+    /// Gets the source text.
+    pub fn as_str(&self) -> &str {
+        self.0.as_str().unwrap()
+    }
+
+    /// Converts this into an owned string.
+    pub fn to_owned(&self) -> String {
+        self.as_str().to_owned()
+    }
+}
+impl Deref for SourceText {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.as_str()
+    }
+}
+impl AsRef<str> for SourceText {
+    fn as_ref(&self) -> &str {
+        self.as_str()
+    }
+}
+impl<T> Index<T> for SourceText
+where
+    str: Index<T>,
+{
+    type Output = <str as Index<T>>::Output;
+    fn index(&self, idx: T) -> &Self::Output {
+        &self.as_str()[idx]
+    }
+}
+impl fmt::Display for SourceText {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.as_str().fmt(f)
     }
 }
-impl<T: SpanRange> SpanRangeExt for T {}
 
-fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
+fn get_source_range(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
     let start = sm.lookup_byte_offset(sp.start);
     let end = sm.lookup_byte_offset(sp.end);
     if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
         return None;
     }
+    sm.ensure_source_file_source_present(&start.sf);
     let range = start.pos.to_usize()..end.pos.to_usize();
     Some(SourceFileRange { sf: start.sf, range })
 }
 
 fn with_source_text<T>(sm: &SourceMap, sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
-    if let Some(src) = get_source_text(sm, sp)
+    if let Some(src) = get_source_range(sm, sp)
         && let Some(src) = src.as_str()
     {
         Some(f(src))
@@ -183,7 +227,7 @@ fn with_source_text_and_range<T>(
     sp: Range<BytePos>,
     f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
 ) -> Option<T> {
-    if let Some(src) = get_source_text(sm, sp)
+    if let Some(src) = get_source_range(sm, sp)
         && let Some(text) = &src.sf.src
     {
         Some(f(text, src.range))
@@ -198,7 +242,7 @@ fn map_range(
     sp: Range<BytePos>,
     f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>,
 ) -> Option<Range<BytePos>> {
-    if let Some(src) = get_source_text(sm, sp.clone())
+    if let Some(src) = get_source_range(sm, sp.clone())
         && let Some(text) = &src.sf.src
         && let Some(range) = f(text, src.range.clone())
     {
@@ -232,13 +276,6 @@ fn trim_start(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
     .unwrap_or(sp)
 }
 
-fn write_source_text_to(sm: &SourceMap, sp: Range<BytePos>, dst: &mut impl fmt::Write) -> fmt::Result {
-    match with_source_text(sm, sp, |src| dst.write_str(src)) {
-        Some(x) => x,
-        None => Err(fmt::Error),
-    }
-}
-
 pub struct SourceFileRange {
     pub sf: Lrc<SourceFile>,
     pub range: Range<usize>,
@@ -247,7 +284,11 @@ impl SourceFileRange {
     /// Attempts to get the text from the source file. This can fail if the source text isn't
     /// loaded.
     pub fn as_str(&self) -> Option<&str> {
-        self.sf.src.as_ref().and_then(|x| x.get(self.range.clone()))
+        self.sf
+            .src
+            .as_ref()
+            .or_else(|| self.sf.external_src.get().and_then(|src| src.get_source()))
+            .and_then(|x| x.get(self.range.clone()))
     }
 }
 
@@ -548,9 +589,10 @@ pub fn snippet_block_with_context<'a>(
     (reindent_multiline(snip, true, indent), from_macro)
 }
 
-/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
-/// will result in the macro call, rather than the expansion, if the span is from a child context.
-/// If the span is not from a child context, it will be used directly instead.
+/// Same as `snippet_with_applicability`, but first walks the span up to the given context.
+///
+/// This will result in the macro call, rather than the expansion, if the span is from a child
+/// context. If the span is not from a child context, it will be used directly instead.
 ///
 /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
 /// would result in `box []`. If given the context of the address of expression, this function will
@@ -593,9 +635,10 @@ fn snippet_with_context_sess<'a>(
 }
 
 /// Walks the span up to the target context, thereby returning the macro call site if the span is
-/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the
-/// case of the span being in a macro expansion, but the target context is from expanding a macro
-/// argument.
+/// inside a macro expansion, or the original span if it is not.
+///
+/// Note this will return `None` in the case of the span being in a macro expansion, but the target
+/// context is from expanding a macro argument.
 ///
 /// Given the following
 ///
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 2f6faba073e..f80981c11af 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -160,8 +160,10 @@ pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symb
 }
 
 /// Returns true if `ty` is a type on which calling `Clone` through a function instead of
-/// as a method, such as `Arc::clone()` is considered idiomatic. Lints should avoid suggesting to
-/// replace instances of `ty::Clone()` by `.clone()` for objects of those types.
+/// as a method, such as `Arc::clone()` is considered idiomatic.
+///
+/// Lints should avoid suggesting to replace instances of `ty::Clone()` by `.clone()` for objects
+/// of those types.
 pub fn should_call_clone_as_function(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
     matches!(
         get_type_diagnostic_name(cx, ty),
@@ -398,8 +400,10 @@ fn is_normalizable_helper<'tcx>(
 }
 
 /// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
-/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
-/// tuples and slices of primitive type) see `is_recursively_primitive_type`
+/// integer or floating-point number type).
+///
+/// For checking aggregation of primitive types (e.g. tuples and slices of primitive type) see
+/// `is_recursively_primitive_type`
 pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
     matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
 }
@@ -455,11 +459,6 @@ pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: LangItem)
     }
 }
 
-/// Gets the diagnostic name of the type, if it has one
-pub fn type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
-    ty.ty_adt_def().and_then(|adt| cx.tcx.get_diagnostic_name(adt.did()))
-}
-
 /// Return `true` if the passed `typ` is `isize` or `usize`.
 pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
     matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
@@ -476,9 +475,10 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
     }
 }
 
-/// Checks if the drop order for a type matters. Some std types implement drop solely to
-/// deallocate memory. For these types, and composites containing them, changing the drop order
-/// won't result in any observable side effects.
+/// Checks if the drop order for a type matters.
+///
+/// Some std types implement drop solely to deallocate memory. For these types, and composites
+/// containing them, changing the drop order won't result in any observable side effects.
 pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
         if !seen.insert(ty) {
@@ -1311,6 +1311,7 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl
 }
 
 /// Checks if a Ty<'_> has some inherent method Symbol.
+///
 /// This does not look for impls in the type's `Deref::Target` type.
 /// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`.
 pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> {
diff --git a/src/tools/clippy/declare_clippy_lint/src/lib.rs b/src/tools/clippy/declare_clippy_lint/src/lib.rs
index ca070f6c250..6aa24329b06 100644
--- a/src/tools/clippy/declare_clippy_lint/src/lib.rs
+++ b/src/tools/clippy/declare_clippy_lint/src/lib.rs
@@ -1,4 +1,4 @@
-#![feature(let_chains)]
+#![feature(let_chains, proc_macro_span)]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
@@ -23,6 +23,7 @@ fn parse_attr<const LEN: usize>(path: [&'static str; LEN], attr: &Attribute) ->
 
 struct ClippyLint {
     attrs: Vec<Attribute>,
+    version: Option<LitStr>,
     explanation: String,
     name: Ident,
     category: Ident,
@@ -41,8 +42,14 @@ impl Parse for ClippyLint {
                 let value = lit.value();
                 let line = value.strip_prefix(' ').unwrap_or(&value);
 
-                if line.starts_with("```") {
-                    explanation += "```\n";
+                if let Some(lang) = line.strip_prefix("```") {
+                    let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
+                    if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
+                        explanation += "```rust\n";
+                    } else {
+                        explanation += line;
+                        explanation.push('\n');
+                    }
                     in_code = !in_code;
                 } else if !(in_code && line.starts_with("# ")) {
                     explanation += line;
@@ -68,6 +75,7 @@ impl Parse for ClippyLint {
 
         Ok(Self {
             attrs,
+            version,
             explanation,
             name,
             category,
@@ -123,6 +131,7 @@ impl Parse for ClippyLint {
 pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
     let ClippyLint {
         attrs,
+        version,
         explanation,
         name,
         category,
@@ -146,6 +155,14 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
     (&mut category[0..1]).make_ascii_uppercase();
     let category_variant = format_ident!("{category}");
 
+    let name_span = name.span().unwrap();
+    let location = format!("{}#L{}", name_span.source_file().path().display(), name_span.line());
+
+    let version = match version {
+        Some(version) => quote!(Some(#version)),
+        None => quote!(None),
+    };
+
     let output = quote! {
         rustc_session::declare_tool_lint! {
             #(#attrs)*
@@ -159,6 +176,8 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
             lint: &#name,
             category: crate::LintCategory::#category_variant,
             explanation: #explanation,
+            location: #location,
+            version: #version,
         };
     };
 
diff --git a/src/tools/clippy/lintcheck/src/json.rs b/src/tools/clippy/lintcheck/src/json.rs
index 4211bce90b2..ee0c80aea52 100644
--- a/src/tools/clippy/lintcheck/src/json.rs
+++ b/src/tools/clippy/lintcheck/src/json.rs
@@ -12,21 +12,21 @@ const TRUNCATION_TOTAL_TARGET: usize = 1000;
 
 #[derive(Debug, Deserialize, Serialize)]
 struct LintJson {
-    lint: String,
-    krate: String,
-    file_name: String,
-    byte_pos: (u32, u32),
-    file_link: String,
+    /// The lint name e.g. `clippy::bytes_nth`
+    name: String,
+    /// The filename and line number e.g. `anyhow-1.0.86/src/error.rs:42`
+    file_line: String,
+    file_url: String,
     rendered: String,
 }
 
 impl LintJson {
     fn key(&self) -> impl Ord + '_ {
-        (self.lint.as_str(), self.file_name.as_str(), self.byte_pos)
+        (self.name.as_str(), self.file_line.as_str())
     }
 
     fn info_text(&self, action: &str) -> String {
-        format!("{action} `{}` in `{}` at {}", self.lint, self.krate, self.file_link)
+        format!("{action} `{}` at [`{}`]({})", self.name, self.file_line, self.file_url)
     }
 }
 
@@ -36,13 +36,16 @@ pub(crate) fn output(clippy_warnings: Vec<ClippyWarning>) -> String {
         .into_iter()
         .map(|warning| {
             let span = warning.span();
+            let file_name = span
+                .file_name
+                .strip_prefix("target/lintcheck/sources/")
+                .unwrap_or(&span.file_name);
+            let file_line = format!("{file_name}:{}", span.line_start);
             LintJson {
-                file_name: span.file_name.clone(),
-                byte_pos: (span.byte_start, span.byte_end),
-                krate: warning.krate,
-                file_link: warning.url,
-                lint: warning.lint,
-                rendered: warning.diag.rendered.unwrap(),
+                name: warning.name,
+                file_line,
+                file_url: warning.url,
+                rendered: warning.diag.rendered.unwrap().trim().to_string(),
             }
         })
         .collect();
@@ -63,7 +66,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) {
     let mut lint_warnings = vec![];
 
     for (name, changes) in &itertools::merge_join_by(old_warnings, new_warnings, |old, new| old.key().cmp(&new.key()))
-        .chunk_by(|change| change.as_ref().into_left().lint.to_string())
+        .chunk_by(|change| change.as_ref().into_left().name.clone())
     {
         let mut added = Vec::new();
         let mut removed = Vec::new();
@@ -162,7 +165,7 @@ fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) {
         return;
     }
 
-    print_h3(&warnings[0].lint, title);
+    print_h3(&warnings[0].name, title);
     println!();
 
     let warnings = truncate(warnings, truncate_after);
@@ -171,7 +174,7 @@ fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) {
         println!("{}", warning.info_text(title));
         println!();
         println!("```");
-        println!("{}", warning.rendered.trim_end());
+        println!("{}", warning.rendered);
         println!("```");
         println!();
     }
@@ -182,7 +185,7 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) {
         return;
     }
 
-    print_h3(&changed[0].0.lint, "Changed");
+    print_h3(&changed[0].0.name, "Changed");
     println!();
 
     let changed = truncate(changed, truncate_after);
@@ -191,7 +194,7 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) {
         println!("{}", new.info_text("Changed"));
         println!();
         println!("```diff");
-        for change in diff::lines(old.rendered.trim_end(), new.rendered.trim_end()) {
+        for change in diff::lines(&old.rendered, &new.rendered) {
             use diff::Result::{Both, Left, Right};
 
             match change {
diff --git a/src/tools/clippy/lintcheck/src/output.rs b/src/tools/clippy/lintcheck/src/output.rs
index 15378630695..e38036315c2 100644
--- a/src/tools/clippy/lintcheck/src/output.rs
+++ b/src/tools/clippy/lintcheck/src/output.rs
@@ -51,7 +51,7 @@ impl RustcIce {
 /// A single warning that clippy issued while checking a `Crate`
 #[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct ClippyWarning {
-    pub lint: String,
+    pub name: String,
     pub diag: Diagnostic,
     pub krate: String,
     /// The URL that points to the file and line of the lint emission
@@ -60,8 +60,8 @@ pub struct ClippyWarning {
 
 impl ClippyWarning {
     pub fn new(mut diag: Diagnostic, base_url: &str, krate: &str) -> Option<Self> {
-        let lint = diag.code.clone()?.code;
-        if !(lint.contains("clippy") || diag.message.contains("clippy"))
+        let name = diag.code.clone()?.code;
+        if !(name.contains("clippy") || diag.message.contains("clippy"))
             || diag.message.contains("could not read cargo metadata")
         {
             return None;
@@ -92,7 +92,7 @@ impl ClippyWarning {
         };
 
         Some(Self {
-            lint,
+            name,
             diag,
             url,
             krate: krate.to_string(),
@@ -108,7 +108,7 @@ impl ClippyWarning {
         let mut file = span.file_name.clone();
         let file_with_pos = format!("{file}:{}:{}", span.line_start, span.line_end);
         match format {
-            OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.lint, self.diag.message),
+            OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.name, self.diag.message),
             OutputFormat::Markdown => {
                 if file.starts_with("target") {
                     file.insert_str(0, "../");
@@ -116,7 +116,7 @@ impl ClippyWarning {
 
                 let mut output = String::from("| ");
                 write!(output, "[`{file_with_pos}`]({file}#L{})", span.line_start).unwrap();
-                write!(output, r#" | `{:<50}` | "{}" |"#, self.lint, self.diag.message).unwrap();
+                write!(output, r#" | `{:<50}` | "{}" |"#, self.name, self.diag.message).unwrap();
                 output.push('\n');
                 output
             },
@@ -164,7 +164,7 @@ fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>)
     let mut counter: HashMap<&String, usize> = HashMap::new();
     warnings
         .iter()
-        .for_each(|wrn| *counter.entry(&wrn.lint).or_insert(0) += 1);
+        .for_each(|wrn| *counter.entry(&wrn.name).or_insert(0) += 1);
 
     // collect into a tupled list for sorting
     let mut stats: Vec<(&&String, &usize)> = counter.iter().collect();
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 5fbe4e544a9..0be2e81810e 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,4 +1,4 @@
 [toolchain]
-channel = "nightly-2024-08-08"
+channel = "nightly-2024-08-23"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index 64253514fbe..9754254cdd0 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -1,25 +1,31 @@
-// We need this feature as it changes `dylib` linking behavior and allows us to link to
-// `rustc_driver`.
-#![feature(rustc_private)]
+#![feature(rustc_private, let_chains)]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(unused_extern_crates)]
 
+use cargo_metadata::diagnostic::{Applicability, Diagnostic};
+use cargo_metadata::Message;
+use clippy_config::ClippyConfiguration;
+use clippy_lints::declared_lints::LINTS;
+use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED};
+use clippy_lints::LintInfo;
+use serde::{Deserialize, Serialize};
+use test_utils::IS_RUSTC_TEST_SUITE;
 use ui_test::custom_flags::rustfix::RustfixMode;
+use ui_test::custom_flags::Flag;
 use ui_test::spanned::Spanned;
+use ui_test::test_result::TestRun;
 use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, OutputConflictHandling};
 
-use std::collections::BTreeMap;
+use std::collections::{BTreeMap, HashMap};
 use std::env::{self, set_var, var_os};
 use std::ffi::{OsStr, OsString};
-use std::fs;
+use std::fmt::Write;
 use std::path::{Path, PathBuf};
-use std::sync::LazyLock;
-use test_utils::IS_RUSTC_TEST_SUITE;
+use std::sync::mpsc::{channel, Sender};
+use std::{fs, iter, thread};
 
 // Test dependencies may need an `extern crate` here to ensure that they show up
 // in the depinfo file (otherwise cargo thinks they are unused)
-extern crate clippy_lints;
-extern crate clippy_utils;
 extern crate futures;
 extern crate if_chain;
 extern crate itertools;
@@ -55,7 +61,7 @@ static TEST_DEPENDENCIES: &[&str] = &[
 /// dependencies must be added to Cargo.toml at the project root. Test
 /// dependencies that are not *directly* used by this test module require an
 /// `extern crate` declaration.
-static EXTERN_FLAGS: LazyLock<Vec<String>> = LazyLock::new(|| {
+fn extern_flags() -> Vec<String> {
     let current_exe_depinfo = {
         let mut path = env::current_exe().unwrap();
         path.set_extension("d");
@@ -103,70 +109,93 @@ static EXTERN_FLAGS: LazyLock<Vec<String>> = LazyLock::new(|| {
         .into_iter()
         .map(|(name, path)| format!("--extern={name}={path}"))
         .collect()
-});
+}
 
 // whether to run internal tests or not
 const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
 
-fn base_config(test_dir: &str) -> (Config, Args) {
-    let mut args = Args::test().unwrap();
-    args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
-
-    let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into()));
-    let mut config = Config {
-        output_conflict_handling: OutputConflictHandling::Error,
-        filter_files: env::var("TESTNAME")
-            .map(|filters| filters.split(',').map(str::to_string).collect())
-            .unwrap_or_default(),
-        target: None,
-        bless_command: Some("cargo uibless".into()),
-        out_dir: target_dir.join("ui_test"),
-        ..Config::rustc(Path::new("tests").join(test_dir))
-    };
-    config.comment_defaults.base().exit_status = None.into();
-    config.comment_defaults.base().require_annotations = None.into();
-    config
-        .comment_defaults
-        .base()
-        .set_custom("rustfix", RustfixMode::Everything);
-    config.comment_defaults.base().diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into();
-    config.with_args(&args);
-    let current_exe_path = env::current_exe().unwrap();
-    let deps_path = current_exe_path.parent().unwrap();
-    let profile_path = deps_path.parent().unwrap();
-
-    config.program.args.extend(
-        [
-            "--emit=metadata",
-            "-Aunused",
-            "-Ainternal_features",
-            "-Zui-testing",
-            "-Zdeduplicate-diagnostics=no",
-            "-Dwarnings",
-            &format!("-Ldependency={}", deps_path.display()),
-        ]
-        .map(OsString::from),
-    );
-
-    config.program.args.extend(EXTERN_FLAGS.iter().map(OsString::from));
-    // Prevent rustc from creating `rustc-ice-*` files the console output is enough.
-    config.program.envs.push(("RUSTC_ICE".into(), Some("0".into())));
+struct TestContext {
+    args: Args,
+    extern_flags: Vec<String>,
+    diagnostic_collector: Option<DiagnosticCollector>,
+    collector_thread: Option<thread::JoinHandle<()>>,
+}
 
-    if let Some(host_libs) = option_env!("HOST_LIBS") {
-        let dep = format!("-Ldependency={}", Path::new(host_libs).join("deps").display());
-        config.program.args.push(dep.into());
+impl TestContext {
+    fn new() -> Self {
+        let mut args = Args::test().unwrap();
+        args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
+        let (diagnostic_collector, collector_thread) = var_os("COLLECT_METADATA")
+            .is_some()
+            .then(DiagnosticCollector::spawn)
+            .unzip();
+        Self {
+            args,
+            extern_flags: extern_flags(),
+            diagnostic_collector,
+            collector_thread,
+        }
     }
 
-    config.program.program = profile_path.join(if cfg!(windows) {
-        "clippy-driver.exe"
-    } else {
-        "clippy-driver"
-    });
-    (config, args)
+    fn base_config(&self, test_dir: &str) -> Config {
+        let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into()));
+        let mut config = Config {
+            output_conflict_handling: OutputConflictHandling::Error,
+            filter_files: env::var("TESTNAME")
+                .map(|filters| filters.split(',').map(str::to_string).collect())
+                .unwrap_or_default(),
+            target: None,
+            bless_command: Some("cargo uibless".into()),
+            out_dir: target_dir.join("ui_test"),
+            ..Config::rustc(Path::new("tests").join(test_dir))
+        };
+        let defaults = config.comment_defaults.base();
+        defaults.exit_status = None.into();
+        defaults.require_annotations = None.into();
+        defaults.diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into();
+        defaults.set_custom("rustfix", RustfixMode::Everything);
+        if let Some(collector) = self.diagnostic_collector.clone() {
+            defaults.set_custom("diagnostic-collector", collector);
+        }
+        config.with_args(&self.args);
+        let current_exe_path = env::current_exe().unwrap();
+        let deps_path = current_exe_path.parent().unwrap();
+        let profile_path = deps_path.parent().unwrap();
+
+        config.program.args.extend(
+            [
+                "--emit=metadata",
+                "-Aunused",
+                "-Ainternal_features",
+                "-Zui-testing",
+                "-Zdeduplicate-diagnostics=no",
+                "-Dwarnings",
+                &format!("-Ldependency={}", deps_path.display()),
+            ]
+            .map(OsString::from),
+        );
+
+        config.program.args.extend(self.extern_flags.iter().map(OsString::from));
+        // Prevent rustc from creating `rustc-ice-*` files the console output is enough.
+        config.program.envs.push(("RUSTC_ICE".into(), Some("0".into())));
+
+        if let Some(host_libs) = option_env!("HOST_LIBS") {
+            let dep = format!("-Ldependency={}", Path::new(host_libs).join("deps").display());
+            config.program.args.push(dep.into());
+        }
+
+        config.program.program = profile_path.join(if cfg!(windows) {
+            "clippy-driver.exe"
+        } else {
+            "clippy-driver"
+        });
+
+        config
+    }
 }
 
-fn run_ui() {
-    let (mut config, args) = base_config("ui");
+fn run_ui(cx: &TestContext) {
+    let mut config = cx.base_config("ui");
     config
         .program
         .envs
@@ -176,30 +205,29 @@ fn run_ui() {
         vec![config],
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
-        status_emitter::Text::from(args.format),
+        status_emitter::Text::from(cx.args.format),
     )
     .unwrap();
 }
 
-fn run_internal_tests() {
-    // only run internal tests with the internal-tests feature
+fn run_internal_tests(cx: &TestContext) {
     if !RUN_INTERNAL_TESTS {
         return;
     }
-    let (mut config, args) = base_config("ui-internal");
+    let mut config = cx.base_config("ui-internal");
     config.bless_command = Some("cargo uitest --features internal -- -- --bless".into());
 
     ui_test::run_tests_generic(
         vec![config],
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
-        status_emitter::Text::from(args.format),
+        status_emitter::Text::from(cx.args.format),
     )
     .unwrap();
 }
 
-fn run_ui_toml() {
-    let (mut config, args) = base_config("ui-toml");
+fn run_ui_toml(cx: &TestContext) {
+    let mut config = cx.base_config("ui-toml");
 
     config
         .comment_defaults
@@ -217,19 +245,19 @@ fn run_ui_toml() {
                 .envs
                 .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into())));
         },
-        status_emitter::Text::from(args.format),
+        status_emitter::Text::from(cx.args.format),
     )
     .unwrap();
 }
 
 // Allow `Default::default` as `OptWithSpan` is not nameable
 #[allow(clippy::default_trait_access)]
-fn run_ui_cargo() {
+fn run_ui_cargo(cx: &TestContext) {
     if IS_RUSTC_TEST_SUITE {
         return;
     }
 
-    let (mut config, args) = base_config("ui-cargo");
+    let mut config = cx.base_config("ui-cargo");
     config.program.input_file_flag = CommandBuilder::cargo().input_file_flag;
     config.program.out_dir_flag = CommandBuilder::cargo().out_dir_flag;
     config.program.args = vec!["clippy".into(), "--color".into(), "never".into(), "--quiet".into()];
@@ -264,23 +292,25 @@ fn run_ui_cargo() {
                 .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path))
         },
         |_config, _file_contents| {},
-        status_emitter::Text::from(args.format),
+        status_emitter::Text::from(cx.args.format),
     )
     .unwrap();
 }
 
 fn main() {
     set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
+
+    let cx = TestContext::new();
+
     // The SPEEDTEST_* env variables can be used to check Clippy's performance on your PR. It runs the
     // affected test 1000 times and gets the average.
     if let Ok(speedtest) = std::env::var("SPEEDTEST") {
         println!("----------- STARTING SPEEDTEST -----------");
         let f = match speedtest.as_str() {
-            "ui" => run_ui as fn(),
-            "cargo" => run_ui_cargo as fn(),
-            "toml" => run_ui_toml as fn(),
-            "internal" => run_internal_tests as fn(),
-            "ui-cargo-toml-metadata" => ui_cargo_toml_metadata as fn(),
+            "ui" => run_ui,
+            "cargo" => run_ui_cargo,
+            "toml" => run_ui_toml,
+            "internal" => run_internal_tests,
 
             _ => panic!("unknown speedtest: {speedtest} || accepted speedtests are: [ui, cargo, toml, internal]"),
         };
@@ -297,7 +327,7 @@ fn main() {
         let mut sum = 0;
         for _ in 0..iterations {
             let start = std::time::Instant::now();
-            f();
+            f(&cx);
             sum += start.elapsed().as_millis();
         }
         println!(
@@ -306,11 +336,17 @@ fn main() {
             sum / u128::from(iterations)
         );
     } else {
-        run_ui();
-        run_ui_toml();
-        run_ui_cargo();
-        run_internal_tests();
+        run_ui(&cx);
+        run_ui_toml(&cx);
+        run_ui_cargo(&cx);
+        run_internal_tests(&cx);
+        drop(cx.diagnostic_collector);
+
         ui_cargo_toml_metadata();
+
+        if let Some(thread) = cx.collector_thread {
+            thread.join().unwrap();
+        }
     }
 }
 
@@ -349,3 +385,180 @@ fn ui_cargo_toml_metadata() {
         );
     }
 }
+
+#[derive(Deserialize)]
+#[serde(untagged)]
+enum DiagnosticOrMessage {
+    Diagnostic(Diagnostic),
+    Message(Message),
+}
+
+/// Collects applicabilities from the diagnostics produced for each UI test, producing the
+/// `util/gh-pages/lints.json` file used by <https://rust-lang.github.io/rust-clippy/>
+#[derive(Debug, Clone)]
+struct DiagnosticCollector {
+    sender: Sender<Vec<u8>>,
+}
+
+impl DiagnosticCollector {
+    #[allow(clippy::assertions_on_constants)]
+    fn spawn() -> (Self, thread::JoinHandle<()>) {
+        assert!(!IS_RUSTC_TEST_SUITE && !RUN_INTERNAL_TESTS);
+
+        let (sender, receiver) = channel::<Vec<u8>>();
+
+        let handle = thread::spawn(|| {
+            let mut applicabilities = HashMap::new();
+
+            for stderr in receiver {
+                for line in stderr.split(|&byte| byte == b'\n') {
+                    let diag = match serde_json::from_slice(line) {
+                        Ok(DiagnosticOrMessage::Diagnostic(diag)) => diag,
+                        Ok(DiagnosticOrMessage::Message(Message::CompilerMessage(message))) => message.message,
+                        _ => continue,
+                    };
+
+                    if let Some(lint) = diag.code.as_ref().and_then(|code| code.code.strip_prefix("clippy::")) {
+                        let applicability = applicabilities
+                            .entry(lint.to_string())
+                            .or_insert(Applicability::Unspecified);
+                        let diag_applicability = diag
+                            .children
+                            .iter()
+                            .flat_map(|child| &child.spans)
+                            .filter_map(|span| span.suggestion_applicability.clone())
+                            .max_by_key(applicability_ord);
+                        if let Some(diag_applicability) = diag_applicability
+                            && applicability_ord(&diag_applicability) > applicability_ord(applicability)
+                        {
+                            *applicability = diag_applicability;
+                        }
+                    }
+                }
+            }
+
+            let configs = clippy_config::get_configuration_metadata();
+            let mut metadata: Vec<LintMetadata> = LINTS
+                .iter()
+                .map(|lint| LintMetadata::new(lint, &applicabilities, &configs))
+                .chain(
+                    iter::zip(DEPRECATED, DEPRECATED_VERSION)
+                        .map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)),
+                )
+                .collect();
+            metadata.sort_unstable_by(|a, b| a.id.cmp(&b.id));
+
+            let json = serde_json::to_string_pretty(&metadata).unwrap();
+            fs::write("util/gh-pages/lints.json", json).unwrap();
+        });
+
+        (Self { sender }, handle)
+    }
+}
+
+fn applicability_ord(applicability: &Applicability) -> u8 {
+    match applicability {
+        Applicability::MachineApplicable => 4,
+        Applicability::HasPlaceholders => 3,
+        Applicability::MaybeIncorrect => 2,
+        Applicability::Unspecified => 1,
+        _ => unimplemented!(),
+    }
+}
+
+impl Flag for DiagnosticCollector {
+    fn post_test_action(
+        &self,
+        _config: &ui_test::per_test_config::TestConfig<'_>,
+        _cmd: &mut std::process::Command,
+        output: &std::process::Output,
+        _build_manager: &ui_test::build_manager::BuildManager<'_>,
+    ) -> Result<Vec<TestRun>, ui_test::Errored> {
+        if !output.stderr.is_empty() {
+            self.sender.send(output.stderr.clone()).unwrap();
+        }
+        Ok(Vec::new())
+    }
+
+    fn clone_inner(&self) -> Box<dyn Flag> {
+        Box::new(self.clone())
+    }
+
+    fn must_be_unique(&self) -> bool {
+        true
+    }
+}
+
+#[derive(Debug, Serialize)]
+struct LintMetadata {
+    id: String,
+    id_location: Option<&'static str>,
+    group: &'static str,
+    level: &'static str,
+    docs: String,
+    version: &'static str,
+    applicability: Applicability,
+}
+
+impl LintMetadata {
+    fn new(lint: &LintInfo, applicabilities: &HashMap<String, Applicability>, configs: &[ClippyConfiguration]) -> Self {
+        let name = lint.name_lower();
+        let applicability = applicabilities
+            .get(&name)
+            .cloned()
+            .unwrap_or(Applicability::Unspecified);
+        let past_names = RENAMED
+            .iter()
+            .filter(|(_, new_name)| new_name.strip_prefix("clippy::") == Some(&name))
+            .map(|(old_name, _)| old_name.strip_prefix("clippy::").unwrap())
+            .collect::<Vec<_>>();
+        let mut docs = lint.explanation.to_string();
+        if !past_names.is_empty() {
+            docs.push_str("\n### Past names\n\n");
+            for past_name in past_names {
+                writeln!(&mut docs, " * {past_name}").unwrap();
+            }
+        }
+        let configs: Vec<_> = configs
+            .iter()
+            .filter(|conf| conf.lints.contains(&name.as_str()))
+            .collect();
+        if !configs.is_empty() {
+            docs.push_str("\n### Configuration\n\n");
+            for config in configs {
+                writeln!(&mut docs, "{config}").unwrap();
+            }
+        }
+        Self {
+            id: name,
+            id_location: Some(lint.location),
+            group: lint.category_str(),
+            level: lint.lint.default_level.as_str(),
+            docs,
+            version: lint.version.unwrap(),
+            applicability,
+        }
+    }
+
+    fn new_deprecated(name: &str, reason: &str, version: &'static str) -> Self {
+        // The reason starts with a lowercase letter and ends without a period.
+        // This needs to be fixed for the website.
+        let mut reason = reason.to_owned();
+        if let Some(reason) = reason.get_mut(0..1) {
+            reason.make_ascii_uppercase();
+        }
+        Self {
+            id: name.strip_prefix("clippy::").unwrap().into(),
+            id_location: None,
+            group: "deprecated",
+            level: "none",
+            version,
+            docs: format!(
+                "### What it does\n\n\
+                Nothing. This lint has been deprecated\n\n\
+                ### Deprecation reason\n\n{reason}.\n",
+            ),
+            applicability: Applicability::Unspecified,
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/config-metadata.rs b/src/tools/clippy/tests/config-metadata.rs
new file mode 100644
index 00000000000..3e3711873ba
--- /dev/null
+++ b/src/tools/clippy/tests/config-metadata.rs
@@ -0,0 +1,78 @@
+#![feature(rustc_private)]
+
+use clippy_config::{get_configuration_metadata, ClippyConfiguration};
+use itertools::Itertools;
+use regex::Regex;
+use std::borrow::Cow;
+use std::{env, fs};
+
+fn metadata() -> impl Iterator<Item = ClippyConfiguration> {
+    get_configuration_metadata()
+        .into_iter()
+        .filter(|config| config.deprecation_reason.is_none())
+        .filter(|config| !config.lints.is_empty())
+}
+
+#[test]
+fn book() {
+    let path = "book/src/lint_configuration.md";
+    let current = fs::read_to_string(path).unwrap();
+
+    let configs = metadata().map(|conf| conf.to_markdown_paragraph()).join("\n");
+    let expected = format!(
+        r#"<!--
+This file is generated by `cargo bless --test config-metadata`.
+Please use that command to update the file and do not edit it by hand.
+-->
+
+# Lint Configuration Options
+
+The following list shows each configuration option, along with a description, its default value, an example
+and lints affected.
+
+---
+
+{}
+"#,
+        configs.trim(),
+    );
+
+    if current != expected {
+        if env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0") {
+            fs::write(path, expected).unwrap();
+        } else {
+            panic!("`{path}` is out of date, run `cargo bless --test config-metadata` to update it");
+        }
+    }
+}
+
+#[test]
+fn changelog() {
+    let path = "CHANGELOG.md";
+    let current = fs::read_to_string(path).unwrap();
+
+    let configs = metadata().map(|conf| conf.to_markdown_link()).join("\n");
+
+    let re = Regex::new(
+        "(?s)\
+        (<!-- begin autogenerated links to configuration documentation -->)\
+        .*\
+        (<!-- end autogenerated links to configuration documentation -->)\
+        ",
+    )
+    .unwrap();
+    let expected = re.replace(&current, format!("$1\n{configs}\n$2"));
+
+    assert!(
+        matches!(expected, Cow::Owned(_)),
+        "failed to find configuration section in `{path}`"
+    );
+
+    if current != expected {
+        if env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0") {
+            fs::write(path, expected.as_bytes()).unwrap();
+        } else {
+            panic!("`{path}` is out of date, run `cargo bless --test config-metadata` to update it");
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr
index 1cc1034cd89..d24b0cd6162 100644
--- a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr
@@ -1,29 +1,44 @@
 error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:5
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13
    |
-LL |     std::f32::MAX;
-   |     ^^^^^^^^^^^^^
+LL |     let _ = std::path::is_separator(' ');
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `-D clippy::absolute-paths` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]`
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13
+   |
+LL |     let _ = ::std::path::MAIN_SEPARATOR;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
+   |
+LL |     let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:41:5
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31
    |
-LL |     core::f32::MAX;
-   |     ^^^^^^^^^^^^^^
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |                               ^^^^^^^^^^^^^^^
 
 error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:42:5
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13
    |
-LL |     ::core::f32::MAX;
-   |     ^^^^^^^^^^^^^^^^
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |             ^^^^^^^^^^^^^^^
 
 error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:58:5
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13
    |
-LL |     ::std::f32::MAX;
-   |     ^^^^^^^^^^^^^^^
+LL |     let _ = std::option::Option::None::<i32>;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr
new file mode 100644
index 00000000000..0cc6566af3a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr
@@ -0,0 +1,14 @@
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
+   |
+LL |     let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.default.stderr b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.default.stderr
new file mode 100644
index 00000000000..53aa9030e0d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.default.stderr
@@ -0,0 +1,80 @@
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13
+   |
+LL |     let _ = std::path::is_separator(' ');
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13
+   |
+LL |     let _ = ::std::path::MAIN_SEPARATOR;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
+   |
+LL |     let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31
+   |
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |                               ^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13
+   |
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |             ^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:37:13
+   |
+LL |     let _ = ::core::clone::Clone::clone(&0i32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:13
+   |
+LL |     let _ = <i32 as core::clone::Clone>::clone(&0i32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13
+   |
+LL |     let _ = std::option::Option::None::<i32>;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:17
+   |
+LL |         impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+   |                 ^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:70:18
+   |
+LL |         where T: core::clone::Clone
+   |                  ^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:32
+   |
+LL |         impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+   |                                ^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:116:5
+   |
+LL |     crate::m1::S
+   |     ^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr
deleted file mode 100644
index f342ebf6632..00000000000
--- a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr
+++ /dev/null
@@ -1,71 +0,0 @@
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:5
-   |
-LL |     std::f32::MAX;
-   |     ^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::absolute-paths` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]`
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:41:5
-   |
-LL |     core::f32::MAX;
-   |     ^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:42:5
-   |
-LL |     ::core::f32::MAX;
-   |     ^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:5
-   |
-LL |     crate::a::b::c::C;
-   |     ^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:44:5
-   |
-LL |     crate::a::b::c::d::e::f::F;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:45:5
-   |
-LL |     crate::a::A;
-   |     ^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:46:5
-   |
-LL |     crate::a::b::B;
-   |     ^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:47:5
-   |
-LL |     crate::a::b::c::C::ZERO;
-   |     ^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:48:5
-   |
-LL |     helper::b::c::d::e::f();
-   |     ^^^^^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:49:5
-   |
-LL |     ::helper::b::c::d::e::f();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:58:5
-   |
-LL |     ::std::f32::MAX;
-   |     ^^^^^^^^^^^^^^^
-
-error: aborting due to 11 previous errors
-
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr
new file mode 100644
index 00000000000..70d71f6c4ea
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr
@@ -0,0 +1,98 @@
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13
+   |
+LL |     let _ = std::path::is_separator(' ');
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13
+   |
+LL |     let _ = ::std::path::MAIN_SEPARATOR;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
+   |
+LL |     let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31
+   |
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |                               ^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13
+   |
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |             ^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:37:13
+   |
+LL |     let _ = ::core::clone::Clone::clone(&0i32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:13
+   |
+LL |     let _ = <i32 as core::clone::Clone>::clone(&0i32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13
+   |
+LL |     let _ = std::option::Option::None::<i32>;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:17
+   |
+LL |         impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+   |                 ^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:70:18
+   |
+LL |         where T: core::clone::Clone
+   |                  ^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:32
+   |
+LL |         impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+   |                                ^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:113:14
+   |
+LL | pub const _: crate::S = {
+   |              ^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:114:9
+   |
+LL |     let crate::S = m1::S;
+   |         ^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:116:5
+   |
+LL |     crate::m1::S
+   |     ^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:122:14
+   |
+LL |     let _ = <crate::S as Clone>::clone(&m1::S);
+   |              ^^^^^^^^
+
+error: aborting due to 15 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.rs b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.rs
index a828701bcee..c024f2f513c 100644
--- a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.rs
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths.rs
@@ -1,97 +1,123 @@
 //@aux-build:../../ui/auxiliary/proc_macros.rs
-//@aux-build:helper.rs
-//@revisions: allow_crates disallow_crates
+//@revisions: default allow_crates allow_long no_short
+//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/default
 //@[allow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates
-//@[disallow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/disallow_crates
-#![allow(clippy::no_effect, clippy::legacy_numeric_constants, unused)]
-#![warn(clippy::absolute_paths)]
-#![feature(decl_macro)]
+//@[allow_long] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_long
+//@[no_short] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/no_short
+#![deny(clippy::absolute_paths)]
 
-extern crate helper;
-#[macro_use]
 extern crate proc_macros;
+use proc_macros::{external, inline_macros, with_span};
 
-pub mod a {
-    pub mod b {
-        pub mod c {
-            pub struct C;
+#[inline_macros]
+fn main() {
+    let _ = std::path::is_separator(' ');
+    //~[default]^ absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[no_short]| absolute_paths
 
-            impl C {
-                pub const ZERO: u32 = 0;
-            }
+    // Make sure this is treated as having three path segments, not four.
+    let _ = ::std::path::MAIN_SEPARATOR;
+    //~[default]^ absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[no_short]| absolute_paths
 
-            pub mod d {
-                pub mod e {
-                    pub mod f {
-                        pub struct F;
-                    }
-                }
-            }
-        }
+    let _ = std::collections::hash_map::HashMap::<i32, i32>::new(); //~ absolute_paths
 
-        pub struct B;
-    }
+    // Note `std::path::Path::new` is treated as having three parts
+    let _: &std::path::Path = std::path::Path::new("");
+    //~[default]^ absolute_paths
+    //~[default]| absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[no_short]| absolute_paths
+    //~[no_short]| absolute_paths
 
-    pub struct A;
-}
+    // Treated as having three parts.
+    let _ = ::core::clone::Clone::clone(&0i32);
+    //~[default]^ absolute_paths
+    //~[no_short]| absolute_paths
+    let _ = <i32 as core::clone::Clone>::clone(&0i32);
+    //~[default]^ absolute_paths
+    //~[no_short]| absolute_paths
+    let _ = std::option::Option::None::<i32>;
+    //~[default]^ absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[no_short]| absolute_paths
 
-fn main() {
-    f32::max(1.0, 2.0);
-    std::f32::MAX;
-    core::f32::MAX;
-    ::core::f32::MAX;
-    crate::a::b::c::C;
-    crate::a::b::c::d::e::f::F;
-    crate::a::A;
-    crate::a::b::B;
-    crate::a::b::c::C::ZERO;
-    helper::b::c::d::e::f();
-    ::helper::b::c::d::e::f();
-    fn b() -> a::b::B {
-        todo!()
+    {
+        // FIXME: macro calls should be checked.
+        let x = 1i32;
+        let _ = core::ptr::addr_of!(x);
     }
-    std::println!("a");
-    let x = 1;
-    std::ptr::addr_of!(x);
-    // Test we handle max segments with `PathRoot` properly; this has 4 segments but we should say it
-    // has 3
-    ::std::f32::MAX;
-    // Do not lint due to the above
-    ::helper::a();
-    // Do not lint
-    helper::a();
-    use crate::a::b::c::C;
-    use a::b;
-    use std::f32::MAX;
-    a::b::c::d::e::f::F;
-    b::c::C;
-    fn a() -> a::A {
-        todo!()
-    }
-    use a::b::c;
 
-    fn c() -> c::C {
-        todo!()
+    {
+        // FIXME: derive macro paths should be checked.
+        #[derive(core::clone::Clone)]
+        struct S;
     }
-    fn d() -> Result<(), ()> {
-        todo!()
+
+    {
+        use core::fmt;
+        use core::marker::PhantomData;
+
+        struct X<T>(PhantomData<T>);
+        impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+        //~[default]^ absolute_paths
+        //~[default]| absolute_paths
+        //~[no_short]| absolute_paths
+        //~[no_short]| absolute_paths
+        where T: core::clone::Clone
+        //~[no_short]^ absolute_paths
+        //~[default]| absolute_paths
+        {
+            fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
+                Ok(())
+            }
+        }
     }
-    external! {
-        crate::a::b::c::C::ZERO;
+
+    {
+        mod m1 {
+            pub(crate) mod m2 {
+                pub(crate) const FOO: i32 = 0;
+            }
+        }
+        let _ = m1::m2::FOO;
     }
-    // For some reason, `path.span.from_expansion()` takes care of this for us
+
     with_span! {
         span
-        crate::a::b::c::C::ZERO;
+        let _ = std::path::is_separator(' ');
     }
-    macro_rules! local_crate {
-        () => {
-            crate::a::b::c::C::ZERO;
-        };
+
+    external! {
+        let _ = std::path::is_separator(' ');
     }
-    macro local_crate_2_0() {
-        crate::a::b::c::C::ZERO;
+
+    inline! {
+        let _ = std::path::is_separator(' ');
     }
-    local_crate!();
-    local_crate_2_0!();
+}
+
+pub use core::cmp::Ordering;
+pub use std::fs::File;
+
+#[derive(Clone)]
+pub struct S;
+mod m1 {
+    pub use crate::S;
+}
+
+//~[no_short]v absolute_paths
+pub const _: crate::S = {
+    let crate::S = m1::S; //~[no_short] absolute_paths
+
+    crate::m1::S
+    //~[default]^ absolute_paths
+    //~[no_short]| absolute_paths
+};
+
+pub fn f() {
+    let _ = <crate::S as Clone>::clone(&m1::S); //~[no_short] absolute_paths
 }
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr
new file mode 100644
index 00000000000..6fc495f0180
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr
@@ -0,0 +1,14 @@
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths_2015.rs:15:13
+   |
+LL |     let _ = ::m1::m2::X;
+   |             ^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths_2015.rs:6:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths_2015.rs b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths_2015.rs
new file mode 100644
index 00000000000..033c4780919
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/absolute_paths_2015.rs
@@ -0,0 +1,16 @@
+//@revisions: default allow_crates
+//@[default]rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/default
+//@[allow_crates]rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates
+//@edition:2015
+
+#![deny(clippy::absolute_paths)]
+
+mod m1 {
+    pub mod m2 {
+        pub struct X;
+    }
+}
+
+fn main() {
+    let _ = ::m1::m2::X; //~[default] absolute_paths
+}
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/allow_crates/clippy.toml b/src/tools/clippy/tests/ui-toml/absolute_paths/allow_crates/clippy.toml
index 59a621e9d1d..9da49abcd31 100644
--- a/src/tools/clippy/tests/ui-toml/absolute_paths/allow_crates/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/allow_crates/clippy.toml
@@ -1,2 +1 @@
-absolute-paths-max-segments = 2
-absolute-paths-allowed-crates = ["crate", "helper"]
+absolute-paths-allowed-crates = ["core", "crate"]
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/allow_long/clippy.toml b/src/tools/clippy/tests/ui-toml/absolute_paths/allow_long/clippy.toml
new file mode 100644
index 00000000000..5992fd1ed82
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/allow_long/clippy.toml
@@ -0,0 +1 @@
+absolute-paths-max-segments = 3
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/auxiliary/helper.rs b/src/tools/clippy/tests/ui-toml/absolute_paths/auxiliary/helper.rs
deleted file mode 100644
index 8e2678f5fe6..00000000000
--- a/src/tools/clippy/tests/ui-toml/absolute_paths/auxiliary/helper.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-pub fn a() {}
-
-pub mod b {
-    pub mod c {
-        pub mod d {
-            pub mod e {
-                pub fn f() {}
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/default/clippy.toml b/src/tools/clippy/tests/ui-toml/absolute_paths/default/clippy.toml
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/default/clippy.toml
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml b/src/tools/clippy/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml
deleted file mode 100644
index d44d648c641..00000000000
--- a/src/tools/clippy/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml
+++ /dev/null
@@ -1 +0,0 @@
-absolute-paths-max-segments = 2
diff --git a/src/tools/clippy/tests/ui-toml/absolute_paths/no_short/clippy.toml b/src/tools/clippy/tests/ui-toml/absolute_paths/no_short/clippy.toml
new file mode 100644
index 00000000000..357524420c5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/absolute_paths/no_short/clippy.toml
@@ -0,0 +1 @@
+absolute-paths-max-segments = 0
diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
index a312df5a43a..3dafea56514 100644
--- a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
+++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
@@ -1,7 +1,7 @@
 //! Tests macro_metavars_in_unsafe with default configuration
 #![feature(decl_macro)]
 #![warn(clippy::macro_metavars_in_unsafe)]
-#![allow(clippy::no_effect)]
+#![allow(clippy::no_effect, clippy::not_unsafe_ptr_arg_deref)]
 
 #[macro_export]
 macro_rules! allow_works {
@@ -237,6 +237,19 @@ macro_rules! nested_macros {
     }};
 }
 
+pub mod issue13219 {
+    #[macro_export]
+    macro_rules! m {
+        ($e:expr) => {
+            // Metavariable in a block tail expression
+            unsafe { $e }
+        };
+    }
+    pub fn f(p: *const i32) -> i32 {
+        m!(*p)
+    }
+}
+
 fn main() {
     allow_works!(1);
     simple!(1);
diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
index d6b97f6fde1..6f0ebcbba02 100644
--- a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
+++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
@@ -1,4 +1,16 @@
 error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:245:13
+   |
+LL |             unsafe { $e }
+   |             ^^^^^^^^^^^^^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+   = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]`
+
+error: this macro expands metavariables in an unsafe block
   --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:19:9
    |
 LL | /         unsafe {
@@ -10,8 +22,6 @@ LL | |         }
    = note: this allows the user of the macro to write unsafe code outside of an unsafe block
    = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
    = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
-   = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]`
 
 error: this macro expands metavariables in an unsafe block
   --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:30:9
@@ -183,5 +193,5 @@ LL | |         }
    = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
    = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
 
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/assigning_clones.fixed b/src/tools/clippy/tests/ui/assigning_clones.fixed
index b376d55a402..09732d1a50c 100644
--- a/src/tools/clippy/tests/ui/assigning_clones.fixed
+++ b/src/tools/clippy/tests/ui/assigning_clones.fixed
@@ -396,3 +396,23 @@ impl<T: Clone> Clone for DerefWrapperWithClone<T> {
         *self = Self(source.0.clone());
     }
 }
+
+#[cfg(test)]
+mod test {
+    #[derive(Default)]
+    struct Data {
+        field: String,
+    }
+
+    fn test_data() -> Data {
+        Data {
+            field: "default_value".to_string(),
+        }
+    }
+
+    #[test]
+    fn test() {
+        let mut data = test_data();
+        data.field = "override_value".to_owned();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/assigning_clones.rs b/src/tools/clippy/tests/ui/assigning_clones.rs
index 11a5d4459c3..6be25ae17a5 100644
--- a/src/tools/clippy/tests/ui/assigning_clones.rs
+++ b/src/tools/clippy/tests/ui/assigning_clones.rs
@@ -396,3 +396,23 @@ impl<T: Clone> Clone for DerefWrapperWithClone<T> {
         *self = Self(source.0.clone());
     }
 }
+
+#[cfg(test)]
+mod test {
+    #[derive(Default)]
+    struct Data {
+        field: String,
+    }
+
+    fn test_data() -> Data {
+        Data {
+            field: "default_value".to_string(),
+        }
+    }
+
+    #[test]
+    fn test() {
+        let mut data = test_data();
+        data.field = "override_value".to_owned();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
index f7e659b10de..329be4d3662 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
@@ -2,7 +2,7 @@ error: called `unwrap` on `x` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/complex_conditionals_nested.rs:13:13
    |
 LL |         if x.is_some() {
-   |         -------------- help: try: `if let Some(..) = x`
+   |         -------------- help: try: `if let Some(<item>) = x`
 LL |             // unnecessary
 LL |             x.unwrap();
    |             ^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
index ddd600418af..f7e338935a7 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
@@ -2,7 +2,7 @@ error: called `unwrap` on `x` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:46:9
    |
 LL |     if x.is_some() {
-   |     -------------- help: try: `if let Some(..) = x`
+   |     -------------- help: try: `if let Some(<item>) = x`
 LL |         // unnecessary
 LL |         x.unwrap();
    |         ^^^^^^^^^^
@@ -17,7 +17,7 @@ error: called `expect` on `x` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:49:9
    |
 LL |     if x.is_some() {
-   |     -------------- help: try: `if let Some(..) = x`
+   |     -------------- help: try: `if let Some(<item>) = x`
 ...
 LL |         x.expect("an error message");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -59,7 +59,7 @@ error: called `unwrap` on `x` after checking its variant with `is_none`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:65:9
    |
 LL |     if x.is_none() {
-   |     -------------- help: try: `if let Some(..) = x`
+   |     -------------- help: try: `if let Some(<item>) = x`
 ...
 LL |         x.unwrap();
    |         ^^^^^^^^^^
@@ -68,7 +68,7 @@ error: called `unwrap` on `x` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:13:13
    |
 LL |         if $a.is_some() {
-   |         --------------- help: try: `if let Some(..) = x`
+   |         --------------- help: try: `if let Some(<item>) = x`
 LL |             // unnecessary
 LL |             $a.unwrap();
    |             ^^^^^^^^^^^
@@ -82,7 +82,7 @@ error: called `unwrap` on `x` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:78:9
    |
 LL |     if x.is_ok() {
-   |     ------------ help: try: `if let Ok(..) = x`
+   |     ------------ help: try: `if let Ok(<item>) = x`
 LL |         // unnecessary
 LL |         x.unwrap();
    |         ^^^^^^^^^^
@@ -91,7 +91,7 @@ error: called `expect` on `x` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:81:9
    |
 LL |     if x.is_ok() {
-   |     ------------ help: try: `if let Ok(..) = x`
+   |     ------------ help: try: `if let Ok(<item>) = x`
 ...
 LL |         x.expect("an error message");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -127,7 +127,7 @@ error: called `unwrap_err` on `x` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:94:9
    |
 LL |     if x.is_ok() {
-   |     ------------ help: try: `if let Err(..) = x`
+   |     ------------ help: try: `if let Err(<item>) = x`
 ...
 LL |         x.unwrap_err();
    |         ^^^^^^^^^^^^^^
@@ -145,7 +145,7 @@ error: called `unwrap_err` on `x` after checking its variant with `is_err`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:102:9
    |
 LL |     if x.is_err() {
-   |     ------------- help: try: `if let Err(..) = x`
+   |     ------------- help: try: `if let Err(<item>) = x`
 ...
 LL |         x.unwrap_err();
    |         ^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ error: called `unwrap` on `x` after checking its variant with `is_err`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:106:9
    |
 LL |     if x.is_err() {
-   |     ------------- help: try: `if let Ok(..) = x`
+   |     ------------- help: try: `if let Ok(<item>) = x`
 ...
 LL |         x.unwrap();
    |         ^^^^^^^^^^
@@ -172,7 +172,7 @@ error: called `unwrap` on `option` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:134:9
    |
 LL |     if option.is_some() {
-   |     ------------------- help: try: `if let Some(..) = &option`
+   |     ------------------- help: try: `if let Some(<item>) = &option`
 LL |         option.as_ref().unwrap();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -189,7 +189,7 @@ error: called `unwrap` on `result` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:144:9
    |
 LL |     if result.is_ok() {
-   |     ----------------- help: try: `if let Ok(..) = &result`
+   |     ----------------- help: try: `if let Ok(<item>) = &result`
 LL |         result.as_ref().unwrap();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -206,7 +206,7 @@ error: called `unwrap` on `option` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:153:9
    |
 LL |     if option.is_some() {
-   |     ------------------- help: try: `if let Some(..) = &mut option`
+   |     ------------------- help: try: `if let Some(<item>) = &mut option`
 LL |         option.as_mut().unwrap();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -223,7 +223,7 @@ error: called `unwrap` on `result` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:162:9
    |
 LL |     if result.is_ok() {
-   |     ----------------- help: try: `if let Ok(..) = &mut result`
+   |     ----------------- help: try: `if let Ok(<item>) = &mut result`
 LL |         result.as_mut().unwrap();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/src/tools/clippy/tests/ui/collapsible_match.stderr b/src/tools/clippy/tests/ui/collapsible_match.stderr
index 01944baee79..1da78b56239 100644
--- a/src/tools/clippy/tests/ui/collapsible_match.stderr
+++ b/src/tools/clippy/tests/ui/collapsible_match.stderr
@@ -223,7 +223,7 @@ help: the outer pattern can be modified to include the inner pattern
 LL |     if let Issue9647::A { a, .. } = x {
    |                           ^ replace this binding
 LL |         if let Some(u) = a {
-   |                ^^^^^^^ with this pattern, prefixed by a:
+   |                ^^^^^^^ with this pattern, prefixed by `a`:
 
 error: this `if let` can be collapsed into the outer `if let`
   --> tests/ui/collapsible_match.rs:292:9
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.fixed b/src/tools/clippy/tests/ui/crashes/ice-3717.fixed
new file mode 100644
index 00000000000..3f54b326979
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-3717.fixed
@@ -0,0 +1,11 @@
+#![deny(clippy::implicit_hasher)]
+
+use std::collections::HashSet;
+
+fn main() {}
+
+pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) {
+    //~^ ERROR: parameter of type `HashSet` should be generalized over different hashers
+    let _ = [0u8; 0];
+    let _: HashSet<usize> = HashSet::default();
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.rs b/src/tools/clippy/tests/ui/crashes/ice-3717.rs
index 770f6cf448a..2890a9277c7 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-3717.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-3717.rs
@@ -1,7 +1,5 @@
 #![deny(clippy::implicit_hasher)]
 
-//@no-rustfix: need to change the suggestion to a multipart suggestion
-
 use std::collections::HashSet;
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.stderr b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr
index 4b4618ee1bb..aac72c66965 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-3717.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr
@@ -1,5 +1,5 @@
 error: parameter of type `HashSet` should be generalized over different hashers
-  --> tests/ui/crashes/ice-3717.rs:9:21
+  --> tests/ui/crashes/ice-3717.rs:7:21
    |
 LL | pub fn ice_3717(_: &HashSet<usize>) {
    |                     ^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
index 56a8d22cb1c..9dafad8b784 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
@@ -3,6 +3,7 @@
 use std::borrow::Cow;
 use std::cell::Cell;
 use std::fmt::Display;
+use std::ptr;
 use std::sync::atomic::AtomicUsize;
 use std::sync::Once;
 
@@ -53,4 +54,20 @@ mod issue_8493 {
     issue_8493!();
 }
 
+#[repr(C, align(8))]
+struct NoAtomic(usize);
+#[repr(C, align(8))]
+struct WithAtomic(AtomicUsize);
+
+const fn with_non_null() -> *const WithAtomic {
+    const NO_ATOMIC: NoAtomic = NoAtomic(0);
+    (&NO_ATOMIC as *const NoAtomic).cast()
+}
+const WITH_ATOMIC: *const WithAtomic = with_non_null();
+
+struct Generic<T>(T);
+impl<T> Generic<T> {
+    const RAW_POINTER: *const Cell<T> = ptr::null();
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr
index 1f2b9561ce5..4a725147142 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr
@@ -1,5 +1,5 @@
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:9:1
+  --> tests/ui/declare_interior_mutable_const/others.rs:10:1
    |
 LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5);
    = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
 
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:10:1
+  --> tests/ui/declare_interior_mutable_const/others.rs:11:1
    |
 LL | const CELL: Cell<usize> = Cell::new(6);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ LL | const CELL: Cell<usize> = Cell::new(6);
    = help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
 
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:11:1
+  --> tests/ui/declare_interior_mutable_const/others.rs:12:1
    |
 LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], V
    = help: consider making this a static item
 
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:16:9
+  --> tests/ui/declare_interior_mutable_const/others.rs:17:9
    |
 LL |         const $name: $ty = $e;
    |         ^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL | declare_const!(_ONCE: Once = Once::new());
    = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:44:13
+  --> tests/ui/declare_interior_mutable_const/others.rs:45:13
    |
 LL |             const _BAZ: Cell<usize> = Cell::new(0);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.fixed b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.fixed
new file mode 100644
index 00000000000..fb0f40b34a4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.fixed
@@ -0,0 +1,13 @@
+// This test checks that words starting with capital letters and ending with "ified" don't
+// trigger the lint.
+
+#![deny(clippy::doc_markdown)]
+
+pub enum OutputFormat {
+    /// `HumaNified`
+    //~^ ERROR: item in documentation is missing backticks
+    Plain,
+    // Should not warn!
+    /// JSONified console output
+    Json,
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.rs b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.rs
new file mode 100644
index 00000000000..8c1e1a3cd6c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.rs
@@ -0,0 +1,13 @@
+// This test checks that words starting with capital letters and ending with "ified" don't
+// trigger the lint.
+
+#![deny(clippy::doc_markdown)]
+
+pub enum OutputFormat {
+    /// HumaNified
+    //~^ ERROR: item in documentation is missing backticks
+    Plain,
+    // Should not warn!
+    /// JSONified console output
+    Json,
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr
new file mode 100644
index 00000000000..ae68a767ec9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr
@@ -0,0 +1,18 @@
+error: item in documentation is missing backticks
+  --> tests/ui/doc/doc_markdown-issue_13097.rs:7:9
+   |
+LL |     /// HumaNified
+   |         ^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui/doc/doc_markdown-issue_13097.rs:4:9
+   |
+LL | #![deny(clippy::doc_markdown)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+help: try
+   |
+LL |     /// `HumaNified`
+   |         ~~~~~~~~~~~~
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/double_must_use.rs b/src/tools/clippy/tests/ui/double_must_use.rs
index 615de3e2474..4460aeb075b 100644
--- a/src/tools/clippy/tests/ui/double_must_use.rs
+++ b/src/tools/clippy/tests/ui/double_must_use.rs
@@ -3,19 +3,19 @@
 
 #[must_use]
 pub fn must_use_result() -> Result<(), ()> {
-    //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already
+    //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already
     unimplemented!();
 }
 
 #[must_use]
 pub fn must_use_tuple() -> (Result<(), ()>, u8) {
-    //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already
+    //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already
     unimplemented!();
 }
 
 #[must_use]
 pub fn must_use_array() -> [Result<(), ()>; 1] {
-    //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already
+    //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already
     unimplemented!();
 }
 
@@ -32,7 +32,7 @@ async fn async_must_use() -> usize {
 
 #[must_use]
 async fn async_must_use_result() -> Result<(), ()> {
-    //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already
+    //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already
     Ok(())
 }
 
diff --git a/src/tools/clippy/tests/ui/double_must_use.stderr b/src/tools/clippy/tests/ui/double_must_use.stderr
index 0f2154ecbcf..b26d1e48a8b 100644
--- a/src/tools/clippy/tests/ui/double_must_use.stderr
+++ b/src/tools/clippy/tests/ui/double_must_use.stderr
@@ -1,36 +1,36 @@
-error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
   --> tests/ui/double_must_use.rs:5:1
    |
 LL | pub fn must_use_result() -> Result<(), ()> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: either add some descriptive text or remove the attribute
+   = help: either add some descriptive message or remove the attribute
    = note: `-D clippy::double-must-use` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`
 
-error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
   --> tests/ui/double_must_use.rs:11:1
    |
 LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: either add some descriptive text or remove the attribute
+   = help: either add some descriptive message or remove the attribute
 
-error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
   --> tests/ui/double_must_use.rs:17:1
    |
 LL | pub fn must_use_array() -> [Result<(), ()>; 1] {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: either add some descriptive text or remove the attribute
+   = help: either add some descriptive message or remove the attribute
 
-error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
   --> tests/ui/double_must_use.rs:34:1
    |
 LL | async fn async_must_use_result() -> Result<(), ()> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: either add some descriptive text or remove the attribute
+   = help: either add some descriptive message or remove the attribute
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed
index f38b34c5a7c..1148f0f6c6a 100644
--- a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed
+++ b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed
@@ -153,3 +153,15 @@ fn main() {
     let r = &x;
     for _ in r {}
 }
+
+#[clippy::msrv = "1.79"]
+pub fn issue_13184() {
+    // https://github.com/rust-lang/rust-clippy/issues/13184
+    // No need to fix, as IntoIterator for Box is valid starting from 1.80
+    let mut values: Box<[u32]> = Box::new([1, 2]);
+    for _ in values.iter() {}
+    for _ in values.iter_mut() {}
+
+    let rvalues = &values;
+    for _ in rvalues.iter() {}
+}
diff --git a/src/tools/clippy/tests/ui/explicit_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_iter_loop.rs
index 2e701ada5ac..4dda2f13e5b 100644
--- a/src/tools/clippy/tests/ui/explicit_iter_loop.rs
+++ b/src/tools/clippy/tests/ui/explicit_iter_loop.rs
@@ -153,3 +153,15 @@ fn main() {
     let r = &x;
     for _ in r.iter() {}
 }
+
+#[clippy::msrv = "1.79"]
+pub fn issue_13184() {
+    // https://github.com/rust-lang/rust-clippy/issues/13184
+    // No need to fix, as IntoIterator for Box is valid starting from 1.80
+    let mut values: Box<[u32]> = Box::new([1, 2]);
+    for _ in values.iter() {}
+    for _ in values.iter_mut() {}
+
+    let rvalues = &values;
+    for _ in rvalues.iter() {}
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_abs.fixed b/src/tools/clippy/tests/ui/floating_point_abs.fixed
index 5312a8b29c6..33183c76972 100644
--- a/src/tools/clippy/tests/ui/floating_point_abs.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_abs.fixed
@@ -1,4 +1,3 @@
-#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
 /// Allow suboptimal ops in constant context
diff --git a/src/tools/clippy/tests/ui/floating_point_abs.rs b/src/tools/clippy/tests/ui/floating_point_abs.rs
index 8619177130c..a08d5bbcef5 100644
--- a/src/tools/clippy/tests/ui/floating_point_abs.rs
+++ b/src/tools/clippy/tests/ui/floating_point_abs.rs
@@ -1,4 +1,3 @@
-#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
 /// Allow suboptimal ops in constant context
diff --git a/src/tools/clippy/tests/ui/floating_point_abs.stderr b/src/tools/clippy/tests/ui/floating_point_abs.stderr
index f5a778c5b76..0c1f68f3b7f 100644
--- a/src/tools/clippy/tests/ui/floating_point_abs.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_abs.stderr
@@ -1,5 +1,5 @@
 error: manual implementation of `abs` method
-  --> tests/ui/floating_point_abs.rs:15:5
+  --> tests/ui/floating_point_abs.rs:14:5
    |
 LL |     if num >= 0.0 { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
@@ -8,43 +8,43 @@ LL |     if num >= 0.0 { num } else { -num }
    = help: to override `-D warnings` add `#[allow(clippy::suboptimal_flops)]`
 
 error: manual implementation of `abs` method
-  --> tests/ui/floating_point_abs.rs:19:5
+  --> tests/ui/floating_point_abs.rs:18:5
    |
 LL |     if 0.0 < num { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
 
 error: manual implementation of `abs` method
-  --> tests/ui/floating_point_abs.rs:23:5
+  --> tests/ui/floating_point_abs.rs:22:5
    |
 LL |     if a.a > 0.0 { a.a } else { -a.a }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
 
 error: manual implementation of `abs` method
-  --> tests/ui/floating_point_abs.rs:27:5
+  --> tests/ui/floating_point_abs.rs:26:5
    |
 LL |     if 0.0 >= num { -num } else { num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
 
 error: manual implementation of `abs` method
-  --> tests/ui/floating_point_abs.rs:31:5
+  --> tests/ui/floating_point_abs.rs:30:5
    |
 LL |     if a.a < 0.0 { -a.a } else { a.a }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
 
 error: manual implementation of negation of `abs` method
-  --> tests/ui/floating_point_abs.rs:35:5
+  --> tests/ui/floating_point_abs.rs:34:5
    |
 LL |     if num < 0.0 { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
 
 error: manual implementation of negation of `abs` method
-  --> tests/ui/floating_point_abs.rs:39:5
+  --> tests/ui/floating_point_abs.rs:38:5
    |
 LL |     if 0.0 >= num { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
 
 error: manual implementation of negation of `abs` method
-  --> tests/ui/floating_point_abs.rs:44:12
+  --> tests/ui/floating_point_abs.rs:43:12
    |
 LL |         a: if a.a >= 0.0 { -a.a } else { a.a },
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()`
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
index 3ce2edf2c71..164aac2601a 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
@@ -1,4 +1,3 @@
-#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
 /// Allow suboptimal_ops in constant context
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
index b5e4a8db4db..ae024b7f224 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.rs
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
@@ -1,4 +1,3 @@
-#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
 /// Allow suboptimal_ops in constant context
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
index 3e1a071de73..9c75909f715 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
@@ -1,5 +1,5 @@
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:20:13
+  --> tests/ui/floating_point_mul_add.rs:19:13
    |
 LL |     let _ = a * b + c;
    |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
@@ -8,73 +8,73 @@ LL |     let _ = a * b + c;
    = help: to override `-D warnings` add `#[allow(clippy::suboptimal_flops)]`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:21:13
+  --> tests/ui/floating_point_mul_add.rs:20:13
    |
 LL |     let _ = a * b - c;
    |             ^^^^^^^^^ help: consider using: `a.mul_add(b, -c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:22:13
+  --> tests/ui/floating_point_mul_add.rs:21:13
    |
 LL |     let _ = c + a * b;
    |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:23:13
+  --> tests/ui/floating_point_mul_add.rs:22:13
    |
 LL |     let _ = c - a * b;
    |             ^^^^^^^^^ help: consider using: `a.mul_add(-b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:24:13
+  --> tests/ui/floating_point_mul_add.rs:23:13
    |
 LL |     let _ = a + 2.0 * 4.0;
    |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:25:13
+  --> tests/ui/floating_point_mul_add.rs:24:13
    |
 LL |     let _ = a + 2. * 4.;
    |             ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:27:13
+  --> tests/ui/floating_point_mul_add.rs:26:13
    |
 LL |     let _ = (a * b) + c;
    |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:28:13
+  --> tests/ui/floating_point_mul_add.rs:27:13
    |
 LL |     let _ = c + (a * b);
    |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:29:13
+  --> tests/ui/floating_point_mul_add.rs:28:13
    |
 LL |     let _ = a * b * c + d;
    |             ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:31:13
+  --> tests/ui/floating_point_mul_add.rs:30:13
    |
 LL |     let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:32:13
+  --> tests/ui/floating_point_mul_add.rs:31:13
    |
 LL |     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:34:13
+  --> tests/ui/floating_point_mul_add.rs:33:13
    |
 LL |     let _ = (a * a + b).sqrt();
    |             ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:37:13
+  --> tests/ui/floating_point_mul_add.rs:36:13
    |
 LL |     let _ = a - (b * u as f64);
    |             ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)`
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.fixed b/src/tools/clippy/tests/ui/floating_point_rad.fixed
index a710bd9bd60..2f93d233cb4 100644
--- a/src/tools/clippy/tests/ui/floating_point_rad.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_rad.fixed
@@ -1,4 +1,3 @@
-#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
 /// Allow suboptimal_flops in constant context
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.rs b/src/tools/clippy/tests/ui/floating_point_rad.rs
index 14656f021df..9690effc4e1 100644
--- a/src/tools/clippy/tests/ui/floating_point_rad.rs
+++ b/src/tools/clippy/tests/ui/floating_point_rad.rs
@@ -1,4 +1,3 @@
-#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
 /// Allow suboptimal_flops in constant context
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.stderr b/src/tools/clippy/tests/ui/floating_point_rad.stderr
index 64674342c2b..b834f5374e0 100644
--- a/src/tools/clippy/tests/ui/floating_point_rad.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_rad.stderr
@@ -1,5 +1,5 @@
 error: conversion to radians can be done more accurately
-  --> tests/ui/floating_point_rad.rs:11:13
+  --> tests/ui/floating_point_rad.rs:10:13
    |
 LL |     let _ = degrees as f64 * std::f64::consts::PI / 180.0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_radians()`
@@ -8,43 +8,43 @@ LL |     let _ = degrees as f64 * std::f64::consts::PI / 180.0;
    = help: to override `-D warnings` add `#[allow(clippy::suboptimal_flops)]`
 
 error: conversion to degrees can be done more accurately
-  --> tests/ui/floating_point_rad.rs:12:13
+  --> tests/ui/floating_point_rad.rs:11:13
    |
 LL |     let _ = degrees as f64 * 180.0 / std::f64::consts::PI;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_degrees()`
 
 error: conversion to degrees can be done more accurately
-  --> tests/ui/floating_point_rad.rs:17:13
+  --> tests/ui/floating_point_rad.rs:16:13
    |
 LL |     let _ = x * 180f32 / std::f32::consts::PI;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
 
 error: conversion to degrees can be done more accurately
-  --> tests/ui/floating_point_rad.rs:18:13
+  --> tests/ui/floating_point_rad.rs:17:13
    |
 LL |     let _ = 90. * 180f64 / std::f64::consts::PI;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_degrees()`
 
 error: conversion to degrees can be done more accurately
-  --> tests/ui/floating_point_rad.rs:19:13
+  --> tests/ui/floating_point_rad.rs:18:13
    |
 LL |     let _ = 90.5 * 180f64 / std::f64::consts::PI;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_degrees()`
 
 error: conversion to radians can be done more accurately
-  --> tests/ui/floating_point_rad.rs:20:13
+  --> tests/ui/floating_point_rad.rs:19:13
    |
 LL |     let _ = x * std::f32::consts::PI / 180f32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
 
 error: conversion to radians can be done more accurately
-  --> tests/ui/floating_point_rad.rs:21:13
+  --> tests/ui/floating_point_rad.rs:20:13
    |
 LL |     let _ = 90. * std::f32::consts::PI / 180f32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_radians()`
 
 error: conversion to radians can be done more accurately
-  --> tests/ui/floating_point_rad.rs:22:13
+  --> tests/ui/floating_point_rad.rs:21:13
    |
 LL |     let _ = 90.5 * std::f32::consts::PI / 180f32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_radians()`
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
index 5778f8f526f..4c324587c96 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
@@ -15,6 +15,14 @@ struct Foo {
     z: i32,
 }
 
+#[derive(Default)]
+#[allow(clippy::inconsistent_struct_constructor)]
+struct Bar {
+    x: i32,
+    y: i32,
+    z: i32,
+}
+
 mod without_base {
     use super::Foo;
 
@@ -70,4 +78,17 @@ mod with_base {
     }
 }
 
+mod with_allow_ty_def {
+    use super::Bar;
+
+    fn test() {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+
+        // Should NOT lint because `Bar` is defined with `#[allow(clippy::inconsistent_struct_constructor)]`
+        Bar { y, x, z };
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
index 9efaf068934..d49f236b9b0 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
@@ -15,6 +15,14 @@ struct Foo {
     z: i32,
 }
 
+#[derive(Default)]
+#[allow(clippy::inconsistent_struct_constructor)]
+struct Bar {
+    x: i32,
+    y: i32,
+    z: i32,
+}
+
 mod without_base {
     use super::Foo;
 
@@ -74,4 +82,17 @@ mod with_base {
     }
 }
 
+mod with_allow_ty_def {
+    use super::Bar;
+
+    fn test() {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+
+        // Should NOT lint because `Bar` is defined with `#[allow(clippy::inconsistent_struct_constructor)]`
+        Bar { y, x, z };
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
index 1192271f911..97bb7c789a7 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
@@ -1,5 +1,5 @@
 error: struct constructor field order is inconsistent with struct definition field order
-  --> tests/ui/inconsistent_struct_constructor.rs:28:9
+  --> tests/ui/inconsistent_struct_constructor.rs:36:9
    |
 LL |         Foo { y, x, z };
    |         ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
@@ -8,7 +8,7 @@ LL |         Foo { y, x, z };
    = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]`
 
 error: struct constructor field order is inconsistent with struct definition field order
-  --> tests/ui/inconsistent_struct_constructor.rs:55:9
+  --> tests/ui/inconsistent_struct_constructor.rs:63:9
    |
 LL | /         Foo {
 LL | |             z,
diff --git a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs
index 2dccadd9fce..c8409d78ed7 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs
+++ b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.rs
@@ -17,7 +17,7 @@ mod multiple {
     //~^ ERROR: `clippy::msrv` is defined multiple times
 
     mod foo {
-        #![clippy::msrv = "1"]
+        #![clippy::msrv = "1.0"]
         #![clippy::msrv = "1.0.0"]
         //~^ ERROR: `clippy::msrv` is defined multiple times
     }
diff --git a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
index b4cb1b5713f..dbc276ed89d 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
+++ b/src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
@@ -31,8 +31,8 @@ LL |         #![clippy::msrv = "1.0.0"]
 note: first definition found here
   --> tests/ui/min_rust_version_invalid_attr.rs:20:9
    |
-LL |         #![clippy::msrv = "1"]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL |         #![clippy::msrv = "1.0"]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/string_slice.rs b/src/tools/clippy/tests/ui/string_slice.rs
index 440a86b104a..1d1911aaa1d 100644
--- a/src/tools/clippy/tests/ui/string_slice.rs
+++ b/src/tools/clippy/tests/ui/string_slice.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
 #[warn(clippy::string_slice)]
 #[allow(clippy::no_effect)]
 
@@ -11,4 +13,7 @@ fn main() {
     let s = String::from(m);
     &s[0..2];
     //~^ ERROR: indexing into a string may panic if the index is within a UTF-8 character
+    let a = Cow::Borrowed("foo");
+    &a[0..3];
+    //~^ ERROR: indexing into a string may panic if the index is within a UTF-8 character
 }
diff --git a/src/tools/clippy/tests/ui/string_slice.stderr b/src/tools/clippy/tests/ui/string_slice.stderr
index 7a4596b5f2d..bc0fcde34b8 100644
--- a/src/tools/clippy/tests/ui/string_slice.stderr
+++ b/src/tools/clippy/tests/ui/string_slice.stderr
@@ -1,5 +1,5 @@
 error: indexing into a string may panic if the index is within a UTF-8 character
-  --> tests/ui/string_slice.rs:5:6
+  --> tests/ui/string_slice.rs:7:6
    |
 LL |     &"Ölkanne"[1..];
    |      ^^^^^^^^^^^^^^
@@ -8,16 +8,22 @@ LL |     &"Ölkanne"[1..];
    = help: to override `-D warnings` add `#[allow(clippy::string_slice)]`
 
 error: indexing into a string may panic if the index is within a UTF-8 character
-  --> tests/ui/string_slice.rs:9:6
+  --> tests/ui/string_slice.rs:11:6
    |
 LL |     &m[2..5];
    |      ^^^^^^^
 
 error: indexing into a string may panic if the index is within a UTF-8 character
-  --> tests/ui/string_slice.rs:12:6
+  --> tests/ui/string_slice.rs:14:6
    |
 LL |     &s[0..2];
    |      ^^^^^^^
 
-error: aborting due to 3 previous errors
+error: indexing into a string may panic if the index is within a UTF-8 character
+  --> tests/ui/string_slice.rs:17:6
+   |
+LL |     &a[0..3];
+   |      ^^^^^^^
+
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.fixed b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.fixed
new file mode 100644
index 00000000000..d4a0cdf3447
--- /dev/null
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.fixed
@@ -0,0 +1,9 @@
+#![warn(clippy::too_long_first_doc_paragraph)]
+
+/// A very short summary.
+///
+/// A much longer explanation that goes into a lot more detail about
+/// how the thing works, possibly with doclinks and so one,
+/// and probably spanning a many rows. Blablabla, it needs to be over
+/// 200 characters so I needed to write something longeeeeeeer.
+pub struct Foo;
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.rs b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.rs
new file mode 100644
index 00000000000..5a3b6c42a32
--- /dev/null
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.rs
@@ -0,0 +1,8 @@
+#![warn(clippy::too_long_first_doc_paragraph)]
+
+/// A very short summary.
+/// A much longer explanation that goes into a lot more detail about
+/// how the thing works, possibly with doclinks and so one,
+/// and probably spanning a many rows. Blablabla, it needs to be over
+/// 200 characters so I needed to write something longeeeeeeer.
+pub struct Foo;
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr
new file mode 100644
index 00000000000..6403265a39c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr
@@ -0,0 +1,20 @@
+error: first doc comment paragraph is too long
+  --> tests/ui/too_long_first_doc_paragraph-fix.rs:3:1
+   |
+LL | / /// A very short summary.
+LL | | /// A much longer explanation that goes into a lot more detail about
+LL | | /// how the thing works, possibly with doclinks and so one,
+LL | | /// and probably spanning a many rows. Blablabla, it needs to be over
+LL | | /// 200 characters so I needed to write something longeeeeeeer.
+   | |_
+   |
+   = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]`
+help: add an empty line
+   |
+LL ~ /// A very short summary.
+LL + ///
+   |
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs
new file mode 100644
index 00000000000..1042249c5b7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs
@@ -0,0 +1,53 @@
+//@no-rustfix
+
+#![warn(clippy::too_long_first_doc_paragraph)]
+
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+pub struct Bar;
+
+// Should not warn! (not an item visible on mod page)
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+impl Bar {}
+
+// Should not warn! (less than 80 characters)
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+///
+/// Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+pub enum Enum {
+    A,
+}
+
+/// Lorem
+/// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+pub union Union {
+    a: u8,
+    b: u8,
+}
+
+// Should not warn! (title)
+/// # bla
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+pub union Union2 {
+    a: u8,
+    b: u8,
+}
+
+// Should not warn! (not public)
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+fn f() {}
+
+fn main() {
+    // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
new file mode 100644
index 00000000000..7f48e5cf884
--- /dev/null
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
@@ -0,0 +1,22 @@
+error: first doc comment paragraph is too long
+  --> tests/ui/too_long_first_doc_paragraph.rs:5:1
+   |
+LL | / /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+LL | | /// gravida non lacinia at, rhoncus eu lacus.
+   | |_
+   |
+   = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]`
+
+error: first doc comment paragraph is too long
+  --> tests/ui/too_long_first_doc_paragraph.rs:26:1
+   |
+LL | / /// Lorem
+LL | | /// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+LL | | /// gravida non lacinia at, rhoncus eu lacus.
+   | |_
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
index fcd60f48bcc..bcdf65b217e 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
@@ -2,316 +2,432 @@ error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:83:13
    |
 LL |     let _ = opt.unwrap_or_else(|| 2);
-   |             ^^^^--------------------
-   |                 |
-   |                 help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]`
+help: use `unwrap_or` instead
+   |
+LL |     let _ = opt.unwrap_or(2);
+   |                 ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:84:13
    |
 LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
-   |             ^^^^---------------------------------
-   |                 |
-   |                 help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = opt.unwrap_or(astronomers_pi);
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:85:13
    |
 LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
-   |             ^^^^-------------------------------------
-   |                 |
-   |                 help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = opt.unwrap_or(ext_str.some_field);
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:87:13
    |
 LL |     let _ = opt.and_then(|_| ext_opt);
-   |             ^^^^---------------------
-   |                 |
-   |                 help: use `and(..)` instead: `and(ext_opt)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _ = opt.and(ext_opt);
+   |                 ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:88:13
    |
 LL |     let _ = opt.or_else(|| ext_opt);
-   |             ^^^^-------------------
-   |                 |
-   |                 help: use `or(..)` instead: `or(ext_opt)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = opt.or(ext_opt);
+   |                 ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:89:13
    |
 LL |     let _ = opt.or_else(|| None);
-   |             ^^^^----------------
-   |                 |
-   |                 help: use `or(..)` instead: `or(None)`
+   |             ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = opt.or(None);
+   |                 ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:90:13
    |
 LL |     let _ = opt.get_or_insert_with(|| 2);
-   |             ^^^^------------------------
-   |                 |
-   |                 help: use `get_or_insert(..)` instead: `get_or_insert(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `get_or_insert` instead
+   |
+LL |     let _ = opt.get_or_insert(2);
+   |                 ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:91:13
    |
 LL |     let _ = opt.ok_or_else(|| 2);
-   |             ^^^^----------------
-   |                 |
-   |                 help: use `ok_or(..)` instead: `ok_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `ok_or` instead
+   |
+LL |     let _ = opt.ok_or(2);
+   |                 ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:92:13
    |
 LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
-   |             ^^^^^^^^^^^^^^^^^-------------------------------
-   |                              |
-   |                              help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
+   |                              ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:93:13
    |
 LL |     let _ = cond.then(|| astronomers_pi);
-   |             ^^^^^-----------------------
-   |                  |
-   |                  help: use `then_some(..)` instead: `then_some(astronomers_pi)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _ = cond.then_some(astronomers_pi);
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:94:13
    |
 LL |     let _ = true.then(|| -> _ {});
-   |             ^^^^^----------------
-   |                  |
-   |                  help: use `then_some(..)` instead: `then_some({})`
+   |             ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _ = true.then_some({});
+   |                  ~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:95:13
    |
 LL |     let _ = true.then(|| {});
-   |             ^^^^^-----------
-   |                  |
-   |                  help: use `then_some(..)` instead: `then_some({})`
+   |             ^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _ = true.then_some({});
+   |                  ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:99:13
    |
 LL |     let _ = Some(1).unwrap_or_else(|| *r);
-   |             ^^^^^^^^---------------------
-   |                     |
-   |                     help: use `unwrap_or(..)` instead: `unwrap_or(*r)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(1).unwrap_or(*r);
+   |                     ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:101:13
    |
 LL |     let _ = Some(1).unwrap_or_else(|| *b);
-   |             ^^^^^^^^---------------------
-   |                     |
-   |                     help: use `unwrap_or(..)` instead: `unwrap_or(*b)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(1).unwrap_or(*b);
+   |                     ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:103:13
    |
 LL |     let _ = Some(1).as_ref().unwrap_or_else(|| &r);
-   |             ^^^^^^^^^^^^^^^^^---------------------
-   |                              |
-   |                              help: use `unwrap_or(..)` instead: `unwrap_or(&r)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(1).as_ref().unwrap_or(&r);
+   |                              ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:104:13
    |
 LL |     let _ = Some(1).as_ref().unwrap_or_else(|| &b);
-   |             ^^^^^^^^^^^^^^^^^---------------------
-   |                              |
-   |                              help: use `unwrap_or(..)` instead: `unwrap_or(&b)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(1).as_ref().unwrap_or(&b);
+   |                              ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:107:13
    |
 LL |     let _ = Some(10).unwrap_or_else(|| 2);
-   |             ^^^^^^^^^--------------------
-   |                      |
-   |                      help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(10).unwrap_or(2);
+   |                      ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:108:13
    |
 LL |     let _ = Some(10).and_then(|_| ext_opt);
-   |             ^^^^^^^^^---------------------
-   |                      |
-   |                      help: use `and(..)` instead: `and(ext_opt)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _ = Some(10).and(ext_opt);
+   |                      ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:109:28
    |
 LL |     let _: Option<usize> = None.or_else(|| ext_opt);
-   |                            ^^^^^-------------------
-   |                                 |
-   |                                 help: use `or(..)` instead: `or(ext_opt)`
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Option<usize> = None.or(ext_opt);
+   |                                 ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:110:13
    |
 LL |     let _ = None.get_or_insert_with(|| 2);
-   |             ^^^^^------------------------
-   |                  |
-   |                  help: use `get_or_insert(..)` instead: `get_or_insert(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `get_or_insert` instead
+   |
+LL |     let _ = None.get_or_insert(2);
+   |                  ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:111:35
    |
 LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
-   |                                   ^^^^^----------------
-   |                                        |
-   |                                        help: use `ok_or(..)` instead: `ok_or(2)`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `ok_or` instead
+   |
+LL |     let _: Result<usize, usize> = None.ok_or(2);
+   |                                        ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:112:28
    |
 LL |     let _: Option<usize> = None.or_else(|| None);
-   |                            ^^^^^----------------
-   |                                 |
-   |                                 help: use `or(..)` instead: `or(None)`
+   |                            ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Option<usize> = None.or(None);
+   |                                 ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:115:13
    |
 LL |     let _ = deep.0.unwrap_or_else(|| 2);
-   |             ^^^^^^^--------------------
-   |                    |
-   |                    help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = deep.0.unwrap_or(2);
+   |                    ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:116:13
    |
 LL |     let _ = deep.0.and_then(|_| ext_opt);
-   |             ^^^^^^^---------------------
-   |                    |
-   |                    help: use `and(..)` instead: `and(ext_opt)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _ = deep.0.and(ext_opt);
+   |                    ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:117:13
    |
 LL |     let _ = deep.0.or_else(|| None);
-   |             ^^^^^^^----------------
-   |                    |
-   |                    help: use `or(..)` instead: `or(None)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = deep.0.or(None);
+   |                    ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:118:13
    |
 LL |     let _ = deep.0.get_or_insert_with(|| 2);
-   |             ^^^^^^^------------------------
-   |                    |
-   |                    help: use `get_or_insert(..)` instead: `get_or_insert(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `get_or_insert` instead
+   |
+LL |     let _ = deep.0.get_or_insert(2);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:119:13
    |
 LL |     let _ = deep.0.ok_or_else(|| 2);
-   |             ^^^^^^^----------------
-   |                    |
-   |                    help: use `ok_or(..)` instead: `ok_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `ok_or` instead
+   |
+LL |     let _ = deep.0.ok_or(2);
+   |                    ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:150:28
    |
 LL |     let _: Option<usize> = None.or_else(|| Some(3));
-   |                            ^^^^^-------------------
-   |                                 |
-   |                                 help: use `or(..)` instead: `or(Some(3))`
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Option<usize> = None.or(Some(3));
+   |                                 ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:151:13
    |
 LL |     let _ = deep.0.or_else(|| Some(3));
-   |             ^^^^^^^-------------------
-   |                    |
-   |                    help: use `or(..)` instead: `or(Some(3))`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = deep.0.or(Some(3));
+   |                    ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:152:13
    |
 LL |     let _ = opt.or_else(|| Some(3));
-   |             ^^^^-------------------
-   |                 |
-   |                 help: use `or(..)` instead: `or(Some(3))`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = opt.or(Some(3));
+   |                 ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:158:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| 2);
-   |             ^^^^^---------------------
-   |                  |
-   |                  help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = res2.unwrap_or(2);
+   |                  ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:159:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
-   |             ^^^^^----------------------------------
-   |                  |
-   |                  help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = res2.unwrap_or(astronomers_pi);
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:160:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
-   |             ^^^^^--------------------------------------
-   |                  |
-   |                  help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = res2.unwrap_or(ext_str.some_field);
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:182:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
-   |                                   ^^^^--------------------
-   |                                       |
-   |                                       help: use `and(..)` instead: `and(Err(2))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _: Result<usize, usize> = res.and(Err(2));
+   |                                       ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:183:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
-   |                                   ^^^^---------------------------------
-   |                                       |
-   |                                       help: use `and(..)` instead: `and(Err(astronomers_pi))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _: Result<usize, usize> = res.and(Err(astronomers_pi));
+   |                                       ~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:184:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
-   |                                   ^^^^-------------------------------------
-   |                                       |
-   |                                       help: use `and(..)` instead: `and(Err(ext_str.some_field))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _: Result<usize, usize> = res.and(Err(ext_str.some_field));
+   |                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:186:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
-   |                                   ^^^^------------------
-   |                                       |
-   |                                       help: use `or(..)` instead: `or(Ok(2))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Result<usize, usize> = res.or(Ok(2));
+   |                                       ~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:187:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
-   |                                   ^^^^-------------------------------
-   |                                       |
-   |                                       help: use `or(..)` instead: `or(Ok(astronomers_pi))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
+   |                                       ~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:188:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
-   |                                   ^^^^-----------------------------------
-   |                                       |
-   |                                       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
+   |                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:189:35
@@ -324,193 +440,265 @@ LL | |     // some lines
 ...  |
 LL | |     // some lines
 LL | |     or_else(|_| Ok(ext_str.some_field));
-   | |_____----------------------------------^
-   |       |
-   |       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+   | |_______________________________________^
+   |
+help: use `or` instead
+   |
+LL |     or(Ok(ext_str.some_field));
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:219:14
    |
 LL |     let _x = false.then(|| i32::MAX + 1);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MAX + 1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MAX + 1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:221:14
    |
 LL |     let _x = false.then(|| i32::MAX * 2);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MAX * 2)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MAX * 2);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:223:14
    |
 LL |     let _x = false.then(|| i32::MAX - 1);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MAX - 1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MAX - 1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:225:14
    |
 LL |     let _x = false.then(|| i32::MIN - 1);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MIN - 1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MIN - 1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:227:14
    |
 LL |     let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2);
-   |              ^^^^^^-------------------------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some((1 + 2 * 3 - 2 / 3 + 9) << 2)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some((1 + 2 * 3 - 2 / 3 + 9) << 2);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:229:14
    |
 LL |     let _x = false.then(|| 255u8 << 7);
-   |              ^^^^^^-------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255u8 << 7)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255u8 << 7);
+   |                    ~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:231:14
    |
 LL |     let _x = false.then(|| 255u8 << 8);
-   |              ^^^^^^-------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255u8 << 8)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255u8 << 8);
+   |                    ~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:233:14
    |
 LL |     let _x = false.then(|| 255u8 >> 8);
-   |              ^^^^^^-------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255u8 >> 8)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255u8 >> 8);
+   |                    ~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:236:14
    |
 LL |     let _x = false.then(|| i32::MAX + -1);
-   |              ^^^^^^----------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MAX + -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MAX + -1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:238:14
    |
 LL |     let _x = false.then(|| -i32::MAX);
-   |              ^^^^^^------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(-i32::MAX)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(-i32::MAX);
+   |                    ~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:240:14
    |
 LL |     let _x = false.then(|| -i32::MIN);
-   |              ^^^^^^------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(-i32::MIN)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(-i32::MIN);
+   |                    ~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:243:14
    |
 LL |     let _x = false.then(|| 255 >> -7);
-   |              ^^^^^^------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255 >> -7)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255 >> -7);
+   |                    ~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:245:14
    |
 LL |     let _x = false.then(|| 255 << -1);
-   |              ^^^^^^------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255 << -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255 << -1);
+   |                    ~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:247:14
    |
 LL |     let _x = false.then(|| 1 / 0);
-   |              ^^^^^^--------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(1 / 0)`
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(1 / 0);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:249:14
    |
 LL |     let _x = false.then(|| x << -1);
-   |              ^^^^^^----------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(x << -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(x << -1);
+   |                    ~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:251:14
    |
 LL |     let _x = false.then(|| x << 2);
-   |              ^^^^^^---------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(x << 2)`
+   |              ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(x << 2);
+   |                    ~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:261:14
    |
 LL |     let _x = false.then(|| x / 0);
-   |              ^^^^^^--------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(x / 0)`
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(x / 0);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:263:14
    |
 LL |     let _x = false.then(|| x % 0);
-   |              ^^^^^^--------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(x % 0)`
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(x % 0);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:266:14
    |
 LL |     let _x = false.then(|| 1 / -1);
-   |              ^^^^^^---------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(1 / -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(1 / -1);
+   |                    ~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:268:14
    |
 LL |     let _x = false.then(|| i32::MIN / -1);
-   |              ^^^^^^----------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MIN / -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MIN / -1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:271:14
    |
 LL |     let _x = false.then(|| i32::MIN / 0);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MIN / 0)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MIN / 0);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:273:14
    |
 LL |     let _x = false.then(|| 4 / 2);
-   |              ^^^^^^--------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(4 / 2)`
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(4 / 2);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:281:14
    |
 LL |     let _x = false.then(|| f1 + f2);
-   |              ^^^^^^----------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(f1 + f2)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(f1 + f2);
+   |                    ~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 63 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
index b566b119571..390235b2124 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr
@@ -2,36 +2,47 @@ error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:13:13
    |
 LL |     let _ = Ok(1).unwrap_or_else(|()| 2);
-   |             ^^^^^^----------------------
-   |                   |
-   |                   help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]`
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Ok(1).unwrap_or(2);
+   |                   ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:19:13
    |
 LL |     let _ = Ok(1).unwrap_or_else(|e::E| 2);
-   |             ^^^^^^------------------------
-   |                   |
-   |                   help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Ok(1).unwrap_or(2);
+   |                   ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:21:13
    |
 LL |     let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
-   |             ^^^^^^-------------------------------------
-   |                   |
-   |                   help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Ok(1).unwrap_or(2);
+   |                   ~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:31:13
    |
 LL |     let _ = true.then(|| -> &[u8] { &[] });
-   |             ^^^^^-------------------------
-   |                  |
-   |                  help: use `then_some(..)` instead: `then_some({ &[] })`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _ = true.then_some({ &[] });
+   |                  ~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index dcf00e4e384..99b3560a064 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -20,6 +20,7 @@ new_pr = true
 [assign]
 contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
 users_on_vacation = [
+    "flip1995",
     "matthiaskrgr",
     "giraffate",
 ]
diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html
index 300c9de178f..f3d7e504fdf 100644
--- a/src/tools/clippy/util/gh-pages/index.html
+++ b/src/tools/clippy/util/gh-pages/index.html
@@ -23,378 +23,25 @@ Otherwise, have a great day =^.^=
     <link id="styleHighlight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/highlight.css">
     <link id="styleNight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/tomorrow-night.css" disabled="true">
     <link id="styleAyu" rel="stylesheet" href="https://rust-lang.github.io/mdBook/ayu-highlight.css" disabled="true">
-    <style>
-        blockquote { font-size: 1em; }
-        [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
-
-        .dropdown-menu {
-            color: var(--fg);
-            background: var(--theme-popup-bg);
-            border: 1px solid var(--theme-popup-border);
-        }
-
-        .dropdown-menu .divider {
-            background-color: var(--theme-popup-border);
-        }
-
-        .dropdown-menu .checkbox {
-            display: block;
-            white-space: nowrap;
-            margin: 0;
-        }
-        .dropdown-menu .checkbox label {
-            padding: 3px 20px;
-            width: 100%;
-        }
-
-        .dropdown-menu .checkbox input {
-            position: relative;
-            margin: 0 0.5rem 0;
-            padding: 0;
-        }
-
-        .dropdown-menu .checkbox:hover {
-            background-color: var(--theme-hover);
-        }
-
-        div.panel div.panel-body button {
-            background: var(--searchbar-bg);
-            color: var(--searchbar-fg);
-            border-color: var(--theme-popup-border);
-        }
-
-        div.panel div.panel-body button:hover {
-            box-shadow: 0 0 3px var(--searchbar-shadow-color);
-        }
-
-        div.panel div.panel-body  button.open {
-            filter: brightness(90%);
-        }
-
-        .dropdown-toggle .badge {
-            background-color: #777;
-        }
-
-        .panel-heading { cursor: pointer; }
-
-        .panel-title { display: flex; flex-wrap: wrap;}
-        .panel-title .label { display: inline-block; }
-
-        .panel-title-name { flex: 1; min-width: 400px;}
-        .panel-title-name span { vertical-align: bottom; }
-
-        .panel .panel-title-name .anchor { display: none; }
-        .panel:hover .panel-title-name .anchor { display: inline;}
-
-        .search-control {
-            margin-top: 15px;
-        }
-
-        @media (min-width: 992px) {
-            .search-control {
-                margin-top: 0;
-            }
-        }
-
-        @media (min-width: 405px) {
-            #upper-filters {
-                display: flex;
-                flex-wrap: wrap;
-            }
-        }
-
-        @media (max-width: 430px) {
-            /* Turn the version filter list to the left */
-            #version-filter-selector {
-                right: 0;
-                left: auto;
-            }
-        }
-
-        @media (max-width: 412px) {
-            #upper-filters,
-            .panel-body .search-control  {
-                padding-right: 8px;
-                padding-left: 8px;
-            }
-        }
-
-        .label {
-            padding-top: 0.3em;
-            padding-bottom: 0.3em;
-        }
-
-        .label-lint-group {
-            min-width: 8em;
-        }
-        .label-lint-level {
-            min-width: 4em;
-        }
-
-        .label-lint-level-allow {
-            background-color: #5cb85c;
-        }
-        .label-lint-level-warn {
-            background-color: #f0ad4e;
-        }
-        .label-lint-level-deny {
-            background-color: #d9534f;
-        }
-        .label-lint-level-none {
-            background-color: #777777;
-            opacity: 0.5;
-        }
-
-        .label-group-deprecated {
-            opacity: 0.5;
-        }
-
-        .label-doc-folding {
-            color: #000;
-            background-color: #fff;
-            border: 1px solid var(--theme-popup-border);
-        }
-        .label-doc-folding:hover {
-            background-color: #e6e6e6;
-        }
-
-        .lint-doc-md > h3 {
-            border-top: 1px solid var(--theme-popup-border);
-            padding: 10px 15px;
-            margin: 0 -15px;
-            font-size: 18px;
-        }
-        .lint-doc-md > h3:first-child {
-            border-top: none;
-            padding-top: 0px;
-        }
-
-        @media (max-width:749px) {
-            .lint-additional-info-container {
-                display: flex;
-                flex-flow: column;
-            }
-            .lint-additional-info-item + .lint-additional-info-item {
-                border-top: 1px solid var(--theme-popup-border);
-            }
-        }
-        @media (min-width:750px) {
-            .lint-additional-info-container {
-                display: flex;
-                flex-flow: row;
-            }
-            .lint-additional-info-item + .lint-additional-info-item {
-                border-left: 1px solid var(--theme-popup-border);
-            }
-        }
-
-        .lint-additional-info-item {
-            display: inline-flex;
-            min-width: 200px;
-            flex-grow: 1;
-            padding: 9px 5px 5px 15px;
-        }
-
-        .label-applicability {
-            background-color: #777777;
-            margin: auto 5px;
-        }
-
-        .label-version {
-            background-color: #777777;
-            margin: auto 5px;
-            font-family: monospace;
-        }
-
-        details {
-            border-radius: 4px;
-            padding: .5em .5em 0;
-        }
-
-        code {
-            white-space: pre !important;
-        }
-
-        summary {
-            font-weight: bold;
-            margin: -.5em -.5em 0;
-            padding: .5em;
-            display: revert;
-        }
-
-        details[open] {
-            padding: .5em;
-        }
-    </style>
-    <style>
-        /* Expanding the mdBoom theme*/
-        .light {
-            --inline-code-bg: #f6f7f6;
-        }
-        .rust {
-            --inline-code-bg: #f6f7f6;
-        }
-        .coal {
-            --inline-code-bg: #1d1f21;
-        }
-        .navy {
-            --inline-code-bg: #1d1f21;
-        }
-        .ayu {
-            --inline-code-bg: #191f26;
-        }
-
-        .theme-dropdown {
-            position: absolute;
-            margin: 0.7em;
-            z-index: 10;
-        }
-
-        /* Applying the mdBook theme */
-        .theme-icon {
-            text-align: center;
-            width: 2em;
-            height: 2em;
-            line-height: 2em;
-            border: solid 1px var(--icons);
-            border-radius: 5px;
-            user-select: none;
-            cursor: pointer;
-        }
-        .theme-icon:hover {
-            background: var(--theme-hover);
-        }
-        .theme-choice {
-            display: none;
-            list-style: none;
-            border: 1px solid var(--theme-popup-border);
-            border-radius: 5px;
-            color: var(--fg);
-            background: var(--theme-popup-bg);
-            padding: 0 0;
-            overflow: hidden;
-        }
-
-        .theme-dropdown.open .theme-choice {
-            display: block;
-        }
-
-        .theme-choice > li {
-            padding: 5px 10px;
-            font-size: 0.8em;
-            user-select: none;
-            cursor: pointer;
-        }
-
-        .theme-choice > li:hover {
-            background: var(--theme-hover);
-        }
-
-        .alert {
-            color: var(--fg);
-            background: var(--theme-hover);
-            border: 1px solid var(--theme-popup-border);
-        }
-        .page-header {
-            border-color: var(--theme-popup-border);
-        }
-        .panel-default > .panel-heading {
-            background: var(--theme-hover);
-            color: var(--fg);
-            border: 1px solid var(--theme-popup-border);
-        }
-        .panel-default > .panel-heading:hover {
-            filter: brightness(90%);
-        }
-        .list-group-item {
-            background: 0%;
-            border: 1px solid var(--theme-popup-border);
-        }
-        .panel, pre, hr {
-            background: var(--bg);
-            border: 1px solid var(--theme-popup-border);
-        }
-
-        #version-filter-selector .checkbox {
-            display: flex;
-        }
-
-        #version-filter {
-            min-width: available;
-        }
-
-        #version-filter li label {
-            padding-right: 0;
-            width: 35%;
-        }
-
-        .version-filter-input {
-            height: 60%;
-            width: 30%;
-            text-align: center;
-            border: none;
-            border-bottom: 1px solid #000000;
-        }
-
-        #filter-label, .filter-clear {
-            background: var(--searchbar-bg);
-            color: var(--searchbar-fg);
-            border-color: var(--theme-popup-border);
-            filter: brightness(95%);
-        }
-        #filter-label:hover, .filter-clear:hover {
-            filter: brightness(90%);
-        }
-        .filter-input {
-            background: var(--searchbar-bg);
-            color: var(--searchbar-fg);
-            border-color: var(--theme-popup-border);
-        }
-
-        .filter-input::-webkit-input-placeholder,
-        .filter-input::-moz-placeholder {
-            color: var(--searchbar-fg);
-            opacity: 30%;
-        }
-
-        .expansion-group {
-            margin-top: 15px;
-            padding: 0px 8px;
-            display: flex;
-            flex-wrap: nowrap;
-        }
-
-        @media (min-width: 992px) {
-            .expansion-group {
-                margin-top: 0;
-                padding: 0px 15px;
-            }
-        }
-
-        .expansion-control {
-            width: 50%;
-        }
-
-        :not(pre) > code {
-            color: var(--inline-code-color);
-            background-color: var(--inline-code-bg);
-        }
-        html {
-            scrollbar-color: var(--scrollbar) var(--bg);
-        }
-        body {
-            background: var(--bg);
-            color: var(--fg);
-        }
-
-    </style>
+    <link rel="stylesheet" href="style.css">
 </head>
 <body ng-app="clippy" ng-controller="lintList">
-    <div theme-dropdown class="theme-dropdown">
-        <div id="theme-icon" class="theme-icon">&#128396;</div>
-        <ul id="theme-menu" class="theme-choice">
-            <li id="{{id}}" ng-repeat="(id, name) in themes" ng-click="selectTheme(id)">{{name}}</li>
-        </ul>
+    <div id="settings-dropdown">
+        <div class="settings-icon" tabindex="-1"></div>
+        <div class="settings-menu" tabindex="-1">
+            <div class="setting-radio-name">Theme</div>
+            <select id="theme-choice" onchange="setTheme(this.value, true)">
+                <option value="ayu">Ayu</option>
+                <option value="coal">Coal</option>
+                <option value="light">Light</option>
+                <option value="navy">Navy</option>
+                <option value="rust">Rust</option>
+            </select>
+            <label>
+                <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)">
+                <span>Disable keyboard shortcuts</span>
+            </label>
+        </div>
     </div>
 
     <div class="container">
@@ -592,7 +239,7 @@ Otherwise, have a great day =^.^=
                         <!-- Applicability -->
                         <div class="lint-additional-info-item">
                             <span> Applicability: </span>
-                            <span class="label label-default label-applicability">{{lint.applicability.applicability}}</span>
+                            <span class="label label-default label-applicability">{{lint.applicability}}</span>
                             <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a>
                         </div>
                         <!-- Clippy version -->
@@ -605,8 +252,8 @@ Otherwise, have a great day =^.^=
                             <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>
                         </div>
                         <!-- Jump to source -->
-                        <div class="lint-additional-info-item" ng-if="lint.id_span">
-                            <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a>
+                        <div class="lint-additional-info-item" ng-if="lint.id_location">
+                            <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/{{lint.id_location}}">View Source</a>
                         </div>
                     </div>
                 </div>
diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js
index ed1e090e1b5..1a5330bc0e5 100644
--- a/src/tools/clippy/util/gh-pages/script.js
+++ b/src/tools/clippy/util/gh-pages/script.js
@@ -50,24 +50,6 @@
                 );
             };
         })
-        .directive('themeDropdown', function ($document) {
-            return {
-                restrict: 'A',
-                link: function ($scope, $element, $attr) {
-                    $element.bind('click', function () {
-                        $element.toggleClass('open');
-                        $element.addClass('open-recent');
-                    });
-
-                    $document.bind('click', function () {
-                        if (!$element.hasClass('open-recent')) {
-                            $element.removeClass('open');
-                        }
-                        $element.removeClass('open-recent');
-                    })
-                }
-            }
-        })
         .directive('filterDropdown', function ($document) {
             return {
                 restrict: 'A',
@@ -114,28 +96,19 @@
                 cargo: true,
                 complexity: true,
                 correctness: true,
-                deprecated: false,
                 nursery: true,
                 pedantic: true,
                 perf: true,
                 restriction: true,
                 style: true,
                 suspicious: true,
+                deprecated: false,
             }
 
             $scope.groups = {
                 ...GROUPS_FILTER_DEFAULT
             };
 
-            const THEMES_DEFAULT = {
-                light: "Light",
-                rust: "Rust",
-                coal: "Coal",
-                navy: "Navy",
-                ayu: "Ayu"
-            };
-            $scope.themes = THEMES_DEFAULT;
-
             $scope.versionFilters = {
                 "≥": {enabled: false, minorVersion: null },
                 "≤": {enabled: false, minorVersion: null },
@@ -153,11 +126,10 @@
             );
 
             const APPLICABILITIES_FILTER_DEFAULT = {
-                Unspecified: true,
-                Unresolved: true,
                 MachineApplicable: true,
                 MaybeIncorrect: true,
-                HasPlaceholders: true
+                HasPlaceholders: true,
+                Unspecified: true,
             };
 
             $scope.applicabilities = {
@@ -321,10 +293,6 @@
                 $location.path($scope.search);
             }
 
-            $scope.selectTheme = function (theme) {
-                setTheme(theme, true);
-            }
-
             $scope.toggleLevels = function (value) {
                 const levels = $scope.levels;
                 for (const key in levels) {
@@ -456,7 +424,7 @@
             }
 
             $scope.byApplicabilities = function (lint) {
-                return $scope.applicabilities[lint.applicability.applicability];
+                return $scope.applicabilities[lint.applicability];
             };
 
             // Show details for one lint
@@ -537,6 +505,16 @@ function getQueryVariable(variable) {
     }
 }
 
+function storeValue(settingName, value) {
+    try {
+        localStorage.setItem(`clippy-lint-list-${settingName}`, value);
+    } catch (e) { }
+}
+
+function loadValue(settingName) {
+    return localStorage.getItem(`clippy-lint-list-${settingName}`);
+}
+
 function setTheme(theme, store) {
     let enableHighlight = false;
     let enableNight = false;
@@ -569,14 +547,14 @@ function setTheme(theme, store) {
     document.getElementById("styleAyu").disabled = !enableAyu;
 
     if (store) {
-        try {
-            localStorage.setItem('clippy-lint-list-theme', theme);
-        } catch (e) { }
+        storeValue("theme", theme);
+    } else {
+        document.getElementById(`theme-choice`).value = theme;
     }
 }
 
 function handleShortcut(ev) {
-    if (ev.ctrlKey || ev.altKey || ev.metaKey) {
+    if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) {
         return;
     }
 
@@ -601,11 +579,51 @@ function handleShortcut(ev) {
 document.addEventListener("keypress", handleShortcut);
 document.addEventListener("keydown", handleShortcut);
 
+function changeSetting(elem) {
+    if (elem.id === "disable-shortcuts") {
+        disableShortcuts = elem.checked;
+        storeValue(elem.id, elem.checked);
+    }
+}
+
+function onEachLazy(lazyArray, func) {
+    const arr = Array.prototype.slice.call(lazyArray);
+    for (const el of arr) {
+        func(el);
+    }
+}
+
+function handleBlur(event) {
+    const parent = document.getElementById("settings-dropdown");
+    if (!parent.contains(document.activeElement) &&
+        !parent.contains(event.relatedTarget)
+    ) {
+        parent.classList.remove("open");
+    }
+}
+
+function generateSettings() {
+    const settings = document.getElementById("settings-dropdown");
+    const settingsButton = settings.querySelector(".settings-icon")
+    settingsButton.onclick = () => settings.classList.toggle("open");
+    settingsButton.onblur = handleBlur;
+    const settingsMenu = settings.querySelector(".settings-menu");
+    settingsMenu.onblur = handleBlur;
+    onEachLazy(
+        settingsMenu.querySelectorAll("input"),
+        el => el.onblur = handleBlur,
+    );
+}
+
+generateSettings();
+
 // loading the theme after the initial load
 const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
-const theme = localStorage.getItem('clippy-lint-list-theme');
+const theme = loadValue('theme');
 if (prefersDark.matches && !theme) {
     setTheme("coal", false);
 } else {
     setTheme(theme, false);
 }
+let disableShortcuts = loadValue('disable-shortcuts') === "true";
+document.getElementById("disable-shortcuts").checked = disableShortcuts;
diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css
new file mode 100644
index 00000000000..a9485d51104
--- /dev/null
+++ b/src/tools/clippy/util/gh-pages/style.css
@@ -0,0 +1,398 @@
+blockquote { font-size: 1em; }
+
+[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
+    display: none !important;
+}
+
+.dropdown-menu {
+    color: var(--fg);
+    background: var(--theme-popup-bg);
+    border: 1px solid var(--theme-popup-border);
+}
+
+.dropdown-menu .divider {
+    background-color: var(--theme-popup-border);
+}
+
+.dropdown-menu .checkbox {
+    display: block;
+    white-space: nowrap;
+    margin: 0;
+}
+.dropdown-menu .checkbox label {
+    padding: 3px 20px;
+    width: 100%;
+}
+
+.dropdown-menu .checkbox input {
+    position: relative;
+    margin: 0 0.5rem 0;
+    padding: 0;
+}
+
+.dropdown-menu .checkbox:hover {
+    background-color: var(--theme-hover);
+}
+
+div.panel div.panel-body button {
+    background: var(--searchbar-bg);
+    color: var(--searchbar-fg);
+    border-color: var(--theme-popup-border);
+}
+
+div.panel div.panel-body button:hover {
+    box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+div.panel div.panel-body  button.open {
+    filter: brightness(90%);
+}
+
+.dropdown-toggle .badge {
+    background-color: #777;
+}
+
+.panel-heading { cursor: pointer; }
+
+.panel-title { display: flex; flex-wrap: wrap;}
+.panel-title .label { display: inline-block; }
+
+.panel-title-name { flex: 1; min-width: 400px;}
+.panel-title-name span { vertical-align: bottom; }
+
+.panel .panel-title-name .anchor { display: none; }
+.panel:hover .panel-title-name .anchor { display: inline;}
+
+.search-control {
+    margin-top: 15px;
+}
+
+@media (min-width: 992px) {
+    .search-control {
+        margin-top: 0;
+    }
+}
+
+@media (min-width: 405px) {
+    #upper-filters {
+        display: flex;
+        flex-wrap: wrap;
+    }
+}
+
+@media (max-width: 430px) {
+    /* Turn the version filter list to the left */
+    #version-filter-selector {
+        right: 0;
+        left: auto;
+    }
+}
+
+@media (max-width: 412px) {
+    #upper-filters,
+    .panel-body .search-control  {
+        padding-right: 8px;
+        padding-left: 8px;
+    }
+}
+
+.label {
+    padding-top: 0.3em;
+    padding-bottom: 0.3em;
+}
+
+.label-lint-group {
+    min-width: 8em;
+}
+.label-lint-level {
+    min-width: 4em;
+}
+
+.label-lint-level-allow {
+    background-color: #5cb85c;
+}
+.label-lint-level-warn {
+    background-color: #f0ad4e;
+}
+.label-lint-level-deny {
+    background-color: #d9534f;
+}
+.label-lint-level-none {
+    background-color: #777777;
+    opacity: 0.5;
+}
+
+.label-group-deprecated {
+    opacity: 0.5;
+}
+
+.label-doc-folding {
+    color: #000;
+    background-color: #fff;
+    border: 1px solid var(--theme-popup-border);
+}
+.label-doc-folding:hover {
+    background-color: #e6e6e6;
+}
+
+.lint-doc-md > h3 {
+    border-top: 1px solid var(--theme-popup-border);
+    padding: 10px 15px;
+    margin: 0 -15px;
+    font-size: 18px;
+}
+.lint-doc-md > h3:first-child {
+    border-top: none;
+    padding-top: 0px;
+}
+
+@media (max-width:749px) {
+    .lint-additional-info-container {
+        display: flex;
+        flex-flow: column;
+    }
+    .lint-additional-info-item + .lint-additional-info-item {
+        border-top: 1px solid var(--theme-popup-border);
+    }
+}
+@media (min-width:750px) {
+    .lint-additional-info-container {
+        display: flex;
+        flex-flow: row;
+    }
+    .lint-additional-info-item + .lint-additional-info-item {
+        border-left: 1px solid var(--theme-popup-border);
+    }
+}
+
+.lint-additional-info-item {
+    display: inline-flex;
+    min-width: 200px;
+    flex-grow: 1;
+    padding: 9px 5px 5px 15px;
+}
+
+.label-applicability {
+    background-color: #777777;
+    margin: auto 5px;
+}
+
+.label-version {
+    background-color: #777777;
+    margin: auto 5px;
+    font-family: monospace;
+}
+
+details {
+    border-radius: 4px;
+    padding: .5em .5em 0;
+}
+
+code {
+    white-space: pre !important;
+}
+
+summary {
+    font-weight: bold;
+    margin: -.5em -.5em 0;
+    padding: .5em;
+    display: revert;
+}
+
+details[open] {
+    padding: .5em;
+}
+
+/* Expanding the mdBook theme*/
+.light {
+    --inline-code-bg: #f6f7f6;
+}
+.rust {
+    --inline-code-bg: #f6f7f6;
+}
+.coal {
+    --inline-code-bg: #1d1f21;
+}
+.navy {
+    --inline-code-bg: #1d1f21;
+}
+.ayu {
+    --inline-code-bg: #191f26;
+}
+
+#settings-dropdown {
+    position: absolute;
+    margin: 0.7em;
+    z-index: 10;
+    display: flex;
+}
+
+/* Applying the mdBook theme */
+.settings-icon {
+    text-align: center;
+    width: 2em;
+    height: 2em;
+    line-height: 2em;
+    border: solid 1px var(--icons);
+    border-radius: 5px;
+    user-select: none;
+    cursor: pointer;
+    background: var(--theme-hover);
+}
+.settings-menu {
+    display: none;
+    list-style: none;
+    border: 1px solid var(--theme-popup-border);
+    border-radius: 5px;
+    color: var(--fg);
+    background: var(--theme-popup-bg);
+    overflow: hidden;
+    padding: 9px;
+    width: 207px;
+    position: absolute;
+    top: 28px;
+}
+
+.settings-icon::before {
+    /* Wheel <https://www.svgrepo.com/svg/384069/settings-cog-gear> */
+    content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \
+enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg">\
+<path d="M10.25,6c0-0.1243286-0.0261841-0.241333-0.0366211-0.362915l1.6077881-1.5545654l\
+-1.25-2.1650391  c0,0-1.2674561,0.3625488-2.1323853,0.6099854c-0.2034912-0.1431885-0.421875\
+-0.2639771-0.6494751-0.3701782L7.25,0h-2.5 c0,0-0.3214111,1.2857666-0.5393066,2.1572876\
+C3.9830933,2.2634888,3.7647095,2.3842773,3.5612183,2.5274658L1.428833,1.9174805 \
+l-1.25,2.1650391c0,0,0.9641113,0.9321899,1.6077881,1.5545654C1.7761841,5.758667,\
+1.75,5.8756714,1.75,6  s0.0261841,0.241333,0.0366211,0.362915L0.178833,7.9174805l1.25,\
+2.1650391l2.1323853-0.6099854  c0.2034912,0.1432495,0.421875,0.2639771,0.6494751,0.3701782\
+L4.75,12h2.5l0.5393066-2.1572876  c0.2276001-0.1062012,0.4459839-0.2269287,0.6494751\
+-0.3701782l2.1323853,0.6099854l1.25-2.1650391L10.2133789,6.362915  C10.2238159,6.241333,\
+10.25,6.1243286,10.25,6z M6,7.5C5.1715698,7.5,4.5,6.8284302,4.5,6S5.1715698,4.5,6,4.5S7.5\
+,5.1715698,7.5,6  S6.8284302,7.5,6,7.5z" fill="black"/></svg>');
+  width: 18px;
+  height: 18px;
+  display: block;
+  filter: invert(0.7);
+  padding-left: 4px;
+  padding-top: 3px;
+}
+
+.settings-menu * {
+    font-weight: normal;
+}
+
+.settings-menu label {
+    cursor: pointer;
+}
+
+#settings-dropdown.open .settings-menu {
+    display: block;
+}
+
+#theme-choice {
+    margin-bottom: 10px;
+    background: var(--searchbar-bg);
+    color: var(--searchbar-fg);
+    border-color: var(--theme-popup-border);
+    border-radius: 5px;
+    cursor: pointer;
+    width: 100%;
+    border-width: 1px;
+    padding: 5px;
+}
+
+.alert {
+    color: var(--fg);
+    background: var(--theme-hover);
+    border: 1px solid var(--theme-popup-border);
+}
+.page-header {
+    border-color: var(--theme-popup-border);
+}
+.panel-default > .panel-heading {
+    background: var(--theme-hover);
+    color: var(--fg);
+    border: 1px solid var(--theme-popup-border);
+}
+.panel-default > .panel-heading:hover {
+    filter: brightness(90%);
+}
+.list-group-item {
+    background: 0%;
+    border: 1px solid var(--theme-popup-border);
+}
+.panel, pre, hr {
+    background: var(--bg);
+    border: 1px solid var(--theme-popup-border);
+}
+
+#version-filter-selector .checkbox {
+    display: flex;
+}
+
+#version-filter {
+    min-width: available;
+}
+
+#version-filter li label {
+    padding-right: 0;
+    width: 35%;
+}
+
+.version-filter-input {
+    height: 60%;
+    width: 30%;
+    text-align: center;
+    border: none;
+    border-bottom: 1px solid #000000;
+}
+
+#filter-label, .filter-clear {
+    background: var(--searchbar-bg);
+    color: var(--searchbar-fg);
+    border-color: var(--theme-popup-border);
+    filter: brightness(95%);
+}
+#filter-label:hover, .filter-clear:hover {
+    filter: brightness(90%);
+}
+.filter-input {
+    background: var(--searchbar-bg);
+    color: var(--searchbar-fg);
+    border-color: var(--theme-popup-border);
+}
+
+.filter-input::-webkit-input-placeholder,
+.filter-input::-moz-placeholder {
+    color: var(--searchbar-fg);
+    opacity: 30%;
+}
+
+.expansion-group {
+    margin-top: 15px;
+    padding: 0px 8px;
+    display: flex;
+    flex-wrap: nowrap;
+}
+
+@media (min-width: 992px) {
+    .expansion-group {
+        margin-top: 0;
+        padding: 0px 15px;
+    }
+}
+
+.expansion-control {
+    width: 50%;
+}
+
+:not(pre) > code {
+    color: var(--inline-code-color);
+    background-color: var(--inline-code-bg);
+}
+html {
+    scrollbar-color: var(--scrollbar) var(--bg);
+}
+body {
+    background: var(--bg);
+    color: var(--fg);
+}
diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs
index 72448c1180a..fa77ab1e011 100644
--- a/src/tools/linkchecker/main.rs
+++ b/src/tools/linkchecker/main.rs
@@ -6,8 +6,10 @@
 //! script is to check all relative links in our documentation to make sure they
 //! actually point to a valid place.
 //!
-//! Currently this doesn't actually do any HTML parsing or anything fancy like
-//! that, it just has a simple "regex" to search for `href` and `id` tags.
+//! Currently uses a combination of HTML parsing to
+//! extract the `href` and `id` attributes,
+//! and regex search on the orignal markdown to handle intra-doc links.
+//!
 //! These values are then translated to file URLs if possible and then the
 //! destination is asserted to exist.
 //!
diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs
index e0aef13ca79..72bb9db7e74 100644
--- a/src/tools/lint-docs/src/lib.rs
+++ b/src/tools/lint-docs/src/lib.rs
@@ -444,6 +444,7 @@ impl<'a> LintExtractor<'a> {
         let mut cmd = Command::new(self.rustc_path);
         if options.contains(&"edition2024") {
             cmd.arg("--edition=2024");
+            cmd.arg("-Zunstable-options");
         } else if options.contains(&"edition2021") {
             cmd.arg("--edition=2021");
         } else if options.contains(&"edition2018") {
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 1eca86baeaa..6124cc327ad 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-fdf61d499c8a8421ecf98e7924bb87caf43a9938
+3f121b9461cce02a703a0e7e450568849dfaa074
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index f72591f0c4b..a4d3e3f7af3 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -1157,7 +1157,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     #[inline]
-    fn get_thread_name<'c>(&'c self, thread: ThreadId) -> Option<&[u8]>
+    fn get_thread_name<'c>(&'c self, thread: ThreadId) -> Option<&'c [u8]>
     where
         'tcx: 'c,
     {
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index f796e845a23..ece393caf9a 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -35,6 +35,7 @@
     clippy::box_default,
     clippy::needless_question_mark,
     clippy::needless_lifetimes,
+    clippy::too_long_first_doc_paragraph,
     rustc::diagnostic_outside_of_impl,
     // We are not implementing queries here so it's fine
     rustc::potential_query_instability,
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr
index 42beed4ecde..a11a2b95689 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_bad_unwind1.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding
   --> $DIR/exported_symbol_bad_unwind1.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr
index 112a9687837..12425cc4892 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 panic in a function that cannot unwind
 stack backtrace:
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr
index 112a9687837..12425cc4892 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 panic in a function that cannot unwind
 stack backtrace:
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr
index bc3e4858716..f9e299bf5d2 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding
   --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
index a2fa4c1d590..83efc9974e8 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/return_pointer_on_unwind.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
   --> $DIR/return_pointer_on_unwind.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr
index 8dd76edafea..67fd60e572e 100644
--- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 aborted execution: attempted to instantiate uninhabited type `!`
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr
index 55f66a275b6..f89a1fc4bbb 100644
--- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 aborted execution: attempted to zero-initialize type `fn()`, which is invalid
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/panic/bad_unwind.stderr b/src/tools/miri/tests/fail/panic/bad_unwind.stderr
index 230e8337c7c..c08fe5153b1 100644
--- a/src/tools/miri/tests/fail/panic/bad_unwind.stderr
+++ b/src/tools/miri/tests/fail/panic/bad_unwind.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/bad_unwind.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding
   --> $DIR/bad_unwind.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/panic/double_panic.stderr b/src/tools/miri/tests/fail/panic/double_panic.stderr
index 5829c1897bb..0395fe418d9 100644
--- a/src/tools/miri/tests/fail/panic/double_panic.stderr
+++ b/src/tools/miri/tests/fail/panic/double_panic.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/double_panic.rs:LL:CC:
 first
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at $DIR/double_panic.rs:LL:CC:
 second
 stack backtrace:
diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.stderr b/src/tools/miri/tests/fail/panic/panic_abort1.stderr
index d4abf19cd1e..6c7cac23bec 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort1.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort1.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/panic_abort1.rs:LL:CC:
 panicking from libstd
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/panic_abort/src/lib.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.stderr b/src/tools/miri/tests/fail/panic/panic_abort2.stderr
index 507f17abf4e..1eda5449d1b 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort2.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort2.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/panic_abort2.rs:LL:CC:
 42-panicking from libstd
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/panic_abort/src/lib.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.stderr b/src/tools/miri/tests/fail/panic/panic_abort3.stderr
index a5d8b4d2eeb..5c7c5e17bee 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort3.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort3.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/panic_abort3.rs:LL:CC:
 panicking from libcore
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/panic_abort/src/lib.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.stderr b/src/tools/miri/tests/fail/panic/panic_abort4.stderr
index 62fbbf942cb..c8104f570f6 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort4.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort4.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/panic_abort4.rs:LL:CC:
 42-panicking from libcore
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
   --> RUSTLIB/panic_abort/src/lib.rs:LL:CC
    |
diff --git a/src/tools/miri/tests/fail/terminate-terminator.stderr b/src/tools/miri/tests/fail/terminate-terminator.stderr
index a5fa0b3e07a..6384689c563 100644
--- a/src/tools/miri/tests/fail/terminate-terminator.stderr
+++ b/src/tools/miri/tests/fail/terminate-terminator.stderr
@@ -3,7 +3,7 @@ warning: You have explicitly enabled MIR optimizations, overriding Miri's defaul
 thread 'main' panicked at $DIR/terminate-terminator.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 panic in a function that cannot unwind
 stack backtrace:
diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.stderr b/src/tools/miri/tests/fail/unwind-action-terminate.stderr
index 547d550d3d0..fd67bdf4a90 100644
--- a/src/tools/miri/tests/fail/unwind-action-terminate.stderr
+++ b/src/tools/miri/tests/fail/unwind-action-terminate.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/unwind-action-terminate.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 panic in a function that cannot unwind
 stack backtrace:
diff --git a/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr b/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr
index 5b309ed09bb..319a10febb3 100644
--- a/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr
+++ b/src/tools/miri/tests/panic/alloc_error_handler_hook.stderr
@@ -1,5 +1,5 @@
 thread 'main' panicked at $DIR/alloc_error_handler_hook.rs:LL:CC:
 alloc error hook called
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 yes we are unwinding!
diff --git a/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr b/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr
index 3d5457799f6..2cdff03f10f 100644
--- a/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr
+++ b/src/tools/miri/tests/panic/alloc_error_handler_panic.stderr
@@ -1,5 +1,5 @@
 thread 'main' panicked at RUSTLIB/std/src/alloc.rs:LL:CC:
 memory allocation of 4 bytes failed
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 yes we are unwinding!
diff --git a/src/tools/miri/tests/panic/div-by-zero-2.stderr b/src/tools/miri/tests/panic/div-by-zero-2.stderr
index f0b84ea6fd6..ed394f76b0e 100644
--- a/src/tools/miri/tests/panic/div-by-zero-2.stderr
+++ b/src/tools/miri/tests/panic/div-by-zero-2.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/div-by-zero-2.rs:LL:CC:
 attempt to divide by zero
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.stderr b/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.stderr
index f77f6f01119..6733f2e42c1 100644
--- a/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.stderr
+++ b/src/tools/miri/tests/panic/function_calls/exported_symbol_good_unwind.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/exported_symbol_good_unwind.rs:LL:CC:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at $DIR/exported_symbol_good_unwind.rs:LL:CC:
 explicit panic
 thread 'main' panicked at $DIR/exported_symbol_good_unwind.rs:LL:CC:
diff --git a/src/tools/miri/tests/panic/oob_subslice.stderr b/src/tools/miri/tests/panic/oob_subslice.stderr
index c116f8eb525..46f0f643a47 100644
--- a/src/tools/miri/tests/panic/oob_subslice.stderr
+++ b/src/tools/miri/tests/panic/oob_subslice.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/oob_subslice.rs:LL:CC:
 range end index 5 out of range for slice of length 4
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/overflowing-lsh-neg.stderr b/src/tools/miri/tests/panic/overflowing-lsh-neg.stderr
index 1d057ea5eb4..be822bd0285 100644
--- a/src/tools/miri/tests/panic/overflowing-lsh-neg.stderr
+++ b/src/tools/miri/tests/panic/overflowing-lsh-neg.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/overflowing-lsh-neg.rs:LL:CC:
 attempt to shift left with overflow
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/overflowing-rsh-1.stderr b/src/tools/miri/tests/panic/overflowing-rsh-1.stderr
index d1a79400bfa..fc090aba5fd 100644
--- a/src/tools/miri/tests/panic/overflowing-rsh-1.stderr
+++ b/src/tools/miri/tests/panic/overflowing-rsh-1.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/overflowing-rsh-1.rs:LL:CC:
 attempt to shift right with overflow
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/overflowing-rsh-2.stderr b/src/tools/miri/tests/panic/overflowing-rsh-2.stderr
index 612b0c0c4c2..77160e1870f 100644
--- a/src/tools/miri/tests/panic/overflowing-rsh-2.stderr
+++ b/src/tools/miri/tests/panic/overflowing-rsh-2.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/overflowing-rsh-2.rs:LL:CC:
 attempt to shift right with overflow
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/panic2.stderr b/src/tools/miri/tests/panic/panic2.stderr
index 792c71346fd..f7408310093 100644
--- a/src/tools/miri/tests/panic/panic2.stderr
+++ b/src/tools/miri/tests/panic/panic2.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/panic2.rs:LL:CC:
 42-panicking from libstd
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/panic3.stderr b/src/tools/miri/tests/panic/panic3.stderr
index f8016bc3912..32ba400e025 100644
--- a/src/tools/miri/tests/panic/panic3.stderr
+++ b/src/tools/miri/tests/panic/panic3.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/panic3.rs:LL:CC:
 panicking from libcore
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/panic4.stderr b/src/tools/miri/tests/panic/panic4.stderr
index 67410bf3b1a..a8a23ee3ce1 100644
--- a/src/tools/miri/tests/panic/panic4.stderr
+++ b/src/tools/miri/tests/panic/panic4.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/panic4.rs:LL:CC:
 42-panicking from libcore
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/transmute_fat2.stderr b/src/tools/miri/tests/panic/transmute_fat2.stderr
index 2ee01d46931..021ca1c4b32 100644
--- a/src/tools/miri/tests/panic/transmute_fat2.stderr
+++ b/src/tools/miri/tests/panic/transmute_fat2.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/transmute_fat2.rs:LL:CC:
 index out of bounds: the len is 0 but the index is 0
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/unsupported_foreign_function.stderr b/src/tools/miri/tests/panic/unsupported_foreign_function.stderr
index d0a7d8dafc5..fcc4220bfce 100644
--- a/src/tools/miri/tests/panic/unsupported_foreign_function.stderr
+++ b/src/tools/miri/tests/panic/unsupported_foreign_function.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/unsupported_foreign_function.rs:LL:CC:
 unsupported Miri functionality: can't call foreign function `foo` on $OS
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/unsupported_syscall.stderr b/src/tools/miri/tests/panic/unsupported_syscall.stderr
index f802159cb1c..660cfba8900 100644
--- a/src/tools/miri/tests/panic/unsupported_syscall.stderr
+++ b/src/tools/miri/tests/panic/unsupported_syscall.stderr
@@ -1,4 +1,4 @@
 thread 'main' panicked at $DIR/unsupported_syscall.rs:LL:CC:
 unsupported Miri functionality: can't execute syscall with ID 0
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/pass/panic/catch_panic.stderr b/src/tools/miri/tests/pass/panic/catch_panic.stderr
index a472a5d80cb..f61b39493ed 100644
--- a/src/tools/miri/tests/pass/panic/catch_panic.stderr
+++ b/src/tools/miri/tests/pass/panic/catch_panic.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/catch_panic.rs:LL:CC:
 Hello from std::panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 Caught panic message (&str): Hello from std::panic
 thread 'main' panicked at $DIR/catch_panic.rs:LL:CC:
 Hello from std::panic: 1
diff --git a/src/tools/miri/tests/pass/panic/concurrent-panic.stderr b/src/tools/miri/tests/pass/panic/concurrent-panic.stderr
index b2a5cf4922c..fe0d16ca78a 100644
--- a/src/tools/miri/tests/pass/panic/concurrent-panic.stderr
+++ b/src/tools/miri/tests/pass/panic/concurrent-panic.stderr
@@ -3,7 +3,7 @@ Thread 1 reported it has started
 thread '<unnamed>' panicked at $DIR/concurrent-panic.rs:LL:CC:
 panic in thread 2
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 Thread 2 blocking on thread 1
 Thread 2 reported it has started
 Unlocking mutex
diff --git a/src/tools/miri/tests/pass/panic/nested_panic_caught.stderr b/src/tools/miri/tests/pass/panic/nested_panic_caught.stderr
index 3efb4be40f9..a346d31f645 100644
--- a/src/tools/miri/tests/pass/panic/nested_panic_caught.stderr
+++ b/src/tools/miri/tests/pass/panic/nested_panic_caught.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/nested_panic_caught.rs:LL:CC:
 once
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'main' panicked at $DIR/nested_panic_caught.rs:LL:CC:
 twice
 stack backtrace:
diff --git a/src/tools/miri/tests/pass/panic/thread_panic.stderr b/src/tools/miri/tests/pass/panic/thread_panic.stderr
index bdfe4f98ba3..0fde5922c19 100644
--- a/src/tools/miri/tests/pass/panic/thread_panic.stderr
+++ b/src/tools/miri/tests/pass/panic/thread_panic.stderr
@@ -1,6 +1,6 @@
 thread '<unnamed>' panicked at $DIR/thread_panic.rs:LL:CC:
 Hello!
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
+note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread 'childthread' panicked at $DIR/thread_panic.rs:LL:CC:
 Hello, world!
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index 1a13d56b0e4..77df6e7beb5 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -12,3 +12,4 @@ regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace
 gimli = "0.31.0"
 build_helper = { path = "../build_helper" }
 serde_json = "1.0"
+libc = "0.2"
diff --git a/src/tools/run-make-support/src/env.rs b/src/tools/run-make-support/src/env.rs
index e6460fb93d3..9acbb16d73e 100644
--- a/src/tools/run-make-support/src/env.rs
+++ b/src/tools/run-make-support/src/env.rs
@@ -24,3 +24,11 @@ pub fn env_var_os(name: &str) -> OsString {
 pub fn no_debug_assertions() -> bool {
     std::env::var_os("NO_DEBUG_ASSERTIONS").is_some()
 }
+
+/// A wrapper around [`std::env::set_current_dir`] which includes the directory
+/// path in the panic message.
+#[track_caller]
+pub fn set_current_dir<P: AsRef<std::path::Path>>(dir: P) {
+    std::env::set_current_dir(dir.as_ref())
+        .expect(&format!("could not set current directory to \"{}\"", dir.as_ref().display()));
+}
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index fc20fd3b2e8..989d00d4c2f 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -36,6 +36,7 @@ pub mod rfs {
 // Re-exports of third-party library crates.
 pub use bstr;
 pub use gimli;
+pub use libc;
 pub use object;
 pub use regex;
 pub use serde_json;
@@ -64,7 +65,7 @@ pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
 pub use diff::{diff, Diff};
 
 /// Panic-on-fail [`std::env::var`] and [`std::env::var_os`] wrappers.
-pub use env::{env_var, env_var_os};
+pub use env::{env_var, env_var_os, set_current_dir};
 
 /// Convenience helpers for running binaries and other commands.
 pub use run::{cmd, run, run_fail, run_with_args};
diff --git a/src/tools/run-make-support/src/path_helpers.rs b/src/tools/run-make-support/src/path_helpers.rs
index 1e6e44c4584..87901793a92 100644
--- a/src/tools/run-make-support/src/path_helpers.rs
+++ b/src/tools/run-make-support/src/path_helpers.rs
@@ -21,6 +21,7 @@ pub fn cwd() -> PathBuf {
 /// # Example
 ///
 /// ```rust
+/// # use run_make_support::path;
 /// let p = path("support_file.txt");
 /// ```
 pub fn path<P: AsRef<Path>>(p: P) -> PathBuf {
diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs
index 12088e5d3b5..3eeba6fd526 100644
--- a/src/tools/run-make-support/src/run.rs
+++ b/src/tools/run-make-support/src/run.rs
@@ -29,6 +29,7 @@ fn run_common(name: &str, args: Option<&[&str]>) -> Command {
         }
         env::join_paths(paths.iter()).unwrap()
     });
+    cmd.env("LC_ALL", "C"); // force english locale
 
     if is_windows() {
         let mut paths = vec![];
@@ -84,5 +85,6 @@ pub fn run_fail(name: &str) -> CompletedProcess {
 pub fn cmd<S: AsRef<OsStr>>(program: S) -> Command {
     let mut command = Command::new(program);
     set_host_rpath(&mut command);
+    command.env("LC_ALL", "C"); // force english locale
     command
 }
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index f55abb513b8..22d1e93bf39 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -6,9 +6,7 @@ run-make/incr-add-rust-src-component/Makefile
 run-make/issue-84395-lto-embed-bitcode/Makefile
 run-make/jobserver-error/Makefile
 run-make/libs-through-symlinks/Makefile
-run-make/libtest-thread-limit/Makefile
 run-make/macos-deployment-target/Makefile
 run-make/split-debuginfo/Makefile
 run-make/symbol-mangling-hashed/Makefile
 run-make/translation/Makefile
-run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 57310977704..5205fa14294 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -1289,8 +1289,7 @@ ui/imports/auxiliary/issue-52891.rs
 ui/imports/auxiliary/issue-55811.rs
 ui/imports/auxiliary/issue-56125.rs
 ui/imports/auxiliary/issue-59764.rs
-ui/imports/auxiliary/issue-85992-extern-1.rs
-ui/imports/auxiliary/issue-85992-extern-2.rs
+ui/imports/auxiliary/issue-85992-extern.rs
 ui/imports/issue-109148.rs
 ui/imports/issue-109343.rs
 ui/imports/issue-113953.rs