about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorOli Scherer <github35764891676564198441@oli-obk.de>2024-12-18 05:28:59 +0000
committerGitHub <noreply@github.com>2024-12-18 05:28:59 +0000
commit955ef0983112c6a10c54457cedc38eedefcdefb9 (patch)
tree95c98c8b70f583931bf51b01a8fcf0cf854f2dc0 /compiler
parente999c48404b492659f90993e9fdd2544c675abce (diff)
parentd49845e5fe45bcffe30df0963a97b2ad859abbf8 (diff)
downloadrust-955ef0983112c6a10c54457cedc38eedefcdefb9.tar.gz
rust-955ef0983112c6a10c54457cedc38eedefcdefb9.zip
Merge pull request #4097 from rust-lang/rustup-2024-12-18
Automatic Rustup
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_abi/src/extern_abi/mod.rs3
-rw-r--r--compiler/rustc_ast/src/ast.rs72
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs266
-rw-r--r--compiler/rustc_ast/src/entry.rs7
-rw-r--r--compiler/rustc_ast/src/expand/allocator.rs2
-rw-r--r--compiler/rustc_ast/src/expand/mod.rs2
-rw-r--r--compiler/rustc_ast/src/format.rs3
-rw-r--r--compiler/rustc_ast/src/lib.rs12
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs7
-rw-r--r--compiler/rustc_ast/src/token.rs5
-rw-r--r--compiler/rustc_ast/src/util/literal.rs3
-rw-r--r--compiler/rustc_ast/src/util/parser.rs2
-rw-r--r--compiler/rustc_ast/src/visit.rs5
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/delegation.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/format.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs13
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs49
-rw-r--r--compiler/rustc_ast_lowering/src/pat.rs12
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs3
-rw-r--r--compiler/rustc_ast_passes/Cargo.toml2
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs5
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs3
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs3
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs26
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/tests.rs3
-rw-r--r--compiler/rustc_attr/src/builtin.rs1349
-rw-r--r--compiler/rustc_attr_data_structures/Cargo.toml (renamed from compiler/rustc_attr/Cargo.toml)2
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs106
-rw-r--r--compiler/rustc_attr_data_structures/src/lib.rs16
-rw-r--r--compiler/rustc_attr_data_structures/src/stability.rs200
-rw-r--r--compiler/rustc_attr_data_structures/src/version.rs (renamed from compiler/rustc_session/src/version.rs)8
-rw-r--r--compiler/rustc_attr_parsing/Cargo.toml21
-rw-r--r--compiler/rustc_attr_parsing/messages.ftl (renamed from compiler/rustc_attr/messages.ftl)80
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs49
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/cfg.rs253
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/confusables.rs21
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/deprecation.rs148
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs17
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/repr.rs215
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/stability.rs384
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/transparency.rs36
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/util.rs36
-rw-r--r--compiler/rustc_attr_parsing/src/lib.rs (renamed from compiler/rustc_attr/src/lib.rs)11
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs (renamed from compiler/rustc_attr/src/session_diagnostics.rs)109
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs3
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs3
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs3
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs33
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs27
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs12
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/var_name.rs3
-rw-r--r--compiler/rustc_borrowck/src/lib.rs11
-rw-r--r--compiler/rustc_borrowck/src/nll.rs5
-rw-r--r--compiler/rustc_borrowck/src/polonius/legacy/accesses.rs85
-rw-r--r--compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs (renamed from compiler/rustc_borrowck/src/polonius/loan_invalidations.rs)16
-rw-r--r--compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs (renamed from compiler/rustc_borrowck/src/polonius/loan_kills.rs)24
-rw-r--r--compiler/rustc_borrowck/src/polonius/legacy/mod.rs223
-rw-r--r--compiler/rustc_borrowck/src/polonius/mod.rs185
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs13
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/mod.rs3
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/polonius.rs136
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs10
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs124
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs3
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs3
-rw-r--r--compiler/rustc_builtin_macros/Cargo.toml2
-rw-r--r--compiler/rustc_builtin_macros/src/alloc_error_handler.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/assert.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/autodiff.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/cfg.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_accessible.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_eval.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/concat.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/concat_idents.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/decodable.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/encodable.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/ty.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/hash.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/edition_panic.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/env.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/global_allocator.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/source_util.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/standard_library_imports.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/trace_macros.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/main_shim.rs3
-rw-r--r--compiler/rustc_codegen_gcc/src/attributes.rs4
-rw-r--r--compiler/rustc_codegen_gcc/src/gcc_util.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs6
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/base.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/callee.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs84
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs150
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs80
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs19
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs8
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml3
-rw-r--r--compiler/rustc_codegen_ssa/src/assert_module_sources.rs11
-rw-r--r--compiler/rustc_codegen_ssa/src/back/archive.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs93
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/naked_asm.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs13
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/backend.rs2
-rw-r--r--compiler/rustc_const_eval/Cargo.toml2
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs2
-rw-r--r--compiler/rustc_const_eval/src/check_consts/mod.rs2
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs3
-rw-r--r--compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/fn_queries.rs5
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs3
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs2
-rw-r--r--compiler/rustc_const_eval/src/util/caller_location.rs2
-rw-r--r--compiler/rustc_driver_impl/Cargo.toml2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs103
-rw-r--r--compiler/rustc_driver_impl/src/pretty.rs3
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs3
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs3
-rw-r--r--compiler/rustc_errors/src/lib.rs8
-rw-r--r--compiler/rustc_expand/Cargo.toml2
-rw-r--r--compiler/rustc_expand/src/base.rs35
-rw-r--r--compiler/rustc_expand/src/build.rs3
-rw-r--r--compiler/rustc_expand/src/config.rs7
-rw-r--r--compiler/rustc_expand/src/errors.rs3
-rw-r--r--compiler/rustc_expand/src/expand.rs5
-rw-r--r--compiler/rustc_expand/src/mbe.rs3
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs3
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs3
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs3
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs8
-rw-r--r--compiler/rustc_expand/src/mbe/metavar_expr.rs3
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs3
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs5
-rw-r--r--compiler/rustc_expand/src/module.rs3
-rw-r--r--compiler/rustc_expand/src/placeholders.rs3
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs5
-rw-r--r--compiler/rustc_feature/src/accepted.rs5
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs5
-rw-r--r--compiler/rustc_feature/src/lib.rs2
-rw-r--r--compiler/rustc_feature/src/removed.rs2
-rw-r--r--compiler/rustc_feature/src/unstable.rs7
-rw-r--r--compiler/rustc_hir/Cargo.toml1
-rw-r--r--compiler/rustc_hir/src/arena.rs2
-rw-r--r--compiler/rustc_hir/src/definitions.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs270
-rw-r--r--compiler/rustc_hir/src/intravisit.rs5
-rw-r--r--compiler/rustc_hir/src/lang_items.rs11
-rw-r--r--compiler/rustc_hir/src/pat_util.rs3
-rw-r--r--compiler/rustc_hir/src/stable_hash_impls.rs10
-rw-r--r--compiler/rustc_hir/src/weak_lang_items.rs2
-rw-r--r--compiler/rustc_hir_analysis/Cargo.toml2
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/entry.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/variance/dump.rs2
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs121
-rw-r--r--compiler/rustc_hir_typeck/Cargo.toml2
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/place_op.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs15
-rw-r--r--compiler/rustc_incremental/src/assert_dep_graph.rs7
-rw-r--r--compiler/rustc_incremental/src/errors.rs3
-rw-r--r--compiler/rustc_incremental/src/persist/dirty_clean.rs9
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs7
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/mod.rs2
-rw-r--r--compiler/rustc_infer/src/traits/util.rs3
-rw-r--r--compiler/rustc_interface/Cargo.toml2
-rw-r--r--compiler/rustc_interface/src/interface.rs3
-rw-r--r--compiler/rustc_interface/src/lib.rs6
-rw-r--r--compiler/rustc_interface/src/passes.rs111
-rw-r--r--compiler/rustc_interface/src/proc_macro_decls.rs2
-rw-r--r--compiler/rustc_interface/src/queries.rs120
-rw-r--r--compiler/rustc_interface/src/tests.rs3
-rw-r--r--compiler/rustc_interface/src/util.rs4
-rw-r--r--compiler/rustc_lint/Cargo.toml2
-rw-r--r--compiler/rustc_lint/messages.ftl7
-rw-r--r--compiler/rustc_lint/src/builtin.rs11
-rw-r--r--compiler/rustc_lint/src/context.rs39
-rw-r--r--compiler/rustc_lint/src/dangling.rs3
-rw-r--r--compiler/rustc_lint/src/early.rs122
-rw-r--r--compiler/rustc_lint/src/early/diagnostics.rs (renamed from compiler/rustc_lint/src/context/diagnostics.rs)15
-rw-r--r--compiler/rustc_lint/src/early/diagnostics/check_cfg.rs (renamed from compiler/rustc_lint/src/context/diagnostics/check_cfg.rs)19
-rw-r--r--compiler/rustc_lint/src/enum_intrinsics_non_enums.rs3
-rw-r--r--compiler/rustc_lint/src/if_let_rescope.rs2
-rw-r--r--compiler/rustc_lint/src/internal.rs45
-rw-r--r--compiler/rustc_lint/src/levels.rs18
-rw-r--r--compiler/rustc_lint/src/lib.rs3
-rw-r--r--compiler/rustc_lint/src/lints.rs72
-rw-r--r--compiler/rustc_lint/src/non_ascii_idents.rs6
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs3
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs3
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs67
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs2
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs3
-rw-r--r--compiler/rustc_lint/src/pass_by_value.rs2
-rw-r--r--compiler/rustc_lint/src/passes.rs8
-rw-r--r--compiler/rustc_lint/src/shadowed_into_iter.rs1
-rw-r--r--compiler/rustc_lint/src/traits.rs2
-rw-r--r--compiler/rustc_lint/src/types.rs42
-rw-r--r--compiler/rustc_lint/src/types/literal.rs2
-rw-r--r--compiler/rustc_lint/src/unqualified_local_imports.rs4
-rw-r--r--compiler/rustc_lint/src/unused.rs3
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs18
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs14
-rw-r--r--compiler/rustc_macros/src/diagnostics/mod.rs2
-rw-r--r--compiler/rustc_metadata/Cargo.toml2
-rw-r--r--compiler/rustc_metadata/src/creader.rs12
-rw-r--r--compiler/rustc_metadata/src/locator.rs3
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs6
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs7
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs7
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs31
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs7
-rw-r--r--compiler/rustc_middle/Cargo.toml2
-rw-r--r--compiler/rustc_middle/src/arena.rs6
-rw-r--r--compiler/rustc_middle/src/dep_graph/dep_node.rs2
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs9
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs2
-rw-r--r--compiler/rustc_middle/src/lint.rs4
-rw-r--r--compiler/rustc_middle/src/metadata.rs2
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs4
-rw-r--r--compiler/rustc_middle/src/middle/limits.rs34
-rw-r--r--compiler/rustc_middle/src/middle/mod.rs3
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs9
-rw-r--r--compiler/rustc_middle/src/mir/consts.rs4
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs3
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs5
-rw-r--r--compiler/rustc_middle/src/mir/query.rs3
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs3
-rw-r--r--compiler/rustc_middle/src/query/erase.rs16
-rw-r--r--compiler/rustc_middle/src/query/keys.rs3
-rw-r--r--compiler/rustc_middle/src/query/mod.rs13
-rw-r--r--compiler/rustc_middle/src/query/on_disk_cache.rs4
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs3
-rw-r--r--compiler/rustc_middle/src/traits/specialization_graph.rs2
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs2
-rw-r--r--compiler/rustc_middle/src/ty/assoc.rs2
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs3
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs118
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs2
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs3
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs4
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs3
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs31
-rw-r--r--compiler/rustc_middle/src/ty/parameterized.rs13
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs3
-rw-r--r--compiler/rustc_middle/src/ty/region.rs3
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs4
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs3
-rw-r--r--compiler/rustc_middle/src/ty/util.rs2
-rw-r--r--compiler/rustc_middle/src/util/call_kind.rs3
-rw-r--r--compiler/rustc_mir_build/src/builder/block.rs (renamed from compiler/rustc_mir_build/src/build/block.rs)6
-rw-r--r--compiler/rustc_mir_build/src/builder/cfg.rs (renamed from compiler/rustc_mir_build/src/build/cfg.rs)2
-rw-r--r--compiler/rustc_mir_build/src/builder/coverageinfo.rs (renamed from compiler/rustc_mir_build/src/build/coverageinfo.rs)4
-rw-r--r--compiler/rustc_mir_build/src/builder/coverageinfo/mcdc.rs (renamed from compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs)2
-rw-r--r--compiler/rustc_mir_build/src/builder/custom/mod.rs (renamed from compiler/rustc_mir_build/src/build/custom/mod.rs)3
-rw-r--r--compiler/rustc_mir_build/src/builder/custom/parse.rs (renamed from compiler/rustc_mir_build/src/build/custom/parse.rs)0
-rw-r--r--compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs (renamed from compiler/rustc_mir_build/src/build/custom/parse/instruction.rs)4
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_constant.rs (renamed from compiler/rustc_mir_build/src/build/expr/as_constant.rs)2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_operand.rs (renamed from compiler/rustc_mir_build/src/build/expr/as_operand.rs)4
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_place.rs (renamed from compiler/rustc_mir_build/src/build/expr/as_place.rs)46
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs (renamed from compiler/rustc_mir_build/src/build/expr/as_rvalue.rs)6
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_temp.rs (renamed from compiler/rustc_mir_build/src/build/expr/as_temp.rs)4
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/category.rs (renamed from compiler/rustc_mir_build/src/build/expr/category.rs)0
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/into.rs (renamed from compiler/rustc_mir_build/src/build/expr/into.rs)22
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/mod.rs (renamed from compiler/rustc_mir_build/src/build/expr/mod.rs)0
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/stmt.rs (renamed from compiler/rustc_mir_build/src/build/expr/stmt.rs)4
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/match_pair.rs (renamed from compiler/rustc_mir_build/src/build/matches/match_pair.rs)8
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/mod.rs (renamed from compiler/rustc_mir_build/src/build/matches/mod.rs)11
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/simplify.rs (renamed from compiler/rustc_mir_build/src/build/matches/simplify.rs)4
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/test.rs (renamed from compiler/rustc_mir_build/src/build/matches/test.rs)7
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/util.rs (renamed from compiler/rustc_mir_build/src/build/matches/util.rs)6
-rw-r--r--compiler/rustc_mir_build/src/builder/misc.rs (renamed from compiler/rustc_mir_build/src/build/misc.rs)2
-rw-r--r--compiler/rustc_mir_build/src/builder/mod.rs (renamed from compiler/rustc_mir_build/src/build/mod.rs)12
-rw-r--r--compiler/rustc_mir_build/src/builder/scope.rs (renamed from compiler/rustc_mir_build/src/build/scope.rs)2
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs21
-rw-r--r--compiler/rustc_mir_build/src/errors.rs3
-rw-r--r--compiler/rustc_mir_build/src/lib.rs9
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs10
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/graphviz.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/rustc_peek.rs3
-rw-r--r--compiler/rustc_mir_transform/Cargo.toml2
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs3
-rw-r--r--compiler/rustc_mir_transform/src/coroutine/by_move_body.rs2
-rw-r--r--compiler/rustc_mir_transform/src/cross_crate_inline.rs2
-rw-r--r--compiler/rustc_mir_transform/src/function_item_references.rs3
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs2
-rw-r--r--compiler/rustc_mir_transform/src/instsimplify.rs3
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs2
-rw-r--r--compiler/rustc_monomorphize/Cargo.toml2
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs3
-rw-r--r--compiler/rustc_monomorphize/src/mono_checks/move_check.rs3
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs7
-rw-r--r--compiler/rustc_parse/src/errors.rs3
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs3
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs3
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs8
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs4
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs3
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs3
-rw-r--r--compiler/rustc_parse/src/parser/item.rs3
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs11
-rw-r--r--compiler/rustc_parse/src/parser/mut_visit/tests.rs3
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs2
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs7
-rw-r--r--compiler/rustc_parse/src/parser/path.rs3
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs3
-rw-r--r--compiler/rustc_parse/src/parser/tests.rs5
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs3
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs8
-rw-r--r--compiler/rustc_passes/Cargo.toml3
-rw-r--r--compiler/rustc_passes/messages.ftl5
-rw-r--r--compiler/rustc_passes/src/abi_test.rs4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs43
-rw-r--r--compiler/rustc_passes/src/dead.rs2
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs7
-rw-r--r--compiler/rustc_passes/src/entry.rs3
-rw-r--r--compiler/rustc_passes/src/errors.rs11
-rw-r--r--compiler/rustc_passes/src/input_stats.rs20
-rw-r--r--compiler/rustc_passes/src/lang_items.rs9
-rw-r--r--compiler/rustc_passes/src/layout_test.rs5
-rw-r--r--compiler/rustc_passes/src/lib_features.rs7
-rw-r--r--compiler/rustc_passes/src/liveness.rs3
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs3
-rw-r--r--compiler/rustc_passes/src/stability.rs31
-rw-r--r--compiler/rustc_privacy/Cargo.toml2
-rw-r--r--compiler/rustc_privacy/src/lib.rs5
-rw-r--r--compiler/rustc_query_system/src/ich/hcx.rs3
-rw-r--r--compiler/rustc_query_system/src/ich/impls_syntax.rs28
-rw-r--r--compiler/rustc_query_system/src/ich/mod.rs2
-rw-r--r--compiler/rustc_resolve/Cargo.toml2
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs21
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs3
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs12
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs9
-rw-r--r--compiler/rustc_resolve/src/errors.rs3
-rw-r--r--compiler/rustc_resolve/src/ident.rs3
-rw-r--r--compiler/rustc_resolve/src/imports.rs3
-rw-r--r--compiler/rustc_resolve/src/late.rs43
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs48
-rw-r--r--compiler/rustc_resolve/src/lib.rs4
-rw-r--r--compiler/rustc_resolve/src/macros.rs8
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs43
-rw-r--r--compiler/rustc_session/src/config/cfg.rs2
-rw-r--r--compiler/rustc_session/src/cstore.rs3
-rw-r--r--compiler/rustc_session/src/lib.rs3
-rw-r--r--compiler/rustc_session/src/output.rs3
-rw-r--r--compiler/rustc_session/src/parse.rs2
-rw-r--r--compiler/rustc_smir/Cargo.toml2
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs2
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs14
-rw-r--r--compiler/rustc_span/src/edit_distance.rs2
-rw-r--r--compiler/rustc_span/src/lib.rs3
-rw-r--r--compiler/rustc_span/src/symbol.rs9
-rw-r--r--compiler/rustc_symbol_mangling/src/test.rs2
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs4
-rw-r--r--compiler/rustc_target/src/asm/mod.rs4
-rw-r--r--compiler/rustc_target/src/spec/mod.rs22
-rw-r--r--compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs4
-rw-r--r--compiler/rustc_target/src/target_features.rs190
-rw-r--r--compiler/rustc_trait_selection/Cargo.toml2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs46
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs34
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs26
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs92
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs8
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs12
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs9
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs43
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs29
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/note.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/region.rs22
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs3
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs12
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs5
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs7
-rw-r--r--compiler/rustc_trait_selection/src/errors/note_and_explain.rs6
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs549
-rw-r--r--compiler/rustc_transmute/src/lib.rs2
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs2
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs3
460 files changed, 4727 insertions, 4442 deletions
diff --git a/compiler/rustc_abi/src/extern_abi/mod.rs b/compiler/rustc_abi/src/extern_abi/mod.rs
index f7e41280131..390f2dbc10f 100644
--- a/compiler/rustc_abi/src/extern_abi/mod.rs
+++ b/compiler/rustc_abi/src/extern_abi/mod.rs
@@ -1,8 +1,7 @@
 use std::fmt;
 
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
-use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 #[cfg(test)]
 mod tests;
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index f0099fa8adc..69c3e0553d4 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -19,7 +19,7 @@
 //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
 
 use std::borrow::Cow;
-use std::{cmp, fmt, mem};
+use std::{cmp, fmt};
 
 pub use GenericArgs::*;
 pub use UnsafeSource::*;
@@ -31,8 +31,7 @@ use rustc_data_structures::sync::Lrc;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 pub use rustc_span::AttrId;
 use rustc_span::source_map::{Spanned, respan};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
+use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 pub use crate::format::*;
@@ -859,6 +858,8 @@ pub enum PatKind {
 pub enum PatFieldsRest {
     /// `module::StructName { field, ..}`
     Rest,
+    /// `module::StructName { field, syntax error }`
+    Recovered(ErrorGuaranteed),
     /// `module::StructName { field }`
     None,
 }
@@ -1758,53 +1759,16 @@ pub enum AttrArgs {
     Eq {
         /// Span of the `=` token.
         eq_span: Span,
-
-        value: AttrArgsEq,
+        expr: P<Expr>,
     },
 }
 
-// The RHS of an `AttrArgs::Eq` starts out as an expression. Once macro
-// expansion is completed, all cases end up either as a meta item literal,
-// which is the form used after lowering to HIR, or as an error.
-#[derive(Clone, Encodable, Decodable, Debug)]
-pub enum AttrArgsEq {
-    Ast(P<Expr>),
-    Hir(MetaItemLit),
-}
-
-impl AttrArgsEq {
-    pub fn span(&self) -> Span {
-        match self {
-            AttrArgsEq::Ast(p) => p.span,
-            AttrArgsEq::Hir(lit) => lit.span,
-        }
-    }
-
-    pub fn unwrap_ast(&self) -> &Expr {
-        match self {
-            AttrArgsEq::Ast(p) => p,
-            AttrArgsEq::Hir(lit) => {
-                unreachable!("in literal form when getting inner tokens: {lit:?}")
-            }
-        }
-    }
-
-    pub fn unwrap_ast_mut(&mut self) -> &mut P<Expr> {
-        match self {
-            AttrArgsEq::Ast(p) => p,
-            AttrArgsEq::Hir(lit) => {
-                unreachable!("in literal form when getting inner tokens: {lit:?}")
-            }
-        }
-    }
-}
-
 impl AttrArgs {
     pub fn span(&self) -> Option<Span> {
         match self {
             AttrArgs::Empty => None,
             AttrArgs::Delimited(args) => Some(args.dspan.entire()),
-            AttrArgs::Eq { eq_span, value } => Some(eq_span.to(value.span())),
+            AttrArgs::Eq { eq_span, expr } => Some(eq_span.to(expr.span)),
         }
     }
 
@@ -1814,27 +1778,7 @@ impl AttrArgs {
         match self {
             AttrArgs::Empty => TokenStream::default(),
             AttrArgs::Delimited(args) => args.tokens.clone(),
-            AttrArgs::Eq { value, .. } => TokenStream::from_ast(value.unwrap_ast()),
-        }
-    }
-}
-
-impl<CTX> HashStable<CTX> for AttrArgs
-where
-    CTX: crate::HashStableContext,
-{
-    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
-        mem::discriminant(self).hash_stable(ctx, hasher);
-        match self {
-            AttrArgs::Empty => {}
-            AttrArgs::Delimited(args) => args.hash_stable(ctx, hasher),
-            AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => {
-                unreachable!("hash_stable {:?}", expr);
-            }
-            AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) } => {
-                eq_span.hash_stable(ctx, hasher);
-                lit.hash_stable(ctx, hasher);
-            }
+            AttrArgs::Eq { expr, .. } => TokenStream::from_ast(expr),
         }
     }
 }
@@ -3051,7 +2995,7 @@ impl NormalAttr {
     }
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Encodable, Decodable, Debug)]
 pub struct AttrItem {
     pub unsafety: Safety,
     pub path: Path,
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 0d79cadef34..4ce1d4882ef 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -1,18 +1,18 @@
 //! Functions dealing with attributes and meta items.
 
+use std::fmt::Debug;
 use std::iter;
 use std::sync::atomic::{AtomicU32, Ordering};
 
 use rustc_index::bit_set::GrowableBitSet;
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 use smallvec::{SmallVec, smallvec};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::ast::{
-    AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID,
-    DelimArgs, Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit,
-    NormalAttr, Path, PathSegment, Safety,
+    AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs,
+    Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
+    PathSegment, Safety,
 };
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Delimiter, Token};
@@ -66,11 +66,27 @@ impl Attribute {
             AttrKind::DocComment(..) => panic!("unexpected doc comment"),
         }
     }
+}
+
+impl AttributeExt for Attribute {
+    fn id(&self) -> AttrId {
+        self.id
+    }
+
+    fn value_span(&self) -> Option<Span> {
+        match &self.kind {
+            AttrKind::Normal(normal) => match &normal.item.args {
+                AttrArgs::Eq { expr, .. } => Some(expr.span),
+                _ => None,
+            },
+            AttrKind::DocComment(..) => None,
+        }
+    }
 
     /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
     /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
     /// a doc comment) will return `false`.
-    pub fn is_doc_comment(&self) -> bool {
+    fn is_doc_comment(&self) -> bool {
         match self.kind {
             AttrKind::Normal(..) => false,
             AttrKind::DocComment(..) => true,
@@ -78,7 +94,7 @@ impl Attribute {
     }
 
     /// For a single-segment attribute, returns its name; otherwise, returns `None`.
-    pub fn ident(&self) -> Option<Ident> {
+    fn ident(&self) -> Option<Ident> {
         match &self.kind {
             AttrKind::Normal(normal) => {
                 if let [ident] = &*normal.item.path.segments {
@@ -91,28 +107,14 @@ impl Attribute {
         }
     }
 
-    pub fn name_or_empty(&self) -> Symbol {
-        self.ident().unwrap_or_else(Ident::empty).name
-    }
-
-    pub fn path(&self) -> SmallVec<[Symbol; 1]> {
-        match &self.kind {
-            AttrKind::Normal(normal) => {
-                normal.item.path.segments.iter().map(|s| s.ident.name).collect()
-            }
-            AttrKind::DocComment(..) => smallvec![sym::doc],
-        }
-    }
-
-    #[inline]
-    pub fn has_name(&self, name: Symbol) -> bool {
+    fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
         match &self.kind {
-            AttrKind::Normal(normal) => normal.item.path == name,
-            AttrKind::DocComment(..) => false,
+            AttrKind::Normal(p) => Some(p.item.path.segments.iter().map(|i| i.ident).collect()),
+            AttrKind::DocComment(_, _) => None,
         }
     }
 
-    pub fn path_matches(&self, name: &[Symbol]) -> bool {
+    fn path_matches(&self, name: &[Symbol]) -> bool {
         match &self.kind {
             AttrKind::Normal(normal) => {
                 normal.item.path.segments.len() == name.len()
@@ -128,7 +130,11 @@ impl Attribute {
         }
     }
 
-    pub fn is_word(&self) -> bool {
+    fn span(&self) -> Span {
+        self.span
+    }
+
+    fn is_word(&self) -> bool {
         if let AttrKind::Normal(normal) = &self.kind {
             matches!(normal.item.args, AttrArgs::Empty)
         } else {
@@ -143,7 +149,7 @@ impl Attribute {
     /// #[attr = ""] // Returns `None`.
     /// #[attr] // Returns `None`.
     /// ```
-    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
+    fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
         match &self.kind {
             AttrKind::Normal(normal) => normal.item.meta_item_list(),
             AttrKind::DocComment(..) => None,
@@ -165,7 +171,7 @@ impl Attribute {
     /// ```text
     /// #[attr("value")]
     /// ```
-    pub fn value_str(&self) -> Option<Symbol> {
+    fn value_str(&self) -> Option<Symbol> {
         match &self.kind {
             AttrKind::Normal(normal) => normal.item.value_str(),
             AttrKind::DocComment(..) => None,
@@ -177,7 +183,7 @@ impl Attribute {
     /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
     /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
     /// * `#[doc(...)]` returns `None`.
-    pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
+    fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
         match &self.kind {
             AttrKind::DocComment(kind, data) => Some((*data, *kind)),
             AttrKind::Normal(normal) if normal.item.path == sym::doc => {
@@ -191,7 +197,7 @@ impl Attribute {
     /// * `///doc` returns `Some("doc")`.
     /// * `#[doc = "doc"]` returns `Some("doc")`.
     /// * `#[doc(...)]` returns `None`.
-    pub fn doc_str(&self) -> Option<Symbol> {
+    fn doc_str(&self) -> Option<Symbol> {
         match &self.kind {
             AttrKind::DocComment(.., data) => Some(*data),
             AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
@@ -199,14 +205,14 @@ impl Attribute {
         }
     }
 
-    pub fn may_have_doc_links(&self) -> bool {
-        self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
+    fn style(&self) -> AttrStyle {
+        self.style
     }
+}
 
-    pub fn is_proc_macro_attr(&self) -> bool {
-        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
-            .iter()
-            .any(|kind| self.has_name(*kind))
+impl Attribute {
+    pub fn may_have_doc_links(&self) -> bool {
+        self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
     }
 
     /// Extracts the MetaItem from inside this Attribute.
@@ -268,7 +274,12 @@ impl AttrItem {
     /// ```
     fn value_str(&self) -> Option<Symbol> {
         match &self.args {
-            AttrArgs::Eq { value, .. } => value.value_str(),
+            AttrArgs::Eq { expr, .. } => match expr.kind {
+                ExprKind::Lit(token_lit) => {
+                    LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
+                }
+                _ => None,
+            },
             AttrArgs::Delimited(_) | AttrArgs::Empty => None,
         }
     }
@@ -287,20 +298,6 @@ impl AttrItem {
     }
 }
 
-impl AttrArgsEq {
-    fn value_str(&self) -> Option<Symbol> {
-        match self {
-            AttrArgsEq::Ast(expr) => match expr.kind {
-                ExprKind::Lit(token_lit) => {
-                    LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
-                }
-                _ => None,
-            },
-            AttrArgsEq::Hir(lit) => lit.kind.str(),
-        }
-    }
-}
-
 impl MetaItem {
     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
     pub fn ident(&self) -> Option<Ident> {
@@ -439,7 +436,8 @@ impl MetaItem {
 }
 
 impl MetaItemKind {
-    fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {
+    // public because it can be called in the hir
+    pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {
         let mut tokens = tokens.trees().peekable();
         let mut result = ThinVec::new();
         while tokens.peek().is_some() {
@@ -492,7 +490,7 @@ impl MetaItemKind {
                 MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List)
             }
             AttrArgs::Delimited(..) => None,
-            AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => match expr.kind {
+            AttrArgs::Eq { expr, .. } => match expr.kind {
                 ExprKind::Lit(token_lit) => {
                     // Turn failures to `None`, we'll get parse errors elsewhere.
                     MetaItemLit::from_token_lit(token_lit, expr.span)
@@ -501,9 +499,6 @@ impl MetaItemKind {
                 }
                 _ => None,
             },
-            AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => {
-                Some(MetaItemKind::NameValue(lit.clone()))
-            }
         }
     }
 }
@@ -704,26 +699,175 @@ pub fn mk_attr_name_value_str(
         tokens: None,
     });
     let path = Path::from_ident(Ident::new(name, span));
-    let args = AttrArgs::Eq { eq_span: span, value: AttrArgsEq::Ast(expr) };
+    let args = AttrArgs::Eq { eq_span: span, expr };
     mk_attr(g, style, unsafety, path, args, span)
 }
 
-pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {
+pub fn filter_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> impl Iterator<Item = &A> {
     attrs.iter().filter(move |attr| attr.has_name(name))
 }
 
-pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
+pub fn find_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> Option<&A> {
     filter_by_name(attrs, name).next()
 }
 
-pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {
+pub fn first_attr_value_str_by_name(attrs: &[impl AttributeExt], name: Symbol) -> Option<Symbol> {
     find_by_name(attrs, name).and_then(|attr| attr.value_str())
 }
 
-pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {
+pub fn contains_name(attrs: &[impl AttributeExt], name: Symbol) -> bool {
     find_by_name(attrs, name).is_some()
 }
 
 pub fn list_contains_name(items: &[MetaItemInner], name: Symbol) -> bool {
     items.iter().any(|item| item.has_name(name))
 }
+
+impl MetaItemLit {
+    pub fn value_str(&self) -> Option<Symbol> {
+        LitKind::from_token_lit(self.as_token_lit()).ok().and_then(|lit| lit.str())
+    }
+}
+
+pub trait AttributeExt: Debug {
+    fn id(&self) -> AttrId;
+
+    fn name_or_empty(&self) -> Symbol {
+        self.ident().unwrap_or_else(Ident::empty).name
+    }
+
+    /// Get the meta item list, `#[attr(meta item list)]`
+    fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>;
+
+    /// Gets the value literal, as string, when using `#[attr = value]`
+    fn value_str(&self) -> Option<Symbol>;
+
+    /// Gets the span of the value literal, as string, when using `#[attr = value]`
+    fn value_span(&self) -> Option<Span>;
+
+    /// For a single-segment attribute, returns its name; otherwise, returns `None`.
+    fn ident(&self) -> Option<Ident>;
+
+    /// Checks whether the path of this attribute matches the name.
+    ///
+    /// Matches one segment of the path to each element in `name`
+    fn path_matches(&self, name: &[Symbol]) -> bool;
+
+    /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
+    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
+    /// a doc comment) will return `false`.
+    fn is_doc_comment(&self) -> bool;
+
+    #[inline]
+    fn has_name(&self, name: Symbol) -> bool {
+        self.ident().map(|x| x.name == name).unwrap_or(false)
+    }
+
+    /// get the span of the entire attribute
+    fn span(&self) -> Span;
+
+    fn is_word(&self) -> bool;
+
+    fn path(&self) -> SmallVec<[Symbol; 1]> {
+        self.ident_path()
+            .map(|i| i.into_iter().map(|i| i.name).collect())
+            .unwrap_or(smallvec![sym::doc])
+    }
+
+    /// Returns None for doc comments
+    fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>>;
+
+    /// Returns the documentation if this is a doc comment or a sugared doc comment.
+    /// * `///doc` returns `Some("doc")`.
+    /// * `#[doc = "doc"]` returns `Some("doc")`.
+    /// * `#[doc(...)]` returns `None`.
+    fn doc_str(&self) -> Option<Symbol>;
+
+    fn is_proc_macro_attr(&self) -> bool {
+        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
+            .iter()
+            .any(|kind| self.has_name(*kind))
+    }
+
+    /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
+    /// * `///doc` returns `Some(("doc", CommentKind::Line))`.
+    /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
+    /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
+    /// * `#[doc(...)]` returns `None`.
+    fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
+
+    fn style(&self) -> AttrStyle;
+}
+
+// FIXME(fn_delegation): use function delegation instead of manually forwarding
+
+impl Attribute {
+    pub fn id(&self) -> AttrId {
+        AttributeExt::id(self)
+    }
+
+    pub fn name_or_empty(&self) -> Symbol {
+        AttributeExt::name_or_empty(self)
+    }
+
+    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
+        AttributeExt::meta_item_list(self)
+    }
+
+    pub fn value_str(&self) -> Option<Symbol> {
+        AttributeExt::value_str(self)
+    }
+
+    pub fn value_span(&self) -> Option<Span> {
+        AttributeExt::value_span(self)
+    }
+
+    pub fn ident(&self) -> Option<Ident> {
+        AttributeExt::ident(self)
+    }
+
+    pub fn path_matches(&self, name: &[Symbol]) -> bool {
+        AttributeExt::path_matches(self, name)
+    }
+
+    pub fn is_doc_comment(&self) -> bool {
+        AttributeExt::is_doc_comment(self)
+    }
+
+    #[inline]
+    pub fn has_name(&self, name: Symbol) -> bool {
+        AttributeExt::has_name(self, name)
+    }
+
+    pub fn span(&self) -> Span {
+        AttributeExt::span(self)
+    }
+
+    pub fn is_word(&self) -> bool {
+        AttributeExt::is_word(self)
+    }
+
+    pub fn path(&self) -> SmallVec<[Symbol; 1]> {
+        AttributeExt::path(self)
+    }
+
+    pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
+        AttributeExt::ident_path(self)
+    }
+
+    pub fn doc_str(&self) -> Option<Symbol> {
+        AttributeExt::doc_str(self)
+    }
+
+    pub fn is_proc_macro_attr(&self) -> bool {
+        AttributeExt::is_proc_macro_attr(self)
+    }
+
+    pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
+        AttributeExt::doc_str_and_comment_kind(self)
+    }
+
+    pub fn style(&self) -> AttrStyle {
+        AttributeExt::style(self)
+    }
+}
diff --git a/compiler/rustc_ast/src/entry.rs b/compiler/rustc_ast/src/entry.rs
index 45c4caca6e9..ab1413d6080 100644
--- a/compiler/rustc_ast/src/entry.rs
+++ b/compiler/rustc_ast/src/entry.rs
@@ -1,7 +1,6 @@
-use rustc_span::Symbol;
-use rustc_span::symbol::sym;
+use rustc_span::{Symbol, sym};
 
-use crate::{Attribute, attr};
+use crate::attr::{self, AttributeExt};
 
 #[derive(Debug)]
 pub enum EntryPointType {
@@ -37,7 +36,7 @@ pub enum EntryPointType {
 }
 
 pub fn entry_point_type(
-    attrs: &[Attribute],
+    attrs: &[impl AttributeExt],
     at_root: bool,
     name: Option<Symbol>,
 ) -> EntryPointType {
diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs
index bee7dfb61da..dd8d5ae624a 100644
--- a/compiler/rustc_ast/src/expand/allocator.rs
+++ b/compiler/rustc_ast/src/expand/allocator.rs
@@ -1,5 +1,5 @@
 use rustc_macros::HashStable_Generic;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 #[derive(Clone, Debug, Copy, Eq, PartialEq, HashStable_Generic)]
 pub enum AllocatorKind {
diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs
index d259677e98e..04c81629323 100644
--- a/compiler/rustc_ast/src/expand/mod.rs
+++ b/compiler/rustc_ast/src/expand/mod.rs
@@ -1,8 +1,8 @@
 //! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`.
 
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+use rustc_span::Ident;
 use rustc_span::def_id::DefId;
-use rustc_span::symbol::Ident;
 
 use crate::MetaItem;
 
diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs
index d5900d83e4d..de628f09853 100644
--- a/compiler/rustc_ast/src/format.rs
+++ b/compiler/rustc_ast/src/format.rs
@@ -1,7 +1,6 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_macros::{Decodable, Encodable};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 use crate::Expr;
 use crate::ptr::P;
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index 7730d0b4b78..6372c66050e 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -44,20 +44,10 @@ pub mod token;
 pub mod tokenstream;
 pub mod visit;
 
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-
 pub use self::ast::*;
 pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasTokens};
 
 /// Requirements for a `StableHashingContext` to be used in this crate.
 /// This is a hack to allow using the `HashStable_Generic` derive macro
 /// instead of implementing everything in `rustc_middle`.
-pub trait HashStableContext: rustc_span::HashStableContext {
-    fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher);
-}
-
-impl<AstCtx: crate::HashStableContext> HashStable<AstCtx> for ast::Attribute {
-    fn hash_stable(&self, hcx: &mut AstCtx, hasher: &mut StableHasher) {
-        hcx.hash_attr(self, hasher)
-    }
-}
+pub trait HashStableContext: rustc_span::HashStableContext {}
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index b44e2d9cace..995924c2a29 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -13,9 +13,8 @@ use std::panic;
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::sync::Lrc;
-use rustc_span::Span;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, Span};
 use smallvec::{Array, SmallVec, smallvec};
 use thin_vec::ThinVec;
 
@@ -451,8 +450,8 @@ fn visit_attr_args<T: MutVisitor>(vis: &mut T, args: &mut AttrArgs) {
     match args {
         AttrArgs::Empty => {}
         AttrArgs::Delimited(args) => visit_delim_args(vis, args),
-        AttrArgs::Eq { eq_span, value } => {
-            vis.visit_expr(value.unwrap_ast_mut());
+        AttrArgs::Eq { eq_span, expr } => {
+            vis.visit_expr(expr);
             vis.visit_span(eq_span);
         }
     }
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 678f43e3511..ab82f18133e 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -11,11 +11,10 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::edition::Edition;
+use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym};
 #[allow(clippy::useless_attribute)] // FIXME: following use of `hidden_glob_reexports` incorrectly triggers `useless_attribute` lint.
 #[allow(hidden_glob_reexports)]
-use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::symbol::{kw, sym};
-use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
+use rustc_span::{Ident, Symbol};
 
 use crate::ast;
 use crate::ptr::P;
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
index 498df5a7144..4459cb962e8 100644
--- a/compiler/rustc_ast/src/util/literal.rs
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -5,8 +5,7 @@ use std::{ascii, fmt, str};
 use rustc_lexer::unescape::{
     MixedUnit, Mode, byte_from_char, unescape_byte, unescape_char, unescape_mixed, unescape_unicode,
 };
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, kw, sym};
+use rustc_span::{Span, Symbol, kw, sym};
 use tracing::debug;
 
 use crate::ast::{self, LitKind, MetaItemLit, StrStyle};
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index e88bf27021a..0d8042005a8 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -1,4 +1,4 @@
-use rustc_span::symbol::kw;
+use rustc_span::kw;
 
 use crate::ast::{self, BinOpKind};
 use crate::token::{self, BinOpToken, Token};
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 22db4438e31..c7cc772dabb 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -15,8 +15,7 @@
 
 pub use rustc_ast_ir::visit::VisitorResult;
 pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list};
-use rustc_span::Span;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, Span};
 
 use crate::ast::*;
 use crate::ptr::P;
@@ -1287,7 +1286,7 @@ pub fn walk_attr_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a AttrArgs) -
     match args {
         AttrArgs::Empty => {}
         AttrArgs::Delimited(_args) => {}
-        AttrArgs::Eq { value, .. } => try_visit!(visitor.visit_expr(value.unwrap_ast())),
+        AttrArgs::Eq { expr, .. } => try_visit!(visitor.visit_expr(expr)),
     }
     V::Result::output()
 }
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 569a15b0e07..2f1f1269ece 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -7,8 +7,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_session::parse::feature_err;
-use rustc_span::symbol::kw;
-use rustc_span::{Span, sym};
+use rustc_span::{Span, kw, sym};
 use rustc_target::asm;
 
 use super::LoweringContext;
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs
index 70c94f4019a..758f1dc1c35 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation.rs
@@ -46,8 +46,7 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::DefId;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{Asyncness, ResolverAstLowering};
-use rustc_span::Span;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, Span};
 use rustc_target::spec::abi;
 use {rustc_ast as ast, rustc_hir as hir};
 
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 2564d4e2772..f727691bf47 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -1,8 +1,7 @@
 use rustc_errors::codes::*;
 use rustc_errors::{Diag, DiagArgFromDisplay, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic};
 use rustc_macros::{Diagnostic, Subdiagnostic};
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 #[derive(Diagnostic)]
 #[diag(ast_lowering_generic_type_with_parentheses, code = E0214)]
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 32905806343..d16a3ce390d 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -13,8 +13,7 @@ use rustc_middle::span_bug;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::errors::report_lit_error;
 use rustc_span::source_map::{Spanned, respan};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, DesugaringKind, Span};
+use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 use visit::{Visitor, walk_expr};
 
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index 653116e1fe0..22aa1e6fc20 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -6,8 +6,7 @@ use rustc_ast::*;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_hir as hir;
 use rustc_session::config::FmtDebug;
-use rustc_span::symbol::{Ident, kw};
-use rustc_span::{Span, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, kw, sym};
 
 use super::LoweringContext;
 
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index d63131eacb5..2cf6a2a909b 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -10,8 +10,7 @@ use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::span_bug;
 use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
 use rustc_span::edit_distance::find_best_match_for_name;
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{DesugaringKind, Span, Symbol};
+use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym};
 use rustc_target::spec::abi;
 use smallvec::{SmallVec, smallvec};
 use thin_vec::ThinVec;
@@ -176,7 +175,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         id: NodeId,
         hir_id: hir::HirId,
         ident: &mut Ident,
-        attrs: &'hir [Attribute],
+        attrs: &'hir [hir::Attribute],
         vis_span: Span,
         i: &ItemKind,
     ) -> hir::ItemKind<'hir> {
@@ -467,7 +466,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         id: NodeId,
         vis_span: Span,
         ident: &mut Ident,
-        attrs: &'hir [Attribute],
+        attrs: &'hir [hir::Attribute],
     ) -> hir::ItemKind<'hir> {
         let path = &tree.prefix;
         let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
@@ -1172,9 +1171,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // we can keep the same name for the parameter.
                 // This lets rustdoc render it correctly in documentation.
                 hir::PatKind::Binding(_, _, ident, _) => (ident, false),
-                hir::PatKind::Wild => {
-                    (Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false)
-                }
+                hir::PatKind::Wild => (Ident::with_dummy_span(rustc_span::kw::Underscore), false),
                 _ => {
                     // Replace the ident for bindings that aren't simple.
                     let name = format!("__arg{index}");
@@ -1392,7 +1389,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
-    pub(super) fn lower_safety(&mut self, s: Safety, default: hir::Safety) -> hir::Safety {
+    pub(super) fn lower_safety(&self, s: Safety, default: hir::Safety) -> hir::Safety {
         match s {
             Safety::Unsafe(_) => hir::Safety::Unsafe,
             Safety::Default => default,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index e4600b0f636..8438a421226 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -41,7 +41,6 @@
 // tidy-alphabetical-end
 
 use rustc_ast::node_id::NodeMap;
-use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, *};
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fingerprint::Fingerprint;
@@ -59,8 +58,7 @@ use rustc_macros::extension;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
 use rustc_session::parse::{add_feature_diagnostics, feature_err};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, DesugaringKind, Span};
+use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
 use smallvec::{SmallVec, smallvec};
 use thin_vec::ThinVec;
 use tracing::{debug, instrument, trace};
@@ -96,7 +94,7 @@ struct LoweringContext<'a, 'hir> {
     /// Bodies inside the owner being lowered.
     bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>,
     /// Attributes inside the owner being lowered.
-    attrs: SortedMap<hir::ItemLocalId, &'hir [Attribute]>,
+    attrs: SortedMap<hir::ItemLocalId, &'hir [hir::Attribute]>,
     /// Collect items that were created by lowering the current owner.
     children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>,
 
@@ -847,7 +845,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         ret
     }
 
-    fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [Attribute] {
+    fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [hir::Attribute] {
         if attrs.is_empty() {
             &[]
         } else {
@@ -859,25 +857,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
     }
 
-    fn lower_attr(&self, attr: &Attribute) -> Attribute {
+    fn lower_attr(&self, attr: &Attribute) -> hir::Attribute {
         // Note that we explicitly do not walk the path. Since we don't really
         // lower attributes (we use the AST version) there is nowhere to keep
         // the `HirId`s. We don't actually need HIR version of attributes anyway.
         // Tokens are also not needed after macro expansion and parsing.
         let kind = match attr.kind {
-            AttrKind::Normal(ref normal) => AttrKind::Normal(P(NormalAttr {
-                item: AttrItem {
-                    unsafety: normal.item.unsafety,
-                    path: normal.item.path.clone(),
-                    args: self.lower_attr_args(&normal.item.args),
-                    tokens: None,
+            AttrKind::Normal(ref normal) => hir::AttrKind::Normal(Box::new(hir::AttrItem {
+                unsafety: self.lower_safety(normal.item.unsafety, hir::Safety::Safe),
+                path: hir::AttrPath {
+                    segments: normal
+                        .item
+                        .path
+                        .segments
+                        .iter()
+                        .map(|i| i.ident)
+                        .collect::<Vec<_>>()
+                        .into_boxed_slice(),
+                    span: normal.item.path.span,
                 },
-                tokens: None,
+                args: self.lower_attr_args(&normal.item.args),
             })),
-            AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
+            AttrKind::DocComment(comment_kind, data) => {
+                hir::AttrKind::DocComment(comment_kind, data)
+            }
         };
 
-        Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) }
+        hir::Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) }
     }
 
     fn alias_attrs(&mut self, id: HirId, target_id: HirId) {
@@ -889,15 +895,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
     }
 
-    fn lower_attr_args(&self, args: &AttrArgs) -> AttrArgs {
+    fn lower_attr_args(&self, args: &AttrArgs) -> hir::AttrArgs {
         match args {
-            AttrArgs::Empty => AttrArgs::Empty,
-            AttrArgs::Delimited(args) => AttrArgs::Delimited(self.lower_delim_args(args)),
+            AttrArgs::Empty => hir::AttrArgs::Empty,
+            AttrArgs::Delimited(args) => hir::AttrArgs::Delimited(self.lower_delim_args(args)),
             // This is an inert key-value attribute - it will never be visible to macros
             // after it gets lowered to HIR. Therefore, we can extract literals to handle
             // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
-            &AttrArgs::Eq { eq_span, ref value } => {
-                let expr = value.unwrap_ast();
+            &AttrArgs::Eq { eq_span, ref expr } => {
                 // In valid code the value always ends up as a single literal. Otherwise, a dummy
                 // literal suffices because the error is handled elsewhere.
                 let lit = if let ExprKind::Lit(token_lit) = expr.kind
@@ -913,7 +918,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         span: DUMMY_SP,
                     }
                 };
-                AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) }
+                hir::AttrArgs::Eq { eq_span, expr: lit }
             }
         }
     }
@@ -2201,7 +2206,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn stmt_let_pat(
         &mut self,
-        attrs: Option<&'hir [Attribute]>,
+        attrs: Option<&'hir [hir::Attribute]>,
         span: Span,
         init: Option<&'hir hir::Expr<'hir>>,
         pat: &'hir hir::Pat<'hir>,
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index c4bae084a3f..a4ab2561b72 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -3,9 +3,8 @@ use rustc_ast::*;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
-use rustc_span::Span;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, Span};
 
 use super::errors::{
     ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
@@ -92,7 +91,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 span: self.lower_span(f.span),
                             }
                         }));
-                        break hir::PatKind::Struct(qpath, fs, *etc == ast::PatFieldsRest::Rest);
+                        break hir::PatKind::Struct(
+                            qpath,
+                            fs,
+                            matches!(
+                                etc,
+                                ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_)
+                            ),
+                        );
                     }
                     PatKind::Tuple(pats) => {
                         let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 133793e26ea..043144a5464 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -6,8 +6,7 @@ use rustc_hir::def::{DefKind, PartialRes, Res};
 use rustc_hir::def_id::DefId;
 use rustc_middle::span_bug;
 use rustc_session::parse::add_feature_diagnostics;
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span, Symbol};
+use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
 use smallvec::{SmallVec, smallvec};
 use tracing::{debug, instrument};
 
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
index eace5ce8208..8046765647e 100644
--- a/compiler/rustc_ast_passes/Cargo.toml
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
 itertools = "0.12"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 290c2e52970..d1cf9c53d66 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -34,8 +34,7 @@ use rustc_session::lint::builtin::{
     PATTERNS_IN_FNS_WITHOUT_BODY,
 };
 use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, kw, sym};
+use rustc_span::{Ident, Span, kw, sym};
 use rustc_target::spec::abi;
 use thin_vec::thin_vec;
 
@@ -342,7 +341,7 @@ impl<'a> AstValidator<'a> {
                     sym::forbid,
                     sym::warn,
                 ];
-                !arr.contains(&attr.name_or_empty()) && rustc_attr::is_builtin_attr(attr)
+                !arr.contains(&attr.name_or_empty()) && rustc_attr_parsing::is_builtin_attr(*attr)
             })
             .for_each(|attr| {
                 if attr.is_doc_comment() {
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 9b600e3ee92..0eb2043eaa3 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -4,8 +4,7 @@ use rustc_ast::ParamKindOrd;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic};
 use rustc_macros::{Diagnostic, Subdiagnostic};
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 use crate::fluent_generated as fluent;
 
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index c10b3296497..f885b20c761 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -4,9 +4,8 @@ use rustc_ast::{NodeId, PatKind, attr, token};
 use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue};
 use rustc_session::Session;
 use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
-use rustc_span::Span;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use rustc_target::spec::abi;
 use thin_vec::ThinVec;
 
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 04ffa2cffe3..70b72e88d7f 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -17,15 +17,15 @@ use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
 use rustc_ast::util::classify;
 use rustc_ast::util::comments::{Comment, CommentStyle};
 use rustc_ast::{
-    self as ast, AttrArgs, AttrArgsEq, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg,
-    GenericBound, InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass,
-    InlineAsmTemplatePiece, PatKind, RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr,
+    self as ast, AttrArgs, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg, GenericBound,
+    InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, PatKind,
+    RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr,
 };
 use rustc_data_structures::sync::Lrc;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::{SourceMap, Spanned};
-use rustc_span::symbol::{Ident, IdentPrinter, Symbol, kw, sym};
-use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Pos, Span};
+use rustc_span::symbol::IdentPrinter;
+use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Ident, Pos, Span, Symbol, kw, sym};
 use thin_vec::ThinVec;
 
 use crate::pp::Breaks::{Consistent, Inconsistent};
@@ -359,7 +359,7 @@ fn binop_to_string(op: BinOpToken) -> &'static str {
     }
 }
 
-fn doc_comment_to_string(
+pub fn doc_comment_to_string(
     comment_kind: CommentKind,
     attr_style: ast::AttrStyle,
     data: Symbol,
@@ -648,20 +648,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
             AttrArgs::Empty => {
                 self.print_path(&item.path, false, 0);
             }
-            AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => {
+            AttrArgs::Eq { expr, .. } => {
                 self.print_path(&item.path, false, 0);
                 self.space();
                 self.word_space("=");
                 let token_str = self.expr_to_string(expr);
                 self.word(token_str);
             }
-            AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => {
-                self.print_path(&item.path, false, 0);
-                self.space();
-                self.word_space("=");
-                let token_str = self.meta_item_lit_to_string(lit);
-                self.word(token_str);
-            }
         }
         match item.unsafety {
             ast::Safety::Unsafe(_) => self.pclose(),
@@ -1661,11 +1654,14 @@ impl<'a> State<'a> {
                     },
                     |f| f.pat.span,
                 );
-                if *etc == ast::PatFieldsRest::Rest {
+                if let ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) = etc {
                     if !fields.is_empty() {
                         self.word_space(",");
                     }
                     self.word("..");
+                    if let ast::PatFieldsRest::Recovered(_) = etc {
+                        self.word("/* recovered parse error */");
+                    }
                 }
                 if !empty {
                     self.space();
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 1ae765c0130..897c275d850 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -3,7 +3,7 @@ use itertools::{Itertools, Position};
 use rustc_ast as ast;
 use rustc_ast::ModKind;
 use rustc_ast::ptr::P;
-use rustc_span::symbol::Ident;
+use rustc_span::Ident;
 
 use crate::pp::Breaks::Inconsistent;
 use crate::pprust::state::fixup::FixupContext;
diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs
index 01e5dff34b7..4c42dd1f202 100644
--- a/compiler/rustc_ast_pretty/src/pprust/tests.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs
@@ -1,6 +1,5 @@
 use rustc_ast as ast;
-use rustc_span::symbol::Ident;
-use rustc_span::{DUMMY_SP, create_default_session_globals_then};
+use rustc_span::{DUMMY_SP, Ident, create_default_session_globals_then};
 use thin_vec::ThinVec;
 
 use super::*;
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
deleted file mode 100644
index 94f9727eb7f..00000000000
--- a/compiler/rustc_attr/src/builtin.rs
+++ /dev/null
@@ -1,1349 +0,0 @@
-//! Parsing and validation of builtin attributes
-
-use std::num::NonZero;
-
-use rustc_abi::Align;
-use rustc_ast::{
-    self as ast, Attribute, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId,
-    attr,
-};
-use rustc_ast_pretty::pprust;
-use rustc_errors::ErrorGuaranteed;
-use rustc_feature::{Features, GatedCfg, find_gated_cfg, is_builtin_attr_name};
-use rustc_macros::{Decodable, Encodable, HashStable_Generic};
-use rustc_session::config::ExpectedValues;
-use rustc_session::lint::BuiltinLintDiag;
-use rustc_session::lint::builtin::UNEXPECTED_CFGS;
-use rustc_session::parse::feature_err;
-use rustc_session::{RustcVersion, Session};
-use rustc_span::Span;
-use rustc_span::hygiene::Transparency;
-use rustc_span::symbol::{Symbol, kw, sym};
-
-use crate::fluent_generated;
-use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
-
-/// The version placeholder that recently stabilized features contain inside the
-/// `since` field of the `#[stable]` attribute.
-///
-/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591).
-pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
-
-pub fn is_builtin_attr(attr: &Attribute) -> bool {
-    attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
-}
-
-pub(crate) enum UnsupportedLiteralReason {
-    Generic,
-    CfgString,
-    CfgBoolean,
-    DeprecatedString,
-    DeprecatedKvPair,
-}
-
-#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
-pub enum InlineAttr {
-    None,
-    Hint,
-    Always,
-    Never,
-}
-
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)]
-pub enum InstructionSetAttr {
-    ArmA32,
-    ArmT32,
-}
-
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
-pub enum OptimizeAttr {
-    None,
-    Speed,
-    Size,
-}
-
-/// Represents the following attributes:
-///
-/// - `#[stable]`
-/// - `#[unstable]`
-#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(HashStable_Generic)]
-pub struct Stability {
-    pub level: StabilityLevel,
-    pub feature: Symbol,
-}
-
-impl Stability {
-    pub fn is_unstable(&self) -> bool {
-        self.level.is_unstable()
-    }
-
-    pub fn is_stable(&self) -> bool {
-        self.level.is_stable()
-    }
-
-    pub fn stable_since(&self) -> Option<StableSince> {
-        self.level.stable_since()
-    }
-}
-
-/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
-#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(HashStable_Generic)]
-pub struct ConstStability {
-    pub level: StabilityLevel,
-    pub feature: Symbol,
-    /// This is true iff the `const_stable_indirect` attribute is present.
-    pub const_stable_indirect: bool,
-    /// whether the function has a `#[rustc_promotable]` attribute
-    pub promotable: bool,
-}
-
-impl ConstStability {
-    pub fn is_const_unstable(&self) -> bool {
-        self.level.is_unstable()
-    }
-
-    pub fn is_const_stable(&self) -> bool {
-        self.level.is_stable()
-    }
-}
-
-/// Represents the `#[rustc_default_body_unstable]` attribute.
-#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(HashStable_Generic)]
-pub struct DefaultBodyStability {
-    pub level: StabilityLevel,
-    pub feature: Symbol,
-}
-
-/// The available stability levels.
-#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
-#[derive(HashStable_Generic)]
-pub enum StabilityLevel {
-    /// `#[unstable]`
-    Unstable {
-        /// Reason for the current stability level.
-        reason: UnstableReason,
-        /// Relevant `rust-lang/rust` issue.
-        issue: Option<NonZero<u32>>,
-        is_soft: bool,
-        /// If part of a feature is stabilized and a new feature is added for the remaining parts,
-        /// then the `implied_by` attribute is used to indicate which now-stable feature previously
-        /// contained an item.
-        ///
-        /// ```pseudo-Rust
-        /// #[unstable(feature = "foo", issue = "...")]
-        /// fn foo() {}
-        /// #[unstable(feature = "foo", issue = "...")]
-        /// fn foobar() {}
-        /// ```
-        ///
-        /// ...becomes...
-        ///
-        /// ```pseudo-Rust
-        /// #[stable(feature = "foo", since = "1.XX.X")]
-        /// fn foo() {}
-        /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")]
-        /// fn foobar() {}
-        /// ```
-        implied_by: Option<Symbol>,
-    },
-    /// `#[stable]`
-    Stable {
-        /// Rust release which stabilized this feature.
-        since: StableSince,
-        /// Is this item allowed to be referred to on stable, despite being contained in unstable
-        /// modules?
-        allowed_through_unstable_modules: bool,
-    },
-}
-
-/// Rust release in which a feature is stabilized.
-#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)]
-#[derive(HashStable_Generic)]
-pub enum StableSince {
-    Version(RustcVersion),
-    /// Stabilized in the upcoming version, whatever number that is.
-    Current,
-    /// Failed to parse a stabilization version.
-    Err,
-}
-
-impl StabilityLevel {
-    pub fn is_unstable(&self) -> bool {
-        matches!(self, StabilityLevel::Unstable { .. })
-    }
-    pub fn is_stable(&self) -> bool {
-        matches!(self, StabilityLevel::Stable { .. })
-    }
-    pub fn stable_since(&self) -> Option<StableSince> {
-        match *self {
-            StabilityLevel::Stable { since, .. } => Some(since),
-            StabilityLevel::Unstable { .. } => None,
-        }
-    }
-}
-
-#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
-#[derive(HashStable_Generic)]
-pub enum UnstableReason {
-    None,
-    Default,
-    Some(Symbol),
-}
-
-impl UnstableReason {
-    fn from_opt_reason(reason: Option<Symbol>) -> Self {
-        // UnstableReason::Default constructed manually
-        match reason {
-            Some(r) => Self::Some(r),
-            None => Self::None,
-        }
-    }
-
-    pub fn to_opt_reason(&self) -> Option<Symbol> {
-        match self {
-            Self::None => None,
-            Self::Default => Some(sym::unstable_location_reason_default),
-            Self::Some(r) => Some(*r),
-        }
-    }
-}
-
-/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules`
-/// attributes in `attrs`. Returns `None` if no stability attributes are found.
-pub fn find_stability(
-    sess: &Session,
-    attrs: &[Attribute],
-    item_sp: Span,
-) -> Option<(Stability, Span)> {
-    let mut stab: Option<(Stability, Span)> = None;
-    let mut allowed_through_unstable_modules = false;
-
-    for attr in attrs {
-        match attr.name_or_empty() {
-            sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true,
-            sym::unstable => {
-                if stab.is_some() {
-                    sess.dcx()
-                        .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
-                    break;
-                }
-
-                if let Some((feature, level)) = parse_unstability(sess, attr) {
-                    stab = Some((Stability { level, feature }, attr.span));
-                }
-            }
-            sym::stable => {
-                if stab.is_some() {
-                    sess.dcx()
-                        .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
-                    break;
-                }
-                if let Some((feature, level)) = parse_stability(sess, attr) {
-                    stab = Some((Stability { level, feature }, attr.span));
-                }
-            }
-            _ => {}
-        }
-    }
-
-    if allowed_through_unstable_modules {
-        match &mut stab {
-            Some((
-                Stability {
-                    level: StabilityLevel::Stable { allowed_through_unstable_modules, .. },
-                    ..
-                },
-                _,
-            )) => *allowed_through_unstable_modules = true,
-            _ => {
-                sess.dcx()
-                    .emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
-            }
-        }
-    }
-
-    stab
-}
-
-/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
-/// attributes in `attrs`. Returns `None` if no stability attributes are found.
-pub fn find_const_stability(
-    sess: &Session,
-    attrs: &[Attribute],
-    item_sp: Span,
-) -> Option<(ConstStability, Span)> {
-    let mut const_stab: Option<(ConstStability, Span)> = None;
-    let mut promotable = false;
-    let mut const_stable_indirect = false;
-
-    for attr in attrs {
-        match attr.name_or_empty() {
-            sym::rustc_promotable => promotable = true,
-            sym::rustc_const_stable_indirect => const_stable_indirect = true,
-            sym::rustc_const_unstable => {
-                if const_stab.is_some() {
-                    sess.dcx()
-                        .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
-                    break;
-                }
-
-                if let Some((feature, level)) = parse_unstability(sess, attr) {
-                    const_stab = Some((
-                        ConstStability {
-                            level,
-                            feature,
-                            const_stable_indirect: false,
-                            promotable: false,
-                        },
-                        attr.span,
-                    ));
-                }
-            }
-            sym::rustc_const_stable => {
-                if const_stab.is_some() {
-                    sess.dcx()
-                        .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
-                    break;
-                }
-                if let Some((feature, level)) = parse_stability(sess, attr) {
-                    const_stab = Some((
-                        ConstStability {
-                            level,
-                            feature,
-                            const_stable_indirect: false,
-                            promotable: false,
-                        },
-                        attr.span,
-                    ));
-                }
-            }
-            _ => {}
-        }
-    }
-
-    // Merge promotable and const_stable_indirect into stability info
-    if promotable {
-        match &mut const_stab {
-            Some((stab, _)) => stab.promotable = promotable,
-            _ => {
-                _ = sess
-                    .dcx()
-                    .emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp })
-            }
-        }
-    }
-    if const_stable_indirect {
-        match &mut const_stab {
-            Some((stab, _)) => {
-                if stab.is_const_unstable() {
-                    stab.const_stable_indirect = true;
-                } else {
-                    _ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing {
-                        span: item_sp,
-                    })
-                }
-            }
-            _ => {
-                // This function has no const stability attribute, but has `const_stable_indirect`.
-                // We ignore that; unmarked functions are subject to recursive const stability
-                // checks by default so we do carry out the user's intent.
-            }
-        }
-    }
-
-    const_stab
-}
-
-/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate
-/// without the `staged_api` feature.
-pub fn unmarked_crate_const_stab(
-    _sess: &Session,
-    attrs: &[Attribute],
-    regular_stab: Stability,
-) -> ConstStability {
-    assert!(regular_stab.level.is_unstable());
-    // The only attribute that matters here is `rustc_const_stable_indirect`.
-    // We enforce recursive const stability rules for those functions.
-    let const_stable_indirect =
-        attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect);
-    ConstStability {
-        feature: regular_stab.feature,
-        const_stable_indirect,
-        promotable: false,
-        level: regular_stab.level,
-    }
-}
-
-/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
-/// Returns `None` if no stability attributes are found.
-pub fn find_body_stability(
-    sess: &Session,
-    attrs: &[Attribute],
-) -> Option<(DefaultBodyStability, Span)> {
-    let mut body_stab: Option<(DefaultBodyStability, Span)> = None;
-
-    for attr in attrs {
-        if attr.has_name(sym::rustc_default_body_unstable) {
-            if body_stab.is_some() {
-                sess.dcx()
-                    .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
-                break;
-            }
-
-            if let Some((feature, level)) = parse_unstability(sess, attr) {
-                body_stab = Some((DefaultBodyStability { level, feature }, attr.span));
-            }
-        }
-    }
-
-    body_stab
-}
-
-fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> {
-    if item.is_some() {
-        sess.dcx().emit_err(session_diagnostics::MultipleItem {
-            span: meta.span,
-            item: pprust::path_to_string(&meta.path),
-        });
-        None
-    } else if let Some(v) = meta.value_str() {
-        *item = Some(v);
-        Some(())
-    } else {
-        sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
-        None
-    }
-}
-
-/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
-/// its stability information.
-fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> {
-    let meta = attr.meta()?;
-    let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None };
-
-    let mut feature = None;
-    let mut since = None;
-    for meta in metas {
-        let Some(mi) = meta.meta_item() else {
-            sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
-                span: meta.span(),
-                reason: UnsupportedLiteralReason::Generic,
-                is_bytestr: false,
-                start_point_span: sess.source_map().start_point(meta.span()),
-            });
-            return None;
-        };
-
-        match mi.name_or_empty() {
-            sym::feature => insert_or_error(sess, mi, &mut feature)?,
-            sym::since => insert_or_error(sess, mi, &mut since)?,
-            _ => {
-                sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
-                    span: meta.span(),
-                    item: pprust::path_to_string(&mi.path),
-                    expected: &["feature", "since"],
-                });
-                return None;
-            }
-        }
-    }
-
-    let feature = match feature {
-        Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
-        Some(_bad_feature) => {
-            Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span }))
-        }
-        None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span })),
-    };
-
-    let since = if let Some(since) = since {
-        if since.as_str() == VERSION_PLACEHOLDER {
-            StableSince::Current
-        } else if let Some(version) = parse_version(since) {
-            StableSince::Version(version)
-        } else {
-            sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span });
-            StableSince::Err
-        }
-    } else {
-        sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span });
-        StableSince::Err
-    };
-
-    match feature {
-        Ok(feature) => {
-            let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
-            Some((feature, level))
-        }
-        Err(ErrorGuaranteed { .. }) => None,
-    }
-}
-
-/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
-/// attribute, and return the feature name and its stability information.
-fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> {
-    let meta = attr.meta()?;
-    let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None };
-
-    let mut feature = None;
-    let mut reason = None;
-    let mut issue = None;
-    let mut issue_num = None;
-    let mut is_soft = false;
-    let mut implied_by = None;
-    for meta in metas {
-        let Some(mi) = meta.meta_item() else {
-            sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
-                span: meta.span(),
-                reason: UnsupportedLiteralReason::Generic,
-                is_bytestr: false,
-                start_point_span: sess.source_map().start_point(meta.span()),
-            });
-            return None;
-        };
-
-        match mi.name_or_empty() {
-            sym::feature => insert_or_error(sess, mi, &mut feature)?,
-            sym::reason => insert_or_error(sess, mi, &mut reason)?,
-            sym::issue => {
-                insert_or_error(sess, mi, &mut issue)?;
-
-                // These unwraps are safe because `insert_or_error` ensures the meta item
-                // is a name/value pair string literal.
-                issue_num = match issue.unwrap().as_str() {
-                    "none" => None,
-                    issue => match issue.parse::<NonZero<u32>>() {
-                        Ok(num) => Some(num),
-                        Err(err) => {
-                            sess.dcx().emit_err(
-                                session_diagnostics::InvalidIssueString {
-                                    span: mi.span,
-                                    cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
-                                        mi.name_value_literal_span().unwrap(),
-                                        err.kind(),
-                                    ),
-                                },
-                            );
-                            return None;
-                        }
-                    },
-                };
-            }
-            sym::soft => {
-                if !mi.is_word() {
-                    sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span });
-                }
-                is_soft = true;
-            }
-            sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?,
-            _ => {
-                sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
-                    span: meta.span(),
-                    item: pprust::path_to_string(&mi.path),
-                    expected: &["feature", "reason", "issue", "soft", "implied_by"],
-                });
-                return None;
-            }
-        }
-    }
-
-    let feature = match feature {
-        Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
-        Some(_bad_feature) => {
-            Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span }))
-        }
-        None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span })),
-    };
-
-    let issue = issue
-        .ok_or_else(|| sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span }));
-
-    match (feature, issue) {
-        (Ok(feature), Ok(_)) => {
-            let level = StabilityLevel::Unstable {
-                reason: UnstableReason::from_opt_reason(reason),
-                issue: issue_num,
-                is_soft,
-                implied_by,
-            };
-            Some((feature, level))
-        }
-        (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
-    }
-}
-
-pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
-    attr::first_attr_value_str_by_name(attrs, sym::crate_name)
-}
-
-#[derive(Clone, Debug)]
-pub struct Condition {
-    pub name: Symbol,
-    pub name_span: Span,
-    pub value: Option<Symbol>,
-    pub value_span: Option<Span>,
-    pub span: Span,
-}
-
-/// Tests if a cfg-pattern matches the cfg set
-pub fn cfg_matches(
-    cfg: &ast::MetaItemInner,
-    sess: &Session,
-    lint_node_id: NodeId,
-    features: Option<&Features>,
-) -> bool {
-    eval_condition(cfg, sess, features, &mut |cfg| {
-        try_gate_cfg(cfg.name, cfg.span, sess, features);
-        match sess.psess.check_config.expecteds.get(&cfg.name) {
-            Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
-                sess.psess.buffer_lint(
-                    UNEXPECTED_CFGS,
-                    cfg.span,
-                    lint_node_id,
-                    BuiltinLintDiag::UnexpectedCfgValue(
-                        (cfg.name, cfg.name_span),
-                        cfg.value.map(|v| (v, cfg.value_span.unwrap())),
-                    ),
-                );
-            }
-            None if sess.psess.check_config.exhaustive_names => {
-                sess.psess.buffer_lint(
-                    UNEXPECTED_CFGS,
-                    cfg.span,
-                    lint_node_id,
-                    BuiltinLintDiag::UnexpectedCfgName(
-                        (cfg.name, cfg.name_span),
-                        cfg.value.map(|v| (v, cfg.value_span.unwrap())),
-                    ),
-                );
-            }
-            _ => { /* not unexpected */ }
-        }
-        sess.psess.config.contains(&(cfg.name, cfg.value))
-    })
-}
-
-fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
-    let gate = find_gated_cfg(|sym| sym == name);
-    if let (Some(feats), Some(gated_cfg)) = (features, gate) {
-        gate_cfg(gated_cfg, span, sess, feats);
-    }
-}
-
-#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
-fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
-    let (cfg, feature, has_feature) = gated_cfg;
-    if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
-        let explain = format!("`cfg({cfg})` is experimental and subject to change");
-        feature_err(sess, *feature, cfg_span, explain).emit();
-    }
-}
-
-/// Parse a rustc version number written inside string literal in an attribute,
-/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
-/// not accepted in this position, unlike when parsing CFG_RELEASE.
-pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
-    let mut components = s.as_str().split('-');
-    let d = components.next()?;
-    if components.next().is_some() {
-        return None;
-    }
-    let mut digits = d.splitn(3, '.');
-    let major = digits.next()?.parse().ok()?;
-    let minor = digits.next()?.parse().ok()?;
-    let patch = digits.next().unwrap_or("0").parse().ok()?;
-    Some(RustcVersion { major, minor, patch })
-}
-
-/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
-/// evaluate individual items.
-pub fn eval_condition(
-    cfg: &ast::MetaItemInner,
-    sess: &Session,
-    features: Option<&Features>,
-    eval: &mut impl FnMut(Condition) -> bool,
-) -> bool {
-    let dcx = sess.dcx();
-
-    let cfg = match cfg {
-        ast::MetaItemInner::MetaItem(meta_item) => meta_item,
-        ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
-            if let Some(features) = features {
-                // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
-                // and `true`, and we want to keep the former working without feature gate
-                gate_cfg(
-                    &(
-                        if *b { kw::True } else { kw::False },
-                        sym::cfg_boolean_literals,
-                        |features: &Features| features.cfg_boolean_literals(),
-                    ),
-                    cfg.span(),
-                    sess,
-                    features,
-                );
-            }
-            return *b;
-        }
-        _ => {
-            dcx.emit_err(session_diagnostics::UnsupportedLiteral {
-                span: cfg.span(),
-                reason: UnsupportedLiteralReason::CfgBoolean,
-                is_bytestr: false,
-                start_point_span: sess.source_map().start_point(cfg.span()),
-            });
-            return false;
-        }
-    };
-
-    match &cfg.kind {
-        ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
-            try_gate_cfg(sym::version, cfg.span, sess, features);
-            let (min_version, span) = match &mis[..] {
-                [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
-                    (sym, span)
-                }
-                [
-                    MetaItemInner::Lit(MetaItemLit { span, .. })
-                    | MetaItemInner::MetaItem(MetaItem { span, .. }),
-                ] => {
-                    dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
-                    return false;
-                }
-                [..] => {
-                    dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
-                        span: cfg.span,
-                    });
-                    return false;
-                }
-            };
-            let Some(min_version) = parse_version(*min_version) else {
-                dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span });
-                return false;
-            };
-
-            // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
-            if sess.psess.assume_incomplete_release {
-                RustcVersion::CURRENT > min_version
-            } else {
-                RustcVersion::CURRENT >= min_version
-            }
-        }
-        ast::MetaItemKind::List(mis) => {
-            for mi in mis.iter() {
-                if mi.meta_item_or_bool().is_none() {
-                    dcx.emit_err(session_diagnostics::UnsupportedLiteral {
-                        span: mi.span(),
-                        reason: UnsupportedLiteralReason::Generic,
-                        is_bytestr: false,
-                        start_point_span: sess.source_map().start_point(mi.span()),
-                    });
-                    return false;
-                }
-            }
-
-            // The unwraps below may look dangerous, but we've already asserted
-            // that they won't fail with the loop above.
-            match cfg.name_or_empty() {
-                sym::any => mis
-                    .iter()
-                    // We don't use any() here, because we want to evaluate all cfg condition
-                    // as eval_condition can (and does) extra checks
-                    .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
-                sym::all => mis
-                    .iter()
-                    // We don't use all() here, because we want to evaluate all cfg condition
-                    // as eval_condition can (and does) extra checks
-                    .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
-                sym::not => {
-                    let [mi] = mis.as_slice() else {
-                        dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
-                        return false;
-                    };
-
-                    !eval_condition(mi, sess, features, eval)
-                }
-                sym::target => {
-                    if let Some(features) = features
-                        && !features.cfg_target_compact()
-                    {
-                        feature_err(
-                            sess,
-                            sym::cfg_target_compact,
-                            cfg.span,
-                            fluent_generated::attr_unstable_cfg_target_compact,
-                        )
-                        .emit();
-                    }
-
-                    mis.iter().fold(true, |res, mi| {
-                        let Some(mut mi) = mi.meta_item().cloned() else {
-                            dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
-                                span: mi.span(),
-                            });
-                            return false;
-                        };
-
-                        if let [seg, ..] = &mut mi.path.segments[..] {
-                            seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
-                        }
-
-                        res & eval_condition(
-                            &ast::MetaItemInner::MetaItem(mi),
-                            sess,
-                            features,
-                            eval,
-                        )
-                    })
-                }
-                _ => {
-                    dcx.emit_err(session_diagnostics::InvalidPredicate {
-                        span: cfg.span,
-                        predicate: pprust::path_to_string(&cfg.path),
-                    });
-                    false
-                }
-            }
-        }
-        ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
-            dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
-            true
-        }
-        MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
-            dcx.emit_err(session_diagnostics::UnsupportedLiteral {
-                span: lit.span,
-                reason: UnsupportedLiteralReason::CfgString,
-                is_bytestr: lit.kind.is_bytestr(),
-                start_point_span: sess.source_map().start_point(lit.span),
-            });
-            true
-        }
-        ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
-            let ident = cfg.ident().expect("multi-segment cfg predicate");
-            eval(Condition {
-                name: ident.name,
-                name_span: ident.span,
-                value: cfg.value_str(),
-                value_span: cfg.name_value_literal_span(),
-                span: cfg.span,
-            })
-        }
-    }
-}
-
-#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
-pub struct Deprecation {
-    pub since: DeprecatedSince,
-    /// The note to issue a reason.
-    pub note: Option<Symbol>,
-    /// A text snippet used to completely replace any use of the deprecated item in an expression.
-    ///
-    /// This is currently unstable.
-    pub suggestion: Option<Symbol>,
-}
-
-/// Release in which an API is deprecated.
-#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
-pub enum DeprecatedSince {
-    RustcVersion(RustcVersion),
-    /// Deprecated in the future ("to be determined").
-    Future,
-    /// `feature(staged_api)` is off. Deprecation versions outside the standard
-    /// library are allowed to be arbitrary strings, for better or worse.
-    NonStandard(Symbol),
-    /// Deprecation version is unspecified but optional.
-    Unspecified,
-    /// Failed to parse a deprecation version, or the deprecation version is
-    /// unspecified and required. An error has already been emitted.
-    Err,
-}
-
-impl Deprecation {
-    /// Whether an item marked with #[deprecated(since = "X")] is currently
-    /// deprecated (i.e., whether X is not greater than the current rustc
-    /// version).
-    pub fn is_in_effect(&self) -> bool {
-        match self.since {
-            DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT,
-            DeprecatedSince::Future => false,
-            // The `since` field doesn't have semantic purpose without `#![staged_api]`.
-            DeprecatedSince::NonStandard(_) => true,
-            // Assume deprecation is in effect if "since" field is absent or invalid.
-            DeprecatedSince::Unspecified | DeprecatedSince::Err => true,
-        }
-    }
-
-    pub fn is_since_rustc_version(&self) -> bool {
-        matches!(self.since, DeprecatedSince::RustcVersion(_))
-    }
-}
-
-/// Finds the deprecation attribute. `None` if none exists.
-pub fn find_deprecation(
-    sess: &Session,
-    features: &Features,
-    attrs: &[Attribute],
-) -> Option<(Deprecation, Span)> {
-    let mut depr: Option<(Deprecation, Span)> = None;
-    let is_rustc = features.staged_api();
-
-    'outer: for attr in attrs {
-        if !attr.has_name(sym::deprecated) {
-            continue;
-        }
-
-        let Some(meta) = attr.meta() else {
-            continue;
-        };
-        let mut since = None;
-        let mut note = None;
-        let mut suggestion = None;
-        match &meta.kind {
-            MetaItemKind::Word => {}
-            MetaItemKind::NameValue(..) => note = meta.value_str(),
-            MetaItemKind::List(list) => {
-                let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
-                    if item.is_some() {
-                        sess.dcx().emit_err(session_diagnostics::MultipleItem {
-                            span: meta.span,
-                            item: pprust::path_to_string(&meta.path),
-                        });
-                        return false;
-                    }
-                    if let Some(v) = meta.value_str() {
-                        *item = Some(v);
-                        true
-                    } else {
-                        if let Some(lit) = meta.name_value_literal() {
-                            sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
-                                span: lit.span,
-                                reason: UnsupportedLiteralReason::DeprecatedString,
-                                is_bytestr: lit.kind.is_bytestr(),
-                                start_point_span: sess.source_map().start_point(lit.span),
-                            });
-                        } else {
-                            sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem {
-                                span: meta.span,
-                            });
-                        }
-
-                        false
-                    }
-                };
-
-                for meta in list {
-                    match meta {
-                        MetaItemInner::MetaItem(mi) => match mi.name_or_empty() {
-                            sym::since => {
-                                if !get(mi, &mut since) {
-                                    continue 'outer;
-                                }
-                            }
-                            sym::note => {
-                                if !get(mi, &mut note) {
-                                    continue 'outer;
-                                }
-                            }
-                            sym::suggestion => {
-                                if !features.deprecated_suggestion() {
-                                    sess.dcx().emit_err(
-                                        session_diagnostics::DeprecatedItemSuggestion {
-                                            span: mi.span,
-                                            is_nightly: sess.is_nightly_build(),
-                                            details: (),
-                                        },
-                                    );
-                                }
-
-                                if !get(mi, &mut suggestion) {
-                                    continue 'outer;
-                                }
-                            }
-                            _ => {
-                                sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
-                                    span: meta.span(),
-                                    item: pprust::path_to_string(&mi.path),
-                                    expected: if features.deprecated_suggestion() {
-                                        &["since", "note", "suggestion"]
-                                    } else {
-                                        &["since", "note"]
-                                    },
-                                });
-                                continue 'outer;
-                            }
-                        },
-                        MetaItemInner::Lit(lit) => {
-                            sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
-                                span: lit.span,
-                                reason: UnsupportedLiteralReason::DeprecatedKvPair,
-                                is_bytestr: false,
-                                start_point_span: sess.source_map().start_point(lit.span),
-                            });
-                            continue 'outer;
-                        }
-                    }
-                }
-            }
-        }
-
-        let since = if let Some(since) = since {
-            if since.as_str() == "TBD" {
-                DeprecatedSince::Future
-            } else if !is_rustc {
-                DeprecatedSince::NonStandard(since)
-            } else if let Some(version) = parse_version(since) {
-                DeprecatedSince::RustcVersion(version)
-            } else {
-                sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span });
-                DeprecatedSince::Err
-            }
-        } else if is_rustc {
-            sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span });
-            DeprecatedSince::Err
-        } else {
-            DeprecatedSince::Unspecified
-        };
-
-        if is_rustc && note.is_none() {
-            sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span });
-            continue;
-        }
-
-        depr = Some((Deprecation { since, note, suggestion }, attr.span));
-    }
-
-    depr
-}
-
-#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
-pub enum ReprAttr {
-    ReprInt(IntType),
-    ReprRust,
-    ReprC,
-    ReprPacked(Align),
-    ReprSimd,
-    ReprTransparent,
-    ReprAlign(Align),
-}
-
-#[derive(Eq, PartialEq, Debug, Copy, Clone)]
-#[derive(Encodable, Decodable, HashStable_Generic)]
-pub enum IntType {
-    SignedInt(ast::IntTy),
-    UnsignedInt(ast::UintTy),
-}
-
-impl IntType {
-    #[inline]
-    pub fn is_signed(self) -> bool {
-        use IntType::*;
-
-        match self {
-            SignedInt(..) => true,
-            UnsignedInt(..) => false,
-        }
-    }
-}
-
-/// Parse #[repr(...)] forms.
-///
-/// Valid repr contents: any of the primitive integral type names (see
-/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
-/// the same discriminant size that the corresponding C enum would or C
-/// structure layout, `packed` to remove padding, and `transparent` to delegate representation
-/// concerns to the only non-ZST field.
-pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
-    if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
-}
-
-pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
-    assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
-    use ReprAttr::*;
-    let mut acc = Vec::new();
-    let dcx = sess.dcx();
-
-    if let Some(items) = attr.meta_item_list() {
-        for item in items {
-            let mut recognised = false;
-            if item.is_word() {
-                let hint = match item.name_or_empty() {
-                    sym::Rust => Some(ReprRust),
-                    sym::C => Some(ReprC),
-                    sym::packed => Some(ReprPacked(Align::ONE)),
-                    sym::simd => Some(ReprSimd),
-                    sym::transparent => Some(ReprTransparent),
-                    sym::align => {
-                        sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg {
-                            span: item.span(),
-                        });
-                        recognised = true;
-                        None
-                    }
-                    name => int_type_of_word(name).map(ReprInt),
-                };
-
-                if let Some(h) = hint {
-                    recognised = true;
-                    acc.push(h);
-                }
-            } else if let Some((name, value)) = item.singleton_lit_list() {
-                let mut literal_error = None;
-                let mut err_span = item.span();
-                if name == sym::align {
-                    recognised = true;
-                    match parse_alignment(&value.kind) {
-                        Ok(literal) => acc.push(ReprAlign(literal)),
-                        Err(message) => {
-                            err_span = value.span;
-                            literal_error = Some(message)
-                        }
-                    };
-                } else if name == sym::packed {
-                    recognised = true;
-                    match parse_alignment(&value.kind) {
-                        Ok(literal) => acc.push(ReprPacked(literal)),
-                        Err(message) => {
-                            err_span = value.span;
-                            literal_error = Some(message)
-                        }
-                    };
-                } else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent)
-                    || int_type_of_word(name).is_some()
-                {
-                    recognised = true;
-                    sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
-                        span: item.span(),
-                        name: name.to_ident_string(),
-                    });
-                }
-                if let Some(literal_error) = literal_error {
-                    sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric {
-                        span: err_span,
-                        repr_arg: name.to_ident_string(),
-                        error_part: literal_error,
-                    });
-                }
-            } else if let Some(meta_item) = item.meta_item() {
-                match &meta_item.kind {
-                    MetaItemKind::NameValue(value) => {
-                        if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
-                            let name = meta_item.name_or_empty().to_ident_string();
-                            recognised = true;
-                            sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric {
-                                span: item.span(),
-                                repr_arg: &name,
-                                cause: IncorrectReprFormatGenericCause::from_lit_kind(
-                                    item.span(),
-                                    &value.kind,
-                                    &name,
-                                ),
-                            });
-                        } else if matches!(
-                            meta_item.name_or_empty(),
-                            sym::Rust | sym::C | sym::simd | sym::transparent
-                        ) || int_type_of_word(meta_item.name_or_empty()).is_some()
-                        {
-                            recognised = true;
-                            sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue {
-                                span: meta_item.span,
-                                name: meta_item.name_or_empty().to_ident_string(),
-                            });
-                        }
-                    }
-                    MetaItemKind::List(nested_items) => {
-                        if meta_item.has_name(sym::align) {
-                            recognised = true;
-                            if let [nested_item] = nested_items.as_slice() {
-                                sess.dcx().emit_err(
-                                    session_diagnostics::IncorrectReprFormatExpectInteger {
-                                        span: nested_item.span(),
-                                    },
-                                );
-                            } else {
-                                sess.dcx().emit_err(
-                                    session_diagnostics::IncorrectReprFormatAlignOneArg {
-                                        span: meta_item.span,
-                                    },
-                                );
-                            }
-                        } else if meta_item.has_name(sym::packed) {
-                            recognised = true;
-                            if let [nested_item] = nested_items.as_slice() {
-                                sess.dcx().emit_err(
-                                    session_diagnostics::IncorrectReprFormatPackedExpectInteger {
-                                        span: nested_item.span(),
-                                    },
-                                );
-                            } else {
-                                sess.dcx().emit_err(
-                                    session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
-                                        span: meta_item.span,
-                                    },
-                                );
-                            }
-                        } else if matches!(
-                            meta_item.name_or_empty(),
-                            sym::Rust | sym::C | sym::simd | sym::transparent
-                        ) || int_type_of_word(meta_item.name_or_empty()).is_some()
-                        {
-                            recognised = true;
-                            sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
-                                span: meta_item.span,
-                                name: meta_item.name_or_empty().to_ident_string(),
-                            });
-                        }
-                    }
-                    _ => (),
-                }
-            }
-            if !recognised {
-                // Not a word we recognize. This will be caught and reported by
-                // the `check_mod_attrs` pass, but this pass doesn't always run
-                // (e.g. if we only pretty-print the source), so we have to gate
-                // the `span_delayed_bug` call as follows:
-                if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) {
-                    dcx.span_delayed_bug(item.span(), "unrecognized representation hint");
-                }
-            }
-        }
-    }
-    acc
-}
-
-fn int_type_of_word(s: Symbol) -> Option<IntType> {
-    use IntType::*;
-
-    match s {
-        sym::i8 => Some(SignedInt(ast::IntTy::I8)),
-        sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
-        sym::i16 => Some(SignedInt(ast::IntTy::I16)),
-        sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
-        sym::i32 => Some(SignedInt(ast::IntTy::I32)),
-        sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
-        sym::i64 => Some(SignedInt(ast::IntTy::I64)),
-        sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
-        sym::i128 => Some(SignedInt(ast::IntTy::I128)),
-        sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
-        sym::isize => Some(SignedInt(ast::IntTy::Isize)),
-        sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
-        _ => None,
-    }
-}
-
-pub enum TransparencyError {
-    UnknownTransparency(Symbol, Span),
-    MultipleTransparencyAttrs(Span, Span),
-}
-
-pub fn find_transparency(
-    attrs: &[Attribute],
-    macro_rules: bool,
-) -> (Transparency, Option<TransparencyError>) {
-    let mut transparency = None;
-    let mut error = None;
-    for attr in attrs {
-        if attr.has_name(sym::rustc_macro_transparency) {
-            if let Some((_, old_span)) = transparency {
-                error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span));
-                break;
-            } else if let Some(value) = attr.value_str() {
-                transparency = Some((
-                    match value {
-                        sym::transparent => Transparency::Transparent,
-                        sym::semitransparent => Transparency::SemiTransparent,
-                        sym::opaque => Transparency::Opaque,
-                        _ => {
-                            error = Some(TransparencyError::UnknownTransparency(value, attr.span));
-                            continue;
-                        }
-                    },
-                    attr.span,
-                ));
-            }
-        }
-    }
-    let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque };
-    (transparency.map_or(fallback, |t| t.0), error)
-}
-
-pub fn allow_internal_unstable<'a>(
-    sess: &'a Session,
-    attrs: &'a [Attribute],
-) -> impl Iterator<Item = Symbol> + 'a {
-    allow_unstable(sess, attrs, sym::allow_internal_unstable)
-}
-
-pub fn rustc_allow_const_fn_unstable<'a>(
-    sess: &'a Session,
-    attrs: &'a [Attribute],
-) -> impl Iterator<Item = Symbol> + 'a {
-    allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
-}
-
-fn allow_unstable<'a>(
-    sess: &'a Session,
-    attrs: &'a [Attribute],
-    symbol: Symbol,
-) -> impl Iterator<Item = Symbol> + 'a {
-    let attrs = attr::filter_by_name(attrs, symbol);
-    let list = attrs
-        .filter_map(move |attr| {
-            attr.meta_item_list().or_else(|| {
-                sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList {
-                    span: attr.span,
-                    name: symbol.to_ident_string(),
-                });
-                None
-            })
-        })
-        .flatten();
-
-    list.into_iter().filter_map(move |it| {
-        let name = it.ident().map(|ident| ident.name);
-        if name.is_none() {
-            sess.dcx().emit_err(session_diagnostics::ExpectsFeatures {
-                span: it.span(),
-                name: symbol.to_ident_string(),
-            });
-        }
-        name
-    })
-}
-
-pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> {
-    if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
-        // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
-        if literal.get().is_power_of_two() {
-            // Only possible error is larger than 2^29
-            literal
-                .get()
-                .try_into()
-                .ok()
-                .and_then(|v| Align::from_bytes(v).ok())
-                .ok_or("larger than 2^29")
-        } else {
-            Err("not a power of two")
-        }
-    } else {
-        Err("not an unsuffixed integer")
-    }
-}
-
-/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
-pub fn parse_confusables(attr: &Attribute) -> Option<Vec<Symbol>> {
-    let meta = attr.meta()?;
-    let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None };
-
-    let mut candidates = Vec::new();
-
-    for meta in metas {
-        let MetaItemInner::Lit(meta_lit) = meta else {
-            return None;
-        };
-        candidates.push(meta_lit.symbol);
-    }
-
-    Some(candidates)
-}
diff --git a/compiler/rustc_attr/Cargo.toml b/compiler/rustc_attr_data_structures/Cargo.toml
index 3b24452450a..2ee58f24470 100644
--- a/compiler/rustc_attr/Cargo.toml
+++ b/compiler/rustc_attr_data_structures/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "rustc_attr"
+name = "rustc_attr_data_structures"
 version = "0.0.0"
 edition = "2021"
 
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
new file mode 100644
index 00000000000..8986bec57de
--- /dev/null
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -0,0 +1,106 @@
+use rustc_abi::Align;
+use rustc_ast as ast;
+use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+use rustc_span::{Span, Symbol};
+
+use crate::RustcVersion;
+
+#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum InlineAttr {
+    None,
+    Hint,
+    Always,
+    Never,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)]
+pub enum InstructionSetAttr {
+    ArmA32,
+    ArmT32,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+pub enum OptimizeAttr {
+    None,
+    Speed,
+    Size,
+}
+
+#[derive(Clone, Debug, Encodable, Decodable)]
+pub enum DiagnosticAttribute {
+    // tidy-alphabetical-start
+    DoNotRecommend,
+    OnUnimplemented,
+    // tidy-alphabetical-end
+}
+
+#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
+pub enum ReprAttr {
+    ReprInt(IntType),
+    ReprRust,
+    ReprC,
+    ReprPacked(Align),
+    ReprSimd,
+    ReprTransparent,
+    ReprAlign(Align),
+}
+pub use ReprAttr::*;
+
+pub enum TransparencyError {
+    UnknownTransparency(Symbol, Span),
+    MultipleTransparencyAttrs(Span, Span),
+}
+
+#[derive(Eq, PartialEq, Debug, Copy, Clone)]
+#[derive(Encodable, Decodable)]
+pub enum IntType {
+    SignedInt(ast::IntTy),
+    UnsignedInt(ast::UintTy),
+}
+
+#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
+pub struct Deprecation {
+    pub since: DeprecatedSince,
+    /// The note to issue a reason.
+    pub note: Option<Symbol>,
+    /// A text snippet used to completely replace any use of the deprecated item in an expression.
+    ///
+    /// This is currently unstable.
+    pub suggestion: Option<Symbol>,
+}
+
+/// Release in which an API is deprecated.
+#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
+pub enum DeprecatedSince {
+    RustcVersion(RustcVersion),
+    /// Deprecated in the future ("to be determined").
+    Future,
+    /// `feature(staged_api)` is off. Deprecation versions outside the standard
+    /// library are allowed to be arbitrary strings, for better or worse.
+    NonStandard(Symbol),
+    /// Deprecation version is unspecified but optional.
+    Unspecified,
+    /// Failed to parse a deprecation version, or the deprecation version is
+    /// unspecified and required. An error has already been emitted.
+    Err,
+}
+
+impl Deprecation {
+    /// Whether an item marked with #[deprecated(since = "X")] is currently
+    /// deprecated (i.e., whether X is not greater than the current rustc
+    /// version).
+    pub fn is_in_effect(&self) -> bool {
+        match self.since {
+            DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT,
+            DeprecatedSince::Future => false,
+            // The `since` field doesn't have semantic purpose without `#![staged_api]`.
+            DeprecatedSince::NonStandard(_) => true,
+            // Assume deprecation is in effect if "since" field is absent or invalid.
+            DeprecatedSince::Unspecified | DeprecatedSince::Err => true,
+        }
+    }
+
+    pub fn is_since_rustc_version(&self) -> bool {
+        matches!(self.since, DeprecatedSince::RustcVersion(_))
+    }
+}
diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs
new file mode 100644
index 00000000000..4f204aeab64
--- /dev/null
+++ b/compiler/rustc_attr_data_structures/src/lib.rs
@@ -0,0 +1,16 @@
+// tidy-alphabetical-start
+#![allow(internal_features)]
+#![doc(rust_logo)]
+#![feature(let_chains)]
+#![feature(rustdoc_internals)]
+#![warn(unreachable_pub)]
+// tidy-alphabetical-end
+
+mod attributes;
+mod stability;
+mod version;
+
+pub use attributes::*;
+pub(crate) use rustc_session::HashStableContext;
+pub use stability::*;
+pub use version::*;
diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs
new file mode 100644
index 00000000000..021fe40e3e0
--- /dev/null
+++ b/compiler/rustc_attr_data_structures/src/stability.rs
@@ -0,0 +1,200 @@
+use std::num::NonZero;
+
+use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+use rustc_span::{Symbol, sym};
+
+use crate::RustcVersion;
+
+/// The version placeholder that recently stabilized features contain inside the
+/// `since` field of the `#[stable]` attribute.
+///
+/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591).
+pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
+
+/// Represents the following attributes:
+///
+/// - `#[stable]`
+/// - `#[unstable]`
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic)]
+pub struct Stability {
+    pub level: StabilityLevel,
+    pub feature: Symbol,
+}
+
+impl Stability {
+    pub fn is_unstable(&self) -> bool {
+        self.level.is_unstable()
+    }
+
+    pub fn is_stable(&self) -> bool {
+        self.level.is_stable()
+    }
+
+    pub fn stable_since(&self) -> Option<StableSince> {
+        self.level.stable_since()
+    }
+}
+
+/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic)]
+pub struct ConstStability {
+    pub level: StabilityLevel,
+    pub feature: Symbol,
+    /// whether the function has a `#[rustc_promotable]` attribute
+    pub promotable: bool,
+    /// This is true iff the `const_stable_indirect` attribute is present.
+    pub const_stable_indirect: bool,
+}
+
+impl ConstStability {
+    pub fn from_partial(
+        PartialConstStability { level, feature, promotable }: PartialConstStability,
+        const_stable_indirect: bool,
+    ) -> Self {
+        Self { const_stable_indirect, level, feature, promotable }
+    }
+
+    /// The stability assigned to unmarked items when -Zforce-unstable-if-unmarked is set.
+    pub fn unmarked(const_stable_indirect: bool, regular_stab: Stability) -> Self {
+        Self {
+            feature: regular_stab.feature,
+            promotable: false,
+            level: regular_stab.level,
+            const_stable_indirect,
+        }
+    }
+
+    pub fn is_const_unstable(&self) -> bool {
+        self.level.is_unstable()
+    }
+
+    pub fn is_const_stable(&self) -> bool {
+        self.level.is_stable()
+    }
+}
+
+/// Excludes `const_stable_indirect`. This is necessary because when `-Zforce-unstable-if-unmarked`
+/// is set, we need to encode standalone `#[rustc_const_stable_indirect]` attributes
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic)]
+pub struct PartialConstStability {
+    pub level: StabilityLevel,
+    pub feature: Symbol,
+    /// whether the function has a `#[rustc_promotable]` attribute
+    pub promotable: bool,
+}
+
+impl PartialConstStability {
+    pub fn is_const_unstable(&self) -> bool {
+        self.level.is_unstable()
+    }
+
+    pub fn is_const_stable(&self) -> bool {
+        self.level.is_stable()
+    }
+}
+
+/// The available stability levels.
+#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
+#[derive(HashStable_Generic)]
+pub enum StabilityLevel {
+    /// `#[unstable]`
+    Unstable {
+        /// Reason for the current stability level.
+        reason: UnstableReason,
+        /// Relevant `rust-lang/rust` issue.
+        issue: Option<NonZero<u32>>,
+        is_soft: bool,
+        /// If part of a feature is stabilized and a new feature is added for the remaining parts,
+        /// then the `implied_by` attribute is used to indicate which now-stable feature previously
+        /// contained an item.
+        ///
+        /// ```pseudo-Rust
+        /// #[unstable(feature = "foo", issue = "...")]
+        /// fn foo() {}
+        /// #[unstable(feature = "foo", issue = "...")]
+        /// fn foobar() {}
+        /// ```
+        ///
+        /// ...becomes...
+        ///
+        /// ```pseudo-Rust
+        /// #[stable(feature = "foo", since = "1.XX.X")]
+        /// fn foo() {}
+        /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")]
+        /// fn foobar() {}
+        /// ```
+        implied_by: Option<Symbol>,
+    },
+    /// `#[stable]`
+    Stable {
+        /// Rust release which stabilized this feature.
+        since: StableSince,
+        /// Is this item allowed to be referred to on stable, despite being contained in unstable
+        /// modules?
+        allowed_through_unstable_modules: bool,
+    },
+}
+
+/// Rust release in which a feature is stabilized.
+#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)]
+#[derive(HashStable_Generic)]
+pub enum StableSince {
+    /// also stores the original symbol for printing
+    Version(RustcVersion),
+    /// Stabilized in the upcoming version, whatever number that is.
+    Current,
+    /// Failed to parse a stabilization version.
+    Err,
+}
+
+impl StabilityLevel {
+    pub fn is_unstable(&self) -> bool {
+        matches!(self, StabilityLevel::Unstable { .. })
+    }
+    pub fn is_stable(&self) -> bool {
+        matches!(self, StabilityLevel::Stable { .. })
+    }
+    pub fn stable_since(&self) -> Option<StableSince> {
+        match *self {
+            StabilityLevel::Stable { since, .. } => Some(since),
+            StabilityLevel::Unstable { .. } => None,
+        }
+    }
+}
+
+#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
+#[derive(HashStable_Generic)]
+pub enum UnstableReason {
+    None,
+    Default,
+    Some(Symbol),
+}
+
+/// Represents the `#[rustc_default_body_unstable]` attribute.
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic)]
+pub struct DefaultBodyStability {
+    pub level: StabilityLevel,
+    pub feature: Symbol,
+}
+
+impl UnstableReason {
+    pub fn from_opt_reason(reason: Option<Symbol>) -> Self {
+        // UnstableReason::Default constructed manually
+        match reason {
+            Some(r) => Self::Some(r),
+            None => Self::None,
+        }
+    }
+
+    pub fn to_opt_reason(&self) -> Option<Symbol> {
+        match self {
+            Self::None => None,
+            Self::Default => Some(sym::unstable_location_reason_default),
+            Self::Some(r) => Some(*r),
+        }
+    }
+}
diff --git a/compiler/rustc_session/src/version.rs b/compiler/rustc_attr_data_structures/src/version.rs
index 1696eaf902b..6be875ad4be 100644
--- a/compiler/rustc_session/src/version.rs
+++ b/compiler/rustc_attr_data_structures/src/version.rs
@@ -1,7 +1,5 @@
-use std::borrow::Cow;
 use std::fmt::{self, Display};
 
-use rustc_errors::IntoDiagArg;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic, current_rustc_version};
 
 #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -21,9 +19,3 @@ impl Display for RustcVersion {
         write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
     }
 }
-
-impl IntoDiagArg for RustcVersion {
-    fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
-        rustc_errors::DiagArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml
new file mode 100644
index 00000000000..7ccedf40c3f
--- /dev/null
+++ b/compiler/rustc_attr_parsing/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "rustc_attr_parsing"
+version = "0.0.0"
+edition = "2021"
+
+[dependencies]
+# tidy-alphabetical-start
+rustc_abi = { path = "../rustc_abi" }
+rustc_ast = { path = "../rustc_ast" }
+rustc_ast_pretty = { path = "../rustc_ast_pretty" }
+rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_feature = { path = "../rustc_feature" }
+rustc_fluent_macro = { path = "../rustc_fluent_macro" }
+rustc_lexer = { path = "../rustc_lexer" }
+rustc_macros = { path = "../rustc_macros" }
+rustc_serialize = { path = "../rustc_serialize" }
+rustc_session = { path = "../rustc_session" }
+rustc_span = { path = "../rustc_span" }
+# tidy-alphabetical-end
diff --git a/compiler/rustc_attr/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl
index 235ab7572c4..faa2865cb91 100644
--- a/compiler/rustc_attr/messages.ftl
+++ b/compiler/rustc_attr_parsing/messages.ftl
@@ -1,46 +1,46 @@
-attr_cfg_predicate_identifier =
+attr_parsing_cfg_predicate_identifier =
     `cfg` predicate key must be an identifier
 
-attr_deprecated_item_suggestion =
+attr_parsing_deprecated_item_suggestion =
     suggestions on deprecated items are unstable
     .help = add `#![feature(deprecated_suggestion)]` to the crate root
     .note = see #94785 for more details
 
-attr_expected_one_cfg_pattern =
+attr_parsing_expected_one_cfg_pattern =
     expected 1 cfg-pattern
 
-attr_expected_single_version_literal =
+attr_parsing_expected_single_version_literal =
     expected single version literal
 
-attr_expected_version_literal =
+attr_parsing_expected_version_literal =
     expected a version literal
 
-attr_expects_feature_list =
+attr_parsing_expects_feature_list =
     `{$name}` expects a list of feature names
 
-attr_expects_features =
+attr_parsing_expects_features =
     `{$name}` expects feature names
 
-attr_incorrect_meta_item =
+attr_parsing_incorrect_meta_item =
     incorrect meta item
 
-attr_incorrect_repr_format_align_one_arg =
+attr_parsing_incorrect_repr_format_align_one_arg =
     incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
 
-attr_incorrect_repr_format_expect_literal_integer =
+attr_parsing_incorrect_repr_format_expect_literal_integer =
     incorrect `repr(align)` attribute format: `align` expects a literal integer as argument
 
-attr_incorrect_repr_format_generic =
+attr_parsing_incorrect_repr_format_generic =
     incorrect `repr({$repr_arg})` attribute format
     .suggestion = use parentheses instead
 
-attr_incorrect_repr_format_packed_expect_integer =
+attr_parsing_incorrect_repr_format_packed_expect_integer =
     incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument
 
-attr_incorrect_repr_format_packed_one_or_zero_arg =
+attr_parsing_incorrect_repr_format_packed_one_or_zero_arg =
     incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all
 
-attr_invalid_issue_string =
+attr_parsing_invalid_issue_string =
     `issue` must be a non-zero numeric string or "none"
     .must_not_be_zero = `issue` must not be "0", use "none" instead
     .empty = cannot parse integer from empty string
@@ -48,77 +48,77 @@ attr_invalid_issue_string =
     .pos_overflow = number too large to fit in target type
     .neg_overflow = number too small to fit in target type
 
-attr_invalid_predicate =
+attr_parsing_invalid_predicate =
     invalid predicate `{$predicate}`
 
-attr_invalid_repr_align_need_arg =
+attr_parsing_invalid_repr_align_need_arg =
     invalid `repr(align)` attribute: `align` needs an argument
     .suggestion = supply an argument here
 
-attr_invalid_repr_generic =
+attr_parsing_invalid_repr_generic =
     invalid `repr({$repr_arg})` attribute: {$error_part}
 
-attr_invalid_repr_hint_no_paren =
+attr_parsing_invalid_repr_hint_no_paren =
     invalid representation hint: `{$name}` does not take a parenthesized argument list
 
-attr_invalid_repr_hint_no_value =
+attr_parsing_invalid_repr_hint_no_value =
     invalid representation hint: `{$name}` does not take a value
 
-attr_invalid_since =
+attr_parsing_invalid_since =
     'since' must be a Rust version number, such as "1.31.0"
 
-attr_missing_feature =
+attr_parsing_missing_feature =
     missing 'feature'
 
-attr_missing_issue =
+attr_parsing_missing_issue =
     missing 'issue'
 
-attr_missing_note =
+attr_parsing_missing_note =
     missing 'note'
 
-attr_missing_since =
+attr_parsing_missing_since =
     missing 'since'
 
-attr_multiple_item =
+attr_parsing_multiple_item =
     multiple '{$item}' items
 
-attr_multiple_stability_levels =
+attr_parsing_multiple_stability_levels =
     multiple stability levels
 
-attr_non_ident_feature =
+attr_parsing_non_ident_feature =
     'feature' is not an identifier
 
-attr_rustc_allowed_unstable_pairing =
+attr_parsing_rustc_allowed_unstable_pairing =
     `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
 
-attr_rustc_const_stable_indirect_pairing =
+attr_parsing_rustc_const_stable_indirect_pairing =
     `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied
 
-attr_rustc_promotable_pairing =
+attr_parsing_rustc_promotable_pairing =
     `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
 
-attr_soft_no_args =
+attr_parsing_soft_no_args =
     `soft` should not have any arguments
 
-attr_unknown_meta_item =
+attr_parsing_unknown_meta_item =
     unknown meta item '{$item}'
     .label = expected one of {$expected}
 
-attr_unknown_version_literal =
+attr_parsing_unknown_version_literal =
     unknown version literal format, assuming it refers to a future version
 
-attr_unstable_cfg_target_compact =
+attr_parsing_unstable_cfg_target_compact =
     compact `cfg(target(..))` is experimental and subject to change
 
-attr_unsupported_literal_cfg_boolean =
+attr_parsing_unsupported_literal_cfg_boolean =
     literal in `cfg` predicate value must be a boolean
-attr_unsupported_literal_cfg_string =
+attr_parsing_unsupported_literal_cfg_string =
     literal in `cfg` predicate value must be a string
-attr_unsupported_literal_deprecated_kv_pair =
+attr_parsing_unsupported_literal_deprecated_kv_pair =
     item in `deprecated` must be a key/value pair
-attr_unsupported_literal_deprecated_string =
+attr_parsing_unsupported_literal_deprecated_string =
     literal in `deprecated` value must be a string
-attr_unsupported_literal_generic =
+attr_parsing_unsupported_literal_generic =
     unsupported literal
-attr_unsupported_literal_suggestion =
+attr_parsing_unsupported_literal_suggestion =
     consider removing the prefix
diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
new file mode 100644
index 00000000000..471168ed4f5
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
@@ -0,0 +1,49 @@
+use rustc_ast::attr::{AttributeExt, filter_by_name};
+use rustc_session::Session;
+use rustc_span::{Symbol, sym};
+
+use crate::session_diagnostics;
+
+pub fn allow_internal_unstable<'a>(
+    sess: &'a Session,
+    attrs: &'a [impl AttributeExt],
+) -> impl Iterator<Item = Symbol> + 'a {
+    allow_unstable(sess, attrs, sym::allow_internal_unstable)
+}
+
+pub fn rustc_allow_const_fn_unstable<'a>(
+    sess: &'a Session,
+    attrs: &'a [impl AttributeExt],
+) -> impl Iterator<Item = Symbol> + 'a {
+    allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
+}
+
+fn allow_unstable<'a>(
+    sess: &'a Session,
+    attrs: &'a [impl AttributeExt],
+    symbol: Symbol,
+) -> impl Iterator<Item = Symbol> + 'a {
+    let attrs = filter_by_name(attrs, symbol);
+    let list = attrs
+        .filter_map(move |attr| {
+            attr.meta_item_list().or_else(|| {
+                sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList {
+                    span: attr.span(),
+                    name: symbol.to_ident_string(),
+                });
+                None
+            })
+        })
+        .flatten();
+
+    list.into_iter().filter_map(move |it| {
+        let name = it.ident().map(|ident| ident.name);
+        if name.is_none() {
+            sess.dcx().emit_err(session_diagnostics::ExpectsFeatures {
+                span: it.span(),
+                name: symbol.to_ident_string(),
+            });
+        }
+        name
+    })
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
new file mode 100644
index 00000000000..bb9aaaa2fea
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
@@ -0,0 +1,253 @@
+//! Parsing and validation of builtin attributes
+
+use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
+use rustc_ast_pretty::pprust;
+use rustc_attr_data_structures::RustcVersion;
+use rustc_feature::{Features, GatedCfg, find_gated_cfg};
+use rustc_session::Session;
+use rustc_session::config::ExpectedValues;
+use rustc_session::lint::BuiltinLintDiag;
+use rustc_session::lint::builtin::UNEXPECTED_CFGS;
+use rustc_session::parse::feature_err;
+use rustc_span::{Span, Symbol, kw, sym};
+
+use crate::util::UnsupportedLiteralReason;
+use crate::{fluent_generated, parse_version, session_diagnostics};
+
+#[derive(Clone, Debug)]
+pub struct Condition {
+    pub name: Symbol,
+    pub name_span: Span,
+    pub value: Option<Symbol>,
+    pub value_span: Option<Span>,
+    pub span: Span,
+}
+
+/// Tests if a cfg-pattern matches the cfg set
+pub fn cfg_matches(
+    cfg: &ast::MetaItemInner,
+    sess: &Session,
+    lint_node_id: NodeId,
+    features: Option<&Features>,
+) -> bool {
+    eval_condition(cfg, sess, features, &mut |cfg| {
+        try_gate_cfg(cfg.name, cfg.span, sess, features);
+        match sess.psess.check_config.expecteds.get(&cfg.name) {
+            Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
+                sess.psess.buffer_lint(
+                    UNEXPECTED_CFGS,
+                    cfg.span,
+                    lint_node_id,
+                    BuiltinLintDiag::UnexpectedCfgValue(
+                        (cfg.name, cfg.name_span),
+                        cfg.value.map(|v| (v, cfg.value_span.unwrap())),
+                    ),
+                );
+            }
+            None if sess.psess.check_config.exhaustive_names => {
+                sess.psess.buffer_lint(
+                    UNEXPECTED_CFGS,
+                    cfg.span,
+                    lint_node_id,
+                    BuiltinLintDiag::UnexpectedCfgName(
+                        (cfg.name, cfg.name_span),
+                        cfg.value.map(|v| (v, cfg.value_span.unwrap())),
+                    ),
+                );
+            }
+            _ => { /* not unexpected */ }
+        }
+        sess.psess.config.contains(&(cfg.name, cfg.value))
+    })
+}
+
+fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
+    let gate = find_gated_cfg(|sym| sym == name);
+    if let (Some(feats), Some(gated_cfg)) = (features, gate) {
+        gate_cfg(gated_cfg, span, sess, feats);
+    }
+}
+
+#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
+fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
+    let (cfg, feature, has_feature) = gated_cfg;
+    if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
+        let explain = format!("`cfg({cfg})` is experimental and subject to change");
+        feature_err(sess, *feature, cfg_span, explain).emit();
+    }
+}
+
+/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
+/// evaluate individual items.
+pub fn eval_condition(
+    cfg: &ast::MetaItemInner,
+    sess: &Session,
+    features: Option<&Features>,
+    eval: &mut impl FnMut(Condition) -> bool,
+) -> bool {
+    let dcx = sess.dcx();
+
+    let cfg = match cfg {
+        ast::MetaItemInner::MetaItem(meta_item) => meta_item,
+        ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
+            if let Some(features) = features {
+                // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
+                // and `true`, and we want to keep the former working without feature gate
+                gate_cfg(
+                    &(
+                        if *b { kw::True } else { kw::False },
+                        sym::cfg_boolean_literals,
+                        |features: &Features| features.cfg_boolean_literals(),
+                    ),
+                    cfg.span(),
+                    sess,
+                    features,
+                );
+            }
+            return *b;
+        }
+        _ => {
+            dcx.emit_err(session_diagnostics::UnsupportedLiteral {
+                span: cfg.span(),
+                reason: UnsupportedLiteralReason::CfgBoolean,
+                is_bytestr: false,
+                start_point_span: sess.source_map().start_point(cfg.span()),
+            });
+            return false;
+        }
+    };
+
+    match &cfg.kind {
+        ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
+            try_gate_cfg(sym::version, cfg.span, sess, features);
+            let (min_version, span) = match &mis[..] {
+                [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
+                    (sym, span)
+                }
+                [
+                    MetaItemInner::Lit(MetaItemLit { span, .. })
+                    | MetaItemInner::MetaItem(MetaItem { span, .. }),
+                ] => {
+                    dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
+                    return false;
+                }
+                [..] => {
+                    dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
+                        span: cfg.span,
+                    });
+                    return false;
+                }
+            };
+            let Some(min_version) = parse_version(*min_version) else {
+                dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span });
+                return false;
+            };
+
+            // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
+            if sess.psess.assume_incomplete_release {
+                RustcVersion::CURRENT > min_version
+            } else {
+                RustcVersion::CURRENT >= min_version
+            }
+        }
+        ast::MetaItemKind::List(mis) => {
+            for mi in mis.iter() {
+                if mi.meta_item_or_bool().is_none() {
+                    dcx.emit_err(session_diagnostics::UnsupportedLiteral {
+                        span: mi.span(),
+                        reason: UnsupportedLiteralReason::Generic,
+                        is_bytestr: false,
+                        start_point_span: sess.source_map().start_point(mi.span()),
+                    });
+                    return false;
+                }
+            }
+
+            // The unwraps below may look dangerous, but we've already asserted
+            // that they won't fail with the loop above.
+            match cfg.name_or_empty() {
+                sym::any => mis
+                    .iter()
+                    // We don't use any() here, because we want to evaluate all cfg condition
+                    // as eval_condition can (and does) extra checks
+                    .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
+                sym::all => mis
+                    .iter()
+                    // We don't use all() here, because we want to evaluate all cfg condition
+                    // as eval_condition can (and does) extra checks
+                    .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
+                sym::not => {
+                    let [mi] = mis.as_slice() else {
+                        dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
+                        return false;
+                    };
+
+                    !eval_condition(mi, sess, features, eval)
+                }
+                sym::target => {
+                    if let Some(features) = features
+                        && !features.cfg_target_compact()
+                    {
+                        feature_err(
+                            sess,
+                            sym::cfg_target_compact,
+                            cfg.span,
+                            fluent_generated::attr_parsing_unstable_cfg_target_compact,
+                        )
+                        .emit();
+                    }
+
+                    mis.iter().fold(true, |res, mi| {
+                        let Some(mut mi) = mi.meta_item().cloned() else {
+                            dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
+                                span: mi.span(),
+                            });
+                            return false;
+                        };
+
+                        if let [seg, ..] = &mut mi.path.segments[..] {
+                            seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
+                        }
+
+                        res & eval_condition(
+                            &ast::MetaItemInner::MetaItem(mi),
+                            sess,
+                            features,
+                            eval,
+                        )
+                    })
+                }
+                _ => {
+                    dcx.emit_err(session_diagnostics::InvalidPredicate {
+                        span: cfg.span,
+                        predicate: pprust::path_to_string(&cfg.path),
+                    });
+                    false
+                }
+            }
+        }
+        ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
+            dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
+            true
+        }
+        MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
+            dcx.emit_err(session_diagnostics::UnsupportedLiteral {
+                span: lit.span,
+                reason: UnsupportedLiteralReason::CfgString,
+                is_bytestr: lit.kind.is_bytestr(),
+                start_point_span: sess.source_map().start_point(lit.span),
+            });
+            true
+        }
+        ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
+            let ident = cfg.ident().expect("multi-segment cfg predicate");
+            eval(Condition {
+                name: ident.name,
+                name_span: ident.span,
+                value: cfg.value_str(),
+                value_span: cfg.name_value_literal_span(),
+                span: cfg.span,
+            })
+        }
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
new file mode 100644
index 00000000000..2ced759fd88
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
@@ -0,0 +1,21 @@
+//! Parsing and validation of builtin attributes
+
+use rustc_ast::MetaItemInner;
+use rustc_ast::attr::AttributeExt;
+use rustc_span::Symbol;
+
+/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
+pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> {
+    let metas = attr.meta_item_list()?;
+
+    let mut candidates = Vec::new();
+
+    for meta in metas {
+        let MetaItemInner::Lit(meta_lit) = meta else {
+            return None;
+        };
+        candidates.push(meta_lit.symbol);
+    }
+
+    Some(candidates)
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
new file mode 100644
index 00000000000..d7415a7198f
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
@@ -0,0 +1,148 @@
+//! Parsing and validation of builtin attributes
+
+use rustc_ast::attr::AttributeExt;
+use rustc_ast::{MetaItem, MetaItemInner};
+use rustc_ast_pretty::pprust;
+use rustc_attr_data_structures::{DeprecatedSince, Deprecation};
+use rustc_feature::Features;
+use rustc_session::Session;
+use rustc_span::{Span, Symbol, sym};
+
+use super::util::UnsupportedLiteralReason;
+use crate::{parse_version, session_diagnostics};
+
+/// Finds the deprecation attribute. `None` if none exists.
+pub fn find_deprecation(
+    sess: &Session,
+    features: &Features,
+    attrs: &[impl AttributeExt],
+) -> Option<(Deprecation, Span)> {
+    let mut depr: Option<(Deprecation, Span)> = None;
+    let is_rustc = features.staged_api();
+
+    'outer: for attr in attrs {
+        if !attr.has_name(sym::deprecated) {
+            continue;
+        }
+
+        let mut since = None;
+        let mut note = None;
+        let mut suggestion = None;
+
+        if attr.is_doc_comment() {
+            continue;
+        } else if attr.is_word() {
+        } else if let Some(value) = attr.value_str() {
+            note = Some(value)
+        } else if let Some(list) = attr.meta_item_list() {
+            let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
+                if item.is_some() {
+                    sess.dcx().emit_err(session_diagnostics::MultipleItem {
+                        span: meta.span,
+                        item: pprust::path_to_string(&meta.path),
+                    });
+                    return false;
+                }
+                if let Some(v) = meta.value_str() {
+                    *item = Some(v);
+                    true
+                } else {
+                    if let Some(lit) = meta.name_value_literal() {
+                        sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
+                            span: lit.span,
+                            reason: UnsupportedLiteralReason::DeprecatedString,
+                            is_bytestr: lit.kind.is_bytestr(),
+                            start_point_span: sess.source_map().start_point(lit.span),
+                        });
+                    } else {
+                        sess.dcx()
+                            .emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
+                    }
+                    false
+                }
+            };
+
+            for meta in &list {
+                match meta {
+                    MetaItemInner::MetaItem(mi) => match mi.name_or_empty() {
+                        sym::since => {
+                            if !get(mi, &mut since) {
+                                continue 'outer;
+                            }
+                        }
+                        sym::note => {
+                            if !get(mi, &mut note) {
+                                continue 'outer;
+                            }
+                        }
+                        sym::suggestion => {
+                            if !features.deprecated_suggestion() {
+                                sess.dcx().emit_err(
+                                    session_diagnostics::DeprecatedItemSuggestion {
+                                        span: mi.span,
+                                        is_nightly: sess.is_nightly_build(),
+                                        details: (),
+                                    },
+                                );
+                            }
+
+                            if !get(mi, &mut suggestion) {
+                                continue 'outer;
+                            }
+                        }
+                        _ => {
+                            sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
+                                span: meta.span(),
+                                item: pprust::path_to_string(&mi.path),
+                                expected: if features.deprecated_suggestion() {
+                                    &["since", "note", "suggestion"]
+                                } else {
+                                    &["since", "note"]
+                                },
+                            });
+                            continue 'outer;
+                        }
+                    },
+                    MetaItemInner::Lit(lit) => {
+                        sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
+                            span: lit.span,
+                            reason: UnsupportedLiteralReason::DeprecatedKvPair,
+                            is_bytestr: false,
+                            start_point_span: sess.source_map().start_point(lit.span),
+                        });
+                        continue 'outer;
+                    }
+                }
+            }
+        } else {
+            continue;
+        }
+
+        let since = if let Some(since) = since {
+            if since.as_str() == "TBD" {
+                DeprecatedSince::Future
+            } else if !is_rustc {
+                DeprecatedSince::NonStandard(since)
+            } else if let Some(version) = parse_version(since) {
+                DeprecatedSince::RustcVersion(version)
+            } else {
+                sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
+                DeprecatedSince::Err
+            }
+        } else if is_rustc {
+            sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
+            DeprecatedSince::Err
+        } else {
+            DeprecatedSince::Unspecified
+        };
+
+        if is_rustc && note.is_none() {
+            sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span() });
+            continue;
+        }
+
+        depr = Some((Deprecation { since, note, suggestion }, attr.span()));
+    }
+
+    depr
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
new file mode 100644
index 00000000000..a78e0b54b64
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -0,0 +1,17 @@
+mod allow_unstable;
+mod cfg;
+mod confusables;
+mod deprecation;
+mod repr;
+mod stability;
+mod transparency;
+
+pub mod util;
+
+pub use allow_unstable::*;
+pub use cfg::*;
+pub use confusables::*;
+pub use deprecation::*;
+pub use repr::*;
+pub use stability::*;
+pub use transparency::*;
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
new file mode 100644
index 00000000000..124f0aa3eff
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -0,0 +1,215 @@
+//! Parsing and validation of builtin attributes
+
+use rustc_abi::Align;
+use rustc_ast::attr::AttributeExt;
+use rustc_ast::{self as ast, MetaItemKind};
+use rustc_attr_data_structures::IntType;
+use rustc_attr_data_structures::ReprAttr::*;
+use rustc_session::Session;
+use rustc_span::{Symbol, sym};
+
+use crate::ReprAttr;
+use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
+
+/// Parse #[repr(...)] forms.
+///
+/// Valid repr contents: any of the primitive integral type names (see
+/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
+/// the same discriminant size that the corresponding C enum would or C
+/// structure layout, `packed` to remove padding, and `transparent` to delegate representation
+/// concerns to the only non-ZST field.
+pub fn find_repr_attrs(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
+    if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
+}
+
+pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
+    assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
+    let mut acc = Vec::new();
+    let dcx = sess.dcx();
+
+    if let Some(items) = attr.meta_item_list() {
+        for item in items {
+            let mut recognised = false;
+            if item.is_word() {
+                let hint = match item.name_or_empty() {
+                    sym::Rust => Some(ReprRust),
+                    sym::C => Some(ReprC),
+                    sym::packed => Some(ReprPacked(Align::ONE)),
+                    sym::simd => Some(ReprSimd),
+                    sym::transparent => Some(ReprTransparent),
+                    sym::align => {
+                        sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg {
+                            span: item.span(),
+                        });
+                        recognised = true;
+                        None
+                    }
+                    name => int_type_of_word(name).map(ReprInt),
+                };
+
+                if let Some(h) = hint {
+                    recognised = true;
+                    acc.push(h);
+                }
+            } else if let Some((name, value)) = item.singleton_lit_list() {
+                let mut literal_error = None;
+                let mut err_span = item.span();
+                if name == sym::align {
+                    recognised = true;
+                    match parse_alignment(&value.kind) {
+                        Ok(literal) => acc.push(ReprAlign(literal)),
+                        Err(message) => {
+                            err_span = value.span;
+                            literal_error = Some(message)
+                        }
+                    };
+                } else if name == sym::packed {
+                    recognised = true;
+                    match parse_alignment(&value.kind) {
+                        Ok(literal) => acc.push(ReprPacked(literal)),
+                        Err(message) => {
+                            err_span = value.span;
+                            literal_error = Some(message)
+                        }
+                    };
+                } else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent)
+                    || int_type_of_word(name).is_some()
+                {
+                    recognised = true;
+                    sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
+                        span: item.span(),
+                        name: name.to_ident_string(),
+                    });
+                }
+                if let Some(literal_error) = literal_error {
+                    sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric {
+                        span: err_span,
+                        repr_arg: name.to_ident_string(),
+                        error_part: literal_error,
+                    });
+                }
+            } else if let Some(meta_item) = item.meta_item() {
+                match &meta_item.kind {
+                    MetaItemKind::NameValue(value) => {
+                        if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
+                            let name = meta_item.name_or_empty().to_ident_string();
+                            recognised = true;
+                            sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric {
+                                span: item.span(),
+                                repr_arg: &name,
+                                cause: IncorrectReprFormatGenericCause::from_lit_kind(
+                                    item.span(),
+                                    &value.kind,
+                                    &name,
+                                ),
+                            });
+                        } else if matches!(
+                            meta_item.name_or_empty(),
+                            sym::Rust | sym::C | sym::simd | sym::transparent
+                        ) || int_type_of_word(meta_item.name_or_empty()).is_some()
+                        {
+                            recognised = true;
+                            sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue {
+                                span: meta_item.span,
+                                name: meta_item.name_or_empty().to_ident_string(),
+                            });
+                        }
+                    }
+                    MetaItemKind::List(nested_items) => {
+                        if meta_item.has_name(sym::align) {
+                            recognised = true;
+                            if let [nested_item] = nested_items.as_slice() {
+                                sess.dcx().emit_err(
+                                    session_diagnostics::IncorrectReprFormatExpectInteger {
+                                        span: nested_item.span(),
+                                    },
+                                );
+                            } else {
+                                sess.dcx().emit_err(
+                                    session_diagnostics::IncorrectReprFormatAlignOneArg {
+                                        span: meta_item.span,
+                                    },
+                                );
+                            }
+                        } else if meta_item.has_name(sym::packed) {
+                            recognised = true;
+                            if let [nested_item] = nested_items.as_slice() {
+                                sess.dcx().emit_err(
+                                    session_diagnostics::IncorrectReprFormatPackedExpectInteger {
+                                        span: nested_item.span(),
+                                    },
+                                );
+                            } else {
+                                sess.dcx().emit_err(
+                                    session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
+                                        span: meta_item.span,
+                                    },
+                                );
+                            }
+                        } else if matches!(
+                            meta_item.name_or_empty(),
+                            sym::Rust | sym::C | sym::simd | sym::transparent
+                        ) || int_type_of_word(meta_item.name_or_empty()).is_some()
+                        {
+                            recognised = true;
+                            sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
+                                span: meta_item.span,
+                                name: meta_item.name_or_empty().to_ident_string(),
+                            });
+                        }
+                    }
+                    _ => (),
+                }
+            }
+            if !recognised {
+                // Not a word we recognize. This will be caught and reported by
+                // the `check_mod_attrs` pass, but this pass doesn't always run
+                // (e.g. if we only pretty-print the source), so we have to gate
+                // the `span_delayed_bug` call as follows:
+                if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) {
+                    dcx.span_delayed_bug(item.span(), "unrecognized representation hint");
+                }
+            }
+        }
+    }
+    acc
+}
+
+fn int_type_of_word(s: Symbol) -> Option<IntType> {
+    use rustc_attr_data_structures::IntType::*;
+
+    match s {
+        sym::i8 => Some(SignedInt(ast::IntTy::I8)),
+        sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
+        sym::i16 => Some(SignedInt(ast::IntTy::I16)),
+        sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
+        sym::i32 => Some(SignedInt(ast::IntTy::I32)),
+        sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
+        sym::i64 => Some(SignedInt(ast::IntTy::I64)),
+        sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
+        sym::i128 => Some(SignedInt(ast::IntTy::I128)),
+        sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
+        sym::isize => Some(SignedInt(ast::IntTy::Isize)),
+        sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
+        _ => None,
+    }
+}
+
+pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> {
+    if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
+        // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
+        if literal.get().is_power_of_two() {
+            // Only possible error is larger than 2^29
+            literal
+                .get()
+                .try_into()
+                .ok()
+                .and_then(|v| Align::from_bytes(v).ok())
+                .ok_or("larger than 2^29")
+        } else {
+            Err("not a power of two")
+        }
+    } else {
+        Err("not an unsuffixed integer")
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
new file mode 100644
index 00000000000..89937e1c593
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -0,0 +1,384 @@
+//! Parsing and validation of builtin attributes
+
+use std::num::NonZero;
+
+use rustc_ast::MetaItem;
+use rustc_ast::attr::AttributeExt;
+use rustc_ast_pretty::pprust;
+use rustc_attr_data_structures::{
+    ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason,
+    VERSION_PLACEHOLDER,
+};
+use rustc_errors::ErrorGuaranteed;
+use rustc_session::Session;
+use rustc_span::{Span, Symbol, sym};
+
+use crate::attributes::util::UnsupportedLiteralReason;
+use crate::{parse_version, session_diagnostics};
+
+/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules`
+/// attributes in `attrs`. Returns `None` if no stability attributes are found.
+pub fn find_stability(
+    sess: &Session,
+    attrs: &[impl AttributeExt],
+    item_sp: Span,
+) -> Option<(Stability, Span)> {
+    let mut stab: Option<(Stability, Span)> = None;
+    let mut allowed_through_unstable_modules = false;
+
+    for attr in attrs {
+        match attr.name_or_empty() {
+            sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true,
+            sym::unstable => {
+                if stab.is_some() {
+                    sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
+                        span: attr.span(),
+                    });
+                    break;
+                }
+
+                if let Some((feature, level)) = parse_unstability(sess, attr) {
+                    stab = Some((Stability { level, feature }, attr.span()));
+                }
+            }
+            sym::stable => {
+                if stab.is_some() {
+                    sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
+                        span: attr.span(),
+                    });
+                    break;
+                }
+                if let Some((feature, level)) = parse_stability(sess, attr) {
+                    stab = Some((Stability { level, feature }, attr.span()));
+                }
+            }
+            _ => {}
+        }
+    }
+
+    if allowed_through_unstable_modules {
+        match &mut stab {
+            Some((
+                Stability {
+                    level: StabilityLevel::Stable { allowed_through_unstable_modules, .. },
+                    ..
+                },
+                _,
+            )) => *allowed_through_unstable_modules = true,
+            _ => {
+                sess.dcx()
+                    .emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
+            }
+        }
+    }
+
+    stab
+}
+
+/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
+/// attributes in `attrs`. Returns `None` if no stability attributes are found.
+pub fn find_const_stability(
+    sess: &Session,
+    attrs: &[impl AttributeExt],
+    item_sp: Span,
+) -> Option<(ConstStability, Span)> {
+    let mut const_stab: Option<(ConstStability, Span)> = None;
+    let mut promotable = false;
+    let mut const_stable_indirect = false;
+
+    for attr in attrs {
+        match attr.name_or_empty() {
+            sym::rustc_promotable => promotable = true,
+            sym::rustc_const_stable_indirect => const_stable_indirect = true,
+            sym::rustc_const_unstable => {
+                if const_stab.is_some() {
+                    sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
+                        span: attr.span(),
+                    });
+                    break;
+                }
+
+                if let Some((feature, level)) = parse_unstability(sess, attr) {
+                    const_stab = Some((
+                        ConstStability {
+                            level,
+                            feature,
+                            const_stable_indirect: false,
+                            promotable: false,
+                        },
+                        attr.span(),
+                    ));
+                }
+            }
+            sym::rustc_const_stable => {
+                if const_stab.is_some() {
+                    sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
+                        span: attr.span(),
+                    });
+                    break;
+                }
+                if let Some((feature, level)) = parse_stability(sess, attr) {
+                    const_stab = Some((
+                        ConstStability {
+                            level,
+                            feature,
+                            const_stable_indirect: false,
+                            promotable: false,
+                        },
+                        attr.span(),
+                    ));
+                }
+            }
+            _ => {}
+        }
+    }
+
+    // Merge promotable and const_stable_indirect into stability info
+    if promotable {
+        match &mut const_stab {
+            Some((stab, _)) => stab.promotable = promotable,
+            _ => {
+                _ = sess
+                    .dcx()
+                    .emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp })
+            }
+        }
+    }
+    if const_stable_indirect {
+        match &mut const_stab {
+            Some((stab, _)) => {
+                if stab.is_const_unstable() {
+                    stab.const_stable_indirect = true;
+                } else {
+                    _ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing {
+                        span: item_sp,
+                    })
+                }
+            }
+            _ => {
+                // This function has no const stability attribute, but has `const_stable_indirect`.
+                // We ignore that; unmarked functions are subject to recursive const stability
+                // checks by default so we do carry out the user's intent.
+            }
+        }
+    }
+
+    const_stab
+}
+
+/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate
+/// without the `staged_api` feature.
+pub fn unmarked_crate_const_stab(
+    _sess: &Session,
+    attrs: &[impl AttributeExt],
+    regular_stab: Stability,
+) -> ConstStability {
+    assert!(regular_stab.level.is_unstable());
+    // The only attribute that matters here is `rustc_const_stable_indirect`.
+    // We enforce recursive const stability rules for those functions.
+    let const_stable_indirect =
+        attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect);
+    ConstStability {
+        feature: regular_stab.feature,
+        const_stable_indirect,
+        promotable: false,
+        level: regular_stab.level,
+    }
+}
+
+/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
+/// Returns `None` if no stability attributes are found.
+pub fn find_body_stability(
+    sess: &Session,
+    attrs: &[impl AttributeExt],
+) -> Option<(DefaultBodyStability, Span)> {
+    let mut body_stab: Option<(DefaultBodyStability, Span)> = None;
+
+    for attr in attrs {
+        if attr.has_name(sym::rustc_default_body_unstable) {
+            if body_stab.is_some() {
+                sess.dcx()
+                    .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span() });
+                break;
+            }
+
+            if let Some((feature, level)) = parse_unstability(sess, attr) {
+                body_stab = Some((DefaultBodyStability { level, feature }, attr.span()));
+            }
+        }
+    }
+
+    body_stab
+}
+
+fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> {
+    if item.is_some() {
+        sess.dcx().emit_err(session_diagnostics::MultipleItem {
+            span: meta.span,
+            item: pprust::path_to_string(&meta.path),
+        });
+        None
+    } else if let Some(v) = meta.value_str() {
+        *item = Some(v);
+        Some(())
+    } else {
+        sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
+        None
+    }
+}
+
+/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
+/// its stability information.
+fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
+    let metas = attr.meta_item_list()?;
+
+    let mut feature = None;
+    let mut since = None;
+    for meta in metas {
+        let Some(mi) = meta.meta_item() else {
+            sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
+                span: meta.span(),
+                reason: UnsupportedLiteralReason::Generic,
+                is_bytestr: false,
+                start_point_span: sess.source_map().start_point(meta.span()),
+            });
+            return None;
+        };
+
+        match mi.name_or_empty() {
+            sym::feature => insert_or_error(sess, mi, &mut feature)?,
+            sym::since => insert_or_error(sess, mi, &mut since)?,
+            _ => {
+                sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
+                    span: meta.span(),
+                    item: pprust::path_to_string(&mi.path),
+                    expected: &["feature", "since"],
+                });
+                return None;
+            }
+        }
+    }
+
+    let feature = match feature {
+        Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
+        Some(_bad_feature) => {
+            Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
+        }
+        None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
+    };
+
+    let since = if let Some(since) = since {
+        if since.as_str() == VERSION_PLACEHOLDER {
+            StableSince::Current
+        } else if let Some(version) = parse_version(since) {
+            StableSince::Version(version)
+        } else {
+            sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
+            StableSince::Err
+        }
+    } else {
+        sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
+        StableSince::Err
+    };
+
+    match feature {
+        Ok(feature) => {
+            let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
+            Some((feature, level))
+        }
+        Err(ErrorGuaranteed { .. }) => None,
+    }
+}
+
+/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
+/// attribute, and return the feature name and its stability information.
+fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
+    let metas = attr.meta_item_list()?;
+
+    let mut feature = None;
+    let mut reason = None;
+    let mut issue = None;
+    let mut issue_num = None;
+    let mut is_soft = false;
+    let mut implied_by = None;
+    for meta in metas {
+        let Some(mi) = meta.meta_item() else {
+            sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
+                span: meta.span(),
+                reason: UnsupportedLiteralReason::Generic,
+                is_bytestr: false,
+                start_point_span: sess.source_map().start_point(meta.span()),
+            });
+            return None;
+        };
+
+        match mi.name_or_empty() {
+            sym::feature => insert_or_error(sess, mi, &mut feature)?,
+            sym::reason => insert_or_error(sess, mi, &mut reason)?,
+            sym::issue => {
+                insert_or_error(sess, mi, &mut issue)?;
+
+                // These unwraps are safe because `insert_or_error` ensures the meta item
+                // is a name/value pair string literal.
+                issue_num = match issue.unwrap().as_str() {
+                    "none" => None,
+                    issue => match issue.parse::<NonZero<u32>>() {
+                        Ok(num) => Some(num),
+                        Err(err) => {
+                            sess.dcx().emit_err(
+                                session_diagnostics::InvalidIssueString {
+                                    span: mi.span,
+                                    cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
+                                        mi.name_value_literal_span().unwrap(),
+                                        err.kind(),
+                                    ),
+                                },
+                            );
+                            return None;
+                        }
+                    },
+                };
+            }
+            sym::soft => {
+                if !mi.is_word() {
+                    sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span });
+                }
+                is_soft = true;
+            }
+            sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?,
+            _ => {
+                sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
+                    span: meta.span(),
+                    item: pprust::path_to_string(&mi.path),
+                    expected: &["feature", "reason", "issue", "soft", "implied_by"],
+                });
+                return None;
+            }
+        }
+    }
+
+    let feature = match feature {
+        Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
+        Some(_bad_feature) => {
+            Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
+        }
+        None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
+    };
+
+    let issue = issue.ok_or_else(|| {
+        sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span() })
+    });
+
+    match (feature, issue) {
+        (Ok(feature), Ok(_)) => {
+            let level = StabilityLevel::Unstable {
+                reason: UnstableReason::from_opt_reason(reason),
+                issue: issue_num,
+                is_soft,
+                implied_by,
+            };
+            Some((feature, level))
+        }
+        (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
new file mode 100644
index 00000000000..f4065a77048
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -0,0 +1,36 @@
+use rustc_ast::attr::AttributeExt;
+use rustc_attr_data_structures::TransparencyError;
+use rustc_span::hygiene::Transparency;
+use rustc_span::sym;
+
+pub fn find_transparency(
+    attrs: &[impl AttributeExt],
+    macro_rules: bool,
+) -> (Transparency, Option<TransparencyError>) {
+    let mut transparency = None;
+    let mut error = None;
+    for attr in attrs {
+        if attr.has_name(sym::rustc_macro_transparency) {
+            if let Some((_, old_span)) = transparency {
+                error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span()));
+                break;
+            } else if let Some(value) = attr.value_str() {
+                transparency = Some((
+                    match value {
+                        sym::transparent => Transparency::Transparent,
+                        sym::semitransparent => Transparency::SemiTransparent,
+                        sym::opaque => Transparency::Opaque,
+                        _ => {
+                            error =
+                                Some(TransparencyError::UnknownTransparency(value, attr.span()));
+                            continue;
+                        }
+                    },
+                    attr.span(),
+                ));
+            }
+        }
+    }
+    let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque };
+    (transparency.map_or(fallback, |t| t.0), error)
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs
new file mode 100644
index 00000000000..e36f7dfff5a
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/util.rs
@@ -0,0 +1,36 @@
+use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name};
+use rustc_attr_data_structures::RustcVersion;
+use rustc_feature::is_builtin_attr_name;
+use rustc_span::{Symbol, sym};
+
+pub(crate) enum UnsupportedLiteralReason {
+    Generic,
+    CfgString,
+    CfgBoolean,
+    DeprecatedString,
+    DeprecatedKvPair,
+}
+
+pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
+    attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
+}
+
+pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
+    first_attr_value_str_by_name(attrs, sym::crate_name)
+}
+
+/// Parse a rustc version number written inside string literal in an attribute,
+/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
+/// not accepted in this position, unlike when parsing CFG_RELEASE.
+pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
+    let mut components = s.as_str().split('-');
+    let d = components.next()?;
+    if components.next().is_some() {
+        return None;
+    }
+    let mut digits = d.splitn(3, '.');
+    let major = digits.next()?.parse().ok()?;
+    let minor = digits.next()?.parse().ok()?;
+    let patch = digits.next().unwrap_or("0").parse().ok()?;
+    Some(RustcVersion { major, minor, patch })
+}
diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs
index bb207c5c952..a1264a6875f 100644
--- a/compiler/rustc_attr/src/lib.rs
+++ b/compiler/rustc_attr_parsing/src/lib.rs
@@ -12,14 +12,11 @@
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
-mod builtin;
+mod attributes;
 mod session_diagnostics;
 
-pub use IntType::*;
-pub use ReprAttr::*;
-pub use StabilityLevel::*;
-pub use builtin::*;
-pub use rustc_ast::attr::*;
-pub(crate) use rustc_session::HashStableContext;
+pub use attributes::*;
+pub use rustc_attr_data_structures::*;
+pub use util::{find_crate_name, is_builtin_attr, parse_version};
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 9d08a9f5754..b1d8ec49774 100644
--- a/compiler/rustc_attr/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -6,17 +6,18 @@ use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuar
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{Span, Symbol};
 
-use crate::{UnsupportedLiteralReason, fluent_generated as fluent};
+use crate::attributes::util::UnsupportedLiteralReason;
+use crate::fluent_generated as fluent;
 
 #[derive(Diagnostic)]
-#[diag(attr_expected_one_cfg_pattern, code = E0536)]
+#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)]
 pub(crate) struct ExpectedOneCfgPattern {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_invalid_predicate, code = E0537)]
+#[diag(attr_parsing_invalid_predicate, code = E0537)]
 pub(crate) struct InvalidPredicate {
     #[primary_span]
     pub span: Span,
@@ -25,7 +26,7 @@ pub(crate) struct InvalidPredicate {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_multiple_item, code = E0538)]
+#[diag(attr_parsing_multiple_item, code = E0538)]
 pub(crate) struct MultipleItem {
     #[primary_span]
     pub span: Span,
@@ -34,7 +35,7 @@ pub(crate) struct MultipleItem {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_incorrect_meta_item, code = E0539)]
+#[diag(attr_parsing_incorrect_meta_item, code = E0539)]
 pub(crate) struct IncorrectMetaItem {
     #[primary_span]
     pub span: Span,
@@ -51,38 +52,38 @@ pub(crate) struct UnknownMetaItem<'a> {
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnknownMetaItem<'_> {
     fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
-        Diag::new(dcx, level, fluent::attr_unknown_meta_item)
+        Diag::new(dcx, level, fluent::attr_parsing_unknown_meta_item)
             .with_span(self.span)
             .with_code(E0541)
             .with_arg("item", self.item)
             .with_arg("expected", expected.join(", "))
-            .with_span_label(self.span, fluent::attr_label)
+            .with_span_label(self.span, fluent::attr_parsing_label)
     }
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_missing_since, code = E0542)]
+#[diag(attr_parsing_missing_since, code = E0542)]
 pub(crate) struct MissingSince {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_missing_note, code = E0543)]
+#[diag(attr_parsing_missing_note, code = E0543)]
 pub(crate) struct MissingNote {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_multiple_stability_levels, code = E0544)]
+#[diag(attr_parsing_multiple_stability_levels, code = E0544)]
 pub(crate) struct MultipleStabilityLevels {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_invalid_issue_string, code = E0545)]
+#[diag(attr_parsing_invalid_issue_string, code = E0545)]
 pub(crate) struct InvalidIssueString {
     #[primary_span]
     pub span: Span,
@@ -95,31 +96,31 @@ pub(crate) struct InvalidIssueString {
 // translatable.
 #[derive(Subdiagnostic)]
 pub(crate) enum InvalidIssueStringCause {
-    #[label(attr_must_not_be_zero)]
+    #[label(attr_parsing_must_not_be_zero)]
     MustNotBeZero {
         #[primary_span]
         span: Span,
     },
 
-    #[label(attr_empty)]
+    #[label(attr_parsing_empty)]
     Empty {
         #[primary_span]
         span: Span,
     },
 
-    #[label(attr_invalid_digit)]
+    #[label(attr_parsing_invalid_digit)]
     InvalidDigit {
         #[primary_span]
         span: Span,
     },
 
-    #[label(attr_pos_overflow)]
+    #[label(attr_parsing_pos_overflow)]
     PosOverflow {
         #[primary_span]
         span: Span,
     },
 
-    #[label(attr_neg_overflow)]
+    #[label(attr_parsing_neg_overflow)]
     NegOverflow {
         #[primary_span]
         span: Span,
@@ -140,21 +141,21 @@ impl InvalidIssueStringCause {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_missing_feature, code = E0546)]
+#[diag(attr_parsing_missing_feature, code = E0546)]
 pub(crate) struct MissingFeature {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_non_ident_feature, code = E0546)]
+#[diag(attr_parsing_non_ident_feature, code = E0546)]
 pub(crate) struct NonIdentFeature {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_missing_issue, code = E0547)]
+#[diag(attr_parsing_missing_issue, code = E0547)]
 pub(crate) struct MissingIssue {
     #[primary_span]
     pub span: Span,
@@ -163,20 +164,20 @@ pub(crate) struct MissingIssue {
 // FIXME: Why is this the same error code as `InvalidReprHintNoParen` and `InvalidReprHintNoValue`?
 // It is more similar to `IncorrectReprFormatGeneric`.
 #[derive(Diagnostic)]
-#[diag(attr_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)]
+#[diag(attr_parsing_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)]
 pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg {
     #[primary_span]
     pub span: Span,
 }
 #[derive(Diagnostic)]
-#[diag(attr_incorrect_repr_format_packed_expect_integer, code = E0552)]
+#[diag(attr_parsing_incorrect_repr_format_packed_expect_integer, code = E0552)]
 pub(crate) struct IncorrectReprFormatPackedExpectInteger {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_invalid_repr_hint_no_paren, code = E0552)]
+#[diag(attr_parsing_invalid_repr_hint_no_paren, code = E0552)]
 pub(crate) struct InvalidReprHintNoParen {
     #[primary_span]
     pub span: Span,
@@ -185,7 +186,7 @@ pub(crate) struct InvalidReprHintNoParen {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_invalid_repr_hint_no_value, code = E0552)]
+#[diag(attr_parsing_invalid_repr_hint_no_value, code = E0552)]
 pub(crate) struct InvalidReprHintNoValue {
     #[primary_span]
     pub span: Span,
@@ -204,14 +205,18 @@ pub(crate) struct UnsupportedLiteral {
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
     fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         let mut diag = Diag::new(dcx, level, match self.reason {
-            UnsupportedLiteralReason::Generic => fluent::attr_unsupported_literal_generic,
-            UnsupportedLiteralReason::CfgString => fluent::attr_unsupported_literal_cfg_string,
-            UnsupportedLiteralReason::CfgBoolean => fluent::attr_unsupported_literal_cfg_boolean,
+            UnsupportedLiteralReason::Generic => fluent::attr_parsing_unsupported_literal_generic,
+            UnsupportedLiteralReason::CfgString => {
+                fluent::attr_parsing_unsupported_literal_cfg_string
+            }
+            UnsupportedLiteralReason::CfgBoolean => {
+                fluent::attr_parsing_unsupported_literal_cfg_boolean
+            }
             UnsupportedLiteralReason::DeprecatedString => {
-                fluent::attr_unsupported_literal_deprecated_string
+                fluent::attr_parsing_unsupported_literal_deprecated_string
             }
             UnsupportedLiteralReason::DeprecatedKvPair => {
-                fluent::attr_unsupported_literal_deprecated_kv_pair
+                fluent::attr_parsing_unsupported_literal_deprecated_kv_pair
             }
         });
         diag.span(self.span);
@@ -219,7 +224,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
         if self.is_bytestr {
             diag.span_suggestion(
                 self.start_point_span,
-                fluent::attr_unsupported_literal_suggestion,
+                fluent::attr_parsing_unsupported_literal_suggestion,
                 "",
                 Applicability::MaybeIncorrect,
             );
@@ -229,7 +234,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_invalid_repr_align_need_arg, code = E0589)]
+#[diag(attr_parsing_invalid_repr_align_need_arg, code = E0589)]
 pub(crate) struct InvalidReprAlignNeedArg {
     #[primary_span]
     #[suggestion(code = "align(...)", applicability = "has-placeholders")]
@@ -237,7 +242,7 @@ pub(crate) struct InvalidReprAlignNeedArg {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_invalid_repr_generic, code = E0589)]
+#[diag(attr_parsing_invalid_repr_generic, code = E0589)]
 pub(crate) struct InvalidReprGeneric<'a> {
     #[primary_span]
     pub span: Span,
@@ -247,21 +252,21 @@ pub(crate) struct InvalidReprGeneric<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_incorrect_repr_format_align_one_arg, code = E0693)]
+#[diag(attr_parsing_incorrect_repr_format_align_one_arg, code = E0693)]
 pub(crate) struct IncorrectReprFormatAlignOneArg {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_incorrect_repr_format_expect_literal_integer, code = E0693)]
+#[diag(attr_parsing_incorrect_repr_format_expect_literal_integer, code = E0693)]
 pub(crate) struct IncorrectReprFormatExpectInteger {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_incorrect_repr_format_generic, code = E0693)]
+#[diag(attr_parsing_incorrect_repr_format_generic, code = E0693)]
 pub(crate) struct IncorrectReprFormatGeneric<'a> {
     #[primary_span]
     pub span: Span,
@@ -274,7 +279,11 @@ pub(crate) struct IncorrectReprFormatGeneric<'a> {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum IncorrectReprFormatGenericCause<'a> {
-    #[suggestion(attr_suggestion, code = "{name}({int})", applicability = "machine-applicable")]
+    #[suggestion(
+        attr_parsing_suggestion,
+        code = "{name}({int})",
+        applicability = "machine-applicable"
+    )]
     Int {
         #[primary_span]
         span: Span,
@@ -286,7 +295,11 @@ pub(crate) enum IncorrectReprFormatGenericCause<'a> {
         int: u128,
     },
 
-    #[suggestion(attr_suggestion, code = "{name}({symbol})", applicability = "machine-applicable")]
+    #[suggestion(
+        attr_parsing_suggestion,
+        code = "{name}({symbol})",
+        applicability = "machine-applicable"
+    )]
     Symbol {
         #[primary_span]
         span: Span,
@@ -312,35 +325,35 @@ impl<'a> IncorrectReprFormatGenericCause<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_rustc_promotable_pairing, code = E0717)]
+#[diag(attr_parsing_rustc_promotable_pairing, code = E0717)]
 pub(crate) struct RustcPromotablePairing {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_rustc_const_stable_indirect_pairing)]
+#[diag(attr_parsing_rustc_const_stable_indirect_pairing)]
 pub(crate) struct RustcConstStableIndirectPairing {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_rustc_allowed_unstable_pairing, code = E0789)]
+#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)]
 pub(crate) struct RustcAllowedUnstablePairing {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_cfg_predicate_identifier)]
+#[diag(attr_parsing_cfg_predicate_identifier)]
 pub(crate) struct CfgPredicateIdentifier {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_deprecated_item_suggestion)]
+#[diag(attr_parsing_deprecated_item_suggestion)]
 pub(crate) struct DeprecatedItemSuggestion {
     #[primary_span]
     pub span: Span,
@@ -353,21 +366,21 @@ pub(crate) struct DeprecatedItemSuggestion {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_expected_single_version_literal)]
+#[diag(attr_parsing_expected_single_version_literal)]
 pub(crate) struct ExpectedSingleVersionLiteral {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_expected_version_literal)]
+#[diag(attr_parsing_expected_version_literal)]
 pub(crate) struct ExpectedVersionLiteral {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_expects_feature_list)]
+#[diag(attr_parsing_expects_feature_list)]
 pub(crate) struct ExpectsFeatureList {
     #[primary_span]
     pub span: Span,
@@ -376,7 +389,7 @@ pub(crate) struct ExpectsFeatureList {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_expects_features)]
+#[diag(attr_parsing_expects_features)]
 pub(crate) struct ExpectsFeatures {
     #[primary_span]
     pub span: Span,
@@ -385,21 +398,21 @@ pub(crate) struct ExpectsFeatures {
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_invalid_since)]
+#[diag(attr_parsing_invalid_since)]
 pub(crate) struct InvalidSince {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_soft_no_args)]
+#[diag(attr_parsing_soft_no_args)]
 pub(crate) struct SoftNoArgs {
     #[primary_span]
     pub span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(attr_unknown_version_literal)]
+#[diag(attr_parsing_unknown_version_literal)]
 pub(crate) struct UnknownVersionLiteral {
     #[primary_span]
     pub span: Span,
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 8dcc5324fdf..8e5944d6cf4 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -34,8 +34,7 @@ use rustc_middle::util::CallKind;
 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::hygiene::DesugaringKind;
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{BytePos, Span, Symbol};
+use rustc_span::{BytePos, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
 use rustc_trait_selection::infer::InferCtxtExt;
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 45a8ef0adb6..22f7f708419 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -17,8 +17,7 @@ use rustc_middle::mir::{
 };
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
-use rustc_span::symbol::{Symbol, kw};
-use rustc_span::{DesugaringKind, Span, sym};
+use rustc_span::{DesugaringKind, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
 use tracing::{debug, instrument};
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 92afad62aa4..67be6ecdeb3 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -20,8 +20,7 @@ use rustc_middle::util::{CallDesugaringKind, call_kind};
 use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::sym;
-use rustc_span::{DUMMY_SP, Span, Symbol};
+use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 4fb7b22f289..2484f817a06 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -15,8 +15,7 @@ use rustc_middle::mir::{
     PlaceRef, ProjectionElem,
 };
 use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast};
-use rustc_span::symbol::{Symbol, kw};
-use rustc_span::{BytePos, DesugaringKind, Span, sym};
+use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits;
@@ -1474,16 +1473,27 @@ fn suggest_ampmut<'tcx>(
     // let x: &i32 = &'a 5;
     //                ^^ lifetime annotation not allowed
     //
-    if let Some(assignment_rhs_span) = opt_assignment_rhs_span
-        && let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span)
-        && let Some(stripped) = src.strip_prefix('&')
+    if let Some(rhs_span) = opt_assignment_rhs_span
+        && let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
+        && let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&')
     {
-        let is_raw_ref = stripped.trim_start().starts_with("raw ");
-        // We don't support raw refs yet
-        if is_raw_ref {
-            return None;
+        // Suggest changing `&raw const` to `&raw mut` if applicable.
+        if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() {
+            let const_idx = rhs_str.find("const").unwrap() as u32;
+            let const_span = rhs_span
+                .with_lo(rhs_span.lo() + BytePos(const_idx))
+                .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32));
+
+            return Some(AmpMutSugg {
+                has_sugg: true,
+                span: const_span,
+                suggestion: "mut".to_owned(),
+                additional: None,
+            });
         }
-        let is_mut = if let Some(rest) = stripped.trim_start().strip_prefix("mut") {
+
+        // Figure out if rhs already is `&mut`.
+        let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") {
             match rest.chars().next() {
                 // e.g. `&mut x`
                 Some(c) if c.is_whitespace() => true,
@@ -1500,9 +1510,8 @@ fn suggest_ampmut<'tcx>(
         // if the reference is already mutable then there is nothing we can do
         // here.
         if !is_mut {
-            let span = assignment_rhs_span;
             // shrink the span to just after the `&` in `&variable`
-            let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
+            let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo();
 
             // FIXME(Ezrashaw): returning is bad because we still might want to
             // update the annotated type, see #106857.
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 6ea5c44dc01..4e6d349d761 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -15,8 +15,7 @@ use rustc_middle::bug;
 use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
 use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, kw};
+use rustc_span::{Ident, Span, kw};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::error_reporting::infer::nice_region_error::{
     self, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, find_anon_type,
@@ -848,7 +847,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 return;
             };
 
-            let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.def_id);
+            let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.scope);
 
             let param = if let Some(param) =
                 find_param_with_region(self.infcx.tcx, self.mir_def_id(), f, outlived_f)
@@ -875,7 +874,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     Some(arg),
                     captures,
                     Some((param.param_ty_span, param.param_ty.to_string())),
-                    Some(suitable_region.def_id),
+                    Some(suitable_region.scope),
                 );
                 return;
             }
@@ -883,7 +882,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             let Some((alias_tys, alias_span, lt_addition_span)) = self
                 .infcx
                 .tcx
-                .return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id)
+                .return_type_impl_or_dyn_traits_with_type_alias(suitable_region.scope)
             else {
                 return;
             };
@@ -1018,18 +1017,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             return;
         };
 
-        let Some((ty_sub, _)) =
-            self.infcx.tcx.is_suitable_region(self.mir_def_id(), sub).and_then(|anon_reg| {
-                find_anon_type(self.infcx.tcx, self.mir_def_id(), sub, &anon_reg.bound_region)
-            })
+        let Some((ty_sub, _)) = self
+            .infcx
+            .tcx
+            .is_suitable_region(self.mir_def_id(), sub)
+            .and_then(|_| find_anon_type(self.infcx.tcx, self.mir_def_id(), sub))
         else {
             return;
         };
 
-        let Some((ty_sup, _)) =
-            self.infcx.tcx.is_suitable_region(self.mir_def_id(), sup).and_then(|anon_reg| {
-                find_anon_type(self.infcx.tcx, self.mir_def_id(), sup, &anon_reg.bound_region)
-            })
+        let Some((ty_sup, _)) = self
+            .infcx
+            .tcx
+            .is_suitable_region(self.mir_def_id(), sup)
+            .and_then(|_| find_anon_type(self.infcx.tcx, self.mir_def_id(), sup))
         else {
             return;
         };
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index d49dee85144..34680c2d0af 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -11,8 +11,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_middle::ty::print::RegionHighlightMode;
 use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, RegionVid, Ty};
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::{Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use tracing::{debug, instrument};
 
@@ -459,11 +458,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
     ) -> RegionNameHighlight {
         let mut highlight = RegionHighlightMode::default();
         highlight.highlighting_region_vid(self.infcx.tcx, needle_fr, counter);
-        let type_name = self
-            .infcx
-            .err_ctxt()
-            .extract_inference_diagnostics_data(ty.into(), Some(highlight))
-            .name;
+        let type_name =
+            self.infcx.err_ctxt().extract_inference_diagnostics_data(ty.into(), highlight).name;
 
         debug!(
             "highlight_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}",
@@ -874,7 +870,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         let type_name = self
             .infcx
             .err_ctxt()
-            .extract_inference_diagnostics_data(yield_ty.into(), Some(highlight))
+            .extract_inference_diagnostics_data(yield_ty.into(), highlight)
             .name;
 
         let yield_span = match tcx.hir_node(self.mir_hir_id()) {
diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
index bda70241267..191c0444c74 100644
--- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
@@ -1,8 +1,7 @@
 use rustc_index::IndexSlice;
 use rustc_middle::mir::{Body, Local};
 use rustc_middle::ty::{self, RegionVid, TyCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::{Span, Symbol};
 use tracing::debug;
 
 use crate::region_infer::RegionInferenceContext;
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 18b7984e90d..63e20b16f7a 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -141,6 +141,9 @@ fn do_mir_borrowck<'tcx>(
 ) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
     let def = input_body.source.def_id().expect_local();
     let infcx = BorrowckInferCtxt::new(tcx, def);
+    if let Some(e) = input_body.tainted_by_errors {
+        infcx.set_tainted_by_errors(e);
+    }
 
     let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
     for var_debug_info in &input_body.var_debug_info {
@@ -162,13 +165,6 @@ fn do_mir_borrowck<'tcx>(
         }
     }
 
-    let diags = &mut diags::BorrowckDiags::new();
-
-    // Gather the upvars of a closure, if any.
-    if let Some(e) = input_body.tainted_by_errors {
-        infcx.set_tainted_by_errors(e);
-    }
-
     // Replace all regions with fresh inference variables. This
     // requires first making our own copy of the MIR. This copy will
     // be modified (in place) to contain non-lexical lifetimes. It
@@ -224,6 +220,7 @@ fn do_mir_borrowck<'tcx>(
 
     // We also have a `#[rustc_regions]` annotation that causes us to dump
     // information.
+    let diags = &mut diags::BorrowckDiags::new();
     nll::dump_annotation(&infcx, body, &regioncx, &opt_closure_req, &opaque_type_values, diags);
 
     let movable_coroutine =
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index be02e2f48df..7656031ed3a 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -21,7 +21,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
 use rustc_mir_dataflow::move_paths::MoveData;
 use rustc_mir_dataflow::points::DenseLocationMap;
 use rustc_session::config::MirIncludeSpans;
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 use tracing::{debug, instrument};
 
 use crate::borrow_set::BorrowSet;
@@ -116,7 +116,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
     let var_origins = infcx.get_region_var_origins();
 
     // If requested, emit legacy polonius facts.
-    polonius::emit_facts(
+    polonius::legacy::emit_facts(
         &mut all_facts,
         infcx.tcx,
         location_table,
@@ -124,6 +124,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
         borrow_set,
         move_data,
         &universal_region_relations,
+        &constraints,
     );
 
     let mut regioncx = RegionInferenceContext::new(
diff --git a/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs
new file mode 100644
index 00000000000..9c4aa8ea1ed
--- /dev/null
+++ b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs
@@ -0,0 +1,85 @@
+use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::{Body, Local, Location, Place};
+use rustc_middle::ty::TyCtxt;
+use rustc_mir_dataflow::move_paths::{LookupResult, MoveData};
+use tracing::debug;
+
+use crate::def_use::{self, DefUse};
+use crate::facts::AllFacts;
+use crate::location::{LocationIndex, LocationTable};
+use crate::universal_regions::UniversalRegions;
+
+/// Emit polonius facts for variable defs, uses, drops, and path accesses.
+pub(crate) fn emit_access_facts<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    facts: &mut AllFacts,
+    body: &Body<'tcx>,
+    location_table: &LocationTable,
+    move_data: &MoveData<'tcx>,
+    universal_regions: &UniversalRegions<'tcx>,
+) {
+    let mut extractor = AccessFactsExtractor { facts, move_data, location_table };
+    extractor.visit_body(body);
+
+    for (local, local_decl) in body.local_decls.iter_enumerated() {
+        debug!("add use_of_var_derefs_origin facts - local={:?}, type={:?}", local, local_decl.ty);
+        tcx.for_each_free_region(&local_decl.ty, |region| {
+            let region_vid = universal_regions.to_region_vid(region);
+            facts.use_of_var_derefs_origin.push((local, region_vid.into()));
+        });
+    }
+}
+
+/// MIR visitor extracting point-wise facts about accesses.
+struct AccessFactsExtractor<'a, 'tcx> {
+    facts: &'a mut AllFacts,
+    move_data: &'a MoveData<'tcx>,
+    location_table: &'a LocationTable,
+}
+
+impl<'tcx> AccessFactsExtractor<'_, 'tcx> {
+    fn location_to_index(&self, location: Location) -> LocationIndex {
+        self.location_table.mid_index(location)
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for AccessFactsExtractor<'a, 'tcx> {
+    fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
+        match def_use::categorize(context) {
+            Some(DefUse::Def) => {
+                debug!("AccessFactsExtractor - emit def");
+                self.facts.var_defined_at.push((local, self.location_to_index(location)));
+            }
+            Some(DefUse::Use) => {
+                debug!("AccessFactsExtractor - emit use");
+                self.facts.var_used_at.push((local, self.location_to_index(location)));
+            }
+            Some(DefUse::Drop) => {
+                debug!("AccessFactsExtractor - emit drop");
+                self.facts.var_dropped_at.push((local, self.location_to_index(location)));
+            }
+            _ => (),
+        }
+    }
+
+    fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
+        self.super_place(place, context, location);
+
+        match context {
+            PlaceContext::NonMutatingUse(_)
+            | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
+                let path = match self.move_data.rev_lookup.find(place.as_ref()) {
+                    LookupResult::Exact(path) | LookupResult::Parent(Some(path)) => path,
+                    _ => {
+                        // There's no path access to emit.
+                        return;
+                    }
+                };
+                debug!("AccessFactsExtractor - emit path access ({path:?}, {location:?})");
+                self.facts.path_accessed_at_base.push((path, self.location_to_index(location)));
+            }
+
+            _ => {}
+        }
+    }
+}
diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
index f646beeecf7..0d5b6f3a2c8 100644
--- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
+++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
@@ -21,22 +21,22 @@ use crate::{
 /// Emit `loan_invalidated_at` facts.
 pub(super) fn emit_loan_invalidations<'tcx>(
     tcx: TyCtxt<'tcx>,
-    all_facts: &mut AllFacts,
-    location_table: &LocationTable,
+    facts: &mut AllFacts,
     body: &Body<'tcx>,
+    location_table: &LocationTable,
     borrow_set: &BorrowSet<'tcx>,
 ) {
     let dominators = body.basic_blocks.dominators();
     let mut visitor =
-        LoanInvalidationsGenerator { all_facts, borrow_set, tcx, location_table, body, dominators };
+        LoanInvalidationsGenerator { facts, borrow_set, tcx, location_table, body, dominators };
     visitor.visit_body(body);
 }
 
 struct LoanInvalidationsGenerator<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    all_facts: &'a mut AllFacts,
-    location_table: &'a LocationTable,
+    facts: &'a mut AllFacts,
     body: &'a Body<'tcx>,
+    location_table: &'a LocationTable,
     dominators: &'a Dominators<BasicBlock>,
     borrow_set: &'a BorrowSet<'tcx>,
 }
@@ -151,7 +151,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
                 let resume = self.location_table.start_index(resume.start_location());
                 for (i, data) in borrow_set.iter_enumerated() {
                     if borrow_of_local_data(data.borrowed_place) {
-                        self.all_facts.loan_invalidated_at.push((resume, i));
+                        self.facts.loan_invalidated_at.push((resume, i));
                     }
                 }
 
@@ -165,7 +165,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
                 let start = self.location_table.start_index(location);
                 for (i, data) in borrow_set.iter_enumerated() {
                     if borrow_of_local_data(data.borrowed_place) {
-                        self.all_facts.loan_invalidated_at.push((start, i));
+                        self.facts.loan_invalidated_at.push((start, i));
                     }
                 }
             }
@@ -409,7 +409,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
     /// Generates a new `loan_invalidated_at(L, B)` fact.
     fn emit_loan_invalidated_at(&mut self, b: BorrowIndex, l: Location) {
         let lidx = self.location_table.start_index(l);
-        self.all_facts.loan_invalidated_at.push((lidx, b));
+        self.facts.loan_invalidated_at.push((lidx, b));
     }
 
     fn check_activations(&mut self, location: Location) {
diff --git a/compiler/rustc_borrowck/src/polonius/loan_kills.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs
index 68e0865ab82..fdde9fa0476 100644
--- a/compiler/rustc_borrowck/src/polonius/loan_kills.rs
+++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs
@@ -14,12 +14,12 @@ use crate::places_conflict;
 /// Emit `loan_killed_at` and `cfg_edge` facts at the same time.
 pub(super) fn emit_loan_kills<'tcx>(
     tcx: TyCtxt<'tcx>,
-    all_facts: &mut AllFacts,
-    location_table: &LocationTable,
+    facts: &mut AllFacts,
     body: &Body<'tcx>,
+    location_table: &LocationTable,
     borrow_set: &BorrowSet<'tcx>,
 ) {
-    let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, all_facts, body };
+    let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, facts, body };
     for (bb, data) in body.basic_blocks.iter_enumerated() {
         visitor.visit_basic_block_data(bb, data);
     }
@@ -27,7 +27,7 @@ pub(super) fn emit_loan_kills<'tcx>(
 
 struct LoanKillsGenerator<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    all_facts: &'a mut AllFacts,
+    facts: &'a mut AllFacts,
     location_table: &'a LocationTable,
     borrow_set: &'a BorrowSet<'tcx>,
     body: &'a Body<'tcx>,
@@ -36,12 +36,12 @@ struct LoanKillsGenerator<'a, 'tcx> {
 impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> {
     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
         // Also record CFG facts here.
-        self.all_facts.cfg_edge.push((
+        self.facts.cfg_edge.push((
             self.location_table.start_index(location),
             self.location_table.mid_index(location),
         ));
 
-        self.all_facts.cfg_edge.push((
+        self.facts.cfg_edge.push((
             self.location_table.mid_index(location),
             self.location_table.start_index(location.successor_within_block()),
         ));
@@ -63,15 +63,15 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> {
 
     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
         // Also record CFG facts here.
-        self.all_facts.cfg_edge.push((
+        self.facts.cfg_edge.push((
             self.location_table.start_index(location),
             self.location_table.mid_index(location),
         ));
 
         let successor_blocks = terminator.successors();
-        self.all_facts.cfg_edge.reserve(successor_blocks.size_hint().0);
+        self.facts.cfg_edge.reserve(successor_blocks.size_hint().0);
         for successor_block in successor_blocks {
-            self.all_facts.cfg_edge.push((
+            self.facts.cfg_edge.push((
                 self.location_table.mid_index(location),
                 self.location_table.start_index(successor_block.start_location()),
             ));
@@ -128,7 +128,7 @@ impl<'tcx> LoanKillsGenerator<'_, 'tcx> {
 
                         if places_conflict {
                             let location_index = self.location_table.mid_index(location);
-                            self.all_facts.loan_killed_at.push((borrow_index, location_index));
+                            self.facts.loan_killed_at.push((borrow_index, location_index));
                         }
                     }
                 }
@@ -140,9 +140,9 @@ impl<'tcx> LoanKillsGenerator<'_, 'tcx> {
     fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) {
         if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
             let location_index = self.location_table.mid_index(location);
-            self.all_facts.loan_killed_at.reserve(borrow_indices.len());
+            self.facts.loan_killed_at.reserve(borrow_indices.len());
             for &borrow_index in borrow_indices {
-                self.all_facts.loan_killed_at.push((borrow_index, location_index));
+                self.facts.loan_killed_at.push((borrow_index, location_index));
             }
         }
     }
diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs
new file mode 100644
index 00000000000..60fd2afe63e
--- /dev/null
+++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs
@@ -0,0 +1,223 @@
+//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation.
+//!
+//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature
+//! parity.
+
+use std::iter;
+
+use either::Either;
+use rustc_middle::mir::{Body, Local, LocalKind, Location, START_BLOCK};
+use rustc_middle::ty::{GenericArg, TyCtxt};
+use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData};
+use tracing::debug;
+
+use crate::borrow_set::BorrowSet;
+use crate::constraints::OutlivesConstraint;
+use crate::facts::{AllFacts, PoloniusRegionVid};
+use crate::location::LocationTable;
+use crate::type_check::MirTypeckRegionConstraints;
+use crate::type_check::free_region_relations::UniversalRegionRelations;
+use crate::universal_regions::UniversalRegions;
+
+mod accesses;
+mod loan_invalidations;
+mod loan_kills;
+
+/// When requested, emit most of the facts needed by polonius:
+/// - moves and assignments
+/// - universal regions and their relations
+/// - CFG points and edges
+/// - loan kills
+/// - loan invalidations
+/// - access facts such as variable definitions, uses, drops, and path accesses
+/// - outlives constraints
+///
+/// The rest of the facts are emitted during typeck and liveness.
+pub(crate) fn emit_facts<'tcx>(
+    all_facts: &mut Option<AllFacts>,
+    tcx: TyCtxt<'tcx>,
+    location_table: &LocationTable,
+    body: &Body<'tcx>,
+    borrow_set: &BorrowSet<'tcx>,
+    move_data: &MoveData<'tcx>,
+    universal_region_relations: &UniversalRegionRelations<'tcx>,
+    constraints: &MirTypeckRegionConstraints<'tcx>,
+) {
+    let Some(facts) = all_facts else {
+        // We don't do anything if there are no facts to fill.
+        return;
+    };
+    let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
+    emit_move_facts(facts, body, location_table, move_data);
+    emit_universal_region_facts(facts, borrow_set, universal_region_relations);
+    loan_kills::emit_loan_kills(tcx, facts, body, location_table, borrow_set);
+    loan_invalidations::emit_loan_invalidations(tcx, facts, body, location_table, borrow_set);
+    accesses::emit_access_facts(
+        tcx,
+        facts,
+        body,
+        location_table,
+        move_data,
+        &universal_region_relations.universal_regions,
+    );
+    emit_outlives_facts(facts, location_table, constraints);
+}
+
+/// Emit facts needed for move/init analysis: moves and assignments.
+fn emit_move_facts(
+    facts: &mut AllFacts,
+    body: &Body<'_>,
+    location_table: &LocationTable,
+    move_data: &MoveData<'_>,
+) {
+    facts.path_is_var.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
+
+    for (child, move_path) in move_data.move_paths.iter_enumerated() {
+        if let Some(parent) = move_path.parent {
+            facts.child_path.push((child, parent));
+        }
+    }
+
+    let fn_entry_start =
+        location_table.start_index(Location { block: START_BLOCK, statement_index: 0 });
+
+    // initialized_at
+    for init in move_data.inits.iter() {
+        match init.location {
+            InitLocation::Statement(location) => {
+                let block_data = &body[location.block];
+                let is_terminator = location.statement_index == block_data.statements.len();
+
+                if is_terminator && init.kind == InitKind::NonPanicPathOnly {
+                    // We are at the terminator of an init that has a panic path,
+                    // and where the init should not happen on panic
+
+                    for successor in block_data.terminator().successors() {
+                        if body[successor].is_cleanup {
+                            continue;
+                        }
+
+                        // The initialization happened in (or rather, when arriving at)
+                        // the successors, but not in the unwind block.
+                        let first_statement = Location { block: successor, statement_index: 0 };
+                        facts
+                            .path_assigned_at_base
+                            .push((init.path, location_table.start_index(first_statement)));
+                    }
+                } else {
+                    // In all other cases, the initialization just happens at the
+                    // midpoint, like any other effect.
+                    facts
+                        .path_assigned_at_base
+                        .push((init.path, location_table.mid_index(location)));
+                }
+            }
+            // Arguments are initialized on function entry
+            InitLocation::Argument(local) => {
+                assert!(body.local_kind(local) == LocalKind::Arg);
+                facts.path_assigned_at_base.push((init.path, fn_entry_start));
+            }
+        }
+    }
+
+    for (local, path) in move_data.rev_lookup.iter_locals_enumerated() {
+        if body.local_kind(local) != LocalKind::Arg {
+            // Non-arguments start out deinitialised; we simulate this with an
+            // initial move:
+            facts.path_moved_at_base.push((path, fn_entry_start));
+        }
+    }
+
+    // moved_out_at
+    // deinitialisation is assumed to always happen!
+    facts
+        .path_moved_at_base
+        .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))));
+}
+
+/// Emit universal regions facts, and their relations.
+fn emit_universal_region_facts(
+    facts: &mut AllFacts,
+    borrow_set: &BorrowSet<'_>,
+    universal_region_relations: &UniversalRegionRelations<'_>,
+) {
+    // 1: universal regions are modeled in Polonius as a pair:
+    // - the universal region vid itself.
+    // - a "placeholder loan" associated to this universal region. Since they don't exist in
+    //   the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
+    //   added to the existing number of loans, as if they succeeded them in the set.
+    //
+    let universal_regions = &universal_region_relations.universal_regions;
+    facts
+        .universal_region
+        .extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from));
+    let borrow_count = borrow_set.len();
+    debug!(
+        "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}",
+        universal_regions.len(),
+        borrow_count
+    );
+
+    for universal_region in universal_regions.universal_regions_iter() {
+        let universal_region_idx = universal_region.index();
+        let placeholder_loan_idx = borrow_count + universal_region_idx;
+        facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into()));
+    }
+
+    // 2: the universal region relations `outlives` constraints are emitted as
+    //  `known_placeholder_subset` facts.
+    for (fr1, fr2) in universal_region_relations.known_outlives() {
+        if fr1 != fr2 {
+            debug!(
+                "emit_universal_region_facts: emitting polonius `known_placeholder_subset` \
+                     fr1={:?}, fr2={:?}",
+                fr1, fr2
+            );
+            facts.known_placeholder_subset.push((fr1.into(), fr2.into()));
+        }
+    }
+}
+
+/// For every potentially drop()-touched region `region` in `local`'s type
+/// (`kind`), emit a `drop_of_var_derefs_origin(local, origin)` fact.
+pub(crate) fn emit_drop_facts<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    local: Local,
+    kind: &GenericArg<'tcx>,
+    universal_regions: &UniversalRegions<'tcx>,
+    all_facts: &mut Option<AllFacts>,
+) {
+    debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind);
+    let Some(facts) = all_facts.as_mut() else { return };
+    let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
+    tcx.for_each_free_region(kind, |drop_live_region| {
+        let region_vid = universal_regions.to_region_vid(drop_live_region);
+        facts.drop_of_var_derefs_origin.push((local, region_vid.into()));
+    });
+}
+
+/// Emit facts about the outlives constraints: the `subset` base relation, i.e. not a transitive
+/// closure.
+fn emit_outlives_facts<'tcx>(
+    facts: &mut AllFacts,
+    location_table: &LocationTable,
+    constraints: &MirTypeckRegionConstraints<'tcx>,
+) {
+    facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map(
+        |constraint: &OutlivesConstraint<'_>| {
+            if let Some(from_location) = constraint.locations.from_location() {
+                Either::Left(iter::once((
+                    constraint.sup.into(),
+                    constraint.sub.into(),
+                    location_table.mid_index(from_location),
+                )))
+            } else {
+                Either::Right(
+                    location_table.all_points().map(move |location| {
+                        (constraint.sup.into(), constraint.sub.into(), location)
+                    }),
+                )
+            }
+        },
+    ));
+}
diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs
index 9fccc00bdaf..9c1583f1988 100644
--- a/compiler/rustc_borrowck/src/polonius/mod.rs
+++ b/compiler/rustc_borrowck/src/polonius/mod.rs
@@ -1,184 +1 @@
-//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation.
-//!
-//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature
-//! parity.
-
-use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK};
-use rustc_middle::ty::TyCtxt;
-use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData};
-use tracing::debug;
-
-use crate::borrow_set::BorrowSet;
-use crate::facts::{AllFacts, PoloniusRegionVid};
-use crate::location::LocationTable;
-use crate::type_check::free_region_relations::UniversalRegionRelations;
-
-mod loan_invalidations;
-mod loan_kills;
-
-/// When requested, emit most of the facts needed by polonius:
-/// - moves and assignments
-/// - universal regions and their relations
-/// - CFG points and edges
-/// - loan kills
-/// - loan invalidations
-///
-/// The rest of the facts are emitted during typeck and liveness.
-pub(crate) fn emit_facts<'tcx>(
-    all_facts: &mut Option<AllFacts>,
-    tcx: TyCtxt<'tcx>,
-    location_table: &LocationTable,
-    body: &Body<'tcx>,
-    borrow_set: &BorrowSet<'tcx>,
-    move_data: &MoveData<'_>,
-    universal_region_relations: &UniversalRegionRelations<'_>,
-) {
-    let Some(all_facts) = all_facts else {
-        // We don't do anything if there are no facts to fill.
-        return;
-    };
-    let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
-    emit_move_facts(all_facts, move_data, location_table, body);
-    emit_universal_region_facts(all_facts, borrow_set, universal_region_relations);
-    emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set);
-    emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set);
-}
-
-/// Emit facts needed for move/init analysis: moves and assignments.
-fn emit_move_facts(
-    all_facts: &mut AllFacts,
-    move_data: &MoveData<'_>,
-    location_table: &LocationTable,
-    body: &Body<'_>,
-) {
-    all_facts
-        .path_is_var
-        .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
-
-    for (child, move_path) in move_data.move_paths.iter_enumerated() {
-        if let Some(parent) = move_path.parent {
-            all_facts.child_path.push((child, parent));
-        }
-    }
-
-    let fn_entry_start =
-        location_table.start_index(Location { block: START_BLOCK, statement_index: 0 });
-
-    // initialized_at
-    for init in move_data.inits.iter() {
-        match init.location {
-            InitLocation::Statement(location) => {
-                let block_data = &body[location.block];
-                let is_terminator = location.statement_index == block_data.statements.len();
-
-                if is_terminator && init.kind == InitKind::NonPanicPathOnly {
-                    // We are at the terminator of an init that has a panic path,
-                    // and where the init should not happen on panic
-
-                    for successor in block_data.terminator().successors() {
-                        if body[successor].is_cleanup {
-                            continue;
-                        }
-
-                        // The initialization happened in (or rather, when arriving at)
-                        // the successors, but not in the unwind block.
-                        let first_statement = Location { block: successor, statement_index: 0 };
-                        all_facts
-                            .path_assigned_at_base
-                            .push((init.path, location_table.start_index(first_statement)));
-                    }
-                } else {
-                    // In all other cases, the initialization just happens at the
-                    // midpoint, like any other effect.
-                    all_facts
-                        .path_assigned_at_base
-                        .push((init.path, location_table.mid_index(location)));
-                }
-            }
-            // Arguments are initialized on function entry
-            InitLocation::Argument(local) => {
-                assert!(body.local_kind(local) == LocalKind::Arg);
-                all_facts.path_assigned_at_base.push((init.path, fn_entry_start));
-            }
-        }
-    }
-
-    for (local, path) in move_data.rev_lookup.iter_locals_enumerated() {
-        if body.local_kind(local) != LocalKind::Arg {
-            // Non-arguments start out deinitialised; we simulate this with an
-            // initial move:
-            all_facts.path_moved_at_base.push((path, fn_entry_start));
-        }
-    }
-
-    // moved_out_at
-    // deinitialisation is assumed to always happen!
-    all_facts
-        .path_moved_at_base
-        .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))));
-}
-
-/// Emit universal regions facts, and their relations.
-fn emit_universal_region_facts(
-    all_facts: &mut AllFacts,
-    borrow_set: &BorrowSet<'_>,
-    universal_region_relations: &UniversalRegionRelations<'_>,
-) {
-    // 1: universal regions are modeled in Polonius as a pair:
-    // - the universal region vid itself.
-    // - a "placeholder loan" associated to this universal region. Since they don't exist in
-    //   the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
-    //   added to the existing number of loans, as if they succeeded them in the set.
-    //
-    let universal_regions = &universal_region_relations.universal_regions;
-    all_facts
-        .universal_region
-        .extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from));
-    let borrow_count = borrow_set.len();
-    debug!(
-        "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}",
-        universal_regions.len(),
-        borrow_count
-    );
-
-    for universal_region in universal_regions.universal_regions_iter() {
-        let universal_region_idx = universal_region.index();
-        let placeholder_loan_idx = borrow_count + universal_region_idx;
-        all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into()));
-    }
-
-    // 2: the universal region relations `outlives` constraints are emitted as
-    //  `known_placeholder_subset` facts.
-    for (fr1, fr2) in universal_region_relations.known_outlives() {
-        if fr1 != fr2 {
-            debug!(
-                "emit_universal_region_facts: emitting polonius `known_placeholder_subset` \
-                     fr1={:?}, fr2={:?}",
-                fr1, fr2
-            );
-            all_facts.known_placeholder_subset.push((fr1.into(), fr2.into()));
-        }
-    }
-}
-
-/// Emit facts about loan invalidations.
-fn emit_loan_invalidations_facts<'tcx>(
-    all_facts: &mut AllFacts,
-    tcx: TyCtxt<'tcx>,
-    location_table: &LocationTable,
-    body: &Body<'tcx>,
-    borrow_set: &BorrowSet<'tcx>,
-) {
-    loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set);
-}
-
-/// Emit facts about CFG points and edges, as well as locations where loans are killed.
-fn emit_cfg_and_loan_kills_facts<'tcx>(
-    all_facts: &mut AllFacts,
-    tcx: TyCtxt<'tcx>,
-    location_table: &LocationTable,
-    body: &Body<'tcx>,
-    borrow_set: &BorrowSet<'tcx>,
-) {
-    loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set);
-}
+pub(crate) mod legacy;
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 7164a129235..3e16a3ca157 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -134,12 +134,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             // the hidden type becomes the opaque type itself. In this case, this was an opaque
             // usage of the opaque type and we can ignore it. This check is mirrored in typeck's
             // writeback.
-            // FIXME(-Znext-solver): This should be unnecessary with the new solver.
-            if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
-                && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
-                && alias_ty.args == opaque_type_key.args
-            {
-                continue;
+            if !infcx.next_trait_solver() {
+                if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
+                    && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
+                    && alias_ty.args == opaque_type_key.args
+                {
+                    continue;
+                }
             }
             // Sometimes two opaque types are the same only after we remap the generic parameters
             // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
index 20d19a53752..683293bf828 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
@@ -17,7 +17,6 @@ use crate::region_infer::values::LivenessValues;
 use crate::universal_regions::UniversalRegions;
 
 mod local_use_map;
-mod polonius;
 mod trace;
 
 /// Combines liveness analysis with initialization analysis to
@@ -45,8 +44,6 @@ pub(super) fn generate<'a, 'tcx>(
     let (relevant_live_locals, boring_locals) =
         compute_relevant_live_locals(typeck.tcx(), &free_regions, body);
 
-    polonius::populate_access_facts(typeck, body, move_data);
-
     trace::trace(
         typeck,
         body,
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs
deleted file mode 100644
index e8d8ae0850b..00000000000
--- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
-use rustc_middle::mir::{Body, Local, Location, Place};
-use rustc_middle::ty::GenericArg;
-use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
-use tracing::debug;
-
-use super::TypeChecker;
-use crate::def_use::{self, DefUse};
-use crate::location::{LocationIndex, LocationTable};
-
-type VarPointRelation = Vec<(Local, LocationIndex)>;
-type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>;
-
-struct UseFactsExtractor<'a, 'tcx> {
-    var_defined_at: &'a mut VarPointRelation,
-    var_used_at: &'a mut VarPointRelation,
-    location_table: &'a LocationTable,
-    var_dropped_at: &'a mut VarPointRelation,
-    move_data: &'a MoveData<'tcx>,
-    path_accessed_at_base: &'a mut PathPointRelation,
-}
-
-// A Visitor to walk through the MIR and extract point-wise facts
-impl<'tcx> UseFactsExtractor<'_, 'tcx> {
-    fn location_to_index(&self, location: Location) -> LocationIndex {
-        self.location_table.mid_index(location)
-    }
-
-    fn insert_def(&mut self, local: Local, location: Location) {
-        debug!("UseFactsExtractor::insert_def()");
-        self.var_defined_at.push((local, self.location_to_index(location)));
-    }
-
-    fn insert_use(&mut self, local: Local, location: Location) {
-        debug!("UseFactsExtractor::insert_use()");
-        self.var_used_at.push((local, self.location_to_index(location)));
-    }
-
-    fn insert_drop_use(&mut self, local: Local, location: Location) {
-        debug!("UseFactsExtractor::insert_drop_use()");
-        self.var_dropped_at.push((local, self.location_to_index(location)));
-    }
-
-    fn insert_path_access(&mut self, path: MovePathIndex, location: Location) {
-        debug!("UseFactsExtractor::insert_path_access({:?}, {:?})", path, location);
-        self.path_accessed_at_base.push((path, self.location_to_index(location)));
-    }
-
-    fn place_to_mpi(&self, place: &Place<'tcx>) -> Option<MovePathIndex> {
-        match self.move_data.rev_lookup.find(place.as_ref()) {
-            LookupResult::Exact(mpi) => Some(mpi),
-            LookupResult::Parent(mmpi) => mmpi,
-        }
-    }
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for UseFactsExtractor<'a, 'tcx> {
-    fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
-        match def_use::categorize(context) {
-            Some(DefUse::Def) => self.insert_def(local, location),
-            Some(DefUse::Use) => self.insert_use(local, location),
-            Some(DefUse::Drop) => self.insert_drop_use(local, location),
-            _ => (),
-        }
-    }
-
-    fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
-        self.super_place(place, context, location);
-        match context {
-            PlaceContext::NonMutatingUse(_) => {
-                if let Some(mpi) = self.place_to_mpi(place) {
-                    self.insert_path_access(mpi, location);
-                }
-            }
-
-            PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
-                if let Some(mpi) = self.place_to_mpi(place) {
-                    self.insert_path_access(mpi, location);
-                }
-            }
-            _ => (),
-        }
-    }
-}
-
-pub(super) fn populate_access_facts<'a, 'tcx>(
-    typeck: &mut TypeChecker<'a, 'tcx>,
-    body: &Body<'tcx>,
-    move_data: &MoveData<'tcx>,
-) {
-    if let Some(facts) = typeck.all_facts.as_mut() {
-        debug!("populate_access_facts()");
-        let location_table = typeck.location_table;
-
-        let mut extractor = UseFactsExtractor {
-            var_defined_at: &mut facts.var_defined_at,
-            var_used_at: &mut facts.var_used_at,
-            var_dropped_at: &mut facts.var_dropped_at,
-            path_accessed_at_base: &mut facts.path_accessed_at_base,
-            location_table,
-            move_data,
-        };
-        extractor.visit_body(body);
-
-        for (local, local_decl) in body.local_decls.iter_enumerated() {
-            debug!(
-                "add use_of_var_derefs_origin facts - local={:?}, type={:?}",
-                local, local_decl.ty
-            );
-            let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
-            let universal_regions = &typeck.universal_regions;
-            typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| {
-                let region_vid = universal_regions.to_region_vid(region);
-                facts.use_of_var_derefs_origin.push((local, region_vid.into()));
-            });
-        }
-    }
-}
-
-/// For every potentially drop()-touched region `region` in `local`'s type
-/// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact.
-pub(super) fn add_drop_of_var_derefs_origin<'tcx>(
-    typeck: &mut TypeChecker<'_, 'tcx>,
-    local: Local,
-    kind: &GenericArg<'tcx>,
-) {
-    debug!("add_drop_of_var_derefs_origin(local={:?}, kind={:?}", local, kind);
-    if let Some(facts) = typeck.all_facts.as_mut() {
-        let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
-        let universal_regions = &typeck.universal_regions;
-        typeck.infcx.tcx.for_each_free_region(kind, |drop_live_region| {
-            let region_vid = universal_regions.to_region_vid(drop_live_region);
-            facts.drop_of_var_derefs_origin.push((local, region_vid.into()));
-        });
-    }
-}
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index 3ec36c16cbf..f510d193dd9 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -15,9 +15,9 @@ use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, Type
 use tracing::debug;
 
 use crate::location::RichLocation;
+use crate::polonius;
 use crate::region_infer::values::{self, LiveLoans};
 use crate::type_check::liveness::local_use_map::LocalUseMap;
-use crate::type_check::liveness::polonius;
 use crate::type_check::{NormalizeLocation, TypeChecker};
 
 /// This is the heart of the liveness computation. For each variable X
@@ -590,7 +590,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
         // the destructor and must be live at this point.
         for &kind in &drop_data.dropck_result.kinds {
             Self::make_all_regions_live(self.elements, self.typeck, kind, live_at);
-            polonius::add_drop_of_var_derefs_origin(self.typeck, dropped_local, &kind);
+            polonius::legacy::emit_drop_facts(
+                self.typeck.tcx(),
+                dropped_local,
+                &kind,
+                self.typeck.universal_regions,
+                self.typeck.all_facts,
+            );
         }
     }
 
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 90d327b0ad2..0c59813d124 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -3,7 +3,6 @@
 use std::rc::Rc;
 use std::{fmt, iter, mem};
 
-use either::Either;
 use rustc_abi::{FIRST_VARIANT, FieldIdx};
 use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
@@ -40,8 +39,7 @@ use rustc_mir_dataflow::move_paths::MoveData;
 use rustc_mir_dataflow::points::DenseLocationMap;
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::sym;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, sym};
 use rustc_trait_selection::traits::query::type_op::custom::{
     CustomTypeOp, scrape_region_constraints,
 };
@@ -107,7 +105,6 @@ mod relate_tys;
 /// # Parameters
 ///
 /// - `infcx` -- inference context to use
-/// - `param_env` -- parameter environment to use for trait solving
 /// - `body` -- MIR body to type-check
 /// - `promoted` -- map of promoted constants within `body`
 /// - `universal_regions` -- the universal regions from `body`s function signature
@@ -155,7 +152,7 @@ pub(crate) fn type_check<'a, 'tcx>(
 
     debug!(?normalized_inputs_and_output);
 
-    let mut checker = TypeChecker {
+    let mut typeck = TypeChecker {
         infcx,
         last_span: body.span,
         body,
@@ -171,24 +168,22 @@ pub(crate) fn type_check<'a, 'tcx>(
         constraints: &mut constraints,
     };
 
-    checker.check_user_type_annotations();
+    typeck.check_user_type_annotations();
 
-    let mut verifier = TypeVerifier { cx: &mut checker, promoted, last_span: body.span };
+    let mut verifier = TypeVerifier { typeck: &mut typeck, promoted, last_span: body.span };
     verifier.visit_body(body);
 
-    checker.typeck_mir(body);
-    checker.equate_inputs_and_outputs(body, &normalized_inputs_and_output);
-    checker.check_signature_annotation(body);
+    typeck.typeck_mir(body);
+    typeck.equate_inputs_and_outputs(body, &normalized_inputs_and_output);
+    typeck.check_signature_annotation(body);
 
-    liveness::generate(&mut checker, body, &elements, flow_inits, move_data);
+    liveness::generate(&mut typeck, body, &elements, flow_inits, move_data);
 
-    translate_outlives_facts(&mut checker);
-    let opaque_type_values = infcx.take_opaque_types();
-
-    let opaque_type_values = opaque_type_values
+    let opaque_type_values = infcx
+        .take_opaque_types()
         .into_iter()
         .map(|(opaque_type_key, decl)| {
-            let _: Result<_, ErrorGuaranteed> = checker.fully_perform_op(
+            let _: Result<_, ErrorGuaranteed> = typeck.fully_perform_op(
                 Locations::All(body.span),
                 ConstraintCategory::OpaqueType,
                 CustomTypeOp::new(
@@ -218,11 +213,11 @@ pub(crate) fn type_check<'a, 'tcx>(
                     match region.kind() {
                         ty::ReVar(_) => region,
                         ty::RePlaceholder(placeholder) => {
-                            checker.constraints.placeholder_region(infcx, placeholder)
+                            typeck.constraints.placeholder_region(infcx, placeholder)
                         }
                         _ => ty::Region::new_var(
                             infcx.tcx,
-                            checker.universal_regions.to_region_vid(region),
+                            typeck.universal_regions.to_region_vid(region),
                         ),
                     }
                 });
@@ -234,30 +229,6 @@ pub(crate) fn type_check<'a, 'tcx>(
     MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
 }
 
-fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
-    if let Some(facts) = typeck.all_facts {
-        let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
-        let location_table = typeck.location_table;
-        facts.subset_base.extend(
-            typeck.constraints.outlives_constraints.outlives().iter().flat_map(
-                |constraint: &OutlivesConstraint<'_>| {
-                    if let Some(from_location) = constraint.locations.from_location() {
-                        Either::Left(iter::once((
-                            constraint.sup.into(),
-                            constraint.sub.into(),
-                            location_table.mid_index(from_location),
-                        )))
-                    } else {
-                        Either::Right(location_table.all_points().map(move |location| {
-                            (constraint.sup.into(), constraint.sub.into(), location)
-                        }))
-                    }
-                },
-            ),
-        );
-    }
-}
-
 #[track_caller]
 fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: String) {
     // We sometimes see MIR failures (notably predicate failures) due to
@@ -276,7 +247,7 @@ enum FieldAccessError {
 /// type, calling `span_mirbug` and returning an error type if there
 /// is a problem.
 struct TypeVerifier<'a, 'b, 'tcx> {
-    cx: &'a mut TypeChecker<'b, 'tcx>,
+    typeck: &'a mut TypeChecker<'b, 'tcx>,
     promoted: &'b IndexSlice<Promoted, Body<'tcx>>,
     last_span: Span,
 }
@@ -298,9 +269,9 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
         self.super_const_operand(constant, location);
         let ty = self.sanitize_type(constant, constant.const_.ty());
 
-        self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| {
-            let live_region_vid = self.cx.universal_regions.to_region_vid(live_region);
-            self.cx.constraints.liveness_constraints.add_location(live_region_vid, location);
+        self.typeck.infcx.tcx.for_each_free_region(&ty, |live_region| {
+            let live_region_vid = self.typeck.universal_regions.to_region_vid(live_region);
+            self.typeck.constraints.liveness_constraints.add_location(live_region_vid, location);
         });
 
         // HACK(compiler-errors): Constants that are gathered into Body.required_consts
@@ -312,14 +283,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
         };
 
         if let Some(annotation_index) = constant.user_ty {
-            if let Err(terr) = self.cx.relate_type_and_user_type(
+            if let Err(terr) = self.typeck.relate_type_and_user_type(
                 constant.const_.ty(),
                 ty::Invariant,
                 &UserTypeProjection { base: annotation_index, projs: vec![] },
                 locations,
                 ConstraintCategory::Boring,
             ) {
-                let annotation = &self.cx.user_type_annotations[annotation_index];
+                let annotation = &self.typeck.user_type_annotations[annotation_index];
                 span_mirbug!(
                     self,
                     constant,
@@ -348,9 +319,12 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                                      promoted: &Body<'tcx>,
                                      ty,
                                      san_ty| {
-                        if let Err(terr) =
-                            verifier.cx.eq_types(ty, san_ty, locations, ConstraintCategory::Boring)
-                        {
+                        if let Err(terr) = verifier.typeck.eq_types(
+                            ty,
+                            san_ty,
+                            locations,
+                            ConstraintCategory::Boring,
+                        ) {
                             span_mirbug!(
                                 verifier,
                                 promoted,
@@ -368,21 +342,21 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                     let promoted_ty = promoted_body.return_ty();
                     check_err(self, promoted_body, ty, promoted_ty);
                 } else {
-                    self.cx.ascribe_user_type(
+                    self.typeck.ascribe_user_type(
                         constant.const_.ty(),
                         ty::UserType::new(ty::UserTypeKind::TypeOf(uv.def, UserArgs {
                             args: uv.args,
                             user_self_ty: None,
                         })),
-                        locations.span(self.cx.body),
+                        locations.span(self.typeck.body),
                     );
                 }
             } else if let Some(static_def_id) = constant.check_static_ptr(tcx) {
                 let unnormalized_ty = tcx.type_of(static_def_id).instantiate_identity();
-                let normalized_ty = self.cx.normalize(unnormalized_ty, locations);
+                let normalized_ty = self.typeck.normalize(unnormalized_ty, locations);
                 let literal_ty = constant.const_.ty().builtin_deref(true).unwrap();
 
-                if let Err(terr) = self.cx.eq_types(
+                if let Err(terr) = self.typeck.eq_types(
                     literal_ty,
                     normalized_ty,
                     locations,
@@ -394,7 +368,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
 
             if let ty::FnDef(def_id, args) = *constant.const_.ty().kind() {
                 let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args);
-                self.cx.normalize_and_prove_instantiated_predicates(
+                self.typeck.normalize_and_prove_instantiated_predicates(
                     def_id,
                     instantiated_predicates,
                     locations,
@@ -404,7 +378,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                     tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)),
                     Some(DefKind::Impl { of_trait: true })
                 ));
-                self.cx.prove_predicates(
+                self.typeck.prove_predicates(
                     args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())),
                     locations,
                     ConstraintCategory::Boring,
@@ -438,7 +412,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
                     local_decl.ty
                 };
 
-                if let Err(terr) = self.cx.relate_type_and_user_type(
+                if let Err(terr) = self.typeck.relate_type_and_user_type(
                     ty,
                     ty::Invariant,
                     user_ty,
@@ -468,11 +442,11 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
 
 impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
     fn body(&self) -> &Body<'tcx> {
-        self.cx.body
+        self.typeck.body
     }
 
     fn tcx(&self) -> TyCtxt<'tcx> {
-        self.cx.infcx.tcx
+        self.typeck.infcx.tcx
     }
 
     fn sanitize_type(&mut self, parent: &dyn fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
@@ -522,7 +496,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
             // whether the bounds fully apply: in effect, the rule is
             // that if a value of some type could implement `Copy`, then
             // it must.
-            self.cx.prove_trait_ref(
+            self.typeck.prove_trait_ref(
                 trait_ref,
                 location.to_locations(),
                 ConstraintCategory::CopyBound,
@@ -537,7 +511,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
         // checker on the promoted MIR, then transfer the constraints back to
         // the main MIR, changing the locations to the provided location.
 
-        let parent_body = mem::replace(&mut self.cx.body, promoted_body);
+        let parent_body = mem::replace(&mut self.typeck.body, promoted_body);
 
         // Use new sets of constraints and closure bounds so that we can
         // modify their locations.
@@ -548,18 +522,18 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
         // Don't try to add borrow_region facts for the promoted MIR
 
         let mut swap_constraints = |this: &mut Self| {
-            mem::swap(this.cx.all_facts, all_facts);
-            mem::swap(&mut this.cx.constraints.outlives_constraints, &mut constraints);
-            mem::swap(&mut this.cx.constraints.liveness_constraints, &mut liveness_constraints);
+            mem::swap(this.typeck.all_facts, all_facts);
+            mem::swap(&mut this.typeck.constraints.outlives_constraints, &mut constraints);
+            mem::swap(&mut this.typeck.constraints.liveness_constraints, &mut liveness_constraints);
         };
 
         swap_constraints(self);
 
         self.visit_body(promoted_body);
 
-        self.cx.typeck_mir(promoted_body);
+        self.typeck.typeck_mir(promoted_body);
 
-        self.cx.body = parent_body;
+        self.typeck.body = parent_body;
         // Merge the outlives constraints back in, at the given location.
         swap_constraints(self);
 
@@ -575,7 +549,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
                 // temporary from the user's point of view.
                 constraint.category = ConstraintCategory::Boring;
             }
-            self.cx.constraints.outlives_constraints.push(constraint)
+            self.typeck.constraints.outlives_constraints.push(constraint)
         }
         // If the region is live at least one location in the promoted MIR,
         // then add a liveness constraint to the main MIR for this region
@@ -585,7 +559,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
         // unordered.
         #[allow(rustc::potential_query_instability)]
         for region in liveness_constraints.live_regions_unordered() {
-            self.cx.constraints.liveness_constraints.add_location(region, location);
+            self.typeck.constraints.liveness_constraints.add_location(region, location);
         }
     }
 
@@ -669,13 +643,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
             },
             ProjectionElem::Field(field, fty) => {
                 let fty = self.sanitize_type(place, fty);
-                let fty = self.cx.normalize(fty, location);
+                let fty = self.typeck.normalize(fty, location);
                 match self.field_ty(place, base, field, location) {
                     Ok(ty) => {
-                        let ty = self.cx.normalize(ty, location);
+                        let ty = self.typeck.normalize(ty, location);
                         debug!(?fty, ?ty);
 
-                        if let Err(terr) = self.cx.relate_types(
+                        if let Err(terr) = self.typeck.relate_types(
                             ty,
                             self.get_ambient_variance(context),
                             fty,
@@ -707,8 +681,8 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
             }
             ProjectionElem::OpaqueCast(ty) => {
                 let ty = self.sanitize_type(place, ty);
-                let ty = self.cx.normalize(ty, location);
-                self.cx
+                let ty = self.typeck.normalize(ty, location);
+                self.typeck
                     .relate_types(
                         ty,
                         self.get_ambient_variance(context),
@@ -817,7 +791,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
         };
 
         if let Some(field) = variant.fields.get(field) {
-            Ok(self.cx.normalize(field.ty(tcx, args), location))
+            Ok(self.typeck.normalize(field.ty(tcx, args), location))
         } else {
             Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
         }
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index 752b2bf1a24..59e2eee41d3 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -13,8 +13,7 @@ use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::fold::FnMutDelegate;
 use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
-use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 use tracing::{debug, instrument};
 
 use crate::constraints::OutlivesConstraint;
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index bb64d646ff3..6b7bf718766 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -33,8 +33,7 @@ use rustc_middle::ty::{
     TyCtxt, TypeVisitableExt,
 };
 use rustc_middle::{bug, span_bug};
-use rustc_span::ErrorGuaranteed;
-use rustc_span::symbol::{kw, sym};
+use rustc_span::{ErrorGuaranteed, kw, sym};
 use tracing::{debug, instrument};
 
 use crate::BorrowckInferCtxt;
diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml
index ef48486f6f1..b50cb35b8e9 100644
--- a/compiler/rustc_builtin_macros/Cargo.toml
+++ b/compiler/rustc_builtin_macros/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
 # tidy-alphabetical-start
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_expand = { path = "../rustc_expand" }
diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
index 0caad997b9d..d2b4e1ca824 100644
--- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
+++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
@@ -3,8 +3,7 @@ use rustc_ast::{
     self as ast, Fn, FnHeader, FnSig, Generics, ItemKind, Safety, Stmt, StmtKind, TyKind,
 };
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, kw, sym};
+use rustc_span::{Ident, Span, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::errors;
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 14ac3cd74e8..cce70fb2ea4 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -10,8 +10,7 @@ use rustc_expand::base::*;
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_parse::parser::Parser;
 use rustc_session::lint;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{ErrorGuaranteed, InnerSpan, Span};
+use rustc_span::{ErrorGuaranteed, Ident, InnerSpan, Span, Symbol, kw, sym};
 use rustc_target::asm::InlineAsmArch;
 use smallvec::smallvec;
 use {rustc_ast as ast, rustc_parse_format as parse};
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs
index 599b180f879..95b31c7e47a 100644
--- a/compiler/rustc_builtin_macros/src/assert.rs
+++ b/compiler/rustc_builtin_macros/src/assert.rs
@@ -8,8 +8,7 @@ use rustc_ast_pretty::pprust;
 use rustc_errors::PResult;
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
 use rustc_parse::parser::Parser;
-use rustc_span::symbol::{Ident, Symbol, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
 use thin_vec::thin_vec;
 
 use crate::edition_panic::use_panic_2021;
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index eb07975d8af..bb9dc651cec 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -8,8 +8,7 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_expand::base::ExtCtxt;
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 pub(super) struct Context<'cx, 'a> {
diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs
index 66bb11ca522..8960cf6ab59 100644
--- a/compiler/rustc_builtin_macros/src/autodiff.rs
+++ b/compiler/rustc_builtin_macros/src/autodiff.rs
@@ -20,8 +20,7 @@ mod llvm_enzyme {
         PatKind, TyKind,
     };
     use rustc_expand::base::{Annotatable, ExtCtxt};
-    use rustc_span::symbol::{Ident, kw, sym};
-    use rustc_span::{Span, Symbol};
+    use rustc_span::{Ident, Span, Symbol, kw, sym};
     use thin_vec::{ThinVec, thin_vec};
     use tracing::{debug, trace};
 
@@ -35,7 +34,7 @@ mod llvm_enzyme {
             FnRetTy::Default(_) => false,
         }
     }
-    fn first_ident(x: &MetaItemInner) -> rustc_span::symbol::Ident {
+    fn first_ident(x: &MetaItemInner) -> rustc_span::Ident {
         let segments = &x.meta_item().unwrap().path.segments;
         assert!(segments.len() == 1);
         segments[0].ident
diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs
index 15993dbf5ec..6e90f1682e3 100644
--- a/compiler/rustc_builtin_macros/src/cfg.rs
+++ b/compiler/rustc_builtin_macros/src/cfg.rs
@@ -7,7 +7,7 @@ use rustc_ast::tokenstream::TokenStream;
 use rustc_errors::PResult;
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
 use rustc_span::Span;
-use {rustc_ast as ast, rustc_attr as attr};
+use {rustc_ast as ast, rustc_attr_parsing as attr};
 
 use crate::errors;
 
diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
index 23578781a83..5f203dd5d11 100644
--- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
@@ -4,8 +4,7 @@ use rustc_ast as ast;
 use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
 use rustc_feature::AttributeTemplate;
 use rustc_parse::validate_attr;
-use rustc_span::Span;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 
 use crate::errors;
 
diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs
index d46a1bd3d31..53c61831b42 100644
--- a/compiler/rustc_builtin_macros/src/cfg_eval.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs
@@ -12,8 +12,7 @@ use rustc_expand::configure;
 use rustc_feature::Features;
 use rustc_parse::parser::{ForceCollect, Parser};
 use rustc_session::Session;
-use rustc_span::Span;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 use smallvec::SmallVec;
 use tracing::instrument;
 
diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs
index a28801f66dd..c200539e128 100644
--- a/compiler/rustc_builtin_macros/src/concat.rs
+++ b/compiler/rustc_builtin_macros/src/concat.rs
@@ -2,7 +2,7 @@ use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::{ExprKind, LitKind, UnOp};
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
 use rustc_session::errors::report_lit_error;
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 
 use crate::errors;
 use crate::util::get_exprs_from_tts;
diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs
index b459cc3007d..208b499eb7a 100644
--- a/compiler/rustc_builtin_macros/src/concat_idents.rs
+++ b/compiler/rustc_builtin_macros/src/concat_idents.rs
@@ -3,8 +3,7 @@ use rustc_ast::token::{self, Token};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast::{AttrVec, DUMMY_NODE_ID, Expr, ExprKind, Path, Ty, TyKind};
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 use crate::errors;
 
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index 60450d085f6..2653a9f48b9 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -6,8 +6,7 @@ use rustc_expand::base::{
 use rustc_feature::AttributeTemplate;
 use rustc_parse::validate_attr;
 use rustc_session::Session;
-use rustc_span::symbol::{Ident, sym};
-use rustc_span::{ErrorGuaranteed, Span};
+use rustc_span::{ErrorGuaranteed, Ident, Span, sym};
 
 use crate::cfg_eval::cfg_eval;
 use crate::errors;
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index f79227d52a8..78f50ba8d29 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -1,8 +1,7 @@
 use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, kw, sym};
+use rustc_span::{Ident, Span, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::deriving::generic::ty::*;
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index bd6f4eb8d99..5790350203a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -1,8 +1,7 @@
 use rustc_ast::{self as ast, MetaItem};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::deriving::generic::ty::*;
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
index 433cbfaa6cb..1ed44c20bc6 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -1,7 +1,6 @@
 use rustc_ast::MetaItem;
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, sym};
+use rustc_span::{Ident, Span, sym};
 use thin_vec::thin_vec;
 
 use crate::deriving::generic::ty::*;
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index ae1e5f4a219..4b93b3414c7 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -1,8 +1,7 @@
 use rustc_ast::ptr::P;
 use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability};
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 use thin_vec::thin_vec;
 
 use crate::deriving::generic::ty::*;
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 99a98325053..7958e037555 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -1,7 +1,6 @@
 use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind};
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, sym};
+use rustc_span::{Ident, Span, sym};
 use thin_vec::thin_vec;
 
 use crate::deriving::generic::ty::*;
diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
index 3bd8f899a4a..49706db0e0b 100644
--- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
@@ -6,12 +6,11 @@ use rustc_ast::{
     self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem,
     TraitBoundModifiers, VariantData, WherePredicate,
 };
-use rustc_attr as attr;
+use rustc_attr_parsing as attr;
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_macros::Diagnostic;
-use rustc_span::symbol::{Ident, sym};
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::errors;
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index 06a598cf3b0..eb01ca3941d 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -1,8 +1,7 @@
 use rustc_ast::{self as ast, EnumDef, MetaItem};
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_session::config::FmtDebug;
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::deriving::generic::ty::*;
diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs
index 469092e7b1c..6348560496e 100644
--- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs
@@ -3,8 +3,7 @@
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, Expr, MetaItem, Mutability};
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::deriving::generic::ty::*;
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 6b1a6effad7..a7d9f608cbd 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -4,8 +4,7 @@ use rustc_ast as ast;
 use rustc_ast::visit::visit_opt;
 use rustc_ast::{EnumDef, VariantData, attr};
 use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{ErrorGuaranteed, Span};
+use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
 use smallvec::SmallVec;
 use thin_vec::{ThinVec, thin_vec};
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
index 5c7583f2a77..20aacb2caca 100644
--- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
@@ -87,8 +87,7 @@
 
 use rustc_ast::{AttrVec, ExprKind, MetaItem, Mutability};
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::deriving::generic::ty::*;
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 846d8784dea..f0a5e44e066 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -185,10 +185,9 @@ use rustc_ast::{
     self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
     Generics, Mutability, PatKind, VariantData,
 };
-use rustc_attr as attr;
+use rustc_attr_parsing as attr;
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 use ty::{Bounds, Path, Ref, Self_, Ty};
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
index b118cbfbd91..af6dc62db7a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
@@ -6,8 +6,7 @@ use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind};
 use rustc_expand::base::ExtCtxt;
 use rustc_span::source_map::respan;
-use rustc_span::symbol::{Ident, Symbol, kw};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw};
 use thin_vec::ThinVec;
 
 /// A path, e.g., `::std::option::Option::<i32>` (global). Has support
diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs
index 2d1f5b70f77..6e6dbe19e4d 100644
--- a/compiler/rustc_builtin_macros/src/deriving/hash.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs
@@ -1,7 +1,6 @@
 use rustc_ast::{MetaItem, Mutability};
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 use thin_vec::thin_vec;
 
 use crate::deriving::generic::ty::*;
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index 681fbd1651d..ec058b41313 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -4,8 +4,7 @@ use rustc_ast as ast;
 use rustc_ast::ptr::P;
 use rustc_ast::{GenericArg, MetaItem};
 use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 macro path_local($x:ident) {
diff --git a/compiler/rustc_builtin_macros/src/edition_panic.rs b/compiler/rustc_builtin_macros/src/edition_panic.rs
index c4164a7db9a..b39c9861fd6 100644
--- a/compiler/rustc_builtin_macros/src/edition_panic.rs
+++ b/compiler/rustc_builtin_macros/src/edition_panic.rs
@@ -3,9 +3,8 @@ use rustc_ast::token::Delimiter;
 use rustc_ast::tokenstream::{DelimSpan, TokenStream};
 use rustc_ast::*;
 use rustc_expand::base::*;
-use rustc_span::Span;
 use rustc_span::edition::Edition;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 
 /// This expands to either
 /// - `$crate::panic::panic_2015!(...)` or
diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs
index 43e2bf1796f..8831261759c 100644
--- a/compiler/rustc_builtin_macros/src/env.rs
+++ b/compiler/rustc_builtin_macros/src/env.rs
@@ -10,8 +10,7 @@ use rustc_ast::token::{self, LitKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::{AstDeref, ExprKind, GenericArg, Mutability};
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
+use rustc_span::{Ident, Span, Symbol, kw, sym};
 use thin_vec::thin_vec;
 
 use crate::errors;
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index c9bd3371e55..b3198e7743d 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -4,8 +4,7 @@ use rustc_errors::{
     SubdiagMessageOp, Subdiagnostic,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 #[derive(Diagnostic)]
 #[diag(builtin_macros_requires_cfg_pattern)]
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 32730ac3867..73d762d21e5 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -13,8 +13,7 @@ use rustc_expand::base::*;
 use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
 use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId};
 use rustc_parse_format as parse;
-use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::{BytePos, ErrorGuaranteed, InnerSpan, Span};
+use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol};
 
 use crate::errors;
 use crate::util::expr_to_spanned_string;
diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs
index b4b18409a18..8388e9dcafb 100644
--- a/compiler/rustc_builtin_macros/src/global_allocator.rs
+++ b/compiler/rustc_builtin_macros/src/global_allocator.rs
@@ -7,8 +7,7 @@ use rustc_ast::{
     Stmt, StmtKind, Ty, TyKind,
 };
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
+use rustc_span::{Ident, Span, Symbol, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::errors;
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index cc4a974e757..6071d36f8eb 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -24,7 +24,7 @@ extern crate proc_macro;
 
 use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind};
 use rustc_expand::proc_macro::BangProcMacro;
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 use crate::deriving::*;
 
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index 707c36d5046..dee185ff0c9 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -11,8 +11,7 @@ use rustc_feature::Features;
 use rustc_session::Session;
 use rustc_span::hygiene::AstPass;
 use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use smallvec::smallvec;
 use thin_vec::{ThinVec, thin_vec};
 
diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index 946fbe918e6..123b96f6bca 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -16,8 +16,7 @@ use rustc_parse::parser::{ForceCollect, Parser};
 use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal};
 use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
 use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::Symbol;
-use rustc_span::{Pos, Span};
+use rustc_span::{Pos, Span, Symbol};
 use smallvec::SmallVec;
 
 use crate::errors;
diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
index a3fc53344ab..1a3f4d2d449 100644
--- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs
+++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
@@ -3,10 +3,9 @@ use rustc_expand::base::{ExtCtxt, ResolverExpand};
 use rustc_expand::expand::ExpansionConfig;
 use rustc_feature::Features;
 use rustc_session::Session;
-use rustc_span::DUMMY_SP;
 use rustc_span::edition::Edition::*;
 use rustc_span::hygiene::AstPass;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
+use rustc_span::{DUMMY_SP, Ident, Symbol, kw, sym};
 use thin_vec::thin_vec;
 
 pub fn inject(
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 664e935ee14..3f73ddbdd29 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -9,8 +9,7 @@ use rustc_ast::{self as ast, GenericParamKind, attr};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, Diag, Level};
 use rustc_expand::base::*;
-use rustc_span::symbol::{Ident, Symbol, sym};
-use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Span};
+use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Ident, Span, Symbol, sym};
 use thin_vec::{ThinVec, thin_vec};
 use tracing::debug;
 
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index e7ff65e08f9..46446598943 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -16,8 +16,7 @@ use rustc_lint_defs::BuiltinLintDiag;
 use rustc_session::Session;
 use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS;
 use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
-use rustc_span::symbol::{Ident, Symbol, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
 use rustc_target::spec::PanicStrategy;
 use smallvec::smallvec;
 use thin_vec::{ThinVec, thin_vec};
diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs
index e624d1da66b..670ddc0415f 100644
--- a/compiler/rustc_builtin_macros/src/trace_macros.rs
+++ b/compiler/rustc_builtin_macros/src/trace_macros.rs
@@ -1,7 +1,6 @@
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
-use rustc_span::Span;
-use rustc_span::symbol::kw;
+use rustc_span::{Span, kw};
 
 use crate::errors;
 
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 5f1b71eff6b..2e5813556aa 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -23,7 +23,7 @@ use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 pub(crate) use self::llvm::codegen_llvm_intrinsic_call;
 use crate::cast::clif_intcast;
diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs
index e480f21b9df..e6bf0d5b47e 100644
--- a/compiler/rustc_codegen_cranelift/src/main_shim.rs
+++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs
@@ -2,8 +2,7 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
 use rustc_hir::LangItem;
 use rustc_middle::ty::{AssocKind, GenericArg};
 use rustc_session::config::{EntryFnType, sigpipe};
-use rustc_span::DUMMY_SP;
-use rustc_span::symbol::Ident;
+use rustc_span::{DUMMY_SP, Ident};
 
 use crate::prelude::*;
 
diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs
index d20e13e15b9..028a5ab5f71 100644
--- a/compiler/rustc_codegen_gcc/src/attributes.rs
+++ b/compiler/rustc_codegen_gcc/src/attributes.rs
@@ -2,8 +2,8 @@
 use gccjit::FnAttribute;
 use gccjit::Function;
 #[cfg(feature = "master")]
-use rustc_attr::InlineAttr;
-use rustc_attr::InstructionSetAttr;
+use rustc_attr_parsing::InlineAttr;
+use rustc_attr_parsing::InstructionSetAttr;
 #[cfg(feature = "master")]
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::ty;
diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs
index 88e5eefd7a1..058a874501b 100644
--- a/compiler/rustc_codegen_gcc/src/gcc_util.rs
+++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs
@@ -96,7 +96,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
                     }
                     Some((_, stability, _)) => {
                         if let Err(reason) =
-                            stability.compute_toggleability(&sess.target).allow_toggle()
+                            stability.toggle_allowed(&sess.target, enable_disable == '+')
                         {
                             sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
                         } else if stability.requires_nightly().is_some() {
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index 764e84be1fe..f2efa981f97 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -35,7 +35,7 @@ extern crate tracing;
 extern crate rustc_abi;
 extern crate rustc_apfloat;
 extern crate rustc_ast;
-extern crate rustc_attr;
+extern crate rustc_attr_parsing;
 extern crate rustc_codegen_ssa;
 extern crate rustc_data_structures;
 extern crate rustc_errors;
@@ -483,9 +483,9 @@ fn target_features_cfg(
         .rust_target_features()
         .iter()
         .filter(|(_, gate, _)| gate.in_cfg())
-        .filter_map(|&(feature, gate, _)| {
+        .filter_map(|(feature, gate, _)| {
             if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() {
-                Some(feature)
+                Some(*feature)
             } else {
                 None
             }
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index 03a871297c4..689986d642d 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -16,7 +16,7 @@ object = { version = "0.36.3", default-features = false, features = ["std", "rea
 rustc-demangle = "0.1.21"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 5552a241060..f8454fd9960 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -1,6 +1,6 @@
 //! Set and unset common attributes on LLVM values.
 
-use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr};
+use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
 use rustc_codegen_ssa::traits::*;
 use rustc_hir::def_id::DefId;
 use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry};
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 66ca4e2b473..45294ea35b1 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -25,8 +25,7 @@ use rustc_session::Session;
 use rustc_session::config::{
     self, Lto, OutputType, Passes, RemapPathScopeComponents, SplitDwarfKind, SwitchWithOptPath,
 };
-use rustc_span::symbol::sym;
-use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext};
+use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym};
 use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel};
 use tracing::debug;
 
diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs
index f62310bd948..d05faf5577b 100644
--- a/compiler/rustc_codegen_llvm/src/base.rs
+++ b/compiler/rustc_codegen_llvm/src/base.rs
@@ -23,7 +23,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::mir::mono::{Linkage, Visibility};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::DebugInfo;
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 use rustc_target::spec::SanitizerSet;
 
 use super::ModuleLlvm;
diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs
index ec77f32caf4..aa9a0f34f55 100644
--- a/compiler/rustc_codegen_llvm/src/callee.rs
+++ b/compiler/rustc_codegen_llvm/src/callee.rs
@@ -106,7 +106,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
                 // This is a monomorphization of a generic function.
                 if !(cx.tcx.sess.opts.share_generics()
                     || tcx.codegen_fn_attrs(instance_def_id).inline
-                        == rustc_attr::InlineAttr::Never)
+                        == rustc_attr_parsing::InlineAttr::Never)
                 {
                     // When not sharing generics, all instances are in the same
                     // crate and have hidden visibility.
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
deleted file mode 100644
index c5d1ebdfe7c..00000000000
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-use rustc_data_structures::captures::Captures;
-use rustc_middle::mir::coverage::{
-    CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op,
-    SourceRegion,
-};
-
-use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
-
-pub(crate) struct FunctionCoverage<'tcx> {
-    pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo,
-    /// If `None`, the corresponding function is unused.
-    ids_info: Option<&'tcx CoverageIdsInfo>,
-}
-
-impl<'tcx> FunctionCoverage<'tcx> {
-    pub(crate) fn new_used(
-        function_coverage_info: &'tcx FunctionCoverageInfo,
-        ids_info: &'tcx CoverageIdsInfo,
-    ) -> Self {
-        Self { function_coverage_info, ids_info: Some(ids_info) }
-    }
-
-    pub(crate) fn new_unused(function_coverage_info: &'tcx FunctionCoverageInfo) -> Self {
-        Self { function_coverage_info, ids_info: None }
-    }
-
-    /// Returns true for a used (called) function, and false for an unused function.
-    pub(crate) fn is_used(&self) -> bool {
-        self.ids_info.is_some()
-    }
-
-    /// Return the source hash, generated from the HIR node structure, and used to indicate whether
-    /// or not the source code structure changed between different compilations.
-    pub(crate) fn source_hash(&self) -> u64 {
-        if self.is_used() { self.function_coverage_info.function_source_hash } else { 0 }
-    }
-
-    /// Convert this function's coverage expression data into a form that can be
-    /// passed through FFI to LLVM.
-    pub(crate) fn counter_expressions(
-        &self,
-    ) -> impl Iterator<Item = CounterExpression> + ExactSizeIterator + Captures<'_> {
-        // We know that LLVM will optimize out any unused expressions before
-        // producing the final coverage map, so there's no need to do the same
-        // thing on the Rust side unless we're confident we can do much better.
-        // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
-
-        self.function_coverage_info.expressions.iter().map(move |&Expression { lhs, op, rhs }| {
-            CounterExpression {
-                lhs: self.counter_for_term(lhs),
-                kind: match op {
-                    Op::Add => ExprKind::Add,
-                    Op::Subtract => ExprKind::Subtract,
-                },
-                rhs: self.counter_for_term(rhs),
-            }
-        })
-    }
-
-    /// Converts this function's coverage mappings into an intermediate form
-    /// that will be used by `mapgen` when preparing for FFI.
-    pub(crate) fn counter_regions(
-        &self,
-    ) -> impl Iterator<Item = (MappingKind, &SourceRegion)> + ExactSizeIterator {
-        self.function_coverage_info.mappings.iter().map(move |mapping| {
-            let Mapping { kind, source_region } = mapping;
-            let kind =
-                kind.map_terms(|term| if self.is_zero_term(term) { CovTerm::Zero } else { term });
-            (kind, source_region)
-        })
-    }
-
-    fn counter_for_term(&self, term: CovTerm) -> Counter {
-        if self.is_zero_term(term) { Counter::ZERO } else { Counter::from_term(term) }
-    }
-
-    fn is_zero_term(&self, term: CovTerm) -> bool {
-        match self.ids_info {
-            Some(ids_info) => ids_info.is_zero_term(term),
-            // This function is unused, so all coverage counters/expressions are zero.
-            None => true,
-        }
-    }
-}
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index 4f2af732527..ca334286200 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -1,6 +1,6 @@
 use std::iter;
 
-use itertools::Itertools as _;
+use itertools::Itertools;
 use rustc_abi::Align;
 use rustc_codegen_ssa::traits::{
     BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
@@ -8,8 +8,8 @@ use rustc_codegen_ssa::traits::{
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_index::IndexVec;
+use rustc_middle::mir;
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_middle::{bug, mir};
 use rustc_session::RemapFileNameExt;
 use rustc_session::config::RemapPathScopeComponents;
 use rustc_span::def_id::DefIdSet;
@@ -18,7 +18,6 @@ use tracing::debug;
 
 use crate::common::CodegenCx;
 use crate::coverageinfo::llvm_cov;
-use crate::coverageinfo::map_data::FunctionCoverage;
 use crate::coverageinfo::mapgen::covfun::prepare_covfun_record;
 use crate::llvm;
 
@@ -49,46 +48,40 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
 
     debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
 
-    // In order to show that unused functions have coverage counts of zero (0), LLVM requires the
-    // functions exist. Generate synthetic functions with a (required) single counter, and add the
-    // MIR `Coverage` code regions to the `function_coverage_map`, before calling
-    // `ctx.take_function_coverage_map()`.
-    if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
-        add_unused_functions(cx);
-    }
-
     // FIXME(#132395): Can this be none even when coverage is enabled?
-    let function_coverage_map = match cx.coverage_cx {
-        Some(ref cx) => cx.take_function_coverage_map(),
+    let instances_used = match cx.coverage_cx {
+        Some(ref cx) => cx.instances_used.borrow(),
         None => return,
     };
-    if function_coverage_map.is_empty() {
-        // This CGU has no functions with coverage instrumentation.
-        return;
-    }
 
-    let all_file_names = function_coverage_map
-        .iter()
-        .map(|(_, fn_cov)| fn_cov.function_coverage_info.body_span)
-        .map(|span| span_file_name(tcx, span));
-    let global_file_table = GlobalFileTable::new(all_file_names);
+    // The order of entries in this global file table needs to be deterministic,
+    // and ideally should also be independent of the details of stable-hashing,
+    // because coverage tests snapshots (`.cov-map`) can observe the order and
+    // would need to be re-blessed if it changes. As long as those requirements
+    // are satisfied, the order can be arbitrary.
+    let mut global_file_table = GlobalFileTable::new();
 
-    // Encode all filenames referenced by coverage mappings in this CGU.
-    let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
-    // The `llvm-cov` tool uses this hash to associate each covfun record with
-    // its corresponding filenames table, since the final binary will typically
-    // contain multiple covmap records from different compilation units.
-    let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
-
-    let mut unused_function_names = Vec::new();
-
-    let covfun_records = function_coverage_map
-        .into_iter()
-        .filter_map(|(instance, function_coverage)| {
-            prepare_covfun_record(tcx, &global_file_table, instance, &function_coverage)
-        })
+    let mut covfun_records = instances_used
+        .iter()
+        .copied()
+        // Sort by symbol name, so that the global file table is built in an
+        // order that doesn't depend on the stable-hash-based order in which
+        // instances were visited during codegen.
+        .sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name)
+        .filter_map(|instance| prepare_covfun_record(tcx, &mut global_file_table, instance, true))
         .collect::<Vec<_>>();
 
+    // In a single designated CGU, also prepare covfun records for functions
+    // in this crate that were instrumented for coverage, but are unused.
+    if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
+        let mut unused_instances = gather_unused_function_instances(cx);
+        // Sort the unused instances by symbol name, for the same reason as the used ones.
+        unused_instances.sort_by_cached_key(|&instance| tcx.symbol_name(instance).name);
+        covfun_records.extend(unused_instances.into_iter().filter_map(|instance| {
+            prepare_covfun_record(tcx, &mut global_file_table, instance, false)
+        }));
+    }
+
     // If there are no covfun records for this CGU, don't generate a covmap record.
     // Emitting a covmap record without any covfun records causes `llvm-cov` to
     // fail when generating coverage reports, and if there are no covfun records
@@ -98,6 +91,15 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
         return;
     }
 
+    // Encode all filenames referenced by coverage mappings in this CGU.
+    let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
+    // The `llvm-cov` tool uses this hash to associate each covfun record with
+    // its corresponding filenames table, since the final binary will typically
+    // contain multiple covmap records from different compilation units.
+    let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
+
+    let mut unused_function_names = vec![];
+
     for covfun in &covfun_records {
         unused_function_names.extend(covfun.mangled_function_name_if_unused());
 
@@ -137,22 +139,13 @@ struct GlobalFileTable {
 }
 
 impl GlobalFileTable {
-    fn new(all_file_names: impl IntoIterator<Item = Symbol>) -> Self {
-        // Collect all of the filenames into a set. Filenames usually come in
-        // contiguous runs, so we can dedup adjacent ones to save work.
-        let mut raw_file_table = all_file_names.into_iter().dedup().collect::<FxIndexSet<Symbol>>();
-
-        // Sort the file table by its actual string values, not the arbitrary
-        // ordering of its symbols.
-        raw_file_table.sort_unstable_by(|a, b| a.as_str().cmp(b.as_str()));
-
-        Self { raw_file_table }
+    fn new() -> Self {
+        Self { raw_file_table: FxIndexSet::default() }
     }
 
-    fn global_file_id_for_file_name(&self, file_name: Symbol) -> GlobalFileId {
-        let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| {
-            bug!("file name not found in prepared global file table: {file_name}");
-        });
+    fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> GlobalFileId {
+        // Ensure the given file has a table entry, and get its index.
+        let (raw_id, _) = self.raw_file_table.insert_full(file_name);
         // The raw file table doesn't include an entry for the working dir
         // (which has ID 0), so add 1 to get the correct ID.
         GlobalFileId::from_usize(raw_id + 1)
@@ -264,39 +257,35 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_
 /// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
 /// We also end up adding their symbol names to a special global array that LLVM will include in
 /// its embedded coverage data.
-fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
+fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec<ty::Instance<'tcx>> {
     assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
 
     let tcx = cx.tcx;
     let usage = prepare_usage_sets(tcx);
 
     let is_unused_fn = |def_id: LocalDefId| -> bool {
-        let def_id = def_id.to_def_id();
-
-        // To be eligible for "unused function" mappings, a definition must:
-        // - Be function-like
+        // Usage sets expect `DefId`, so convert from `LocalDefId`.
+        let d: DefId = LocalDefId::to_def_id(def_id);
+        // To be potentially eligible for "unused function" mappings, a definition must:
+        // - Be eligible for coverage instrumentation
         // - Not participate directly in codegen (or have lost all its coverage statements)
         // - Not have any coverage statements inlined into codegenned functions
-        tcx.def_kind(def_id).is_fn_like()
-            && (!usage.all_mono_items.contains(&def_id)
-                || usage.missing_own_coverage.contains(&def_id))
-            && !usage.used_via_inlining.contains(&def_id)
+        tcx.is_eligible_for_coverage(def_id)
+            && (!usage.all_mono_items.contains(&d) || usage.missing_own_coverage.contains(&d))
+            && !usage.used_via_inlining.contains(&d)
     };
 
-    // Scan for unused functions that were instrumented for coverage.
-    for def_id in tcx.mir_keys(()).iter().copied().filter(|&def_id| is_unused_fn(def_id)) {
-        // Get the coverage info from MIR, skipping functions that were never instrumented.
-        let body = tcx.optimized_mir(def_id);
-        let Some(function_coverage_info) = body.function_coverage_info.as_deref() else { continue };
+    // FIXME(#79651): Consider trying to filter out dummy instantiations of
+    // unused generic functions from library crates, because they can produce
+    // "unused instantiation" in coverage reports even when they are actually
+    // used by some downstream crate in the same binary.
 
-        // FIXME(79651): Consider trying to filter out dummy instantiations of
-        // unused generic functions from library crates, because they can produce
-        // "unused instantiation" in coverage reports even when they are actually
-        // used by some downstream crate in the same binary.
-
-        debug!("generating unused fn: {def_id:?}");
-        add_unused_function_coverage(cx, def_id, function_coverage_info);
-    }
+    tcx.mir_keys(())
+        .iter()
+        .copied()
+        .filter(|&def_id| is_unused_fn(def_id))
+        .map(|def_id| make_dummy_instance(tcx, def_id))
+        .collect::<Vec<_>>()
 }
 
 struct UsageSets<'tcx> {
@@ -361,16 +350,11 @@ fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
     UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
 }
 
-fn add_unused_function_coverage<'tcx>(
-    cx: &CodegenCx<'_, 'tcx>,
-    def_id: LocalDefId,
-    function_coverage_info: &'tcx mir::coverage::FunctionCoverageInfo,
-) {
-    let tcx = cx.tcx;
-    let def_id = def_id.to_def_id();
+fn make_dummy_instance<'tcx>(tcx: TyCtxt<'tcx>, local_def_id: LocalDefId) -> ty::Instance<'tcx> {
+    let def_id = local_def_id.to_def_id();
 
     // Make a dummy instance that fills in all generics with placeholders.
-    let instance = ty::Instance::new(
+    ty::Instance::new(
         def_id,
         ty::GenericArgs::for_item(tcx, def_id, |param, _| {
             if let ty::GenericParamDefKind::Lifetime = param.kind {
@@ -379,9 +363,5 @@ fn add_unused_function_coverage<'tcx>(
                 tcx.mk_param_from_def(param)
             }
         }),
-    );
-
-    // An unused function's mappings will all be rewritten to map to zero.
-    let function_coverage = FunctionCoverage::new_unused(function_coverage_info);
-    cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage);
+    )
 }
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
index 33e7a0f2f20..8e853f057be 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
@@ -11,13 +11,14 @@ use rustc_codegen_ssa::traits::{
     BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
 };
 use rustc_middle::bug;
-use rustc_middle::mir::coverage::MappingKind;
+use rustc_middle::mir::coverage::{
+    CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op,
+};
 use rustc_middle::ty::{Instance, TyCtxt};
 use rustc_target::spec::HasTargetSpec;
 use tracing::debug;
 
 use crate::common::CodegenCx;
-use crate::coverageinfo::map_data::FunctionCoverage;
 use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, span_file_name};
 use crate::coverageinfo::{ffi, llvm_cov};
 use crate::llvm;
@@ -45,20 +46,25 @@ impl<'tcx> CovfunRecord<'tcx> {
 
 pub(crate) fn prepare_covfun_record<'tcx>(
     tcx: TyCtxt<'tcx>,
-    global_file_table: &GlobalFileTable,
+    global_file_table: &mut GlobalFileTable,
     instance: Instance<'tcx>,
-    function_coverage: &FunctionCoverage<'tcx>,
+    is_used: bool,
 ) -> Option<CovfunRecord<'tcx>> {
+    let fn_cov_info = tcx.instance_mir(instance.def).function_coverage_info.as_deref()?;
+    let ids_info = tcx.coverage_ids_info(instance.def);
+
+    let expressions = prepare_expressions(fn_cov_info, ids_info, is_used);
+
     let mut covfun = CovfunRecord {
         mangled_function_name: tcx.symbol_name(instance).name,
-        source_hash: function_coverage.source_hash(),
-        is_used: function_coverage.is_used(),
+        source_hash: if is_used { fn_cov_info.function_source_hash } else { 0 },
+        is_used,
         virtual_file_mapping: VirtualFileMapping::default(),
-        expressions: function_coverage.counter_expressions().collect::<Vec<_>>(),
+        expressions,
         regions: ffi::Regions::default(),
     };
 
-    fill_region_tables(tcx, global_file_table, function_coverage, &mut covfun);
+    fill_region_tables(tcx, global_file_table, fn_cov_info, ids_info, &mut covfun);
 
     if covfun.regions.has_no_regions() {
         if covfun.is_used {
@@ -72,20 +78,50 @@ pub(crate) fn prepare_covfun_record<'tcx>(
     Some(covfun)
 }
 
+/// Convert the function's coverage-counter expressions into a form suitable for FFI.
+fn prepare_expressions(
+    fn_cov_info: &FunctionCoverageInfo,
+    ids_info: &CoverageIdsInfo,
+    is_used: bool,
+) -> Vec<ffi::CounterExpression> {
+    // If any counters or expressions were removed by MIR opts, replace their
+    // terms with zero.
+    let counter_for_term = |term| {
+        if !is_used || ids_info.is_zero_term(term) {
+            ffi::Counter::ZERO
+        } else {
+            ffi::Counter::from_term(term)
+        }
+    };
+
+    // We know that LLVM will optimize out any unused expressions before
+    // producing the final coverage map, so there's no need to do the same
+    // thing on the Rust side unless we're confident we can do much better.
+    // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
+    fn_cov_info
+        .expressions
+        .iter()
+        .map(move |&Expression { lhs, op, rhs }| ffi::CounterExpression {
+            lhs: counter_for_term(lhs),
+            kind: match op {
+                Op::Add => ffi::ExprKind::Add,
+                Op::Subtract => ffi::ExprKind::Subtract,
+            },
+            rhs: counter_for_term(rhs),
+        })
+        .collect::<Vec<_>>()
+}
+
 /// Populates the mapping region tables in the current function's covfun record.
 fn fill_region_tables<'tcx>(
     tcx: TyCtxt<'tcx>,
-    global_file_table: &GlobalFileTable,
-    function_coverage: &FunctionCoverage<'tcx>,
+    global_file_table: &mut GlobalFileTable,
+    fn_cov_info: &'tcx FunctionCoverageInfo,
+    ids_info: &'tcx CoverageIdsInfo,
     covfun: &mut CovfunRecord<'tcx>,
 ) {
-    let counter_regions = function_coverage.counter_regions();
-    if counter_regions.is_empty() {
-        return;
-    }
-
     // Currently a function's mappings must all be in the same file as its body span.
-    let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span);
+    let file_name = span_file_name(tcx, fn_cov_info.body_span);
 
     // Look up the global file ID for that filename.
     let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
@@ -99,10 +135,14 @@ fn fill_region_tables<'tcx>(
 
     // For each counter/region pair in this function+file, convert it to a
     // form suitable for FFI.
-    for (mapping_kind, region) in counter_regions {
-        debug!("Adding counter {mapping_kind:?} to map for {region:?}");
-        let span = ffi::CoverageSpan::from_source_region(local_file_id, region);
-        match mapping_kind {
+    let is_zero_term = |term| !covfun.is_used || ids_info.is_zero_term(term);
+    for Mapping { kind, ref source_region } in &fn_cov_info.mappings {
+        // If the mapping refers to counters/expressions that were removed by
+        // MIR opts, replace those occurrences with zero.
+        let kind = kind.map_terms(|term| if is_zero_term(term) { CovTerm::Zero } else { term });
+
+        let span = ffi::CoverageSpan::from_source_region(local_file_id, source_region);
+        match kind {
             MappingKind::Code(term) => {
                 code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
             }
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index 82b6677e203..7311cd9d230 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -5,7 +5,7 @@ use rustc_abi::Size;
 use rustc_codegen_ssa::traits::{
     BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
 };
-use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
+use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_middle::mir::coverage::CoverageKind;
 use rustc_middle::ty::Instance;
 use rustc_middle::ty::layout::HasTyCtxt;
@@ -13,18 +13,16 @@ use tracing::{debug, instrument};
 
 use crate::builder::Builder;
 use crate::common::CodegenCx;
-use crate::coverageinfo::map_data::FunctionCoverage;
 use crate::llvm;
 
 pub(crate) mod ffi;
 mod llvm_cov;
-pub(crate) mod map_data;
 mod mapgen;
 
 /// Extra per-CGU context/state needed for coverage instrumentation.
 pub(crate) struct CguCoverageContext<'ll, 'tcx> {
     /// Coverage data for each instrumented function identified by DefId.
-    pub(crate) function_coverage_map: RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverage<'tcx>>>,
+    pub(crate) instances_used: RefCell<FxIndexSet<Instance<'tcx>>>,
     pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
     pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
 
@@ -34,17 +32,13 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> {
 impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
     pub(crate) fn new() -> Self {
         Self {
-            function_coverage_map: Default::default(),
+            instances_used: RefCell::<FxIndexSet<_>>::default(),
             pgo_func_name_var_map: Default::default(),
             mcdc_condition_bitmap_map: Default::default(),
             covfun_section_name: Default::default(),
         }
     }
 
-    fn take_function_coverage_map(&self) -> FxIndexMap<Instance<'tcx>, FunctionCoverage<'tcx>> {
-        self.function_coverage_map.replace(FxIndexMap::default())
-    }
-
     /// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is
     /// called condition bitmap. In order to handle nested decisions, several condition bitmaps can
     /// be allocated for a function body. These values are named `mcdc.addr.{i}` and are a 32-bit
@@ -157,12 +151,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
         // Mark the instance as used in this CGU, for coverage purposes.
         // This includes functions that were not partitioned into this CGU,
         // but were MIR-inlined into one of this CGU's functions.
-        coverage_cx.function_coverage_map.borrow_mut().entry(instance).or_insert_with(|| {
-            FunctionCoverage::new_used(
-                function_coverage_info,
-                bx.tcx.coverage_ids_info(instance.def),
-            )
-        });
+        coverage_cx.instances_used.borrow_mut().insert(instance);
 
         match *kind {
             CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
index aef8642f199..2c9f1cda13a 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
@@ -7,7 +7,7 @@ use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::bug;
 use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerType;
 use rustc_session::config::{CrateType, DebugInfo};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 use crate::builder::Builder;
 use crate::common::CodegenCx;
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 59275254022..40248a9009a 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -16,8 +16,7 @@ use rustc_middle::ty::{
     self, AdtKind, CoroutineArgsExt, Instance, PolyExistentialTraitRef, Ty, TyCtxt, Visibility,
 };
 use rustc_session::config::{self, DebugInfo, Lto};
-use rustc_span::symbol::Symbol;
-use rustc_span::{DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, hygiene};
+use rustc_span::{DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Symbol, hygiene};
 use rustc_symbol_mangling::typeid_for_trait_ref;
 use rustc_target::spec::DebuginfoKind;
 use smallvec::smallvec;
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index a8fdfbed592..fae698bea2a 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -19,9 +19,8 @@ use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
 use rustc_middle::ty::{self, GenericArgsRef, Instance, Ty, TypeVisitableExt};
 use rustc_session::Session;
 use rustc_session::config::{self, DebugInfo};
-use rustc_span::symbol::Symbol;
 use rustc_span::{
-    BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span, StableSourceFileId,
+    BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span, StableSourceFileId, Symbol,
 };
 use smallvec::SmallVec;
 use tracing::debug;
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index af8562db054..b079eb8fe0c 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -43,7 +43,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_middle::util::Providers;
 use rustc_session::Session;
 use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest};
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 
 mod back {
     pub(crate) mod archive;
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index bfec7d708cf..628c0b1c29c 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -15,7 +15,7 @@ use rustc_fs_util::path_to_c_string;
 use rustc_middle::bug;
 use rustc_session::Session;
 use rustc_session::config::{PrintKind, PrintRequest};
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
 use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
 
@@ -373,9 +373,9 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol>
         .rust_target_features()
         .iter()
         .filter(|(_, gate, _)| gate.in_cfg())
-        .filter_map(|&(feature, gate, _)| {
+        .filter_map(|(feature, gate, _)| {
             if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() {
-                Some(feature)
+                Some(*feature)
             } else {
                 None
             }
@@ -718,7 +718,7 @@ pub(crate) fn global_llvm_features(
                         }
                         Some((_, stability, _)) => {
                             if let Err(reason) =
-                                stability.compute_toggleability(&sess.target).allow_toggle()
+                                stability.toggle_allowed(&sess.target, enable_disable == '+')
                             {
                                 sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
                             } else if stability.requires_nightly().is_some() {
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 450a95ae20c..628543443b3 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -17,12 +17,13 @@ rustc_abi = { path = "../rustc_abi" }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
 rustc_fs_util = { path = "../rustc_fs_util" }
 rustc_hir = { path = "../rustc_hir" }
+rustc_hir_pretty = { path = "../rustc_hir_pretty" }
 rustc_incremental = { path = "../rustc_incremental" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs
index 11bcd727501..ab65319e3d3 100644
--- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs
+++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs
@@ -26,15 +26,14 @@
 use std::borrow::Cow;
 use std::fmt;
 
-use rustc_ast as ast;
 use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::{DiagArgValue, IntoDiagArg};
+use rustc_hir as hir;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::mir::mono::CodegenUnitNameBuilder;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
-use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 use thin_vec::ThinVec;
 use tracing::debug;
 
@@ -77,7 +76,7 @@ struct AssertModuleSource<'tcx> {
 }
 
 impl<'tcx> AssertModuleSource<'tcx> {
-    fn check_attr(&mut self, attr: &ast::Attribute) {
+    fn check_attr(&mut self, attr: &hir::Attribute) {
         let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) {
             (CguReuse::PreLto, ComparisonKind::AtLeast)
         } else if attr.has_name(sym::rustc_partition_codegened) {
@@ -158,7 +157,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
         );
     }
 
-    fn field(&self, attr: &ast::Attribute, name: Symbol) -> Symbol {
+    fn field(&self, attr: &hir::Attribute, name: Symbol) -> Symbol {
         for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
             if item.has_name(name) {
                 if let Some(value) = item.value_str() {
@@ -177,7 +176,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
 
     /// Scan for a `cfg="foo"` attribute and check whether we have a
     /// cfg flag called `foo`.
-    fn check_config(&self, attr: &ast::Attribute) -> bool {
+    fn check_config(&self, attr: &hir::Attribute) -> bool {
         let config = &self.tcx.sess.psess.config;
         let value = self.field(attr, sym::cfg);
         debug!("check_config(config={:?}, value={:?})", config, value);
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index d4836eb7a1d..d9eece1d8dc 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -14,7 +14,7 @@ use object::read::macho::FatArch;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::memmap::Mmap;
 use rustc_session::Session;
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 use tempfile::Builder as TempFileBuilder;
 use tracing::trace;
 
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index cfd40a575b1..f4f6161ebbc 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -35,7 +35,7 @@ use rustc_session::utils::NativeLibKind;
 /// For all the linkers we support, and information they might
 /// need out of the shared crate context before we get rid of it.
 use rustc_session::{Session, filesearch};
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 use rustc_target::spec::crt_objects::CrtObjects;
 use rustc_target::spec::{
     Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures,
@@ -2982,7 +2982,7 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) {
 
 fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
     match lib.cfg {
-        Some(ref cfg) => rustc_attr::cfg_matches(cfg, sess, CRATE_NODE_ID, None),
+        Some(ref cfg) => rustc_attr_parsing::cfg_matches(cfg, sess, CRATE_NODE_ID, None),
         None => true,
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 301b22f2be4..8a2f3d73bc1 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -16,7 +16,7 @@ use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, S
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld};
 use tracing::{debug, warn};
 
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 788a8a13b3e..60ab2919352 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -311,7 +311,8 @@ fn exported_symbols_provider_local(
             }
 
             if !tcx.sess.opts.share_generics() {
-                if tcx.codegen_fn_attrs(mono_item.def_id()).inline == rustc_attr::InlineAttr::Never
+                if tcx.codegen_fn_attrs(mono_item.def_id()).inline
+                    == rustc_attr_parsing::InlineAttr::Never
                 {
                     // this is OK, we explicitly allow sharing inline(never) across crates even
                     // without share-generics.
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 683defcafee..b40bb4ed5d2 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -33,8 +33,7 @@ use rustc_session::config::{
     self, CrateType, Lto, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath,
 };
 use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::sym;
-use rustc_span::{FileName, InnerSpan, Span, SpanData};
+use rustc_span::{FileName, InnerSpan, Span, SpanData, sym};
 use rustc_target::spec::{MergeFunctions, SanitizerSet};
 use tracing::debug;
 
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 27c9cb0b31e..77e1fed720d 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -5,7 +5,6 @@ use std::time::{Duration, Instant};
 use itertools::Itertools;
 use rustc_abi::FIRST_VARIANT;
 use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name};
-use rustc_attr as attr;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
 use rustc_data_structures::sync::{Lrc, par_map};
@@ -25,12 +24,12 @@ use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
 use rustc_session::Session;
 use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType};
-use rustc_span::symbol::sym;
-use rustc_span::{DUMMY_SP, Symbol};
+use rustc_span::{DUMMY_SP, Symbol, sym};
 use rustc_trait_selection::infer::at::ToTrace;
 use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
 use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
 use tracing::{debug, info};
+use {rustc_ast as ast, rustc_attr_parsing as attr};
 
 use crate::assert_module_sources::CguReuse;
 use crate::back::link::are_upstream_rust_objects_already_included;
@@ -873,7 +872,8 @@ impl CrateInfo {
             crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect();
         let local_crate_name = tcx.crate_name(LOCAL_CRATE);
         let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID);
-        let subsystem = attr::first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem);
+        let subsystem =
+            ast::attr::first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem);
         let windows_subsystem = subsystem.map(|subsystem| {
             if subsystem != sym::windows && subsystem != sym::console {
                 tcx.dcx().emit_fatal(errors::InvalidWindowsSubsystem { subsystem });
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index ab2d24e8d2d..cdb72aba36f 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -1,5 +1,6 @@
-use rustc_ast::{MetaItemInner, MetaItemKind, ast, attr};
-use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr, list_contains_name};
+use rustc_ast::attr::list_contains_name;
+use rustc_ast::{MetaItemInner, attr};
+use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::codes::*;
 use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
@@ -15,8 +16,7 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::{self as ty, TyCtxt};
 use rustc_session::parse::feature_err;
 use rustc_session::{Session, lint};
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, sym};
+use rustc_span::{Ident, Span, sym};
 use rustc_target::spec::{SanitizerSet, abi};
 
 use crate::errors;
@@ -251,7 +251,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             sym::target_feature => {
                 if !tcx.is_closure_like(did.to_def_id())
                     && let Some(fn_sig) = fn_sig()
-                    && fn_sig.skip_binder().safety() == hir::Safety::Safe
+                    && fn_sig.skip_binder().safety().is_safe()
                 {
                     if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
                         // The `#[target_feature]` attribute is allowed on
@@ -425,7 +425,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     && let [item] = items.as_slice()
                     && let Some((sym::align, literal)) = item.singleton_lit_list()
                 {
-                    rustc_attr::parse_alignment(&literal.kind)
+                    rustc_attr_parsing::parse_alignment(&literal.kind)
                         .map_err(|msg| {
                             struct_span_code_err!(
                                 tcx.dcx(),
@@ -525,28 +525,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         if !attr.has_name(sym::inline) {
             return ia;
         }
-        match attr.meta_kind() {
-            Some(MetaItemKind::Word) => InlineAttr::Hint,
-            Some(MetaItemKind::List(ref items)) => {
-                inline_span = Some(attr.span);
-                if items.len() != 1 {
-                    struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument")
-                        .emit();
-                    InlineAttr::None
-                } else if list_contains_name(items, sym::always) {
-                    InlineAttr::Always
-                } else if list_contains_name(items, sym::never) {
-                    InlineAttr::Never
-                } else {
-                    struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
-                        .with_help("valid inline arguments are `always` and `never`")
-                        .emit();
+        if attr.is_word() {
+            InlineAttr::Hint
+        } else if let Some(ref items) = attr.meta_item_list() {
+            inline_span = Some(attr.span);
+            if items.len() != 1 {
+                struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit();
+                InlineAttr::None
+            } else if list_contains_name(items, sym::always) {
+                InlineAttr::Always
+            } else if list_contains_name(items, sym::never) {
+                InlineAttr::Never
+            } else {
+                struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
+                    .with_help("valid inline arguments are `always` and `never`")
+                    .emit();
 
-                    InlineAttr::None
-                }
+                InlineAttr::None
             }
-            Some(MetaItemKind::NameValue(_)) => ia,
-            None => ia,
+        } else {
+            ia
         }
     });
 
@@ -562,27 +560,24 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             return ia;
         }
         let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
-        match attr.meta_kind() {
-            Some(MetaItemKind::Word) => {
+        if attr.is_word() {
+            err(attr.span, "expected one argument");
+            ia
+        } else if let Some(ref items) = attr.meta_item_list() {
+            inline_span = Some(attr.span);
+            if items.len() != 1 {
                 err(attr.span, "expected one argument");
-                ia
-            }
-            Some(MetaItemKind::List(ref items)) => {
-                inline_span = Some(attr.span);
-                if items.len() != 1 {
-                    err(attr.span, "expected one argument");
-                    OptimizeAttr::None
-                } else if list_contains_name(items, sym::size) {
-                    OptimizeAttr::Size
-                } else if list_contains_name(items, sym::speed) {
-                    OptimizeAttr::Speed
-                } else {
-                    err(items[0].span(), "invalid argument");
-                    OptimizeAttr::None
-                }
+                OptimizeAttr::None
+            } else if list_contains_name(items, sym::size) {
+                OptimizeAttr::Size
+            } else if list_contains_name(items, sym::speed) {
+                OptimizeAttr::Speed
+            } else {
+                err(items[0].span(), "invalid argument");
+                OptimizeAttr::None
             }
-            Some(MetaItemKind::NameValue(_)) => ia,
-            None => ia,
+        } else {
+            OptimizeAttr::None
         }
     });
 
@@ -730,7 +725,7 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     false
 }
 
-fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
+fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
     use rustc_ast::{LitIntType, LitKind, MetaItemLit};
     let meta_item_list = attr.meta_item_list();
     let meta_item_list = meta_item_list.as_deref();
@@ -795,7 +790,7 @@ struct MixedExportNameAndNoMangleState<'a> {
     export_name: Option<Span>,
     hir_id: Option<HirId>,
     no_mangle: Option<Span>,
-    no_mangle_attr: Option<&'a ast::Attribute>,
+    no_mangle_attr: Option<&'a hir::Attribute>,
 }
 
 impl<'a> MixedExportNameAndNoMangleState<'a> {
@@ -803,7 +798,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
         self.export_name = Some(span);
     }
 
-    fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a ast::Attribute) {
+    fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) {
         self.no_mangle = Some(span);
         self.hir_id = Some(hir_id);
         self.no_mangle_attr = Some(attr_name);
@@ -824,7 +819,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
                 no_mangle,
                 errors::MixedExportNameAndNoMangle {
                     no_mangle,
-                    no_mangle_attr: rustc_ast_pretty::pprust::attribute_to_string(no_mangle_attr),
+                    no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr),
                     export_name,
                     removal_span: no_mangle,
                 },
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 7dc8ab38a97..65c6067c740 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -42,7 +42,7 @@ use rustc_session::Session;
 use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
 use rustc_session::cstore::{self, CrateSource};
 use rustc_session::utils::NativeLibKind;
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 
 pub mod assert_module_sources;
 pub mod back;
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index d4d7f16db55..843a996d2bf 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -10,8 +10,7 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::{Instance, Ty};
 use rustc_middle::{bug, mir, ty};
 use rustc_session::config::DebugInfo;
-use rustc_span::symbol::{Symbol, kw};
-use rustc_span::{BytePos, Span, hygiene};
+use rustc_span::{BytePos, Span, Symbol, hygiene, kw};
 
 use super::operand::{OperandRef, OperandValue};
 use super::place::{PlaceRef, PlaceValue};
diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
index c08758a9796..cac3cc587cb 100644
--- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
@@ -1,4 +1,4 @@
-use rustc_attr::InstructionSetAttr;
+use rustc_attr_parsing::InstructionSetAttr;
 use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility};
 use rustc_middle::mir::{Body, InlineAsmOperand};
 use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf};
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index c38484109d2..a9e80e27ed4 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -422,10 +422,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
             layout.size
         };
 
-        let llval = bx.inbounds_gep(bx.cx().backend_type(self.layout), self.val.llval, &[
-            bx.cx().const_usize(0),
-            llindex,
-        ]);
+        let llval = bx.inbounds_gep(bx.cx().backend_type(layout), self.val.llval, &[llindex]);
         let align = self.val.align.restrict_for_offset(offset);
         PlaceValue::new_sized(llval, align).with_type(layout)
     }
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index fa600ec7166..7e80d014ea2 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -1,16 +1,15 @@
-use rustc_ast::ast;
-use rustc_attr::InstructionSetAttr;
+use rustc_attr_parsing::InstructionSetAttr;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::Applicability;
+use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::parse::feature_err;
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use rustc_target::target_features;
 
 use crate::errors;
@@ -19,7 +18,7 @@ use crate::errors;
 /// Enabled target features are added to `target_features`.
 pub(crate) fn from_target_feature_attr(
     tcx: TyCtxt<'_>,
-    attr: &ast::Attribute,
+    attr: &hir::Attribute,
     rust_target_features: &UnordMap<String, target_features::StabilityComputed>,
     target_features: &mut Vec<TargetFeature>,
 ) {
@@ -65,7 +64,7 @@ pub(crate) fn from_target_feature_attr(
 
             // Only allow target features whose feature gates have been enabled
             // and which are permitted to be toggled.
-            if let Err(reason) = stability.allow_toggle() {
+            if let Err(reason) = stability.toggle_allowed(/*enable*/ true) {
                 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
                     span: item.span(),
                     feature,
@@ -160,7 +159,7 @@ pub(crate) fn provide(providers: &mut Providers) {
                     .target
                     .rust_target_features()
                     .iter()
-                    .map(|&(a, b, _)| (a.to_string(), b.compute_toggleability(target)))
+                    .map(|(a, b, _)| (a.to_string(), b.compute_toggleability(target)))
                     .collect()
             }
         },
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index 4b17db2c49e..ebcf118b903 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_middle::util::Providers;
 use rustc_session::Session;
 use rustc_session::config::{self, OutputFilenames, PrintRequest};
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 
 use super::CodegenObject;
 use super::write::WriteBackendMethods;
diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml
index 41136019a88..7717cd2c696 100644
--- a/compiler/rustc_const_eval/Cargo.toml
+++ b/compiler/rustc_const_eval/Cargo.toml
@@ -9,7 +9,7 @@ either = "1"
 rustc_abi = { path = "../rustc_abi" }
 rustc_apfloat = "0.2.0"
 rustc_ast = { path = "../rustc_ast" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 16610ebfca2..f4257ad9671 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -6,7 +6,7 @@ use std::mem;
 use std::num::NonZero;
 use std::ops::Deref;
 
-use rustc_attr::{ConstStability, StabilityLevel};
+use rustc_attr_parsing::{ConstStability, StabilityLevel};
 use rustc_errors::{Diag, ErrorGuaranteed};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir, LangItem};
diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs
index 80d3c6448aa..ab68691f1b9 100644
--- a/compiler/rustc_const_eval/src/check_consts/mod.rs
+++ b/compiler/rustc_const_eval/src/check_consts/mod.rs
@@ -9,7 +9,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::{self, PolyFnSig, TyCtxt};
 use rustc_middle::{bug, mir};
 use rustc_span::Symbol;
-use {rustc_attr as attr, rustc_hir as hir};
+use {rustc_attr_parsing as attr, rustc_hir as hir};
 
 pub use self::qualifs::Qualif;
 
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index 23f2aa4d029..afb7900c4b0 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -15,8 +15,7 @@ use rustc_middle::ty::{
     suggest_constraining_type_param,
 };
 use rustc_middle::util::{CallDesugaringKind, CallKind, call_kind};
-use rustc_span::symbol::sym;
-use rustc_span::{BytePos, Pos, Span, Symbol};
+use rustc_span::{BytePos, Pos, Span, Symbol, sym};
 use rustc_trait_selection::traits::SelectionContext;
 use tracing::debug;
 
diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
index 951e19b470b..16e142a85ee 100644
--- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
@@ -1,7 +1,7 @@
 use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::{self, BasicBlock, Location};
 use rustc_middle::ty::TyCtxt;
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 use tracing::trace;
 
 use super::ConstCx;
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
index f5396c62433..babf99c4c1f 100644
--- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -57,9 +57,8 @@ fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
             Some(stab) => {
                 if cfg!(debug_assertions) && stab.promotable {
                     let sig = tcx.fn_sig(def_id);
-                    assert_eq!(
-                        sig.skip_binder().safety(),
-                        hir::Safety::Safe,
+                    assert!(
+                        sig.skip_binder().safety().is_safe(),
                         "don't mark const unsafe fns as promotable",
                         // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
                     );
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 23683851799..9c660ef0b18 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -13,8 +13,7 @@ use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, mir};
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use tracing::debug;
 
 use super::error::*;
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index a79923e8555..1af8438534f 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -11,7 +11,7 @@ use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
 use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement};
 use rustc_middle::ty::{GenericArgsRef, Ty, TyCtxt};
 use rustc_middle::{bug, ty};
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 use tracing::trace;
 
 use super::memory::MemoryKind;
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 8b7b78c7129..5fa632fc57a 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -6,7 +6,7 @@ use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty};
 use rustc_middle::{bug, mir, span_bug};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 use tracing::trace;
 
 use super::{ImmTy, InterpCx, Machine, MemPlaceMeta, interp_ok, throw_ub};
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 273eaf42d87..8e18b243906 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -26,7 +26,7 @@ use rustc_middle::mir::interpret::{
 };
 use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 use tracing::trace;
 
 use super::machine::AllocMap;
diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs
index 6593547cd23..6dd9447cf5a 100644
--- a/compiler/rustc_const_eval/src/util/caller_location.rs
+++ b/compiler/rustc_const_eval/src/util/caller_location.rs
@@ -3,7 +3,7 @@ use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self};
 use rustc_middle::{bug, mir};
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 use tracing::trace;
 
 use crate::const_eval::{CanAccessMutGlobal, CompileTimeInterpCx, mk_eval_cx_to_read_const_val};
diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml
index 81f15ebcbf8..2f0fe64b096 100644
--- a/compiler/rustc_driver_impl/Cargo.toml
+++ b/compiler/rustc_driver_impl/Cargo.toml
@@ -9,7 +9,7 @@ rustc_ast = { path = "../rustc_ast" }
 rustc_ast_lowering = { path = "../rustc_ast_lowering" }
 rustc_ast_passes = { path = "../rustc_ast_passes" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_borrowck = { path = "../rustc_borrowck" }
 rustc_builtin_macros = { path = "../rustc_builtin_macros" }
 rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index b80736f41ad..ed49dfe1761 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -45,7 +45,7 @@ use rustc_errors::registry::Registry;
 use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown};
 use rustc_feature::find_gated_cfg;
 use rustc_interface::util::{self, get_codegen_backend};
-use rustc_interface::{Linker, interface, passes};
+use rustc_interface::{Linker, create_and_enter_global_ctxt, interface, passes};
 use rustc_lint::unerased_lint_store;
 use rustc_metadata::creader::MetadataLoader;
 use rustc_metadata::locator;
@@ -112,7 +112,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
     crate::DEFAULT_LOCALE_RESOURCE,
     rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE,
     rustc_ast_passes::DEFAULT_LOCALE_RESOURCE,
-    rustc_attr::DEFAULT_LOCALE_RESOURCE,
+    rustc_attr_parsing::DEFAULT_LOCALE_RESOURCE,
     rustc_borrowck::DEFAULT_LOCALE_RESOURCE,
     rustc_builtin_macros::DEFAULT_LOCALE_RESOURCE,
     rustc_codegen_ssa::DEFAULT_LOCALE_RESOURCE,
@@ -387,76 +387,69 @@ fn run_compiler(
             return early_exit();
         }
 
-        let linker = compiler.enter(|queries| {
+        // Parse the crate root source code (doesn't parse submodules yet)
+        // Everything else is parsed during macro expansion.
+        let krate = passes::parse(sess);
+
+        // If pretty printing is requested: Figure out the representation, print it and exit
+        if let Some(pp_mode) = sess.opts.pretty {
+            if pp_mode.needs_ast_map() {
+                create_and_enter_global_ctxt(compiler, krate, |tcx| {
+                    tcx.ensure().early_lint_checks(());
+                    pretty::print(sess, pp_mode, pretty::PrintExtra::NeedsAstMap { tcx });
+                    passes::write_dep_info(tcx);
+                });
+            } else {
+                pretty::print(sess, pp_mode, pretty::PrintExtra::AfterParsing { krate: &krate });
+            }
+            trace!("finished pretty-printing");
+            return early_exit();
+        }
+
+        if callbacks.after_crate_root_parsing(compiler, &krate) == Compilation::Stop {
+            return early_exit();
+        }
+
+        if sess.opts.unstable_opts.parse_crate_root_only {
+            return early_exit();
+        }
+
+        let linker = create_and_enter_global_ctxt(compiler, krate, |tcx| {
             let early_exit = || {
                 sess.dcx().abort_if_errors();
                 None
             };
 
-            // Parse the crate root source code (doesn't parse submodules yet)
-            // Everything else is parsed during macro expansion.
-            queries.parse();
-
-            // If pretty printing is requested: Figure out the representation, print it and exit
-            if let Some(pp_mode) = sess.opts.pretty {
-                if pp_mode.needs_ast_map() {
-                    queries.global_ctxt().enter(|tcx| {
-                        tcx.ensure().early_lint_checks(());
-                        pretty::print(sess, pp_mode, pretty::PrintExtra::NeedsAstMap { tcx });
-                        passes::write_dep_info(tcx);
-                    });
-                } else {
-                    let krate = queries.parse();
-                    pretty::print(sess, pp_mode, pretty::PrintExtra::AfterParsing {
-                        krate: &*krate.borrow(),
-                    });
-                }
-                trace!("finished pretty-printing");
+            // Make sure name resolution and macro expansion is run.
+            let _ = tcx.resolver_for_lowering();
+
+            if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
+                dump_feature_usage_metrics(tcx, metrics_dir);
+            }
+
+            if callbacks.after_expansion(compiler, tcx) == Compilation::Stop {
                 return early_exit();
             }
 
-            if callbacks.after_crate_root_parsing(compiler, &*queries.parse().borrow())
-                == Compilation::Stop
+            passes::write_dep_info(tcx);
+
+            if sess.opts.output_types.contains_key(&OutputType::DepInfo)
+                && sess.opts.output_types.len() == 1
             {
                 return early_exit();
             }
 
-            if sess.opts.unstable_opts.parse_crate_root_only {
+            if sess.opts.unstable_opts.no_analysis {
                 return early_exit();
             }
 
-            queries.global_ctxt().enter(|tcx| {
-                // Make sure name resolution and macro expansion is run.
-                let _ = tcx.resolver_for_lowering();
-
-                if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
-                    dump_feature_usage_metrics(tcx, metrics_dir);
-                }
-
-                if callbacks.after_expansion(compiler, tcx) == Compilation::Stop {
-                    return early_exit();
-                }
-
-                passes::write_dep_info(tcx);
-
-                if sess.opts.output_types.contains_key(&OutputType::DepInfo)
-                    && sess.opts.output_types.len() == 1
-                {
-                    return early_exit();
-                }
-
-                if sess.opts.unstable_opts.no_analysis {
-                    return early_exit();
-                }
+            tcx.ensure().analysis(());
 
-                tcx.ensure().analysis(());
-
-                if callbacks.after_analysis(compiler, tcx) == Compilation::Stop {
-                    return early_exit();
-                }
+            if callbacks.after_analysis(compiler, tcx) == Compilation::Stop {
+                return early_exit();
+            }
 
-                Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend))
-            })
+            Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend))
         });
 
         // Linking is done outside the `compiler.enter()` so that the
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index 5df960be307..5a1a873d4bd 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -11,8 +11,7 @@ use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::Session;
 use rustc_session::config::{OutFileName, PpHirMode, PpMode, PpSourceMode};
 use rustc_smir::rustc_internal::pretty::write_smir_pretty;
-use rustc_span::FileName;
-use rustc_span::symbol::Ident;
+use rustc_span::{FileName, Ident};
 use tracing::debug;
 use {rustc_ast as ast, rustc_hir_pretty as pprust_hir};
 
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 4352de3ad25..05b9cbfbc06 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -11,8 +11,7 @@ use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and};
 use rustc_lint_defs::Applicability;
 use rustc_macros::{Decodable, Encodable};
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::Symbol;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol};
 use tracing::debug;
 
 use crate::snippet::Style;
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index b4510371323..d179396398f 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -9,9 +9,8 @@ use rustc_abi::TargetDataLayoutErrors;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_ast_pretty::pprust;
 use rustc_macros::Subdiagnostic;
-use rustc_span::Span;
 use rustc_span::edition::Edition;
-use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol};
+use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
 use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTuple};
 use rustc_type_ir::{ClosureKind, FloatTy};
 use {rustc_ast as ast, rustc_hir as hir};
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 25d41462556..58fe3ec4b85 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -1569,18 +1569,18 @@ impl DiagCtxtInner {
                 debug!(?diagnostic);
                 debug!(?self.emitted_diagnostics);
 
-                let already_emitted_sub = |sub: &mut Subdiag| {
+                let not_yet_emitted = |sub: &mut Subdiag| {
                     debug!(?sub);
                     if sub.level != OnceNote && sub.level != OnceHelp {
-                        return false;
+                        return true;
                     }
                     let mut hasher = StableHasher::new();
                     sub.hash(&mut hasher);
                     let diagnostic_hash = hasher.finish();
                     debug!(?diagnostic_hash);
-                    !self.emitted_diagnostics.insert(diagnostic_hash)
+                    self.emitted_diagnostics.insert(diagnostic_hash)
                 };
-                diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {});
+                diagnostic.children.retain_mut(not_yet_emitted);
                 if already_emitted {
                     let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`";
                     diagnostic.sub(Note, msg, MultiSpan::new());
diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml
index ce014364b0d..eb93972387d 100644
--- a/compiler/rustc_expand/Cargo.toml
+++ b/compiler/rustc_expand/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_passes = { path = "../rustc_ast_passes" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index bed500c3032..e6adbc0f0ac 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -4,13 +4,13 @@ use std::path::Component::Prefix;
 use std::path::{Path, PathBuf};
 use std::rc::Rc;
 
-use rustc_ast::attr::MarkedAttrs;
+use rustc_ast::attr::{AttributeExt, MarkedAttrs};
 use rustc_ast::ptr::P;
 use rustc_ast::token::Nonterminal;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{AssocCtxt, Visitor};
 use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
-use rustc_attr::{self as attr, Deprecation, Stability};
+use rustc_attr_parsing::{self as attr, Deprecation, Stability};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::sync::{self, Lrc};
 use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
@@ -25,8 +25,7 @@ use rustc_span::def_id::{CrateNum, DefId, LocalDefId};
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind};
 use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, FileName, Span};
+use rustc_span::{DUMMY_SP, FileName, Ident, Span, Symbol, kw, sym};
 use smallvec::{SmallVec, smallvec};
 use thin_vec::ThinVec;
 
@@ -782,10 +781,12 @@ impl SyntaxExtension {
         }
     }
 
-    fn collapse_debuginfo_by_name(attr: &Attribute) -> Result<CollapseMacroDebuginfo, Span> {
+    fn collapse_debuginfo_by_name(
+        attr: &impl AttributeExt,
+    ) -> Result<CollapseMacroDebuginfo, Span> {
         let list = attr.meta_item_list();
         let Some([MetaItemInner::MetaItem(item)]) = list.as_deref() else {
-            return Err(attr.span);
+            return Err(attr.span());
         };
         if !item.is_word() {
             return Err(item.span);
@@ -805,9 +806,9 @@ impl SyntaxExtension {
     /// | (unspecified) | no  | if-ext        | if-ext   | yes |
     /// | external      | no  | if-ext        | if-ext   | yes |
     /// | yes           | yes | yes           | yes      | yes |
-    fn get_collapse_debuginfo(sess: &Session, attrs: &[ast::Attribute], ext: bool) -> bool {
+    fn get_collapse_debuginfo(sess: &Session, attrs: &[impl AttributeExt], ext: bool) -> bool {
         let flag = sess.opts.cg.collapse_macro_debuginfo;
-        let attr = attr::find_by_name(attrs, sym::collapse_debuginfo)
+        let attr = ast::attr::find_by_name(attrs, sym::collapse_debuginfo)
             .and_then(|attr| {
                 Self::collapse_debuginfo_by_name(attr)
                     .map_err(|span| {
@@ -816,7 +817,7 @@ impl SyntaxExtension {
                     .ok()
             })
             .unwrap_or_else(|| {
-                if attr::contains_name(attrs, sym::rustc_builtin_macro) {
+                if ast::attr::contains_name(attrs, sym::rustc_builtin_macro) {
                     CollapseMacroDebuginfo::Yes
                 } else {
                     CollapseMacroDebuginfo::Unspecified
@@ -842,20 +843,20 @@ impl SyntaxExtension {
         helper_attrs: Vec<Symbol>,
         edition: Edition,
         name: Symbol,
-        attrs: &[ast::Attribute],
+        attrs: &[impl AttributeExt],
         is_local: bool,
     ) -> SyntaxExtension {
         let allow_internal_unstable =
-            attr::allow_internal_unstable(sess, attrs).collect::<Vec<Symbol>>();
+            rustc_attr_parsing::allow_internal_unstable(sess, attrs).collect::<Vec<Symbol>>();
 
-        let allow_internal_unsafe = attr::contains_name(attrs, sym::allow_internal_unsafe);
-        let local_inner_macros = attr::find_by_name(attrs, sym::macro_export)
+        let allow_internal_unsafe = ast::attr::contains_name(attrs, sym::allow_internal_unsafe);
+        let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export)
             .and_then(|macro_export| macro_export.meta_item_list())
-            .is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros));
+            .is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros));
         let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local);
         tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe);
 
-        let (builtin_name, helper_attrs) = attr::find_by_name(attrs, sym::rustc_builtin_macro)
+        let (builtin_name, helper_attrs) = ast::attr::find_by_name(attrs, sym::rustc_builtin_macro)
             .map(|attr| {
                 // Override `helper_attrs` passed above if it's a built-in macro,
                 // marking `proc_macro_derive` macros as built-in is not a realistic use case.
@@ -1305,7 +1306,7 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe
 
 pub fn parse_macro_name_and_helper_attrs(
     dcx: DiagCtxtHandle<'_>,
-    attr: &Attribute,
+    attr: &impl AttributeExt,
     macro_type: &str,
 ) -> Option<(Symbol, Vec<Symbol>)> {
     // Once we've located the `#[proc_macro_derive]` attribute, verify
@@ -1313,7 +1314,7 @@ pub fn parse_macro_name_and_helper_attrs(
     // `#[proc_macro_derive(Foo, attributes(A, ..))]`
     let list = attr.meta_item_list()?;
     let ([trait_attr] | [trait_attr, _]) = list.as_slice() else {
-        dcx.emit_err(errors::AttrNoArguments { span: attr.span });
+        dcx.emit_err(errors::AttrNoArguments { span: attr.span() });
         return None;
     };
     let Some(trait_attr) = trait_attr.meta_item() else {
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index a673e2e3250..22bfda34cc0 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -4,8 +4,7 @@ use rustc_ast::{
     self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, attr, token,
 };
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use crate::base::ExtCtxt;
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index dc6aa110f45..91624c7554c 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -8,7 +8,7 @@ use rustc_ast::tokenstream::{
 use rustc_ast::{
     self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId,
 };
-use rustc_attr as attr;
+use rustc_attr_parsing as attr;
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_feature::{
     ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features,
@@ -18,8 +18,7 @@ use rustc_lint_defs::BuiltinLintDiag;
 use rustc_parse::validate_attr;
 use rustc_session::Session;
 use rustc_session::parse::feature_err;
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use thin_vec::ThinVec;
 use tracing::instrument;
 
@@ -362,7 +361,7 @@ impl<'a> StripUnconfigured<'a> {
         ));
 
         let tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::new(trees)));
-        let attr = attr::mk_attr_from_item(
+        let attr = ast::attr::mk_attr_from_item(
             &self.sess.psess.attr_id_generator,
             item,
             tokens,
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs
index 7bd7c305539..89bdc7b6dfa 100644
--- a/compiler/rustc_expand/src/errors.rs
+++ b/compiler/rustc_expand/src/errors.rs
@@ -4,8 +4,7 @@ use rustc_ast::ast;
 use rustc_errors::codes::*;
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::Limit;
-use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent};
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
 
 #[derive(Diagnostic)]
 #[diag(expand_expr_repeat_no_syntax_vars)]
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 690e080fbfc..ec497f6f8f1 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -29,8 +29,7 @@ use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
 use rustc_session::parse::feature_err;
 use rustc_session::{Limit, Session};
 use rustc_span::hygiene::SyntaxContext;
-use rustc_span::symbol::{Ident, sym};
-use rustc_span::{ErrorGuaranteed, FileName, LocalExpnId, Span};
+use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, sym};
 use smallvec::SmallVec;
 
 use crate::base::*;
@@ -1913,7 +1912,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                     self.cx.current_expansion.lint_node_id,
                     BuiltinLintDiag::UnusedDocComment(attr.span),
                 );
-            } else if rustc_attr::is_builtin_attr(attr) {
+            } else if rustc_attr_parsing::is_builtin_attr(attr) {
                 let attr_name = attr.ident().unwrap().name;
                 // `#[cfg]` and `#[cfg_attr]` are special - they are
                 // eagerly evaluated.
diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs
index e5d098f63d6..4ff8c02bcdb 100644
--- a/compiler/rustc_expand/src/mbe.rs
+++ b/compiler/rustc_expand/src/mbe.rs
@@ -16,8 +16,7 @@ use metavar_expr::MetaVarExpr;
 use rustc_ast::token::{Delimiter, NonterminalKind, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpacing, DelimSpan};
 use rustc_macros::{Decodable, Encodable};
-use rustc_span::Span;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, Span};
 
 /// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`.
 /// The delimiters are not represented explicitly in the `tts` vector.
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index 77b8d228922..9f48835e15d 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -7,8 +7,7 @@ use rustc_macros::Subdiagnostic;
 use rustc_parse::parser::{Parser, Recovery, token_descr};
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::Ident;
-use rustc_span::{ErrorGuaranteed, Span};
+use rustc_span::{ErrorGuaranteed, Ident, Span};
 use tracing::debug;
 
 use super::macro_rules::{NoopTracker, parser_from_cx};
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index 1498b9cbd5d..729dec2bfbd 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -115,8 +115,7 @@ use rustc_lint_defs::BuiltinLintDiag;
 use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER};
 use rustc_session::parse::ParseSess;
 use rustc_span::edition::Edition;
-use rustc_span::symbol::{MacroRulesNormalizedIdent, kw};
-use rustc_span::{ErrorGuaranteed, Span};
+use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw};
 use smallvec::SmallVec;
 
 use super::quoted::VALID_FRAGMENT_NAMES_MSG;
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index 2a8dddc1e00..d709fd79281 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -82,8 +82,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_lint_defs::pluralize;
 use rustc_parse::parser::{ParseNtResult, Parser, token_descr};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent};
+use rustc_span::{Ident, MacroRulesNormalizedIdent, Span};
 
 use crate::mbe::macro_rules::Tracker;
 use crate::mbe::{KleeneOp, TokenTree};
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index a373c753cc1..7ac9f453bae 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -3,13 +3,14 @@ use std::collections::hash_map::Entry;
 use std::{mem, slice};
 
 use ast::token::IdentIsRaw;
+use rustc_ast::attr::AttributeExt;
 use rustc_ast::token::NtPatKind::*;
 use rustc_ast::token::TokenKind::*;
 use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream};
 use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
 use rustc_ast_pretty::pprust;
-use rustc_attr::{self as attr, TransparencyError};
+use rustc_attr_parsing::{self as attr, TransparencyError};
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::{Applicability, ErrorGuaranteed};
 use rustc_feature::Features;
@@ -20,10 +21,9 @@ use rustc_lint_defs::builtin::{
 use rustc_parse::parser::{ParseNtResult, Parser, Recovery};
 use rustc_session::Session;
 use rustc_session::parse::ParseSess;
-use rustc_span::Span;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::Transparency;
-use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, kw, sym};
+use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, kw, sym};
 use tracing::{debug, instrument, trace, trace_span};
 
 use super::diagnostics;
@@ -371,7 +371,7 @@ pub fn compile_declarative_macro(
     features: &Features,
     macro_def: &ast::MacroDef,
     ident: Ident,
-    attrs: &[ast::Attribute],
+    attrs: &[impl AttributeExt],
     span: Span,
     node_id: NodeId,
     edition: Edition,
diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs
index 810a5d30c7e..da6e620a24f 100644
--- a/compiler/rustc_expand/src/mbe/metavar_expr.rs
+++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs
@@ -5,8 +5,7 @@ use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, PResult};
 use rustc_macros::{Decodable, Encodable};
 use rustc_session::parse::ParseSess;
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers";
 pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal";
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index 36094707fac..1addfabea23 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -4,9 +4,8 @@ use rustc_ast_pretty::pprust;
 use rustc_feature::Features;
 use rustc_session::Session;
 use rustc_session::parse::feature_err;
-use rustc_span::Span;
 use rustc_span::edition::Edition;
-use rustc_span::symbol::{Ident, kw, sym};
+use rustc_span::{Ident, Span, kw, sym};
 
 use crate::errors;
 use crate::mbe::macro_parser::count_metavar_decls;
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index b77d02e630a..4fb1eadd486 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -11,8 +11,9 @@ use rustc_parse::lexer::nfc_normalize;
 use rustc_parse::parser::ParseNtResult;
 use rustc_session::parse::{ParseSess, SymbolGallery};
 use rustc_span::hygiene::{LocalExpnId, Transparency};
-use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, sym};
-use rustc_span::{Span, Symbol, SyntaxContext, with_metavar_spans};
+use rustc_span::{
+    Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, sym, with_metavar_spans,
+};
 use smallvec::{SmallVec, smallvec};
 
 use crate::errors::{
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index 85ea42e78ad..a001b1d3dc8 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -7,8 +7,7 @@ use rustc_errors::{Diag, ErrorGuaranteed};
 use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, validate_attr};
 use rustc_session::Session;
 use rustc_session::parse::ParseSess;
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, sym};
+use rustc_span::{Ident, Span, sym};
 use thin_vec::ThinVec;
 
 use crate::base::ModuleData;
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index 9e459bd81a1..e969f2d4fb5 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -4,8 +4,7 @@ use rustc_ast::token::Delimiter;
 use rustc_ast::visit::AssocCtxt;
 use rustc_ast::{self as ast, Safety};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_span::DUMMY_SP;
-use rustc_span::symbol::Ident;
+use rustc_span::{DUMMY_SP, Ident};
 use smallvec::{SmallVec, smallvec};
 use thin_vec::ThinVec;
 
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 263df235b3e..0adff4eaf9d 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -18,8 +18,7 @@ use rustc_parse::parser::Parser;
 use rustc_parse::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal};
 use rustc_session::parse::ParseSess;
 use rustc_span::def_id::CrateNum;
-use rustc_span::symbol::{self, Symbol, sym};
-use rustc_span::{BytePos, FileName, Pos, SourceFile, Span};
+use rustc_span::{BytePos, FileName, Pos, SourceFile, Span, Symbol, sym};
 use smallvec::{SmallVec, smallvec};
 
 use crate::base::ExtCtxt;
@@ -230,7 +229,7 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
                 })),
 
                 Lifetime(name, is_raw) => {
-                    let ident = symbol::Ident::new(name, span).without_first_quote();
+                    let ident = rustc_span::Ident::new(name, span).without_first_quote();
                     trees.extend([
                         TokenTree::Punct(Punct { ch: b'\'', joint: true, span }),
                         TokenTree::Ident(Ident { sym: ident.name, is_raw: is_raw.into(), span }),
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index c5913ed27cf..21fd11c1c5d 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -1,6 +1,6 @@
 //! List of the accepted feature gates.
 
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 use super::{Feature, to_nonzero};
 
@@ -157,6 +157,9 @@ declare_features! (
     (accepted, const_refs_to_static, "1.83.0", Some(119618)),
     /// Allows implementing `Copy` for closures where possible (RFC 2132).
     (accepted, copy_closures, "1.26.0", Some(44490)),
+    /// Allows function attribute `#[coverage(on/off)]`, to control coverage
+    /// instrumentation of that function.
+    (accepted, coverage_attribute, "CURRENT_RUSTC_VERSION", Some(84605)),
     /// Allows `crate` in paths.
     (accepted, crate_in_paths, "1.30.0", Some(45477)),
     /// Allows users to provide classes for fenced code block using `class:classname`.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 3bf485c2eb6..a065db7f7d0 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -6,7 +6,7 @@ use AttributeDuplicates::*;
 use AttributeGate::*;
 use AttributeType::*;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 use crate::{Features, Stability};
 
@@ -480,10 +480,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
         EncodeCrossCrate::No, experimental!(no_sanitize)
     ),
-    gated!(
+    ungated!(
         coverage, Normal, template!(OneOf: &[sym::off, sym::on]),
         ErrorPreceding, EncodeCrossCrate::No,
-        coverage_attribute, experimental!(coverage)
     ),
 
     ungated!(
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index 5d27b8f542c..6db512ace1b 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -28,7 +28,7 @@ mod tests;
 
 use std::num::NonZero;
 
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 
 #[derive(Debug, Clone)]
 pub struct Feature {
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index e25840ba5fc..388ed9d08fa 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -1,6 +1,6 @@
 //! List of the removed feature gates.
 
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 use super::{Feature, to_nonzero};
 
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 45c63b03fe9..6570f9b565f 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -3,8 +3,7 @@
 use std::path::PathBuf;
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 
 use super::{Feature, to_nonzero};
 
@@ -332,6 +331,7 @@ declare_features! (
     (unstable, hexagon_target_feature, "1.27.0", Some(44839)),
     (unstable, lahfsahf_target_feature, "1.78.0", Some(44839)),
     (unstable, loongarch_target_feature, "1.73.0", Some(44839)),
+    (unstable, m68k_target_feature, "CURRENT_RUSTC_VERSION", Some(134328)),
     (unstable, mips_target_feature, "1.27.0", Some(44839)),
     (unstable, powerpc_target_feature, "1.27.0", Some(44839)),
     (unstable, prfchw_target_feature, "1.78.0", Some(44839)),
@@ -447,9 +447,6 @@ declare_features! (
     (unstable, coroutine_clone, "1.65.0", Some(95360)),
     /// Allows defining coroutines.
     (unstable, coroutines, "1.21.0", Some(43122)),
-    /// Allows function attribute `#[coverage(on/off)]`, to control coverage
-    /// instrumentation of that function.
-    (unstable, coverage_attribute, "1.74.0", Some(84605)),
     /// Allows non-builtin attributes in inner attribute position.
     (unstable, custom_inner_attributes, "1.30.0", Some(54726)),
     /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml
index 85c6da83379..5bfc4756ec6 100644
--- a/compiler/rustc_hir/Cargo.toml
+++ b/compiler/rustc_hir/Cargo.toml
@@ -16,5 +16,6 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+thin-vec = "0.2.12"
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs
index f1f624269ae..88c0d223fd3 100644
--- a/compiler/rustc_hir/src/arena.rs
+++ b/compiler/rustc_hir/src/arena.rs
@@ -6,7 +6,7 @@ macro_rules! arena_types {
         $macro!([
             // HIR types
             [] asm_template: rustc_ast::InlineAsmTemplatePiece,
-            [] attribute: rustc_ast::Attribute,
+            [] attribute: rustc_hir::Attribute,
             [] owner_info: rustc_hir::OwnerInfo<'tcx>,
             [] use_path: rustc_hir::UsePath<'tcx>,
             [] lit: rustc_hir::Lit,
diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs
index d85f9586d42..dc527240f74 100644
--- a/compiler/rustc_hir/src/definitions.rs
+++ b/compiler/rustc_hir/src/definitions.rs
@@ -11,7 +11,7 @@ use rustc_data_structures::stable_hasher::{Hash64, StableHasher};
 use rustc_data_structures::unord::UnordMap;
 use rustc_index::IndexVec;
 use rustc_macros::{Decodable, Encodable};
-use rustc_span::symbol::{Symbol, kw, sym};
+use rustc_span::{Symbol, kw, sym};
 use tracing::{debug, instrument};
 
 pub use crate::def_id::DefPathHash;
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 2e795d84559..398b694ae6b 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1,10 +1,13 @@
 use std::fmt;
 
 use rustc_abi::ExternAbi;
+// ignore-tidy-filelength
+use rustc_ast::attr::AttributeExt;
+use rustc_ast::token::CommentKind;
 use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
 use rustc_ast::{
-    self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
-    LitKind, TraitObjectSyntax, UintTy,
+    self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece,
+    IntTy, Label, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy,
 };
 pub use rustc_ast::{
     BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy,
@@ -17,10 +20,10 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Span};
+use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
 use rustc_target::asm::InlineAsmRegOrRegClass;
 use smallvec::SmallVec;
+use thin_vec::ThinVec;
 use tracing::debug;
 
 use crate::LangItem;
@@ -937,6 +940,250 @@ pub struct ParentedNode<'tcx> {
     pub node: Node<'tcx>,
 }
 
+/// Arguments passed to an attribute macro.
+#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
+pub enum AttrArgs {
+    /// No arguments: `#[attr]`.
+    Empty,
+    /// Delimited arguments: `#[attr()/[]/{}]`.
+    Delimited(DelimArgs),
+    /// Arguments of a key-value attribute: `#[attr = "value"]`.
+    Eq {
+        /// Span of the `=` token.
+        eq_span: Span,
+        /// The "value".
+        expr: MetaItemLit,
+    },
+}
+
+#[derive(Clone, Debug, Encodable, Decodable)]
+pub enum AttrKind {
+    /// A normal attribute.
+    Normal(Box<AttrItem>),
+
+    /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
+    /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
+    /// variant (which is much less compact and thus more expensive).
+    DocComment(CommentKind, Symbol),
+}
+
+#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
+pub struct AttrPath {
+    pub segments: Box<[Ident]>,
+    pub span: Span,
+}
+
+#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
+pub struct AttrItem {
+    pub unsafety: Safety,
+    // Not lowered to hir::Path because we have no NodeId to resolve to.
+    pub path: AttrPath,
+    pub args: AttrArgs,
+}
+
+#[derive(Clone, Debug, Encodable, Decodable)]
+pub struct Attribute {
+    pub kind: AttrKind,
+    pub id: AttrId,
+    /// Denotes if the attribute decorates the following construct (outer)
+    /// or the construct this attribute is contained within (inner).
+    pub style: AttrStyle,
+    pub span: Span,
+}
+
+impl Attribute {
+    pub fn get_normal_item(&self) -> &AttrItem {
+        match &self.kind {
+            AttrKind::Normal(normal) => &normal,
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+        }
+    }
+
+    pub fn unwrap_normal_item(self) -> AttrItem {
+        match self.kind {
+            AttrKind::Normal(normal) => *normal,
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
+        }
+    }
+
+    pub fn value_lit(&self) -> Option<&MetaItemLit> {
+        match &self.kind {
+            AttrKind::Normal(n) => match n.as_ref() {
+                AttrItem { args: AttrArgs::Eq { expr, .. }, .. } => Some(expr),
+                _ => None,
+            },
+            _ => None,
+        }
+    }
+}
+
+impl AttributeExt for Attribute {
+    fn id(&self) -> AttrId {
+        self.id
+    }
+
+    fn meta_item_list(&self) -> Option<ThinVec<ast::MetaItemInner>> {
+        match &self.kind {
+            AttrKind::Normal(n) => match n.as_ref() {
+                AttrItem { args: AttrArgs::Delimited(d), .. } => {
+                    ast::MetaItemKind::list_from_tokens(d.tokens.clone())
+                }
+                _ => None,
+            },
+            _ => None,
+        }
+    }
+
+    fn value_str(&self) -> Option<Symbol> {
+        self.value_lit().and_then(|x| x.value_str())
+    }
+
+    fn value_span(&self) -> Option<Span> {
+        self.value_lit().map(|i| i.span)
+    }
+
+    /// For a single-segment attribute, returns its name; otherwise, returns `None`.
+    fn ident(&self) -> Option<Ident> {
+        match &self.kind {
+            AttrKind::Normal(n) => {
+                if let [ident] = n.path.segments.as_ref() {
+                    Some(*ident)
+                } else {
+                    None
+                }
+            }
+            AttrKind::DocComment(..) => None,
+        }
+    }
+
+    fn path_matches(&self, name: &[Symbol]) -> bool {
+        match &self.kind {
+            AttrKind::Normal(n) => {
+                n.path.segments.len() == name.len()
+                    && n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n)
+            }
+            AttrKind::DocComment(..) => false,
+        }
+    }
+
+    fn is_doc_comment(&self) -> bool {
+        matches!(self.kind, AttrKind::DocComment(..))
+    }
+
+    fn span(&self) -> Span {
+        self.span
+    }
+
+    fn is_word(&self) -> bool {
+        match &self.kind {
+            AttrKind::Normal(n) => {
+                matches!(n.args, AttrArgs::Empty)
+            }
+            AttrKind::DocComment(..) => false,
+        }
+    }
+
+    fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
+        match &self.kind {
+            AttrKind::Normal(n) => Some(n.path.segments.iter().copied().collect()),
+            AttrKind::DocComment(..) => None,
+        }
+    }
+
+    fn doc_str(&self) -> Option<Symbol> {
+        match &self.kind {
+            AttrKind::DocComment(.., data) => Some(*data),
+            AttrKind::Normal(_) if self.has_name(sym::doc) => self.value_str(),
+            _ => None,
+        }
+    }
+    fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
+        match &self.kind {
+            AttrKind::DocComment(kind, data) => Some((*data, *kind)),
+            AttrKind::Normal(_) if self.name_or_empty() == sym::doc => {
+                self.value_str().map(|s| (s, CommentKind::Line))
+            }
+            _ => None,
+        }
+    }
+
+    fn style(&self) -> AttrStyle {
+        self.style
+    }
+}
+
+// FIXME(fn_delegation): use function delegation instead of manually forwarding
+impl Attribute {
+    pub fn id(&self) -> AttrId {
+        AttributeExt::id(self)
+    }
+
+    pub fn name_or_empty(&self) -> Symbol {
+        AttributeExt::name_or_empty(self)
+    }
+
+    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
+        AttributeExt::meta_item_list(self)
+    }
+
+    pub fn value_str(&self) -> Option<Symbol> {
+        AttributeExt::value_str(self)
+    }
+
+    pub fn value_span(&self) -> Option<Span> {
+        AttributeExt::value_span(self)
+    }
+
+    pub fn ident(&self) -> Option<Ident> {
+        AttributeExt::ident(self)
+    }
+
+    pub fn path_matches(&self, name: &[Symbol]) -> bool {
+        AttributeExt::path_matches(self, name)
+    }
+
+    pub fn is_doc_comment(&self) -> bool {
+        AttributeExt::is_doc_comment(self)
+    }
+
+    #[inline]
+    pub fn has_name(&self, name: Symbol) -> bool {
+        AttributeExt::has_name(self, name)
+    }
+
+    pub fn span(&self) -> Span {
+        AttributeExt::span(self)
+    }
+
+    pub fn is_word(&self) -> bool {
+        AttributeExt::is_word(self)
+    }
+
+    pub fn path(&self) -> SmallVec<[Symbol; 1]> {
+        AttributeExt::path(self)
+    }
+
+    pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
+        AttributeExt::ident_path(self)
+    }
+
+    pub fn doc_str(&self) -> Option<Symbol> {
+        AttributeExt::doc_str(self)
+    }
+
+    pub fn is_proc_macro_attr(&self) -> bool {
+        AttributeExt::is_proc_macro_attr(self)
+    }
+
+    pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
+        AttributeExt::doc_str_and_comment_kind(self)
+    }
+
+    pub fn style(&self) -> AttrStyle {
+        AttributeExt::style(self)
+    }
+}
+
 /// Attributes owned by a HIR owner.
 #[derive(Debug)]
 pub struct AttributeMap<'tcx> {
@@ -3434,6 +3681,19 @@ impl Safety {
             Self::Safe => "",
         }
     }
+
+    #[inline]
+    pub fn is_unsafe(self) -> bool {
+        !self.is_safe()
+    }
+
+    #[inline]
+    pub fn is_safe(self) -> bool {
+        match self {
+            Self::Unsafe => false,
+            Self::Safe => true,
+        }
+    }
 }
 
 impl fmt::Display for Safety {
@@ -3478,7 +3738,7 @@ impl FnHeader {
     }
 
     pub fn is_unsafe(&self) -> bool {
-        matches!(&self.safety, Safety::Unsafe)
+        self.safety.is_unsafe()
     }
 }
 
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 6d481f7536a..387a195cb29 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -64,11 +64,10 @@
 //! This order consistency is required in a few places in rustc, for
 //! example coroutine inference, and possibly also HIR borrowck.
 
+use rustc_ast::Label;
 use rustc_ast::visit::{VisitorResult, try_visit, visit_opt, walk_list};
-use rustc_ast::{Attribute, Label};
-use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 use crate::hir::*;
 
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 15cb331d07a..fae3b778d7b 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -7,12 +7,11 @@
 //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`.
 //! * Functions called by the compiler itself.
 
-use rustc_ast as ast;
+use rustc_ast::attr::AttributeExt;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, kw, sym};
+use rustc_span::{Span, Symbol, kw, sym};
 
 use crate::def_id::DefId;
 use crate::{MethodKind, Target};
@@ -153,11 +152,11 @@ impl<CTX> HashStable<CTX> for LangItem {
 
 /// Extracts the first `lang = "$name"` out of a list of attributes.
 /// The `#[panic_handler]` attribute is also extracted out when found.
-pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
+pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> {
     attrs.iter().find_map(|attr| {
         Some(match attr {
-            _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span),
-            _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span),
+            _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()),
+            _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()),
             _ => return None,
         })
     })
diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs
index 2ebbb4a06b6..bb853985c7d 100644
--- a/compiler/rustc_hir/src/pat_util.rs
+++ b/compiler/rustc_hir/src/pat_util.rs
@@ -1,7 +1,6 @@
 use std::iter::Enumerate;
 
-use rustc_span::Span;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, Span};
 
 use crate::def::{CtorOf, DefKind, Res};
 use crate::def_id::{DefId, DefIdSet};
diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs
index fe169e989ec..db0d0fcf3b9 100644
--- a/compiler/rustc_hir/src/stable_hash_impls.rs
+++ b/compiler/rustc_hir/src/stable_hash_impls.rs
@@ -2,7 +2,8 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHas
 use rustc_span::def_id::DefPathHash;
 
 use crate::hir::{
-    AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId,
+    Attribute, AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes,
+    TraitItemId,
 };
 use crate::hir_id::{HirId, ItemLocalId};
 
@@ -12,6 +13,7 @@ use crate::hir_id::{HirId, ItemLocalId};
 pub trait HashStableContext:
     rustc_ast::HashStableContext + rustc_target::HashStableContext
 {
+    fn hash_attr(&mut self, _: &Attribute, hasher: &mut StableHasher);
 }
 
 impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for HirId {
@@ -113,3 +115,9 @@ impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Crate<'_> {
         opt_hir_hash.unwrap().hash_stable(hcx, hasher)
     }
 }
+
+impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Attribute {
+    fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
+        hcx.hash_attr(self, hasher)
+    }
+}
diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs
index 337859cd1fb..b4e548effd4 100644
--- a/compiler/rustc_hir/src/weak_lang_items.rs
+++ b/compiler/rustc_hir/src/weak_lang_items.rs
@@ -1,6 +1,6 @@
 //! Validity checking for weak lang items
 
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 use crate::LangItem;
 
diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
index 581ef2272d1..196d7d99e93 100644
--- a/compiler/rustc_hir_analysis/Cargo.toml
+++ b/compiler/rustc_hir_analysis/Cargo.toml
@@ -13,7 +13,7 @@ itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 022a6d457ec..5548a6a6ef7 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::MultiSpan;
 use rustc_errors::codes::*;
 use rustc_hir::def::{CtorKind, DefKind};
-use rustc_hir::{Node, Safety, intravisit};
+use rustc_hir::{Node, intravisit};
 use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::{Obligation, ObligationCauseCode};
 use rustc_lint_defs::builtin::{
@@ -31,7 +31,7 @@ use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_type_ir::fold::TypeFoldable;
 use tracing::{debug, instrument};
 use ty::TypingMode;
-use {rustc_attr as attr, rustc_hir as hir};
+use {rustc_attr_parsing as attr, rustc_hir as hir};
 
 use super::compare_impl_item::check_type_bounds;
 use super::*;
@@ -161,7 +161,7 @@ fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) {
     };
     let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id);
     for field in def.all_fields() {
-        if field.safety != Safety::Unsafe {
+        if !field.safety.is_unsafe() {
             continue;
         }
         let Ok(field_ty) = tcx.try_normalize_erasing_regions(typing_env, field.ty(tcx, args))
diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs
index f3dd13c84b9..245085b332c 100644
--- a/compiler/rustc_hir_analysis/src/check/entry.rs
+++ b/compiler/rustc_hir_analysis/src/check/entry.rs
@@ -7,9 +7,8 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode};
 use rustc_session::config::EntryFnType;
-use rustc_span::Span;
 use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
 
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 2e6b511412b..39479401910 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -8,8 +8,7 @@ use rustc_middle::bug;
 use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 use crate::check::check_function_signature;
 use crate::errors::{
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 61e203a1ff6..0b0c92a726d 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -89,8 +89,7 @@ use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypingMode
 use rustc_middle::{bug, span_bug};
 use rustc_session::parse::feature_err;
 use rustc_span::def_id::CRATE_DEF_ID;
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{BytePos, DUMMY_SP, Span, Symbol};
+use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::error_reporting::infer::ObligationCauseExt as _;
 use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor;
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 95ad8225f61..059b8dcd975 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -23,8 +23,7 @@ use rustc_middle::ty::{
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::parse::feature_err;
-use rustc_span::symbol::{Ident, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::regions::InferCtxtRegionExt;
 use rustc_trait_selection::traits::misc::{
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index 2afc2aec1ba..8f6f5b5f222 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -13,8 +13,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::bug;
 use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams, simplify_type};
 use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt};
-use rustc_span::ErrorGuaranteed;
-use rustc_span::symbol::sym;
+use rustc_span::{ErrorGuaranteed, sym};
 
 use crate::errors;
 
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 8c9da78a659..5e662bb96bc 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -38,8 +38,7 @@ use rustc_middle::ty::fold::fold_regions;
 use rustc_middle::ty::util::{Discr, IntTypeExt};
 use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypingMode};
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::ObligationCtxt;
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index f52d4f42eca..075615de522 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -8,8 +8,7 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::lint;
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, kw};
+use rustc_span::{Span, Symbol, kw};
 use tracing::{debug, instrument};
 
 use crate::delegation::inherit_generics_for_delegation_item;
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 1a6c0a93436..c5fb3474022 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -9,8 +9,7 @@ use rustc_middle::ty::{
     self, GenericPredicates, ImplTraitInTraitData, Ty, TyCtxt, TypeVisitable, TypeVisitor, Upcast,
 };
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::Ident;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span};
 use tracing::{debug, instrument, trace};
 
 use super::item_bounds::explicit_item_bounds_with_filter;
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 0f797bcdae4..7c65e9613b6 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -26,9 +26,8 @@ use rustc_middle::middle::resolve_bound_vars::*;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor};
 use rustc_middle::{bug, span_bug};
-use rustc_span::Span;
 use rustc_span::def_id::{DefId, LocalDefId, LocalDefIdMap};
-use rustc_span::symbol::{Ident, sym};
+use rustc_span::{Ident, Span, sym};
 use tracing::{debug, debug_span, instrument};
 
 use crate::errors;
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 5595504c3d9..c0526903e88 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -10,8 +10,7 @@ use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::util::IntTypeExt;
 use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::Ident;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span};
 
 use super::{ItemCtxt, bad_placeholder};
 use crate::errors::TypeofReservedKeywordUsed;
@@ -473,7 +472,7 @@ fn infer_placeholder_type<'tcx>(
 fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) {
     if !tcx.features().inherent_associated_types() {
         use rustc_session::parse::feature_err;
-        use rustc_span::symbol::sym;
+        use rustc_span::sym;
         feature_err(
             &tcx.sess,
             sym::inherent_associated_types,
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index d46f60b16f5..00ba1741ed7 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -6,8 +6,7 @@ use rustc_errors::{
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 use crate::fluent_generated as fluent;
 mod pattern_types;
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 0404e38a293..e949d4a1126 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -9,8 +9,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::bug;
 use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
-use rustc_span::symbol::Ident;
-use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
+use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::traits;
 use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use smallvec::SmallVec;
@@ -745,7 +744,7 @@ fn check_assoc_const_binding_type<'tcx>(
     let generics = tcx.generics_of(enclosing_item_owner_id);
     for index in collector.params {
         let param = generics.param_at(index as _, tcx);
-        let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper;
+        let is_self_param = param.name == kw::SelfUpper;
         guar.get_or_insert(cx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding {
             span: assoc_const.span,
             assoc_const,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 00c1f9b332b..0623d35853e 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -16,8 +16,7 @@ use rustc_middle::ty::{
 };
 use rustc_session::parse::feature_err;
 use rustc_span::edit_distance::find_best_match_for_name;
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{BytePos, DUMMY_SP, Span, Symbol};
+use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
 use rustc_trait_selection::traits::{
     FulfillmentError, TraitAliasExpansionInfo, dyn_compatibility_violations_for_assoc_item,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index c3f4fc8699a..df00948dd21 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -9,7 +9,7 @@ use rustc_middle::ty::{
     self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty,
 };
 use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
-use rustc_span::symbol::{kw, sym};
+use rustc_span::{kw, sym};
 use smallvec::SmallVec;
 use tracing::{debug, instrument};
 
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index a357ade0294..78057d5a997 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -45,8 +45,7 @@ use rustc_middle::ty::{
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
 use rustc_span::edit_distance::find_best_match_for_name;
-use rustc_span::symbol::{Ident, Symbol, kw};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::wf::object_region_bounds;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
diff --git a/compiler/rustc_hir_analysis/src/variance/dump.rs b/compiler/rustc_hir_analysis/src/variance/dump.rs
index a0fdf95a831..31f0adf720d 100644
--- a/compiler/rustc_hir_analysis/src/variance/dump.rs
+++ b/compiler/rustc_hir_analysis/src/variance/dump.rs
@@ -2,7 +2,7 @@ use std::fmt::Write;
 
 use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
 use rustc_middle::ty::{GenericArgs, TyCtxt};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 fn format_variances(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String {
     let variances = tcx.variances_of(def_id);
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 8dd8a5c98ef..de2a7726e9b 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -11,16 +11,17 @@ use std::vec;
 
 use rustc_abi::ExternAbi;
 use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
+use rustc_ast::{DUMMY_NODE_ID, DelimArgs};
 use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
 use rustc_ast_pretty::pp::{self, Breaks};
+use rustc_ast_pretty::pprust::state::MacHeader;
 use rustc_ast_pretty::pprust::{Comments, PrintState};
 use rustc_hir::{
     BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind,
     HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term,
 };
-use rustc_span::FileName;
 use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::{Ident, Symbol, kw};
+use rustc_span::{FileName, Ident, Span, Symbol, kw};
 use {rustc_ast as ast, rustc_hir as hir};
 
 pub fn id_to_string(map: &dyn rustc_hir::intravisit::Map<'_>, hir_id: HirId) -> String {
@@ -68,15 +69,115 @@ impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> {
 pub struct State<'a> {
     pub s: pp::Printer,
     comments: Option<Comments<'a>>,
-    attrs: &'a dyn Fn(HirId) -> &'a [ast::Attribute],
+    attrs: &'a dyn Fn(HirId) -> &'a [hir::Attribute],
     ann: &'a (dyn PpAnn + 'a),
 }
 
 impl<'a> State<'a> {
-    fn attrs(&self, id: HirId) -> &'a [ast::Attribute] {
+    fn attrs(&self, id: HirId) -> &'a [hir::Attribute] {
         (self.attrs)(id)
     }
 
+    fn print_inner_attributes(&mut self, attrs: &[hir::Attribute]) -> bool {
+        self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
+    }
+
+    fn print_outer_attributes(&mut self, attrs: &[hir::Attribute]) -> bool {
+        self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
+    }
+
+    fn print_either_attributes(
+        &mut self,
+        attrs: &[hir::Attribute],
+        kind: ast::AttrStyle,
+        is_inline: bool,
+        trailing_hardbreak: bool,
+    ) -> bool {
+        let mut printed = false;
+        for attr in attrs {
+            if attr.style == kind {
+                self.print_attribute_inline(attr, is_inline);
+                if is_inline {
+                    self.nbsp();
+                }
+                printed = true;
+            }
+        }
+        if printed && trailing_hardbreak && !is_inline {
+            self.hardbreak_if_not_bol();
+        }
+        printed
+    }
+
+    fn print_attribute_inline(&mut self, attr: &hir::Attribute, is_inline: bool) {
+        if !is_inline {
+            self.hardbreak_if_not_bol();
+        }
+        self.maybe_print_comment(attr.span.lo());
+        match &attr.kind {
+            hir::AttrKind::Normal(normal) => {
+                match attr.style {
+                    ast::AttrStyle::Inner => self.word("#!["),
+                    ast::AttrStyle::Outer => self.word("#["),
+                }
+                if normal.unsafety == hir::Safety::Unsafe {
+                    self.word("unsafe(");
+                }
+                self.print_attr_item(&normal, attr.span);
+                if normal.unsafety == hir::Safety::Unsafe {
+                    self.word(")");
+                }
+                self.word("]");
+            }
+            hir::AttrKind::DocComment(comment_kind, data) => {
+                self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string(
+                    *comment_kind,
+                    attr.style,
+                    *data,
+                ));
+                self.hardbreak()
+            }
+        }
+    }
+
+    fn print_attr_item(&mut self, item: &hir::AttrItem, span: Span) {
+        self.ibox(0);
+        let path = ast::Path {
+            span,
+            segments: item
+                .path
+                .segments
+                .iter()
+                .map(|i| ast::PathSegment { ident: *i, args: None, id: DUMMY_NODE_ID })
+                .collect(),
+            tokens: None,
+        };
+
+        match &item.args {
+            hir::AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self
+                .print_mac_common(
+                    Some(MacHeader::Path(&path)),
+                    false,
+                    None,
+                    *delim,
+                    tokens,
+                    true,
+                    span,
+                ),
+            hir::AttrArgs::Empty => {
+                PrintState::print_path(self, &path, false, 0);
+            }
+            hir::AttrArgs::Eq { eq_span: _, expr } => {
+                PrintState::print_path(self, &path, false, 0);
+                self.space();
+                self.word_space("=");
+                let token_str = self.meta_item_lit_to_string(expr);
+                self.word(token_str);
+            }
+        }
+        self.end();
+    }
+
     fn print_node(&mut self, node: Node<'_>) {
         match node {
             Node::Param(a) => self.print_param(a),
@@ -164,7 +265,7 @@ pub fn print_crate<'a>(
     krate: &hir::Mod<'_>,
     filename: FileName,
     input: String,
-    attrs: &'a dyn Fn(HirId) -> &'a [ast::Attribute],
+    attrs: &'a dyn Fn(HirId) -> &'a [hir::Attribute],
     ann: &'a dyn PpAnn,
 ) -> String {
     let mut s = State {
@@ -191,6 +292,10 @@ where
     printer.s.eof()
 }
 
+pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String {
+    to_string(ann, |s| s.print_attribute_inline(attr, false))
+}
+
 pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String {
     to_string(ann, |s| s.print_type(ty))
 }
@@ -242,7 +347,7 @@ impl<'a> State<'a> {
         self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span);
     }
 
-    fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) {
+    fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[hir::Attribute]) {
         self.print_inner_attributes(attrs);
         for &item_id in _mod.item_ids {
             self.ann.nested(self, Nested::Item(item_id));
@@ -926,14 +1031,14 @@ impl<'a> State<'a> {
         self.print_block_maybe_unclosed(blk, &[], false)
     }
 
-    fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) {
+    fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[hir::Attribute]) {
         self.print_block_maybe_unclosed(blk, attrs, true)
     }
 
     fn print_block_maybe_unclosed(
         &mut self,
         blk: &hir::Block<'_>,
-        attrs: &[ast::Attribute],
+        attrs: &[hir::Attribute],
         close_box: bool,
     ) {
         match blk.rules {
diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml
index 8ddbb4b3397..1f5acaa58a9 100644
--- a/compiler/rustc_hir_typeck/Cargo.toml
+++ b/compiler/rustc_hir_typeck/Cargo.toml
@@ -9,7 +9,7 @@ itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_ir = { path = "../rustc_ast_ir" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index b430f48965a..9d364775445 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -13,9 +13,8 @@ use rustc_middle::ty::adjustment::{
 };
 use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
-use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::{Ident, sym};
+use rustc_span::{Ident, Span, sym};
 use rustc_trait_selection::error_reporting::traits::DefIdOrName;
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 80b91c21598..59c06cbc5b5 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -43,8 +43,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDe
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
 use rustc_span::def_id::LOCAL_CRATE;
-use rustc_span::symbol::sym;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::{debug, instrument};
 
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 51d78646373..b0698183b57 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -10,7 +10,7 @@ use rustc_infer::infer::RegionVariableOrigin;
 use rustc_infer::traits::WellFormedLoc;
 use rustc_middle::ty::{self, Binder, Ty, TyCtxt};
 use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
 use tracing::{debug, instrument};
 
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index bf41dcbe4a3..e3705945f33 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -59,8 +59,7 @@ use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
 use rustc_session::parse::feature_err;
-use rustc_span::symbol::sym;
-use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
+use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span, sym};
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::{
@@ -863,7 +862,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             let outer_universe = self.infcx.universe();
 
             let result = if let ty::FnPtr(_, hdr_b) = b.kind()
-                && let (hir::Safety::Safe, hir::Safety::Unsafe) = (fn_ty_a.safety(), hdr_b.safety)
+                && fn_ty_a.safety().is_safe()
+                && hdr_b.safety.is_unsafe()
             {
                 let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
                 self.unify_and(unsafe_a, b, to_unsafe)
@@ -925,7 +925,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
                     // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
 
-                    if b_hdr.safety == hir::Safety::Safe
+                    if b_hdr.safety.is_safe()
                         && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
                     {
                         return Err(TypeError::TargetFeatureCast(def_id));
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index d0272651c08..e51323fc5c8 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -9,8 +9,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, AssocItem, Ty, TypeFoldable, TypeVisitableExt};
-use rustc_span::symbol::sym;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::ObligationCause;
 use tracing::instrument;
@@ -1118,7 +1117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Constructor,
         }
         let mut maybe_emit_help = |def_id: hir::def_id::DefId,
-                                   callable: rustc_span::symbol::Ident,
+                                   callable: Ident,
                                    args: &[hir::Expr<'_>],
                                    kind: CallableKind| {
             let arg_idx = args.iter().position(|a| a.hir_id == expr.hir_id).unwrap();
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 7746a5a7132..ff09583cc65 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -10,8 +10,7 @@ use rustc_errors::{
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{self, Ty};
 use rustc_span::edition::{Edition, LATEST_STABLE_EDITION};
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 use crate::fluent_generated as fluent;
 
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 66978399efb..caea53d9200 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -30,11 +30,10 @@ use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_session::parse::feature_err;
-use rustc_span::Span;
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
+use rustc_span::{Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
 use tracing::{debug, instrument, trace};
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index b57c248ed2b..c128485d93e 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -762,7 +762,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>(
                     if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id)
                         && let method_ty = self.fcx.tcx.type_of(def_id).instantiate_identity()
                         && let sig = method_ty.fn_sig(self.fcx.tcx)
-                        && let hir::Safety::Unsafe = sig.safety()
+                        && sig.safety().is_unsafe()
                     {
                         let mut collector = InferVarCollector {
                             value: (ex.hir_id, ex.span, UnsafeUseReason::Method),
@@ -782,7 +782,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>(
 
                     if func_ty.is_fn()
                         && let sig = func_ty.fn_sig(self.fcx.tcx)
-                        && let hir::Safety::Unsafe = sig.safety()
+                        && sig.safety().is_unsafe()
                     {
                         let mut collector = InferVarCollector {
                             value: (ex.hir_id, ex.span, UnsafeUseReason::Call),
@@ -813,7 +813,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>(
                     // `is_fn` excludes closures, but those can't be unsafe.
                     if ty.is_fn()
                         && let sig = ty.fn_sig(self.fcx.tcx)
-                        && let hir::Safety::Unsafe = sig.safety()
+                        && sig.safety().is_unsafe()
                     {
                         let mut collector = InferVarCollector {
                             value: (ex.hir_id, ex.span, UnsafeUseReason::Path),
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 0dacfc9b7bf..d6948081505 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -29,10 +29,9 @@ use rustc_middle::ty::{
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
-use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::hygiene::DesugaringKind;
-use rustc_span::symbol::kw;
+use rustc_span::{Span, kw};
 use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
 use rustc_trait_selection::traits::{
     self, NormalizeExt, ObligationCauseCode, StructurallyNormalizeExt,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
index 6eaba641888..ee0436f73e1 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
@@ -5,8 +5,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_infer::traits::ObligationCauseCode;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
-use rustc_span::Span;
-use rustc_span::symbol::kw;
+use rustc_span::{Span, kw};
 use rustc_trait_selection::traits;
 
 use crate::FnCtxt;
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 6b1cceefbee..c1f08d237eb 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -22,8 +22,7 @@ use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_session::Session;
-use rustc_span::symbol::{Ident, kw};
-use rustc_span::{DUMMY_SP, Span, sym};
+use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
 use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext};
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index b9011e89f04..d432199f037 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -17,8 +17,7 @@ use rustc_infer::infer;
 use rustc_infer::traits::Obligation;
 use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
 use rustc_session::Session;
-use rustc_span::symbol::Ident;
-use rustc_span::{self, DUMMY_SP, Span, sym};
+use rustc_span::{self, DUMMY_SP, Ident, Span, sym};
 use rustc_trait_selection::error_reporting::TypeErrCtxt;
 use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations;
 use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 21b1768ae6f..964ef5b2106 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -24,8 +24,7 @@ use rustc_middle::ty::{
 };
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Ident, sym};
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::error_reporting::traits::DefIdOrName;
 use rustc_trait_selection::infer::InferCtxtExt;
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index e0b6ea0ac9d..4008021c3a8 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -19,8 +19,7 @@ use rustc_middle::ty::{
     self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TypeVisitableExt,
 };
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::Ident;
-use rustc_span::{ErrorGuaranteed, Span};
+use rustc_span::{ErrorGuaranteed, Ident, Span};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::{self, NormalizeExt};
 use tracing::{debug, instrument};
diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
index b20592c85d2..5ccfcf93f69 100644
--- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
+++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs
@@ -8,9 +8,7 @@ use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::lint::builtin::{RUST_2021_PRELUDE_COLLISIONS, RUST_2024_PRELUDE_COLLISIONS};
-use rustc_span::Span;
-use rustc_span::symbol::kw::{Empty, Underscore};
-use rustc_span::symbol::{Ident, sym};
+use rustc_span::{Ident, Span, kw, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::debug;
 
@@ -369,11 +367,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .collect();
 
         // Find an identifier with which this trait was imported (note that `_` doesn't count).
-        let any_id = import_items
-            .iter()
-            .find_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None });
+        let any_id = import_items.iter().find_map(|item| {
+            if item.ident.name != kw::Underscore { Some(item.ident) } else { None }
+        });
         if let Some(any_id) = any_id {
-            if any_id.name == Empty {
+            if any_id.name == kw::Empty {
                 // Glob import, so just use its name.
                 return None;
             } else {
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 3b377076545..bc304c2b687 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -25,8 +25,7 @@ use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::edit_distance::{
     edit_distance_with_substrings, find_best_match_for_name_with_substrings,
 };
-use rustc_span::symbol::{Ident, sym};
-use rustc_span::{DUMMY_SP, Span, Symbol};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
 use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::CanonicalTyGoal;
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 6b1a288510a..1de88d52767 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -8,7 +8,7 @@ use std::borrow::Cow;
 
 use hir::Expr;
 use rustc_ast::ast::Mutability;
-use rustc_attr::parse_confusables;
+use rustc_attr_parsing::parse_confusables;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordSet;
@@ -27,9 +27,9 @@ use rustc_middle::ty::print::{
 };
 use rustc_middle::ty::{self, GenericArgKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::def_id::DefIdSet;
-use rustc_span::symbol::{Ident, kw, sym};
 use rustc_span::{
-    DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span, Symbol, edit_distance,
+    DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, Ident, MacroKind, Span, Symbol, edit_distance,
+    kw, sym,
 };
 use rustc_trait_selection::error_reporting::traits::DefIdOrName;
 use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote;
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index 72b930ee84d..805079afdb0 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -11,9 +11,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
 use rustc_session::errors::ExprParenthesesNeeded;
-use rustc_span::Span;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Ident, sym};
+use rustc_span::{Ident, Span, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
 use rustc_type_ir::TyKind::*;
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index e7726845652..06392deb8ff 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -23,8 +23,7 @@ use rustc_session::parse::feature_err;
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{BytePos, DUMMY_SP, Span};
+use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
 use tracing::{debug, instrument, trace};
diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs
index 7c667511aa9..ba635071135 100644
--- a/compiler/rustc_hir_typeck/src/place_op.rs
+++ b/compiler/rustc_hir_typeck/src/place_op.rs
@@ -7,8 +7,7 @@ use rustc_middle::ty::adjustment::{
     PointerCoercion,
 };
 use rustc_middle::ty::{self, Ty};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, sym};
+use rustc_span::{Ident, Span, sym};
 use tracing::debug;
 use {rustc_ast as ast, rustc_hir as hir};
 
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 4f283644cbe..b3085843141 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -14,7 +14,7 @@
 //! to everything owned by `x`, so the result is the same for something
 //! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
 //! struct). These adjustments are performed in
-//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code
+//! `adjust_for_non_move_closure` (you can trace backwards through the code
 //! from there).
 //!
 //! The fact that we are inferring borrow kinds as we go results in a
@@ -1684,8 +1684,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // want to capture by ref to allow precise capture using reborrows.
             //
             // If the data will be moved out of this place, then the place will be truncated
-            // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
-            // the closure.
+            // at the first Deref in `adjust_for_move_closure` and then moved into the closure.
             hir::CaptureBy::Value { .. } if !place.deref_tys().any(Ty::is_ref) => {
                 ty::UpvarCapture::ByValue
             }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 6f1e3a0cf8c..5612aa75aae 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -15,8 +15,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, fold_regions};
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperFoldable};
-use rustc_span::Span;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
 use rustc_trait_selection::solve;
 use tracing::{debug, instrument};
@@ -555,11 +554,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span);
             let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span);
 
-            if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
-                && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
-                && alias_ty.args == opaque_type_key.args
-            {
-                continue;
+            if !self.fcx.next_trait_solver() {
+                if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
+                    && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
+                    && alias_ty.args == opaque_type_key.args
+                {
+                    continue;
+                }
             }
 
             // Here we only detect impl trait definition conflicts when they
diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs
index 1f46155abc8..569034954c3 100644
--- a/compiler/rustc_incremental/src/assert_dep_graph.rs
+++ b/compiler/rustc_incremental/src/assert_dep_graph.rs
@@ -47,10 +47,9 @@ use rustc_middle::dep_graph::{
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::{bug, span_bug};
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use tracing::debug;
-use {rustc_ast as ast, rustc_graphviz as dot, rustc_hir as hir};
+use {rustc_graphviz as dot, rustc_hir as hir};
 
 use crate::errors;
 
@@ -106,7 +105,7 @@ struct IfThisChanged<'tcx> {
 }
 
 impl<'tcx> IfThisChanged<'tcx> {
-    fn argument(&self, attr: &ast::Attribute) -> Option<Symbol> {
+    fn argument(&self, attr: &hir::Attribute) -> Option<Symbol> {
         let mut value = None;
         for list_item in attr.meta_item_list().unwrap_or_default() {
             match list_item.ident() {
diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs
index cb21f975878..b4a207386dc 100644
--- a/compiler/rustc_incremental/src/errors.rs
+++ b/compiler/rustc_incremental/src/errors.rs
@@ -1,8 +1,7 @@
 use std::path::{Path, PathBuf};
 
 use rustc_macros::Diagnostic;
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 #[derive(Diagnostic)]
 #[diag(incremental_unrecognized_depnode)]
diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs
index 2075d4214c1..b99872e7ae6 100644
--- a/compiler/rustc_incremental/src/persist/dirty_clean.rs
+++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs
@@ -19,16 +19,17 @@
 //! Errors are reported if we are in the suitable configuration but
 //! the required condition is not met.
 
-use rustc_ast::{self as ast, Attribute, MetaItemInner};
+use rustc_ast::{self as ast, MetaItemInner};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::unord::UnordSet;
 use rustc_hir::def_id::LocalDefId;
-use rustc_hir::{ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit};
+use rustc_hir::{
+    Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit,
+};
 use rustc_middle::dep_graph::{DepNode, DepNodeExt, label_strs};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::TyCtxt;
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use thin_vec::ThinVec;
 use tracing::debug;
 
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 544f941dda5..5086b741a83 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -41,8 +41,7 @@ use rustc_middle::ty::{
     GenericParamDefKind, InferConst, IntVid, PseudoCanonicalInput, Ty, TyCtxt, TyVid,
     TypeVisitable, TypingEnv, TypingMode,
 };
-use rustc_span::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::{Span, Symbol};
 use snapshot::undo_log::InferCtxtUndoLogs;
 use tracing::{debug, instrument};
 use type_variable::TypeVariableOrigin;
@@ -689,7 +688,7 @@ impl<'tcx> InferCtxt<'tcx> {
     /// Require that the region `r` be equal to one of the regions in
     /// the set `regions`.
     #[instrument(skip(self), level = "debug")]
-    pub fn member_constraint(
+    pub fn add_member_constraint(
         &self,
         key: ty::OpaqueTypeKey<'tcx>,
         definition_span: Span,
@@ -697,7 +696,7 @@ impl<'tcx> InferCtxt<'tcx> {
         region: ty::Region<'tcx>,
         in_regions: Lrc<Vec<ty::Region<'tcx>>>,
     ) {
-        self.inner.borrow_mut().unwrap_region_constraints().member_constraint(
+        self.inner.borrow_mut().unwrap_region_constraints().add_member_constraint(
             key,
             definition_span,
             hidden_ty,
diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
index b64686afd23..8650c20559f 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
@@ -364,7 +364,7 @@ impl<'tcx> InferCtxt<'tcx> {
         concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
             tcx: self.tcx,
             op: |r| {
-                self.member_constraint(
+                self.add_member_constraint(
                     opaque_type_key,
                     span,
                     concrete_ty,
diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
index 270217e26b7..61ce86e7767 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
@@ -466,7 +466,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
         }
     }
 
-    pub(super) fn member_constraint(
+    pub(super) fn add_member_constraint(
         &mut self,
         key: ty::OpaqueTypeKey<'tcx>,
         definition_span: Span,
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index a977bd15d3b..ab8ada1596c 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -1,7 +1,6 @@
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::ty::{self, ToPolyTraitRef, TyCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, Span};
 pub use rustc_type_ir::elaborate::*;
 
 use crate::traits::{self, Obligation, ObligationCauseCode, PredicateObligation};
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index 7a2ba07ce87..dcb9c5d22d6 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -11,7 +11,7 @@ rustc_ast = { path = "../rustc_ast" }
 rustc_ast_lowering = { path = "../rustc_ast_lowering" }
 rustc_ast_passes = { path = "../rustc_ast_passes" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_borrowck = { path = "../rustc_borrowck" }
 rustc_builtin_macros = { path = "../rustc_builtin_macros" }
 rustc_codegen_llvm = { path = "../rustc_codegen_llvm", optional = true }
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 91f190c6a28..1456255ea14 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -22,9 +22,8 @@ use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileN
 use rustc_session::filesearch::{self, sysroot_candidates};
 use rustc_session::parse::ParseSess;
 use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint};
-use rustc_span::FileName;
 use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs};
-use rustc_span::symbol::sym;
+use rustc_span::{FileName, sym};
 use tracing::trace;
 
 use crate::util;
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index 1c4dda2a436..a2a29612e48 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -8,7 +8,7 @@
 // tidy-alphabetical-end
 
 mod callbacks;
-mod errors;
+pub mod errors;
 pub mod interface;
 pub mod passes;
 mod proc_macro_decls;
@@ -17,8 +17,8 @@ pub mod util;
 
 pub use callbacks::setup_callbacks;
 pub use interface::{Config, run_compiler};
-pub use passes::DEFAULT_QUERY_PROVIDERS;
-pub use queries::{Linker, Queries};
+pub use passes::{DEFAULT_QUERY_PROVIDERS, create_and_enter_global_ctxt, parse};
+pub use queries::Linker;
 
 #[cfg(test)]
 mod tests;
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 430bc7db077..02905e632ab 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -32,8 +32,7 @@ use rustc_session::cstore::Untracked;
 use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name};
 use rustc_session::search_paths::PathKind;
 use rustc_session::{Limit, Session};
-use rustc_span::symbol::{Symbol, sym};
-use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm};
+use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Symbol, sym};
 use rustc_target::spec::PanicStrategy;
 use rustc_trait_selection::traits;
 use tracing::{info, instrument};
@@ -41,7 +40,7 @@ use tracing::{info, instrument};
 use crate::interface::Compiler;
 use crate::{errors, proc_macro_decls, util};
 
-pub(crate) fn parse<'a>(sess: &'a Session) -> ast::Crate {
+pub fn parse<'a>(sess: &'a Session) -> ast::Crate {
     let krate = sess
         .time("parse_crate", || {
             let mut parser = unwrap_or_emit_fatal(match &sess.io.input {
@@ -76,6 +75,7 @@ fn pre_expansion_lint<'a>(
         || {
             rustc_lint::check_ast_node(
                 sess,
+                None,
                 features,
                 true,
                 lint_store,
@@ -310,6 +310,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) {
     let lint_store = unerased_lint_store(tcx.sess);
     rustc_lint::check_ast_node(
         sess,
+        Some(tcx),
         tcx.features(),
         false,
         lint_store,
@@ -709,13 +710,11 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
     *providers
 });
 
-pub(crate) fn create_global_ctxt<'tcx>(
-    compiler: &'tcx Compiler,
+pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
+    compiler: &Compiler,
     mut krate: rustc_ast::Crate,
-    gcx_cell: &'tcx OnceLock<GlobalCtxt<'tcx>>,
-    arena: &'tcx WorkerLocal<Arena<'tcx>>,
-    hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>,
-) -> &'tcx GlobalCtxt<'tcx> {
+    f: F,
+) -> T {
     let sess = &compiler.sess;
 
     rustc_builtin_macros::cmdline_attrs::inject(
@@ -763,44 +762,64 @@ pub(crate) fn create_global_ctxt<'tcx>(
 
     let incremental = dep_graph.is_fully_enabled();
 
-    sess.time("setup_global_ctxt", || {
-        let qcx = gcx_cell.get_or_init(move || {
-            TyCtxt::create_global_ctxt(
-                sess,
-                crate_types,
-                stable_crate_id,
-                arena,
-                hir_arena,
-                untracked,
-                dep_graph,
-                rustc_query_impl::query_callbacks(arena),
-                rustc_query_impl::query_system(
-                    providers.queries,
-                    providers.extern_queries,
-                    query_result_on_disk_cache,
-                    incremental,
-                ),
-                providers.hooks,
-                compiler.current_gcx.clone(),
-            )
-        });
-
-        qcx.enter(|tcx| {
-            let feed = tcx.create_crate_num(stable_crate_id).unwrap();
-            assert_eq!(feed.key(), LOCAL_CRATE);
-            feed.crate_name(crate_name);
+    let gcx_cell = OnceLock::new();
+    let arena = WorkerLocal::new(|_| Arena::default());
+    let hir_arena = WorkerLocal::new(|_| rustc_hir::Arena::default());
+
+    // This closure is necessary to force rustc to perform the correct lifetime
+    // subtyping for GlobalCtxt::enter to be allowed.
+    let inner: Box<
+        dyn for<'tcx> FnOnce(
+            &'tcx Compiler,
+            &'tcx OnceLock<GlobalCtxt<'tcx>>,
+            &'tcx WorkerLocal<Arena<'tcx>>,
+            &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>,
+            F,
+        ) -> T,
+    > = Box::new(move |compiler, gcx_cell, arena, hir_arena, f| {
+        let sess = &compiler.sess;
+
+        TyCtxt::create_global_ctxt(
+            gcx_cell,
+            sess,
+            crate_types,
+            stable_crate_id,
+            arena,
+            hir_arena,
+            untracked,
+            dep_graph,
+            rustc_query_impl::query_callbacks(arena),
+            rustc_query_impl::query_system(
+                providers.queries,
+                providers.extern_queries,
+                query_result_on_disk_cache,
+                incremental,
+            ),
+            providers.hooks,
+            compiler.current_gcx.clone(),
+            |tcx| {
+                let feed = tcx.create_crate_num(stable_crate_id).unwrap();
+                assert_eq!(feed.key(), LOCAL_CRATE);
+                feed.crate_name(crate_name);
+
+                let feed = tcx.feed_unit_query();
+                feed.features_query(tcx.arena.alloc(rustc_expand::config::features(
+                    sess,
+                    &pre_configured_attrs,
+                    crate_name,
+                )));
+                feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs))));
+                feed.output_filenames(Arc::new(outputs));
+
+                let res = f(tcx);
+                // FIXME maybe run finish even when a fatal error occured? or at least tcx.alloc_self_profile_query_strings()?
+                tcx.finish();
+                res
+            },
+        )
+    });
 
-            let feed = tcx.feed_unit_query();
-            feed.features_query(tcx.arena.alloc(rustc_expand::config::features(
-                sess,
-                &pre_configured_attrs,
-                crate_name,
-            )));
-            feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs))));
-            feed.output_filenames(Arc::new(outputs));
-        });
-        qcx
-    })
+    inner(compiler, &gcx_cell, &arena, &hir_arena, f)
 }
 
 /// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses.
diff --git a/compiler/rustc_interface/src/proc_macro_decls.rs b/compiler/rustc_interface/src/proc_macro_decls.rs
index 2c8014d8b3a..82593dbc2b7 100644
--- a/compiler/rustc_interface/src/proc_macro_decls.rs
+++ b/compiler/rustc_interface/src/proc_macro_decls.rs
@@ -2,7 +2,7 @@ use rustc_ast::attr;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 fn proc_macro_decls_static(tcx: TyCtxt<'_>, (): ()) -> Option<LocalDefId> {
     let mut decls = None;
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index bb2ad3b3dd0..c8914c9be9c 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -1,117 +1,18 @@
 use std::any::Any;
-use std::cell::{RefCell, RefMut};
 use std::sync::Arc;
 
-use rustc_ast as ast;
 use rustc_codegen_ssa::CodegenResults;
 use rustc_codegen_ssa::traits::CodegenBackend;
-use rustc_data_structures::steal::Steal;
 use rustc_data_structures::svh::Svh;
-use rustc_data_structures::sync::{OnceLock, WorkerLocal};
 use rustc_hir::def_id::LOCAL_CRATE;
-use rustc_middle::arena::Arena;
 use rustc_middle::dep_graph::DepGraph;
-use rustc_middle::ty::{GlobalCtxt, TyCtxt};
+use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_session::config::{self, OutputFilenames, OutputType};
 
 use crate::errors::FailedWritingFile;
-use crate::interface::Compiler;
 use crate::passes;
 
-/// Represent the result of a query.
-///
-/// This result can be stolen once with the [`steal`] method and generated with the [`compute`] method.
-///
-/// [`steal`]: Steal::steal
-/// [`compute`]: Self::compute
-pub struct Query<T> {
-    /// `None` means no value has been computed yet.
-    result: RefCell<Option<Steal<T>>>,
-}
-
-impl<T> Query<T> {
-    fn compute<F: FnOnce() -> T>(&self, f: F) -> QueryResult<'_, T> {
-        QueryResult(RefMut::map(
-            self.result.borrow_mut(),
-            |r: &mut Option<Steal<T>>| -> &mut Steal<T> {
-                r.get_or_insert_with(|| Steal::new(f()))
-            },
-        ))
-    }
-}
-
-pub struct QueryResult<'a, T>(RefMut<'a, Steal<T>>);
-
-impl<'a, T> std::ops::Deref for QueryResult<'a, T> {
-    type Target = RefMut<'a, Steal<T>>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl<'a, T> std::ops::DerefMut for QueryResult<'a, T> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
-    }
-}
-
-impl<'a, 'tcx> QueryResult<'a, &'tcx GlobalCtxt<'tcx>> {
-    pub fn enter<T>(&mut self, f: impl FnOnce(TyCtxt<'tcx>) -> T) -> T {
-        (*self.0).borrow().enter(f)
-    }
-}
-
-pub struct Queries<'tcx> {
-    compiler: &'tcx Compiler,
-    gcx_cell: OnceLock<GlobalCtxt<'tcx>>,
-
-    arena: WorkerLocal<Arena<'tcx>>,
-    hir_arena: WorkerLocal<rustc_hir::Arena<'tcx>>,
-
-    parse: Query<ast::Crate>,
-    // This just points to what's in `gcx_cell`.
-    gcx: Query<&'tcx GlobalCtxt<'tcx>>,
-}
-
-impl<'tcx> Queries<'tcx> {
-    pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> {
-        Queries {
-            compiler,
-            gcx_cell: OnceLock::new(),
-            arena: WorkerLocal::new(|_| Arena::default()),
-            hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()),
-            parse: Query { result: RefCell::new(None) },
-            gcx: Query { result: RefCell::new(None) },
-        }
-    }
-
-    pub fn finish(&'tcx self) {
-        if let Some(gcx) = self.gcx_cell.get() {
-            gcx.finish();
-        }
-    }
-
-    pub fn parse(&self) -> QueryResult<'_, ast::Crate> {
-        self.parse.compute(|| passes::parse(&self.compiler.sess))
-    }
-
-    pub fn global_ctxt(&'tcx self) -> QueryResult<'tcx, &'tcx GlobalCtxt<'tcx>> {
-        self.gcx.compute(|| {
-            let krate = self.parse().steal();
-
-            passes::create_global_ctxt(
-                self.compiler,
-                krate,
-                &self.gcx_cell,
-                &self.arena,
-                &self.hir_arena,
-            )
-        })
-    }
-}
-
 pub struct Linker {
     dep_graph: DepGraph,
     output_filenames: Arc<OutputFilenames>,
@@ -186,22 +87,3 @@ impl Linker {
         codegen_backend.link(sess, codegen_results, &self.output_filenames)
     }
 }
-
-impl Compiler {
-    pub fn enter<F, T>(&self, f: F) -> T
-    where
-        F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T,
-    {
-        // Must declare `_timer` first so that it is dropped after `queries`.
-        let _timer;
-        let queries = Queries::new(self);
-        let ret = f(&queries);
-
-        // The timer's lifetime spans the dropping of `queries`, which contains
-        // the global context.
-        _timer = self.sess.timer("free_global_ctxt");
-        queries.finish();
-
-        ret
-    }
-}
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index e76e9ca9f85..d103f7f45e2 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -23,8 +23,7 @@ use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, filesearch, getopts};
 use rustc_span::edition::{DEFAULT_EDITION, Edition};
 use rustc_span::source_map::{RealFileLoader, SourceMapInputs};
-use rustc_span::symbol::sym;
-use rustc_span::{FileName, SourceFileHashAlgorithm};
+use rustc_span::{FileName, SourceFileHashAlgorithm, sym};
 use rustc_target::spec::{
     CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy,
     RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, WasmCAbi,
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 2af25bfd3aa..984b8104f53 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -18,7 +18,7 @@ use rustc_session::{EarlyDiagCtxt, Session, filesearch};
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::SourceMapInputs;
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 use rustc_target::spec::Target;
 use tracing::info;
 
@@ -453,7 +453,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
         .opts
         .crate_name
         .clone()
-        .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string()));
+        .or_else(|| rustc_attr_parsing::find_crate_name(attrs).map(|n| n.to_string()));
 
     match sess.io.output_file {
         None => {
diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml
index ec5f0f06c59..cc5a9029633 100644
--- a/compiler/rustc_lint/Cargo.toml
+++ b/compiler/rustc_lint/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 01d9ac20fae..7522e21d0ef 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -536,9 +536,6 @@ lint_non_camel_case_type = {$sort} `{$name}` should have an upper camel case nam
     .suggestion = convert the identifier to upper camel case
     .label = should have an UpperCamelCase name
 
-lint_non_existent_doc_keyword = found non-existing keyword `{$keyword}` used in `#[doc(keyword = "...")]`
-    .help = only existing keywords are allowed in core/std
-
 lint_non_fmt_panic = panic message is not a string literal
     .note = this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021
     .more_info_note = for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
@@ -806,14 +803,14 @@ lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_printl
 lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead
 lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}
 lint_unexpected_cfg_add_cmdline_arg = to expect this configuration use `{$cmdline_arg}`
-lint_unexpected_cfg_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of it's defining crate, try updating your dependencies with `cargo update`
+lint_unexpected_cfg_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}`
 
 lint_unexpected_cfg_define_features = consider defining some features in `Cargo.toml`
 lint_unexpected_cfg_doc_cargo = see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
 lint_unexpected_cfg_doc_rustc = see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
 
 lint_unexpected_cfg_from_external_macro_origin = using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate
-lint_unexpected_cfg_from_external_macro_refer = try refering to `{$macro_name}` crate for guidance on how handle this unexpected cfg
+lint_unexpected_cfg_from_external_macro_refer = try referring to `{$macro_name}` crate for guidance on how handle this unexpected cfg
 lint_unexpected_cfg_name = unexpected `cfg` condition name: `{$name}`
 lint_unexpected_cfg_name_expected_names = expected names are: {$possibilities}{$and_more ->
         [0] {""}
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 3543784bc72..58465ec1cd9 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -39,8 +39,7 @@ pub use rustc_session::lint::builtin::*;
 use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
 use rustc_span::edition::Edition;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{BytePos, InnerSpan, Span};
+use rustc_span::{BytePos, Ident, InnerSpan, Span, Symbol, kw, sym};
 use rustc_target::asm::InlineAsmArch;
 use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
 use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy;
@@ -387,7 +386,7 @@ pub struct MissingDoc;
 
 impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
 
-fn has_doc(attr: &ast::Attribute) -> bool {
+fn has_doc(attr: &hir::Attribute) -> bool {
     if attr.is_doc_comment() {
         return true;
     }
@@ -1012,7 +1011,7 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN
 impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         let attrs = cx.tcx.hir().attrs(it.hir_id());
-        let check_no_mangle_on_generic_fn = |no_mangle_attr: &ast::Attribute,
+        let check_no_mangle_on_generic_fn = |no_mangle_attr: &hir::Attribute,
                                              impl_generics: Option<&hir::Generics<'_>>,
                                              generics: &hir::Generics<'_>,
                                              span| {
@@ -1176,7 +1175,7 @@ declare_lint_pass!(
 );
 
 impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
-    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
+    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) {
         if attr.has_name(sym::feature)
             && let Some(items) = attr.meta_item_list()
         {
@@ -1814,7 +1813,7 @@ declare_lint! {
     "detects edition keywords being used as an identifier",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "issue #49716 <https://github.com/rust-lang/rust/issues/49716>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/gen-keyword.html>",
     };
 }
 
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 44c7888a530..50bb1fb942e 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -20,12 +20,11 @@ use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
 use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
 use rustc_session::lint::{
-    BuiltinLintDiag, FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId,
+    FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId,
 };
 use rustc_session::{LintStoreMarker, Session};
-use rustc_span::Span;
 use rustc_span::edit_distance::find_best_match_for_names;
-use rustc_span::symbol::{Ident, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 use tracing::debug;
 use {rustc_abi as abi, rustc_hir as hir};
 
@@ -33,8 +32,6 @@ use self::TargetLint::*;
 use crate::levels::LintLevelsBuilder;
 use crate::passes::{EarlyLintPassObject, LateLintPassObject};
 
-mod diagnostics;
-
 type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync;
 type LateLintPassFactory =
     dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync;
@@ -511,38 +508,6 @@ pub struct EarlyContext<'a> {
     pub buffered: LintBuffer,
 }
 
-impl EarlyContext<'_> {
-    /// Emit a lint at the appropriate level, with an associated span and an existing
-    /// diagnostic.
-    ///
-    /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature
-    #[rustc_lint_diagnostics]
-    pub fn span_lint_with_diagnostics(
-        &self,
-        lint: &'static Lint,
-        span: MultiSpan,
-        diagnostic: BuiltinLintDiag,
-    ) {
-        self.opt_span_lint_with_diagnostics(lint, Some(span), diagnostic);
-    }
-
-    /// Emit a lint at the appropriate level, with an optional associated span and an existing
-    /// diagnostic.
-    ///
-    /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature
-    #[rustc_lint_diagnostics]
-    pub fn opt_span_lint_with_diagnostics(
-        &self,
-        lint: &'static Lint,
-        span: Option<MultiSpan>,
-        diagnostic: BuiltinLintDiag,
-    ) {
-        self.opt_span_lint(lint, span, |diag| {
-            diagnostics::decorate_lint(self.sess(), diagnostic, diag);
-        });
-    }
-}
-
 pub trait LintContext {
     fn sess(&self) -> &Session;
 
diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs
index 10769b57a76..98b717a3070 100644
--- a/compiler/rustc_lint/src/dangling.rs
+++ b/compiler/rustc_lint/src/dangling.rs
@@ -4,8 +4,7 @@ use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem};
 use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_session::{declare_lint, impl_lint_pass};
-use rustc_span::Span;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 
 use crate::lints::DanglingPointersFromTemporaries;
 use crate::{LateContext, LateLintPass};
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index a68a2a7f983..bc7cd3d118c 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -9,35 +9,39 @@ use rustc_ast::visit::{self as ast_visit, Visitor, walk_list};
 use rustc_ast::{self as ast, HasAttrs};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_feature::Features;
-use rustc_middle::ty::RegisteredTools;
+use rustc_middle::ty::{RegisteredTools, TyCtxt};
 use rustc_session::Session;
 use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
-use rustc_span::Span;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, Span};
 use tracing::debug;
 
-use crate::context::{EarlyContext, LintStore};
+use crate::context::{EarlyContext, LintContext, LintStore};
 use crate::passes::{EarlyLintPass, EarlyLintPassObject};
 
+mod diagnostics;
+
 macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
     $cx.pass.$f(&$cx.context, $($args),*);
 }) }
 
 /// Implements the AST traversal for early lint passes. `T` provides the
 /// `check_*` methods.
-pub struct EarlyContextAndPass<'a, T: EarlyLintPass> {
-    context: EarlyContext<'a>,
+pub struct EarlyContextAndPass<'ecx, 'tcx, T: EarlyLintPass> {
+    context: EarlyContext<'ecx>,
+    tcx: Option<TyCtxt<'tcx>>,
     pass: T,
 }
 
-impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
+impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> {
     // This always-inlined function is for the hot call site.
     #[inline(always)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     fn inlined_check_id(&mut self, id: ast::NodeId) {
         for early_lint in self.context.buffered.take(id) {
             let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint;
-            self.context.opt_span_lint_with_diagnostics(lint_id.lint, span, diagnostic);
+            self.context.opt_span_lint(lint_id.lint, span, |diag| {
+                diagnostics::decorate_lint(self.context.sess(), self.tcx, diagnostic, diag);
+            });
         }
     }
 
@@ -49,7 +53,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
     /// Merge the lints specified by any lint attributes into the
     /// current lint context, call the provided function, then reset the
     /// lints in effect to their previous state.
-    fn with_lint_attrs<F>(&mut self, id: ast::NodeId, attrs: &'a [ast::Attribute], f: F)
+    fn with_lint_attrs<F>(&mut self, id: ast::NodeId, attrs: &'_ [ast::Attribute], f: F)
     where
         F: FnOnce(&mut Self),
     {
@@ -67,19 +71,21 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
     }
 }
 
-impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> {
-    fn visit_coroutine_kind(&mut self, coroutine_kind: &'a ast::CoroutineKind) -> Self::Result {
+impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast>
+    for EarlyContextAndPass<'ecx, 'tcx, T>
+{
+    fn visit_coroutine_kind(&mut self, coroutine_kind: &'ast ast::CoroutineKind) -> Self::Result {
         self.check_id(coroutine_kind.closure_id());
     }
 
-    fn visit_param(&mut self, param: &'a ast::Param) {
+    fn visit_param(&mut self, param: &'ast ast::Param) {
         self.with_lint_attrs(param.id, &param.attrs, |cx| {
             lint_callback!(cx, check_param, param);
             ast_visit::walk_param(cx, param);
         });
     }
 
-    fn visit_item(&mut self, it: &'a ast::Item) {
+    fn visit_item(&mut self, it: &'ast ast::Item) {
         self.with_lint_attrs(it.id, &it.attrs, |cx| {
             lint_callback!(cx, check_item, it);
             ast_visit::walk_item(cx, it);
@@ -87,31 +93,31 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
         })
     }
 
-    fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) {
+    fn visit_foreign_item(&mut self, it: &'ast ast::ForeignItem) {
         self.with_lint_attrs(it.id, &it.attrs, |cx| {
             ast_visit::walk_item(cx, it);
         })
     }
 
-    fn visit_pat(&mut self, p: &'a ast::Pat) {
+    fn visit_pat(&mut self, p: &'ast ast::Pat) {
         lint_callback!(self, check_pat, p);
         self.check_id(p.id);
         ast_visit::walk_pat(self, p);
         lint_callback!(self, check_pat_post, p);
     }
 
-    fn visit_pat_field(&mut self, field: &'a ast::PatField) {
+    fn visit_pat_field(&mut self, field: &'ast ast::PatField) {
         self.with_lint_attrs(field.id, &field.attrs, |cx| {
             ast_visit::walk_pat_field(cx, field);
         });
     }
 
-    fn visit_anon_const(&mut self, c: &'a ast::AnonConst) {
+    fn visit_anon_const(&mut self, c: &'ast ast::AnonConst) {
         self.check_id(c.id);
         ast_visit::walk_anon_const(self, c);
     }
 
-    fn visit_expr(&mut self, e: &'a ast::Expr) {
+    fn visit_expr(&mut self, e: &'ast ast::Expr) {
         self.with_lint_attrs(e.id, &e.attrs, |cx| {
             lint_callback!(cx, check_expr, e);
             ast_visit::walk_expr(cx, e);
@@ -119,13 +125,13 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
         })
     }
 
-    fn visit_expr_field(&mut self, f: &'a ast::ExprField) {
+    fn visit_expr_field(&mut self, f: &'ast ast::ExprField) {
         self.with_lint_attrs(f.id, &f.attrs, |cx| {
             ast_visit::walk_expr_field(cx, f);
         })
     }
 
-    fn visit_stmt(&mut self, s: &'a ast::Stmt) {
+    fn visit_stmt(&mut self, s: &'ast ast::Stmt) {
         // Add the statement's lint attributes to our
         // current state when checking the statement itself.
         // This allows us to handle attributes like
@@ -145,33 +151,33 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
         ast_visit::walk_stmt(self, s);
     }
 
-    fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, span: Span, id: ast::NodeId) {
+    fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, span: Span, id: ast::NodeId) {
         lint_callback!(self, check_fn, fk, span, id);
         self.check_id(id);
         ast_visit::walk_fn(self, fk);
     }
 
-    fn visit_variant_data(&mut self, s: &'a ast::VariantData) {
+    fn visit_variant_data(&mut self, s: &'ast ast::VariantData) {
         if let Some(ctor_node_id) = s.ctor_node_id() {
             self.check_id(ctor_node_id);
         }
         ast_visit::walk_struct_def(self, s);
     }
 
-    fn visit_field_def(&mut self, s: &'a ast::FieldDef) {
+    fn visit_field_def(&mut self, s: &'ast ast::FieldDef) {
         self.with_lint_attrs(s.id, &s.attrs, |cx| {
             ast_visit::walk_field_def(cx, s);
         })
     }
 
-    fn visit_variant(&mut self, v: &'a ast::Variant) {
+    fn visit_variant(&mut self, v: &'ast ast::Variant) {
         self.with_lint_attrs(v.id, &v.attrs, |cx| {
             lint_callback!(cx, check_variant, v);
             ast_visit::walk_variant(cx, v);
         })
     }
 
-    fn visit_ty(&mut self, t: &'a ast::Ty) {
+    fn visit_ty(&mut self, t: &'ast ast::Ty) {
         lint_callback!(self, check_ty, t);
         self.check_id(t.id);
         ast_visit::walk_ty(self, t);
@@ -181,55 +187,55 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
         lint_callback!(self, check_ident, ident);
     }
 
-    fn visit_local(&mut self, l: &'a ast::Local) {
+    fn visit_local(&mut self, l: &'ast ast::Local) {
         self.with_lint_attrs(l.id, &l.attrs, |cx| {
             lint_callback!(cx, check_local, l);
             ast_visit::walk_local(cx, l);
         })
     }
 
-    fn visit_block(&mut self, b: &'a ast::Block) {
+    fn visit_block(&mut self, b: &'ast ast::Block) {
         lint_callback!(self, check_block, b);
         self.check_id(b.id);
         ast_visit::walk_block(self, b);
     }
 
-    fn visit_arm(&mut self, a: &'a ast::Arm) {
+    fn visit_arm(&mut self, a: &'ast ast::Arm) {
         self.with_lint_attrs(a.id, &a.attrs, |cx| {
             lint_callback!(cx, check_arm, a);
             ast_visit::walk_arm(cx, a);
         })
     }
 
-    fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) {
+    fn visit_generic_arg(&mut self, arg: &'ast ast::GenericArg) {
         lint_callback!(self, check_generic_arg, arg);
         ast_visit::walk_generic_arg(self, arg);
     }
 
-    fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
+    fn visit_generic_param(&mut self, param: &'ast ast::GenericParam) {
         self.with_lint_attrs(param.id, &param.attrs, |cx| {
             lint_callback!(cx, check_generic_param, param);
             ast_visit::walk_generic_param(cx, param);
         });
     }
 
-    fn visit_generics(&mut self, g: &'a ast::Generics) {
+    fn visit_generics(&mut self, g: &'ast ast::Generics) {
         lint_callback!(self, check_generics, g);
         ast_visit::walk_generics(self, g);
     }
 
-    fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) {
+    fn visit_where_predicate(&mut self, p: &'ast ast::WherePredicate) {
         lint_callback!(self, enter_where_predicate, p);
         ast_visit::walk_where_predicate(self, p);
         lint_callback!(self, exit_where_predicate, p);
     }
 
-    fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) {
+    fn visit_poly_trait_ref(&mut self, t: &'ast ast::PolyTraitRef) {
         lint_callback!(self, check_poly_trait_ref, t);
         ast_visit::walk_poly_trait_ref(self, t);
     }
 
-    fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
+    fn visit_assoc_item(&mut self, item: &'ast ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
         self.with_lint_attrs(item.id, &item.attrs, |cx| {
             match ctxt {
                 ast_visit::AssocCtxt::Trait => {
@@ -243,32 +249,32 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
         });
     }
 
-    fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) {
+    fn visit_lifetime(&mut self, lt: &'ast ast::Lifetime, _: ast_visit::LifetimeCtxt) {
         self.check_id(lt.id);
         ast_visit::walk_lifetime(self, lt);
     }
 
-    fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) {
+    fn visit_path(&mut self, p: &'ast ast::Path, id: ast::NodeId) {
         self.check_id(id);
         ast_visit::walk_path(self, p);
     }
 
-    fn visit_path_segment(&mut self, s: &'a ast::PathSegment) {
+    fn visit_path_segment(&mut self, s: &'ast ast::PathSegment) {
         self.check_id(s.id);
         ast_visit::walk_path_segment(self, s);
     }
 
-    fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
+    fn visit_attribute(&mut self, attr: &'ast ast::Attribute) {
         lint_callback!(self, check_attribute, attr);
         ast_visit::walk_attribute(self, attr);
     }
 
-    fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
+    fn visit_mac_def(&mut self, mac: &'ast ast::MacroDef, id: ast::NodeId) {
         lint_callback!(self, check_mac_def, mac);
         self.check_id(id);
     }
 
-    fn visit_mac_call(&mut self, mac: &'a ast::MacCall) {
+    fn visit_mac_call(&mut self, mac: &'ast ast::MacCall) {
         lint_callback!(self, check_mac, mac);
         ast_visit::walk_mac(self, mac);
     }
@@ -310,28 +316,18 @@ crate::early_lint_methods!(impl_early_lint_pass, []);
 /// This trait generalizes over those nodes.
 pub trait EarlyCheckNode<'a>: Copy {
     fn id(self) -> ast::NodeId;
-    fn attrs<'b>(self) -> &'b [ast::Attribute]
-    where
-        'a: 'b;
-    fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
-    where
-        'a: 'b;
+    fn attrs(self) -> &'a [ast::Attribute];
+    fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>);
 }
 
 impl<'a> EarlyCheckNode<'a> for (&'a ast::Crate, &'a [ast::Attribute]) {
     fn id(self) -> ast::NodeId {
         ast::CRATE_NODE_ID
     }
-    fn attrs<'b>(self) -> &'b [ast::Attribute]
-    where
-        'a: 'b,
-    {
+    fn attrs(self) -> &'a [ast::Attribute] {
         self.1
     }
-    fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
-    where
-        'a: 'b,
-    {
+    fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>) {
         lint_callback!(cx, check_crate, self.0);
         ast_visit::walk_crate(cx, self.0);
         lint_callback!(cx, check_crate_post, self.0);
@@ -342,16 +338,10 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast::
     fn id(self) -> ast::NodeId {
         self.0
     }
-    fn attrs<'b>(self) -> &'b [ast::Attribute]
-    where
-        'a: 'b,
-    {
+    fn attrs(self) -> &'a [ast::Attribute] {
         self.1
     }
-    fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
-    where
-        'a: 'b,
-    {
+    fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>) {
         walk_list!(cx, visit_attribute, self.1);
         walk_list!(cx, visit_item, self.2);
     }
@@ -359,6 +349,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast::
 
 pub fn check_ast_node<'a>(
     sess: &Session,
+    tcx: Option<TyCtxt<'_>>,
     features: &Features,
     pre_expansion: bool,
     lint_store: &LintStore,
@@ -382,22 +373,23 @@ pub fn check_ast_node<'a>(
     let passes =
         if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes };
     if passes.is_empty() {
-        check_ast_node_inner(sess, check_node, context, builtin_lints);
+        check_ast_node_inner(sess, tcx, check_node, context, builtin_lints);
     } else {
         let mut passes: Vec<_> = passes.iter().map(|mk_pass| (mk_pass)()).collect();
         passes.push(Box::new(builtin_lints));
         let pass = RuntimeCombinedEarlyLintPass { passes: &mut passes[..] };
-        check_ast_node_inner(sess, check_node, context, pass);
+        check_ast_node_inner(sess, tcx, check_node, context, pass);
     }
 }
 
 fn check_ast_node_inner<'a, T: EarlyLintPass>(
     sess: &Session,
+    tcx: Option<TyCtxt<'_>>,
     check_node: impl EarlyCheckNode<'a>,
     context: EarlyContext<'_>,
     pass: T,
 ) {
-    let mut cx = EarlyContextAndPass { context, pass };
+    let mut cx = EarlyContextAndPass { context, tcx, pass };
 
     cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx));
 
diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs
index a3731e31c2b..6d73715562b 100644
--- a/compiler/rustc_lint/src/context/diagnostics.rs
+++ b/compiler/rustc_lint/src/early/diagnostics.rs
@@ -8,17 +8,22 @@ use rustc_errors::{
     Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion,
 };
 use rustc_middle::middle::stability;
+use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_session::lint::{BuiltinLintDiag, ElidedLifetimeResolution};
-use rustc_span::BytePos;
-use rustc_span::symbol::kw;
+use rustc_span::{BytePos, kw};
 use tracing::debug;
 
 use crate::lints::{self, ElidedNamedLifetime};
 
 mod check_cfg;
 
-pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) {
+pub(super) fn decorate_lint(
+    sess: &Session,
+    tcx: Option<TyCtxt<'_>>,
+    diagnostic: BuiltinLintDiag,
+    diag: &mut Diag<'_, ()>,
+) {
     match diagnostic {
         BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => {
             let spans: Vec<_> = content
@@ -199,10 +204,10 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
             .decorate_lint(diag);
         }
         BuiltinLintDiag::UnexpectedCfgName(name, value) => {
-            check_cfg::unexpected_cfg_name(sess, name, value).decorate_lint(diag);
+            check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag);
         }
         BuiltinLintDiag::UnexpectedCfgValue(name, value) => {
-            check_cfg::unexpected_cfg_value(sess, name, value).decorate_lint(diag);
+            check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag);
         }
         BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => {
             let suggestion = match sugg {
diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs
index 63a722f6067..eebb131599a 100644
--- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs
+++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs
@@ -1,10 +1,10 @@
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::bug;
+use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_session::config::ExpectedValues;
 use rustc_span::edit_distance::find_best_match_for_name;
-use rustc_span::symbol::Ident;
-use rustc_span::{ExpnKind, Span, Symbol, sym};
+use rustc_span::{ExpnKind, Ident, Span, Symbol, sym};
 
 use crate::lints;
 
@@ -73,17 +73,20 @@ fn rustc_macro_help(span: Span) -> Option<lints::UnexpectedCfgRustcMacroHelp> {
     }
 }
 
-fn cargo_macro_help(span: Span) -> Option<lints::UnexpectedCfgCargoMacroHelp> {
+fn cargo_macro_help(
+    tcx: Option<TyCtxt<'_>>,
+    span: Span,
+) -> Option<lints::UnexpectedCfgCargoMacroHelp> {
     let oexpn = span.ctxt().outer_expn_data();
     if let Some(def_id) = oexpn.macro_def_id
         && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind
         && def_id.krate != LOCAL_CRATE
+        && let Some(tcx) = tcx
     {
         Some(lints::UnexpectedCfgCargoMacroHelp {
             macro_kind: macro_kind.descr(),
             macro_name,
-            // FIXME: Get access to a `TyCtxt` from an `EarlyContext`
-            // crate_name: cx.tcx.crate_name(def_id.krate),
+            crate_name: tcx.crate_name(def_id.krate),
         })
     } else {
         None
@@ -92,6 +95,7 @@ fn cargo_macro_help(span: Span) -> Option<lints::UnexpectedCfgCargoMacroHelp> {
 
 pub(super) fn unexpected_cfg_name(
     sess: &Session,
+    tcx: Option<TyCtxt<'_>>,
     (name, name_span): (Symbol, Span),
     value: Option<(Symbol, Span)>,
 ) -> lints::UnexpectedCfgName {
@@ -223,7 +227,7 @@ pub(super) fn unexpected_cfg_name(
         };
         lints::unexpected_cfg_name::InvocationHelp::Cargo {
             help,
-            macro_help: cargo_macro_help(name_span),
+            macro_help: cargo_macro_help(tcx, name_span),
         }
     } else {
         let help = lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No));
@@ -238,6 +242,7 @@ pub(super) fn unexpected_cfg_name(
 
 pub(super) fn unexpected_cfg_value(
     sess: &Session,
+    tcx: Option<TyCtxt<'_>>,
     (name, name_span): (Symbol, Span),
     value: Option<(Symbol, Span)>,
 ) -> lints::UnexpectedCfgValue {
@@ -339,7 +344,7 @@ pub(super) fn unexpected_cfg_value(
         };
         lints::unexpected_cfg_value::InvocationHelp::Cargo {
             help,
-            macro_help: cargo_macro_help(name_span),
+            macro_help: cargo_macro_help(tcx, name_span),
         }
     } else {
         let help = if can_suggest_adding_value {
diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
index 8e22a9bdc45..6556a8d8f2d 100644
--- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
+++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
@@ -2,8 +2,7 @@ use rustc_hir as hir;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_span::Span;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 
 use crate::context::LintContext;
 use crate::lints::{EnumIntrinsicsMemDiscriminate, EnumIntrinsicsMemVariant};
diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs
index 1402129195f..259ea908fc6 100644
--- a/compiler/rustc_lint/src/if_let_rescope.rs
+++ b/compiler/rustc_lint/src/if_let_rescope.rs
@@ -84,7 +84,7 @@ declare_lint! {
     rewriting in `match` is an option to preserve the semantics up to Edition 2021",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "issue #124085 <https://github.com/rust-lang/rust/issues/124085>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>",
     };
 }
 
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 482650e04e8..d32666d8895 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -10,13 +10,12 @@ use rustc_hir::{
 };
 use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
-use rustc_span::symbol::{Symbol, kw, sym};
+use rustc_span::{Span, sym};
 use tracing::debug;
 
 use crate::lints::{
-    BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword,
+    BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand,
     NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag,
     SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrInherentUsage,
     UntranslatableDiag,
@@ -376,46 +375,6 @@ impl EarlyLintPass for LintPassImpl {
 }
 
 declare_tool_lint! {
-    /// The `existing_doc_keyword` lint detects use `#[doc()]` keywords
-    /// that don't exist, e.g. `#[doc(keyword = "..")]`.
-    pub rustc::EXISTING_DOC_KEYWORD,
-    Allow,
-    "Check that documented keywords in std and core actually exist",
-    report_in_external_macro: true
-}
-
-declare_lint_pass!(ExistingDocKeyword => [EXISTING_DOC_KEYWORD]);
-
-fn is_doc_keyword(s: Symbol) -> bool {
-    s <= kw::Union
-}
-
-impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword {
-    fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
-        for attr in cx.tcx.hir().attrs(item.hir_id()) {
-            if !attr.has_name(sym::doc) {
-                continue;
-            }
-            if let Some(list) = attr.meta_item_list() {
-                for nested in list {
-                    if nested.has_name(sym::keyword) {
-                        let keyword = nested
-                            .value_str()
-                            .expect("#[doc(keyword = \"...\")] expected a value!");
-                        if is_doc_keyword(keyword) {
-                            return;
-                        }
-                        cx.emit_span_lint(EXISTING_DOC_KEYWORD, attr.span, NonExistentDocKeyword {
-                            keyword,
-                        });
-                    }
-                }
-            }
-        }
-    }
-}
-
-declare_tool_lint! {
     /// The `untranslatable_diagnostic` lint detects messages passed to functions with `impl
     /// Into<{D,Subd}iagMessage` parameters without using translatable Fluent strings.
     ///
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 4b1dafbdbee..0185f46c35a 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1,3 +1,4 @@
+use rustc_ast::attr::AttributeExt;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
@@ -19,8 +20,7 @@ use rustc_session::lint::builtin::{
     UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES,
 };
 use rustc_session::lint::{Level, Lint, LintExpectationId, LintId};
-use rustc_span::symbol::{Symbol, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 use tracing::{debug, instrument};
 use {rustc_ast as ast, rustc_hir as hir};
 
@@ -371,7 +371,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
 
     /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s,
     /// but that is handled with more care
-    fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) {
+    fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) {
         if matches!(
             Level::from_attr(attribute),
             Some(
@@ -383,10 +383,9 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
             )
         ) {
             let store = unerased_lint_store(self.tcx.sess);
-            let Some(meta) = attribute.meta() else { return };
             // Lint attributes are always a metalist inside a
             // metalist (even with just one lint).
-            let Some(meta_item_list) = meta.meta_item_list() else { return };
+            let Some(meta_item_list) = attribute.meta_item_list() else { return };
 
             for meta_list in meta_item_list {
                 // Convert Path to String
@@ -686,7 +685,12 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         };
     }
 
-    fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>) {
+    fn add(
+        &mut self,
+        attrs: &[impl AttributeExt],
+        is_crate_node: bool,
+        source_hir_id: Option<HirId>,
+    ) {
         let sess = self.sess;
         for (attr_index, attr) in attrs.iter().enumerate() {
             if attr.has_name(sym::automatically_derived) {
@@ -910,7 +914,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
 
                 let src = LintLevelSource::Node { name, span: sp, reason };
                 for &id in ids {
-                    if self.check_gated_lint(id, attr.span, false) {
+                    if self.check_gated_lint(id, attr.span(), false) {
                         self.insert_spec(id, (level, src));
                     }
                 }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index a99c94592b3..d7f0d2a6941 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -600,8 +600,6 @@ fn register_internals(store: &mut LintStore) {
     store.register_late_mod_pass(|_| Box::new(DefaultHashTypes));
     store.register_lints(&QueryStability::lint_vec());
     store.register_late_mod_pass(|_| Box::new(QueryStability));
-    store.register_lints(&ExistingDocKeyword::lint_vec());
-    store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword));
     store.register_lints(&TyTyKind::lint_vec());
     store.register_late_mod_pass(|_| Box::new(TyTyKind));
     store.register_lints(&TypeIr::lint_vec());
@@ -629,7 +627,6 @@ fn register_internals(store: &mut LintStore) {
         LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO),
         LintId::of(USAGE_OF_QUALIFIED_TY),
         LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT),
-        LintId::of(EXISTING_DOC_KEYWORD),
         LintId::of(BAD_OPT_ACCESS),
         LintId::of(SPAN_USE_EQ_CTXT),
     ]);
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 5da9f6d2053..ac995b59caf 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -16,8 +16,7 @@ use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt};
 use rustc_session::Session;
 use rustc_session::lint::AmbiguityErrorDiag;
 use rustc_span::edition::Edition;
-use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent};
-use rustc_span::{Span, Symbol, sym};
+use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, kw, sym};
 
 use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds};
 use crate::errors::{OverruledAttributeSub, RequestedLevel};
@@ -951,13 +950,6 @@ pub(crate) struct NonGlobImportTypeIrInherent {
 pub(crate) struct LintPassByHand;
 
 #[derive(LintDiagnostic)]
-#[diag(lint_non_existent_doc_keyword)]
-#[help]
-pub(crate) struct NonExistentDocKeyword {
-    pub keyword: Symbol,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_diag_out_of_impl)]
 pub(crate) struct DiagOutOfImpl;
 
@@ -1816,14 +1808,14 @@ pub(crate) enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> {
 }
 
 #[derive(LintDiagnostic)]
-pub(crate) enum UnpredictableFunctionPointerComparisons<'a> {
+pub(crate) enum UnpredictableFunctionPointerComparisons<'a, 'tcx> {
     #[diag(lint_unpredictable_fn_pointer_comparisons)]
     #[note(lint_note_duplicated_fn)]
     #[note(lint_note_deduplicated_fn)]
     #[note(lint_note_visit_fn_addr_eq)]
     Suggestion {
         #[subdiagnostic]
-        sugg: UnpredictableFunctionPointerComparisonsSuggestion<'a>,
+        sugg: UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx>,
     },
     #[diag(lint_unpredictable_fn_pointer_comparisons)]
     #[note(lint_note_duplicated_fn)]
@@ -1833,22 +1825,40 @@ pub(crate) enum UnpredictableFunctionPointerComparisons<'a> {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(
-    lint_fn_addr_eq_suggestion,
-    style = "verbose",
-    applicability = "maybe-incorrect"
-)]
-pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> {
-    pub ne: &'a str,
-    pub cast_right: String,
-    pub deref_left: &'a str,
-    pub deref_right: &'a str,
-    #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")]
-    pub left: Span,
-    #[suggestion_part(code = ", {deref_right}")]
-    pub middle: Span,
-    #[suggestion_part(code = "{cast_right})")]
-    pub right: Span,
+pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> {
+    #[multipart_suggestion(
+        lint_fn_addr_eq_suggestion,
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    FnAddrEq {
+        ne: &'a str,
+        deref_left: &'a str,
+        deref_right: &'a str,
+        #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")]
+        left: Span,
+        #[suggestion_part(code = ", {deref_right}")]
+        middle: Span,
+        #[suggestion_part(code = ")")]
+        right: Span,
+    },
+    #[multipart_suggestion(
+        lint_fn_addr_eq_suggestion,
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    FnAddrEqWithCast {
+        ne: &'a str,
+        deref_left: &'a str,
+        deref_right: &'a str,
+        fn_sig: rustc_middle::ty::PolyFnSig<'tcx>,
+        #[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")]
+        left: Span,
+        #[suggestion_part(code = ", {deref_right}")]
+        middle: Span,
+        #[suggestion_part(code = " as {fn_sig})")]
+        right: Span,
+    },
 }
 
 pub(crate) struct ImproperCTypes<'a> {
@@ -2187,8 +2197,7 @@ pub(crate) struct UnexpectedCfgRustcMacroHelp {
 pub(crate) struct UnexpectedCfgCargoMacroHelp {
     pub macro_kind: &'static str,
     pub macro_name: Symbol,
-    // FIXME: Figure out a way to get the crate name
-    // crate_name: String,
+    pub crate_name: Symbol,
 }
 
 #[derive(LintDiagnostic)]
@@ -2205,8 +2214,7 @@ pub(crate) struct UnexpectedCfgName {
 pub(crate) mod unexpected_cfg_name {
     use rustc_errors::DiagSymbolList;
     use rustc_macros::Subdiagnostic;
-    use rustc_span::symbol::Ident;
-    use rustc_span::{Span, Symbol};
+    use rustc_span::{Ident, Span, Symbol};
 
     #[derive(Subdiagnostic)]
     pub(crate) enum CodeSuggestion {
@@ -2679,7 +2687,7 @@ impl<G: EmissionGuarantee> LintDiagnostic<'_, G> for ElidedNamedLifetime {
         //  but currently this lint's suggestions can conflict with those of `clippy::needless_lifetimes`:
         //  https://github.com/rust-lang/rust/pull/129840#issuecomment-2323349119
         // HACK: `'static` suggestions will never sonflict, emit only those for now.
-        if name != rustc_span::symbol::kw::StaticLifetime {
+        if name != kw::StaticLifetime {
             return;
         }
         match kind {
diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs
index 9b495c19990..593c8616c1d 100644
--- a/compiler/rustc_lint/src/non_ascii_idents.rs
+++ b/compiler/rustc_lint/src/non_ascii_idents.rs
@@ -2,7 +2,7 @@ use rustc_ast as ast;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::unord::UnordMap;
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 use unicode_security::general_security_profile::IdentifierType;
 
 use crate::lints::{
@@ -205,7 +205,7 @@ impl EarlyLintPass for NonAsciiIdents {
                     (IdentifierType::Not_NFKC, "Not_NFKC"),
                 ] {
                     let codepoints: Vec<_> =
-                        chars.extract_if(|(_, ty)| *ty == Some(id_ty)).collect();
+                        chars.extract_if(.., |(_, ty)| *ty == Some(id_ty)).collect();
                     if codepoints.is_empty() {
                         continue;
                     }
@@ -217,7 +217,7 @@ impl EarlyLintPass for NonAsciiIdents {
                 }
 
                 let remaining = chars
-                    .extract_if(|(c, _)| !GeneralSecurityProfile::identifier_allowed(*c))
+                    .extract_if(.., |(c, _)| !GeneralSecurityProfile::identifier_allowed(*c))
                     .collect::<Vec<_>>();
                 if !remaining.is_empty() {
                     cx.emit_span_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints {
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index 36b1ff59c67..6f047b4da49 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -8,8 +8,7 @@ use rustc_parse_format::{ParseMode, Parser, Piece};
 use rustc_session::lint::FutureIncompatibilityReason;
 use rustc_session::{declare_lint, declare_lint_pass};
 use rustc_span::edition::Edition;
-use rustc_span::symbol::kw;
-use rustc_span::{InnerSpan, Span, Symbol, hygiene, sym};
+use rustc_span::{InnerSpan, Span, Symbol, hygiene, kw, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 
 use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused};
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index 3c33b2dd478..a1cc3a85109 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -5,8 +5,7 @@ use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
-use rustc_span::symbol::kw;
-use rustc_span::{ExpnKind, MacroKind, Span, sym};
+use rustc_span::{ExpnKind, MacroKind, Span, kw, sym};
 
 use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
 use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 4d8ebf2909e..70dce78b572 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -1,14 +1,13 @@
 use rustc_abi::ExternAbi;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::FnKind;
-use rustc_hir::{GenericParamKind, PatKind};
+use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatKind};
 use rustc_middle::ty;
 use rustc_session::config::CrateType;
 use rustc_session::{declare_lint, declare_lint_pass};
 use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::{Ident, sym};
-use rustc_span::{BytePos, Span};
-use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
+use rustc_span::{BytePos, Ident, Span, sym};
+use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir};
 
 use crate::lints::{
     NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
@@ -342,35 +341,35 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
         let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name {
             Some(Ident::from_str(name))
         } else {
-            attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name)
-                .and_then(|attr| attr.meta())
-                .and_then(|meta| {
-                    meta.name_value_literal().and_then(|lit| {
-                        if let ast::LitKind::Str(name, ..) = lit.kind {
-                            // Discard the double quotes surrounding the literal.
-                            let sp = cx
-                                .sess()
-                                .source_map()
-                                .span_to_snippet(lit.span)
-                                .ok()
-                                .and_then(|snippet| {
-                                    let left = snippet.find('"')?;
-                                    let right =
-                                        snippet.rfind('"').map(|pos| snippet.len() - pos)?;
-
-                                    Some(
-                                        lit.span
-                                            .with_lo(lit.span.lo() + BytePos(left as u32 + 1))
-                                            .with_hi(lit.span.hi() - BytePos(right as u32)),
-                                    )
-                                })
-                                .unwrap_or(lit.span);
-
-                            Some(Ident::new(name, sp))
-                        } else {
-                            None
-                        }
-                    })
+            ast::attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name)
+                .and_then(|attr| {
+                    if let AttrKind::Normal(n) = &attr.kind
+                        && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: ref lit }, .. } =
+                            n.as_ref()
+                        && let ast::LitKind::Str(name, ..) = lit.kind
+                    {
+                        // Discard the double quotes surrounding the literal.
+                        let sp = cx
+                            .sess()
+                            .source_map()
+                            .span_to_snippet(lit.span)
+                            .ok()
+                            .and_then(|snippet| {
+                                let left = snippet.find('"')?;
+                                let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?;
+
+                                Some(
+                                    lit.span
+                                        .with_lo(lit.span.lo() + BytePos(left as u32 + 1))
+                                        .with_hi(lit.span.hi() - BytePos(right as u32)),
+                                )
+                            })
+                            .unwrap_or(lit.span);
+
+                        Some(Ident::new(name, sp))
+                    } else {
+                        None
+                    }
                 })
         };
 
@@ -502,7 +501,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         let attrs = cx.tcx.hir().attrs(it.hir_id());
         match it.kind {
-            hir::ItemKind::Static(..) if !attr::contains_name(attrs, sym::no_mangle) => {
+            hir::ItemKind::Static(..) if !ast::attr::contains_name(attrs, sym::no_mangle) => {
                 NonUpperCaseGlobals::check_upper_case(cx, "static variable", &it.ident);
             }
             hir::ItemKind::Const(..) => {
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index 76dc96ae00f..fa519281be5 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -3,7 +3,7 @@ use rustc_hir::{Expr, ExprKind};
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 use crate::context::LintContext;
 use crate::lints::{
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 5de0d4bc870..53a411e2b87 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -5,8 +5,7 @@ use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::print::{PrintTraitPredicateExt as _, TraitPredPrintModifiersAndPath};
 use rustc_middle::ty::{self, Ty, TypeFoldable};
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_span::Span;
-use rustc_span::symbol::kw;
+use rustc_span::{Span, kw};
 use rustc_trait_selection::traits::{self, ObligationCtxt};
 
 use crate::{LateContext, LateLintPass, LintContext};
diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs
index ec306f5f834..3f264859d48 100644
--- a/compiler/rustc_lint/src/pass_by_value.rs
+++ b/compiler/rustc_lint/src/pass_by_value.rs
@@ -3,7 +3,7 @@ use rustc_hir::def::Res;
 use rustc_hir::{GenericArg, PathSegment, QPath, TyKind};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 use crate::lints::PassByValueDiag;
 use crate::{LateContext, LateLintPass, LintContext};
diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs
index 9d84d36e779..3a323298bee 100644
--- a/compiler/rustc_lint/src/passes.rs
+++ b/compiler/rustc_lint/src/passes.rs
@@ -42,9 +42,9 @@ macro_rules! late_lint_methods {
             fn check_field_def(a: &'tcx rustc_hir::FieldDef<'tcx>);
             fn check_variant(a: &'tcx rustc_hir::Variant<'tcx>);
             fn check_path(a: &rustc_hir::Path<'tcx>, b: rustc_hir::HirId);
-            fn check_attribute(a: &'tcx rustc_ast::Attribute);
-            fn check_attributes(a: &'tcx [rustc_ast::Attribute]);
-            fn check_attributes_post(a: &'tcx [rustc_ast::Attribute]);
+            fn check_attribute(a: &'tcx rustc_hir::Attribute);
+            fn check_attributes(a: &'tcx [rustc_hir::Attribute]);
+            fn check_attributes_post(a: &'tcx [rustc_hir::Attribute]);
         ]);
     )
 }
@@ -136,7 +136,7 @@ macro_rules! early_lint_methods {
     ($macro:path, $args:tt) => (
         $macro!($args, [
             fn check_param(a: &rustc_ast::Param);
-            fn check_ident(a: &rustc_span::symbol::Ident);
+            fn check_ident(a: &rustc_span::Ident);
             fn check_crate(a: &rustc_ast::Crate);
             fn check_crate_post(a: &rustc_ast::Crate);
             fn check_item(a: &rustc_ast::Item);
diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs
index a73904cd776..f5ab44d7469 100644
--- a/compiler/rustc_lint/src/shadowed_into_iter.rs
+++ b/compiler/rustc_lint/src/shadowed_into_iter.rs
@@ -61,6 +61,7 @@ declare_lint! {
     "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/intoiterator-box-slice.html>"
     };
 }
 
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index b793ec6a493..a9797c3b32a 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -1,6 +1,6 @@
 use rustc_hir::{self as hir, LangItem};
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 use crate::lints::{DropGlue, DropTraitConstraintsDiag};
 use crate::{LateContext, LateLintPass, LintContext};
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 33650be056d..0c58c804353 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -12,8 +12,7 @@ use rustc_middle::ty::{
 };
 use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
 use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol, source_map};
+use rustc_span::{Span, Symbol, source_map, sym};
 use tracing::debug;
 use {rustc_ast as ast, rustc_hir as hir};
 
@@ -483,29 +482,36 @@ fn lint_fn_pointer<'tcx>(
     let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
     let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
 
-    // We only check for a right cast as `FnDef` == `FnPtr` is not possible,
-    // only `FnPtr == FnDef` is possible.
-    let cast_right = if !r_ty.is_fn_ptr() {
-        let fn_sig = r_ty.fn_sig(cx.tcx);
-        format!(" as {fn_sig}")
-    } else {
-        String::new()
-    };
+    let sugg =
+        // We only check for a right cast as `FnDef` == `FnPtr` is not possible,
+        // only `FnPtr == FnDef` is possible.
+        if !r_ty.is_fn_ptr() {
+            let fn_sig = r_ty.fn_sig(cx.tcx);
 
-    cx.emit_span_lint(
-        UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
-        e.span,
-        UnpredictableFunctionPointerComparisons::Suggestion {
-            sugg: UnpredictableFunctionPointerComparisonsSuggestion {
+            UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEqWithCast {
                 ne,
+                fn_sig,
                 deref_left,
                 deref_right,
                 left,
                 middle,
                 right,
-                cast_right,
-            },
-        },
+            }
+        } else {
+            UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEq {
+                ne,
+                deref_left,
+                deref_right,
+                left,
+                middle,
+                right,
+            }
+        };
+
+    cx.emit_span_lint(
+        UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
+        e.span,
+        UnpredictableFunctionPointerComparisons::Suggestion { sugg },
     );
 }
 
diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs
index dca42fea57d..83942918e3b 100644
--- a/compiler/rustc_lint/src/types/literal.rs
+++ b/compiler/rustc_lint/src/types/literal.rs
@@ -3,7 +3,7 @@ use rustc_abi::{Integer, Size};
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::{bug, ty};
-use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
+use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir};
 
 use crate::LateContext;
 use crate::context::LintContext;
diff --git a/compiler/rustc_lint/src/unqualified_local_imports.rs b/compiler/rustc_lint/src/unqualified_local_imports.rs
index bea01a33bd6..c9dd6b32d88 100644
--- a/compiler/rustc_lint/src/unqualified_local_imports.rs
+++ b/compiler/rustc_lint/src/unqualified_local_imports.rs
@@ -1,7 +1,7 @@
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{self as hir};
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_span::symbol::kw;
+use rustc_span::kw;
 
 use crate::{LateContext, LateLintPass, LintContext, lints};
 
@@ -31,7 +31,7 @@ declare_lint! {
     ///
     /// This lint is meant to be used with the (unstable) rustfmt setting `group_imports = "StdExternalCrate"`.
     /// That setting makes rustfmt group `self::`, `super::`, and `crate::` imports separately from those
-    /// refering to other crates. However, rustfmt cannot know whether `use c::S;` refers to a local module `c`
+    /// referring to other crates. However, rustfmt cannot know whether `use c::S;` refers to a local module `c`
     /// or an external crate `c`, so it always gets categorized as an import from another crate.
     /// To ensure consistent grouping of imports from the local crate, all local imports must
     /// start with `self::`, `super::`, or `crate::`. This lint can be used to enforce that style.
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 9cad5d98562..2b3cb14f9e9 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -11,8 +11,7 @@ use rustc_hir::{self as hir, LangItem};
 use rustc_infer::traits::util::elaborate;
 use rustc_middle::ty::{self, Ty, adjustment};
 use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
-use rustc_span::symbol::{Symbol, kw, sym};
-use rustc_span::{BytePos, Span};
+use rustc_span::{BytePos, Span, Symbol, kw, sym};
 use tracing::instrument;
 
 use crate::lints::{
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 54e927df3c4..2f23ab27492 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -1677,7 +1677,7 @@ declare_lint! {
     "detects patterns whose meaning will change in Rust 2024",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "123076",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>",
     };
 }
 
@@ -2606,7 +2606,7 @@ declare_lint! {
     "unsafe operations in unsafe functions without an explicit unsafe block are deprecated",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "issue #71668 <https://github.com/rust-lang/rust/issues/71668>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html>",
         explain_reason: false
     };
     @edition Edition2024 => Warn;
@@ -4189,7 +4189,7 @@ declare_lint! {
     "never type fallback affecting unsafe function calls",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(Edition::Edition2024),
-        reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>",
     };
     @edition Edition2024 => Deny;
     report_in_external_macro
@@ -4243,7 +4243,7 @@ declare_lint! {
     "never type fallback affecting unsafe function calls",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionAndFutureReleaseError(Edition::Edition2024),
-        reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>",
     };
     report_in_external_macro
 }
@@ -4790,7 +4790,7 @@ declare_lint! {
     "detects unsafe functions being used as safe functions",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "issue #27970 <https://github.com/rust-lang/rust/issues/27970>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/newly-unsafe-functions.html>",
     };
 }
 
@@ -4826,7 +4826,7 @@ declare_lint! {
     "detects missing unsafe keyword on extern declarations",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "issue #123743 <https://github.com/rust-lang/rust/issues/123743>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-extern.html>",
     };
 }
 
@@ -4867,7 +4867,7 @@ declare_lint! {
     "detects unsafe attributes outside of unsafe",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "issue #123757 <https://github.com/rust-lang/rust/issues/123757>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-attributes.html>",
     };
 }
 
@@ -5069,7 +5069,7 @@ declare_lint! {
     "Detect and warn on significant change in drop order in tail expression location",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "issue #123739 <https://github.com/rust-lang/rust/issues/123739>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>",
     };
 }
 
@@ -5108,7 +5108,7 @@ declare_lint! {
     "will be parsed as a guarded string in Rust 2024",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "issue #123735 <https://github.com/rust-lang/rust/issues/123735>",
+        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/reserved-syntax.html>",
     };
     crate_level_only
 }
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index eb761bd6475..7786d3eb59a 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -3,8 +3,9 @@
 // tidy-alphabetical-end
 
 use rustc_abi::ExternAbi;
+use rustc_ast::AttrId;
+use rustc_ast::attr::AttributeExt;
 use rustc_ast::node_id::NodeId;
-use rustc_ast::{AttrId, Attribute};
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::{
     HashStable, StableCompare, StableHasher, ToStableHashKey,
@@ -14,8 +15,7 @@ use rustc_hir::def::Namespace;
 use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 pub use rustc_span::edition::Edition;
-use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent};
-use rustc_span::{Span, Symbol, sym};
+use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym};
 use serde::{Deserialize, Serialize};
 
 pub use self::Level::*;
@@ -221,8 +221,8 @@ impl Level {
     }
 
     /// Converts an `Attribute` to a level.
-    pub fn from_attr(attr: &Attribute) -> Option<Self> {
-        Self::from_symbol(attr.name_or_empty(), Some(attr.id))
+    pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> {
+        Self::from_symbol(attr.name_or_empty(), Some(attr.id()))
     }
 
     /// Converts a `Symbol` to a level.
@@ -931,7 +931,7 @@ macro_rules! declare_lint {
             desc: $desc,
             is_externally_loaded: false,
             $($v: true,)*
-            $(feature_gate: Some(rustc_span::symbol::sym::$gate),)?
+            $(feature_gate: Some(rustc_span::sym::$gate),)?
             $(future_incompatible: Some($crate::FutureIncompatibleInfo {
                 reason: $reason,
                 $($field: $val,)*
@@ -976,7 +976,7 @@ macro_rules! declare_tool_lint {
             report_in_external_macro: $external,
             future_incompatible: None,
             is_externally_loaded: true,
-            $(feature_gate: Some(rustc_span::symbol::sym::$gate),)?
+            $(feature_gate: Some(rustc_span::sym::$gate),)?
             crate_level_only: false,
             $(eval_always: $eval_always,)?
             ..$crate::Lint::default_fields_for_macro()
diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs
index 0f7b8921509..91398f1a9da 100644
--- a/compiler/rustc_macros/src/diagnostics/mod.rs
+++ b/compiler/rustc_macros/src/diagnostics/mod.rs
@@ -16,7 +16,7 @@ use synstructure::Structure;
 /// # extern crate rustc_errors;
 /// # use rustc_errors::Applicability;
 /// # extern crate rustc_span;
-/// # use rustc_span::{symbol::Ident, Span};
+/// # use rustc_span::{Ident, Span};
 /// # extern crate rust_middle;
 /// # use rustc_middle::ty::Ty;
 /// #[derive(Diagnostic)]
diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml
index 12519be9870..0b9fdbbd3da 100644
--- a/compiler/rustc_metadata/Cargo.toml
+++ b/compiler/rustc_metadata/Cargo.toml
@@ -11,7 +11,7 @@ libloading = "0.8.0"
 odht = { version = "0.3.1", features = ["nightly"] }
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_expand = { path = "../rustc_expand" }
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 29dba2bca61..c8715f94d5d 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -17,6 +17,7 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard};
 use rustc_errors::DiagCtxtHandle;
 use rustc_expand::base::SyntaxExtension;
 use rustc_fs_util::try_canonicalize;
+use rustc_hir as hir;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId};
 use rustc_hir::definitions::Definitions;
 use rustc_index::IndexVec;
@@ -28,8 +29,7 @@ use rustc_session::lint::{self, BuiltinLintDiag};
 use rustc_session::output::validate_crate_name;
 use rustc_session::search_paths::PathKind;
 use rustc_span::edition::Edition;
-use rustc_span::symbol::{Ident, Symbol, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
 use rustc_target::spec::{PanicStrategy, Target, TargetTuple};
 use tracing::{debug, info, trace};
 
@@ -97,7 +97,13 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
 }
 
 pub enum LoadedMacro {
-    MacroDef { def: MacroDef, ident: Ident, attrs: AttrVec, span: Span, edition: Edition },
+    MacroDef {
+        def: MacroDef,
+        ident: Ident,
+        attrs: Vec<hir::Attribute>,
+        span: Span,
+        edition: Edition,
+    },
     ProcMacro(SyntaxExtension),
 }
 
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index d59ec7af6ec..b9ebf17af24 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -229,8 +229,7 @@ use rustc_session::cstore::CrateSource;
 use rustc_session::filesearch::FileSearch;
 use rustc_session::search_paths::PathKind;
 use rustc_session::utils::CanonicalizedPath;
-use rustc_span::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::{Span, Symbol};
 use rustc_target::spec::{Target, TargetTuple};
 use tracing::{debug, info};
 
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 8bd2281981b..2a1e4b261e7 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
 
 use rustc_abi::ExternAbi;
 use rustc_ast::CRATE_NODE_ID;
-use rustc_attr as attr;
+use rustc_attr_parsing as attr;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::query::LocalCrate;
 use rustc_middle::ty::{self, List, Ty, TyCtxt};
@@ -16,7 +16,7 @@ use rustc_session::parse::feature_err;
 use rustc_session::search_paths::PathKind;
 use rustc_session::utils::NativeLibKind;
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 use rustc_target::spec::LinkSelfContainedComponents;
 
 use crate::{errors, fluent_generated};
@@ -544,7 +544,7 @@ impl<'tcx> Collector<'tcx> {
             // can move them to the end of the list below.
             let mut existing = self
                 .libs
-                .extract_if(|lib| {
+                .extract_if(.., |lib| {
                     if lib.name.as_str() == passed_lib.name {
                         // FIXME: This whole logic is questionable, whether modifiers are
                         // involved or not, library reordering and kind overriding without
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index b9586338655..90b1d2952c5 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -31,8 +31,7 @@ use rustc_serialize::{Decodable, Decoder};
 use rustc_session::Session;
 use rustc_session::cstore::{CrateSource, ExternCrate};
 use rustc_span::hygiene::HygieneDecodeContext;
-use rustc_span::symbol::kw;
-use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext};
+use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext, kw};
 use tracing::debug;
 
 use crate::creader::CStore;
@@ -874,7 +873,7 @@ impl MetadataBlob {
                         let def_key = root.tables.def_keys.get(blob, item).unwrap().decode(blob);
                         #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))]
                         let def_name = if item == CRATE_DEF_INDEX {
-                            rustc_span::symbol::kw::Crate
+                            kw::Crate
                         } else {
                             def_key
                                 .disambiguated_data
@@ -1369,7 +1368,7 @@ impl<'a> CrateMetadataRef<'a> {
         self,
         id: DefIndex,
         sess: &'a Session,
-    ) -> impl Iterator<Item = ast::Attribute> + 'a {
+    ) -> impl Iterator<Item = hir::Attribute> + 'a {
         self.root
             .tables
             .attributes
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index f01ad31d0bb..527f2f10205 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -1,7 +1,7 @@
 use std::any::Any;
 use std::mem;
 
-use rustc_attr::Deprecation;
+use rustc_attr_parsing::Deprecation;
 use rustc_data_structures::sync::Lrc;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE};
@@ -17,9 +17,8 @@ use rustc_middle::ty::{self, TyCtxt};
 use rustc_middle::util::Providers;
 use rustc_session::cstore::{CrateStore, ExternCrate};
 use rustc_session::{Session, StableCrateId};
-use rustc_span::Span;
 use rustc_span::hygiene::ExpnId;
-use rustc_span::symbol::{Symbol, kw};
+use rustc_span::{Span, Symbol, kw};
 
 use super::{Decodable, DecodeContext, DecodeIterator};
 use crate::creader::{CStore, LoadedMacro};
@@ -344,7 +343,7 @@ provide! { tcx, def_id, other, cdata,
     }
     associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) }
     inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
-    item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) }
+    attrs_for_def => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) }
     is_mir_available => { cdata.is_item_mir_available(def_id.index) }
     is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) }
     cross_crate_inlinable => { table_direct }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index df5b06c6d16..fff6f3f804f 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -4,7 +4,7 @@ use std::fs::File;
 use std::io::{Read, Seek, Write};
 use std::path::{Path, PathBuf};
 
-use rustc_ast::Attribute;
+use rustc_ast::attr::AttributeExt;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::memmap::{Mmap, MmapMut};
 use rustc_data_structures::sync::{Lrc, join, par_for_each_in};
@@ -27,9 +27,9 @@ use rustc_middle::{bug, span_bug};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque};
 use rustc_session::config::{CrateType, OptLevel};
 use rustc_span::hygiene::HygieneEncodeContext;
-use rustc_span::symbol::sym;
 use rustc_span::{
     ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, SyntaxContext,
+    sym,
 };
 use tracing::{debug, instrument, trace};
 
@@ -710,15 +710,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE),
                 has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE),
                 has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE),
-                has_default_lib_allocator: attr::contains_name(attrs, sym::default_lib_allocator),
+                has_default_lib_allocator: ast::attr::contains_name(
+                    attrs,
+                    sym::default_lib_allocator,
+                ),
                 proc_macro_data,
                 debugger_visualizers,
-                compiler_builtins: attr::contains_name(attrs, sym::compiler_builtins),
-                needs_allocator: attr::contains_name(attrs, sym::needs_allocator),
-                needs_panic_runtime: attr::contains_name(attrs, sym::needs_panic_runtime),
-                no_builtins: attr::contains_name(attrs, sym::no_builtins),
-                panic_runtime: attr::contains_name(attrs, sym::panic_runtime),
-                profiler_runtime: attr::contains_name(attrs, sym::profiler_runtime),
+                compiler_builtins: ast::attr::contains_name(attrs, sym::compiler_builtins),
+                needs_allocator: ast::attr::contains_name(attrs, sym::needs_allocator),
+                needs_panic_runtime: ast::attr::contains_name(attrs, sym::needs_panic_runtime),
+                no_builtins: ast::attr::contains_name(attrs, sym::no_builtins),
+                panic_runtime: ast::attr::contains_name(attrs, sym::panic_runtime),
+                profiler_runtime: ast::attr::contains_name(attrs, sym::profiler_runtime),
                 symbol_mangling_version: tcx.sess.opts.get_symbol_mangling_version(),
 
                 crate_deps,
@@ -814,7 +817,7 @@ struct AnalyzeAttrState<'a> {
 /// visibility: this is a piece of data that can be computed once per defid, and not once per
 /// attribute. Some attributes would only be usable downstream if they are public.
 #[inline]
-fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState<'_>) -> bool {
+fn analyze_attr(attr: &impl AttributeExt, state: &mut AnalyzeAttrState<'_>) -> bool {
     let mut should_encode = false;
     if !rustc_feature::encode_cross_crate(attr.name_or_empty()) {
         // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates.
@@ -1354,7 +1357,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             .hir()
             .attrs(tcx.local_def_id_to_hir_id(def_id))
             .iter()
-            .filter(|attr| analyze_attr(attr, &mut state));
+            .filter(|attr| analyze_attr(*attr, &mut state));
 
         record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter);
 
@@ -1917,11 +1920,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 // Proc-macros may have attributes like `#[allow_internal_unstable]`,
                 // so downstream crates need access to them.
                 let attrs = hir.attrs(proc_macro);
-                let macro_kind = if attr::contains_name(attrs, sym::proc_macro) {
+                let macro_kind = if ast::attr::contains_name(attrs, sym::proc_macro) {
                     MacroKind::Bang
-                } else if attr::contains_name(attrs, sym::proc_macro_attribute) {
+                } else if ast::attr::contains_name(attrs, sym::proc_macro_attribute) {
                     MacroKind::Attr
-                } else if let Some(attr) = attr::find_by_name(attrs, sym::proc_macro_derive) {
+                } else if let Some(attr) = ast::attr::find_by_name(attrs, sym::proc_macro_derive) {
                     // This unwrap chain should have been checked by the proc-macro harness.
                     name = attr.meta_item_list().unwrap()[0]
                         .meta_item()
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 4961464833a..64a1c70dd2f 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -36,11 +36,10 @@ use rustc_session::config::SymbolManglingVersion;
 use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib};
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData};
-use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span};
+use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Ident, Span, Symbol};
 use rustc_target::spec::{PanicStrategy, TargetTuple};
 use table::TableBuilder;
-use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
+use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir};
 
 use crate::creader::CrateMetadataRef;
 
@@ -403,7 +402,7 @@ define_tables! {
     cross_crate_inlinable: Table<DefIndex, bool>,
 
 - optional:
-    attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
+    attributes: Table<DefIndex, LazyArray<hir::Attribute>>,
     // For non-reexported names in a module every name is associated with a separate `DefId`,
     // so we can take their names, visibilities etc from other encoded tables.
     module_children_non_reexports: Table<DefIndex, LazyArray<DefIndex>>,
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 3bda3a4aa63..e64500f812a 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -17,7 +17,7 @@ rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_ir = { path = "../rustc_ast_ir" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_error_messages = { path = "../rustc_error_messages" } # Used for intra-doc links
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index b664230d10b..750531b638e 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -85,9 +85,9 @@ macro_rules! arena_types {
             [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap<rustc_hir::HirId, rustc_hir::Upvar>,
             [] dyn_compatibility_violations: rustc_middle::traits::DynCompatibilityViolation,
             [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>,
-            [decode] attribute: rustc_ast::Attribute,
-            [] name_set: rustc_data_structures::unord::UnordSet<rustc_span::symbol::Symbol>,
-            [] ordered_name_set: rustc_data_structures::fx::FxIndexSet<rustc_span::symbol::Symbol>,
+            [decode] attribute: rustc_hir::Attribute,
+            [] name_set: rustc_data_structures::unord::UnordSet<rustc_span::Symbol>,
+            [] ordered_name_set: rustc_data_structures::fx::FxIndexSet<rustc_span::Symbol>,
             [] pats: rustc_middle::ty::PatternKind<'tcx>,
 
             // Note that this deliberately duplicates items in the `rustc_hir::arena`,
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs
index 87bbeb178ee..fcfc31575f8 100644
--- a/compiler/rustc_middle/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs
@@ -64,7 +64,7 @@ pub use rustc_query_system::dep_graph::DepNode;
 use rustc_query_system::dep_graph::FingerprintStyle;
 pub use rustc_query_system::dep_graph::dep_node::DepKind;
 pub(crate) use rustc_query_system::dep_graph::{DepContext, DepNodeParams};
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 
 use crate::mir::mono::MonoItem;
 use crate::ty::TyCtxt;
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 0c701c834f2..308078ddf87 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -9,11 +9,10 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
 use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::*;
+use rustc_hir_pretty as pprust_hir;
 use rustc_middle::hir::nested_filter;
 use rustc_span::def_id::StableCrateId;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{ErrorGuaranteed, Span};
-use {rustc_ast as ast, rustc_hir_pretty as pprust_hir};
+use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
 
 use crate::hir::ModuleItems;
 use crate::middle::debugger_visualizer::DebuggerVisualizerFile;
@@ -381,7 +380,7 @@ impl<'hir> Map<'hir> {
     /// Gets the attributes on the crate. This is preferable to
     /// invoking `krate.attrs` because it registers a tighter
     /// dep-graph access.
-    pub fn krate_attrs(self) -> &'hir [ast::Attribute] {
+    pub fn krate_attrs(self) -> &'hir [Attribute] {
         self.attrs(CRATE_HIR_ID)
     }
 
@@ -792,7 +791,7 @@ impl<'hir> Map<'hir> {
 
     /// Given a node ID, gets a list of attributes associated with the AST
     /// corresponding to the node-ID.
-    pub fn attrs(self, id: HirId) -> &'hir [ast::Attribute] {
+    pub fn attrs(self, id: HirId) -> &'hir [Attribute] {
         self.tcx.hir_attrs(id.owner).get(id.local_id)
     }
 
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index ad0d70152e1..ffefd81cd08 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -151,7 +151,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         node: OwnerNode<'_>,
         bodies: &SortedMap<ItemLocalId, &Body<'_>>,
-        attrs: &SortedMap<ItemLocalId, &[rustc_ast::Attribute]>,
+        attrs: &SortedMap<ItemLocalId, &[Attribute]>,
     ) -> (Option<Fingerprint>, Option<Fingerprint>) {
         if self.needs_crate_hash() {
             self.with_stable_hashing_context(|mut hcx| {
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 971d036fa69..620d9f1c357 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -9,7 +9,7 @@ use rustc_session::Session;
 use rustc_session::lint::builtin::{self, FORBIDDEN_LINT_GROUPS};
 use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId};
 use rustc_span::hygiene::{ExpnKind, MacroKind};
-use rustc_span::{DUMMY_SP, DesugaringKind, Span, Symbol, symbol};
+use rustc_span::{DUMMY_SP, DesugaringKind, Span, Symbol, kw};
 use tracing::instrument;
 
 use crate::ty::TyCtxt;
@@ -37,7 +37,7 @@ pub enum LintLevelSource {
 impl LintLevelSource {
     pub fn name(&self) -> Symbol {
         match *self {
-            LintLevelSource::Default => symbol::kw::Default,
+            LintLevelSource::Default => kw::Default,
             LintLevelSource::Node { name, .. } => name,
             LintLevelSource::CommandLine(name, _) => name,
         }
diff --git a/compiler/rustc_middle/src/metadata.rs b/compiler/rustc_middle/src/metadata.rs
index c3175c6bdf5..57c8960943b 100644
--- a/compiler/rustc_middle/src/metadata.rs
+++ b/compiler/rustc_middle/src/metadata.rs
@@ -1,7 +1,7 @@
 use rustc_hir::def::Res;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
+use rustc_span::Ident;
 use rustc_span::def_id::DefId;
-use rustc_span::symbol::Ident;
 use smallvec::SmallVec;
 
 use crate::ty;
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 44428471a5f..241767fe249 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -1,7 +1,7 @@
 use rustc_abi::Align;
-use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr};
+use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 use rustc_target::spec::SanitizerSet;
 
 use crate::mir::mono::Linkage;
diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs
index 270bcabcc86..8a367a947d1 100644
--- a/compiler/rustc_middle/src/middle/limits.rs
+++ b/compiler/rustc_middle/src/middle/limits.rs
@@ -10,9 +10,9 @@
 
 use std::num::IntErrorKind;
 
-use rustc_ast::Attribute;
+use rustc_ast::attr::AttributeExt;
 use rustc_session::{Limit, Limits, Session};
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 use crate::error::LimitInvalid;
 use crate::query::Providers;
@@ -35,32 +35,36 @@ pub fn provide(providers: &mut Providers) {
     }
 }
 
-pub fn get_recursion_limit(krate_attrs: &[Attribute], sess: &Session) -> Limit {
+pub fn get_recursion_limit(krate_attrs: &[impl AttributeExt], sess: &Session) -> Limit {
     get_limit(krate_attrs, sess, sym::recursion_limit, 128)
 }
 
-fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: usize) -> Limit {
+fn get_limit(
+    krate_attrs: &[impl AttributeExt],
+    sess: &Session,
+    name: Symbol,
+    default: usize,
+) -> Limit {
     match get_limit_size(krate_attrs, sess, name) {
         Some(size) => Limit::new(size),
         None => Limit::new(default),
     }
 }
 
-pub fn get_limit_size(krate_attrs: &[Attribute], sess: &Session, name: Symbol) -> Option<usize> {
+pub fn get_limit_size(
+    krate_attrs: &[impl AttributeExt],
+    sess: &Session,
+    name: Symbol,
+) -> Option<usize> {
     for attr in krate_attrs {
         if !attr.has_name(name) {
             continue;
         }
 
-        if let Some(s) = attr.value_str() {
-            match s.as_str().parse() {
+        if let Some(sym) = attr.value_str() {
+            match sym.as_str().parse() {
                 Ok(n) => return Some(n),
                 Err(e) => {
-                    let value_span = attr
-                        .meta()
-                        .and_then(|meta| meta.name_value_literal_span())
-                        .unwrap_or(attr.span);
-
                     let error_str = match e.kind() {
                         IntErrorKind::PosOverflow => "`limit` is too large",
                         IntErrorKind::Empty => "`limit` must be a non-negative integer",
@@ -71,7 +75,11 @@ pub fn get_limit_size(krate_attrs: &[Attribute], sess: &Session, name: Symbol) -
                         IntErrorKind::Zero => bug!("zero is a valid `limit`"),
                         kind => bug!("unimplemented IntErrorKind variant: {:?}", kind),
                     };
-                    sess.dcx().emit_err(LimitInvalid { span: attr.span, value_span, error_str });
+                    sess.dcx().emit_err(LimitInvalid {
+                        span: attr.span(),
+                        value_span: attr.value_span().unwrap(),
+                        error_str,
+                    });
                 }
             }
         }
diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs
index 83873439bd9..692fe027c49 100644
--- a/compiler/rustc_middle/src/middle/mod.rs
+++ b/compiler/rustc_middle/src/middle/mod.rs
@@ -6,8 +6,7 @@ pub mod lang_items;
 pub mod lib_features {
     use rustc_data_structures::unord::UnordMap;
     use rustc_macros::{HashStable, TyDecodable, TyEncodable};
-    use rustc_span::Span;
-    use rustc_span::symbol::Symbol;
+    use rustc_span::{Span, Symbol};
 
     #[derive(Copy, Clone, Debug, PartialEq, Eq)]
     #[derive(HashStable, TyEncodable, TyDecodable)]
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 94d13021612..84c3c2eb49e 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -4,7 +4,7 @@
 use std::num::NonZero;
 
 use rustc_ast::NodeId;
-use rustc_attr::{
+use rustc_attr_parsing::{
     self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability,
 };
 use rustc_data_structures::unord::UnordMap;
@@ -18,8 +18,7 @@ use rustc_session::Session;
 use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
 use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer};
 use rustc_session::parse::feature_err_issue;
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use tracing::debug;
 
 pub use self::StabilityLevel::*;
@@ -392,7 +391,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
         match stability {
             Some(Stability {
-                level: attr::Unstable { reason, issue, is_soft, implied_by },
+                level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by },
                 feature,
                 ..
             }) => {
@@ -475,7 +474,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
         match stability {
             Some(DefaultBodyStability {
-                level: attr::Unstable { reason, issue, is_soft, .. },
+                level: attr::StabilityLevel::Unstable { reason, issue, is_soft, .. },
                 feature,
             }) => {
                 if span.allows_unstable(feature) {
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index d8a39191920..1231ea88569 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -5,7 +5,7 @@ use rustc_hir::def_id::DefId;
 use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
 use rustc_session::RemapFileNameExt;
 use rustc_session::config::RemapPathScopeComponents;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol};
 use rustc_type_ir::visit::TypeVisitableExt;
 
 use super::interpret::ReportedErrorInfo;
@@ -569,7 +569,7 @@ impl<'tcx> TyCtxt<'tcx> {
         let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
         let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
         self.const_caller_location(
-            rustc_span::symbol::Symbol::intern(
+            Symbol::intern(
                 &caller
                     .file
                     .name
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 56340ff0095..7f3239fa57a 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -26,8 +26,7 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
 use rustc_serialize::{Decodable, Encodable};
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::Symbol;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol};
 use tracing::trace;
 
 pub use self::query::*;
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 266dc7ad2b3..27168b2a9f2 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -1,7 +1,7 @@
 use std::fmt;
 use std::hash::Hash;
 
-use rustc_attr::InlineAttr;
+use rustc_attr_parsing::InlineAttr;
 use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN};
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::FxIndexMap;
@@ -13,8 +13,7 @@ use rustc_index::Idx;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 use rustc_query_system::ich::StableHashingContext;
 use rustc_session::config::OptLevel;
-use rustc_span::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::{Span, Symbol};
 use rustc_target::spec::SymbolVisibility;
 use tracing::debug;
 
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index f690359e012..429be9bc725 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -11,8 +11,7 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_index::bit_set::BitMatrix;
 use rustc_index::{Idx, IndexVec};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
-use rustc_span::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::{Span, Symbol};
 use smallvec::SmallVec;
 
 use super::{ConstValue, SourceInfo};
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index fea940ea47c..b7ece5ffa62 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -10,10 +10,9 @@ use rustc_hir::CoroutineKind;
 use rustc_hir::def_id::DefId;
 use rustc_index::IndexVec;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
-use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::Symbol;
+use rustc_span::{Span, Symbol};
 use rustc_target::asm::InlineAsmRegOrRegClass;
 use smallvec::SmallVec;
 
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 013847f0b2d..b72c0e776fe 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -239,9 +239,9 @@ trivial! {
     bool,
     Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>,
     Option<rustc_ast::expand::allocator::AllocatorKind>,
-    Option<rustc_attr::ConstStability>,
-    Option<rustc_attr::DefaultBodyStability>,
-    Option<rustc_attr::Stability>,
+    Option<rustc_attr_parsing::ConstStability>,
+    Option<rustc_attr_parsing::DefaultBodyStability>,
+    Option<rustc_attr_parsing::Stability>,
     Option<rustc_data_structures::svh::Svh>,
     Option<rustc_hir::def::DefKind>,
     Option<rustc_hir::CoroutineKind>,
@@ -264,10 +264,10 @@ trivial! {
     Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>,
     rustc_abi::ReprOptions,
     rustc_ast::expand::allocator::AllocatorKind,
-    rustc_attr::ConstStability,
-    rustc_attr::DefaultBodyStability,
-    rustc_attr::Deprecation,
-    rustc_attr::Stability,
+    rustc_attr_parsing::ConstStability,
+    rustc_attr_parsing::DefaultBodyStability,
+    rustc_attr_parsing::Deprecation,
+    rustc_attr_parsing::Stability,
     rustc_data_structures::svh::Svh,
     rustc_errors::ErrorGuaranteed,
     rustc_hir::Constness,
@@ -332,7 +332,7 @@ trivial! {
     rustc_span::ExpnId,
     rustc_span::Span,
     rustc_span::Symbol,
-    rustc_span::symbol::Ident,
+    rustc_span::Ident,
     rustc_target::spec::PanicStrategy,
     rustc_type_ir::Variance,
     u32,
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index 66fec2dd0f7..e243425c0b7 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -4,8 +4,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId,
 use rustc_hir::hir_id::{HirId, OwnerId};
 use rustc_query_system::dep_graph::DepNodeIndex;
 use rustc_query_system::query::{DefIdCache, DefaultCache, SingleCache, VecCache};
-use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol};
 
 use crate::infer::canonical::CanonicalQueryInput;
 use crate::mir::mono::CollectionMode;
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index cc4e31294bd..2c2dffe8b88 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -41,10 +41,9 @@ use rustc_session::cstore::{
 use rustc_session::lint::LintExpectationId;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::Symbol;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol};
 use rustc_target::spec::PanicStrategy;
-use {rustc_abi as abi, rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
+use {rustc_abi as abi, rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir};
 
 use crate::infer::canonical::{self, Canonical};
 use crate::lint::LintExpectation;
@@ -680,7 +679,7 @@ rustc_queries! {
     /// of supertraits that define the given associated type. This is used to avoid
     /// cycles in resolving type-dependent associated item paths like `T::Item`.
     query explicit_supertraits_containing_assoc_item(
-        key: (DefId, rustc_span::symbol::Ident)
+        key: (DefId, rustc_span::Ident)
     ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
         desc { |tcx| "computing the super traits of `{}` with associated type name `{}`",
             tcx.def_path_str(key.0),
@@ -709,7 +708,7 @@ rustc_queries! {
     /// To avoid cycles within the predicates of a single item we compute
     /// per-type-parameter predicates for resolving `T::AssocTy`.
     query type_param_predicates(
-        key: (LocalDefId, LocalDefId, rustc_span::symbol::Ident)
+        key: (LocalDefId, LocalDefId, rustc_span::Ident)
     ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
         desc { |tcx| "computing the bounds for type parameter `{}`", tcx.hir().ty_param_name(key.1) }
     }
@@ -1267,7 +1266,7 @@ rustc_queries! {
     /// Returns the attributes on the item at `def_id`.
     ///
     /// Do not use this directly, use `tcx.get_attrs` instead.
-    query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] {
+    query attrs_for_def(def_id: DefId) -> &'tcx [hir::Attribute] {
         desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) }
         separate_provide_extern
     }
@@ -1284,7 +1283,7 @@ rustc_queries! {
         desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) }
     }
 
-    query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] {
+    query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::Ident] {
         desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) }
         separate_provide_extern
     }
diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs
index 119a99e1bf7..4a144ebb899 100644
--- a/compiler/rustc_middle/src/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/query/on_disk_cache.rs
@@ -798,8 +798,8 @@ macro_rules! impl_ref_decoder {
 
 impl_ref_decoder! {<'tcx>
     Span,
-    rustc_ast::Attribute,
-    rustc_span::symbol::Ident,
+    rustc_hir::Attribute,
+    rustc_span::Ident,
     ty::Variance,
     rustc_span::def_id::DefId,
     rustc_span::def_id::LocalDefId,
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 8c434265d27..b34a1782581 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -20,8 +20,7 @@ use rustc_macros::{
     Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable,
 };
 use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId};
-use rustc_span::symbol::Symbol;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol};
 // FIXME: Remove this import and import via `solve::`
 pub use rustc_type_ir::solve::BuiltinImplSource;
 use smallvec::{SmallVec, smallvec};
diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs
index 515aabbe2fc..ed7c98ee0e0 100644
--- a/compiler/rustc_middle/src/traits/specialization_graph.rs
+++ b/compiler/rustc_middle/src/traits/specialization_graph.rs
@@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::{DefId, DefIdMap};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 use crate::error::StrictCoherenceNeedsNegativeCoherence;
 use crate::ty::fast_reject::SimplifiedType;
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 447cbc8932e..8f4137cc011 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -17,7 +17,7 @@ use rustc_index::{IndexSlice, IndexVec};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 use rustc_query_system::ich::StableHashingContext;
 use rustc_session::DataTypeKind;
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 use rustc_type_ir::solve::AdtDestructorKind;
 use tracing::{debug, info, trace};
 
diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs
index 62157d9bfe2..6309dd2e490 100644
--- a/compiler/rustc_middle/src/ty/assoc.rs
+++ b/compiler/rustc_middle/src/ty/assoc.rs
@@ -3,7 +3,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Namespace};
 use rustc_hir::def_id::DefId;
 use rustc_macros::{Decodable, Encodable, HashStable};
-use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::{Ident, Symbol};
 
 use super::{TyCtxt, Visibility};
 use crate::ty;
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index 6ab4d76e545..33d39b137b6 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -7,8 +7,7 @@ use rustc_hir::HirId;
 use rustc_hir::def_id::LocalDefId;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
 use rustc_span::def_id::LocalDefIdMap;
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 use super::TyCtxt;
 use crate::hir::place::{
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index aba5719138c..94bf1aa4f03 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -548,7 +548,7 @@ macro_rules! impl_arena_copy_decoder {
 
 impl_arena_copy_decoder! {<'tcx>
     Span,
-    rustc_span::symbol::Ident,
+    rustc_span::Ident,
     ty::Variance,
     rustc_span::def_id::DefId,
     rustc_span::def_id::LocalDefId,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index db1a479f580..54ee582f4de 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -10,10 +10,11 @@ use std::cmp::Ordering;
 use std::hash::{Hash, Hasher};
 use std::marker::PhantomData;
 use std::ops::{Bound, Deref};
+use std::sync::OnceLock;
 use std::{fmt, iter, mem};
 
 use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx};
-use rustc_ast::{self as ast, attr};
+use rustc_ast as ast;
 use rustc_data_structures::defer;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::FxHashMap;
@@ -29,13 +30,12 @@ use rustc_data_structures::unord::UnordSet;
 use rustc_errors::{
     Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan,
 };
-use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind};
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::definitions::Definitions;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{HirId, Node, TraitCandidate};
+use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate};
 use rustc_index::IndexVec;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 use rustc_query_system::cache::WithDepNode;
@@ -47,8 +47,7 @@ use rustc_session::cstore::{CrateStoreDyn, Untracked};
 use rustc_session::lint::Lint;
 use rustc_session::{Limit, MetadataKind, Session};
 use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
@@ -586,7 +585,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     }
 
     fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool {
-        self.trait_def(trait_def_id).safety == hir::Safety::Unsafe
+        self.trait_def(trait_def_id).safety.is_unsafe()
     }
 
     fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
@@ -722,7 +721,7 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
     }
 
     fn is_safe(self) -> bool {
-        matches!(self, hir::Safety::Safe)
+        self.is_safe()
     }
 
     fn prefix_str(self) -> &'static str {
@@ -1119,10 +1118,10 @@ impl<'tcx> CommonConsts<'tcx> {
 /// either a `ReEarlyParam` or `ReLateParam`.
 #[derive(Debug)]
 pub struct FreeRegionInfo {
-    /// `LocalDefId` of the free region.
-    pub def_id: LocalDefId,
-    /// the bound region corresponding to free region.
-    pub bound_region: ty::BoundRegionKind,
+    /// `LocalDefId` of the scope.
+    pub scope: LocalDefId,
+    /// the `DefId` of the free region.
+    pub region_def_id: DefId,
     /// checks if bound region is in Impl Item
     pub is_impl_item: bool,
 }
@@ -1347,46 +1346,6 @@ pub struct GlobalCtxt<'tcx> {
 
     /// Stores memory for globals (statics/consts).
     pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>,
-
-    current_gcx: CurrentGcx,
-}
-
-impl<'tcx> GlobalCtxt<'tcx> {
-    /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of
-    /// `f`.
-    pub fn enter<F, R>(&'tcx self, f: F) -> R
-    where
-        F: FnOnce(TyCtxt<'tcx>) -> R,
-    {
-        let icx = tls::ImplicitCtxt::new(self);
-
-        // Reset `current_gcx` to `None` when we exit.
-        let _on_drop = defer(move || {
-            *self.current_gcx.value.write() = None;
-        });
-
-        // Set this `GlobalCtxt` as the current one.
-        {
-            let mut guard = self.current_gcx.value.write();
-            assert!(guard.is_none(), "no `GlobalCtxt` is currently set");
-            *guard = Some(self as *const _ as *const ());
-        }
-
-        tls::enter_context(&icx, || f(icx.tcx))
-    }
-
-    pub fn finish(&'tcx self) {
-        // We assume that no queries are run past here. If there are new queries
-        // after this point, they'll show up as "<unknown>" in self-profiling data.
-        self.enter(|tcx| tcx.alloc_self_profile_query_strings());
-
-        self.enter(|tcx| tcx.save_dep_graph());
-        self.enter(|tcx| tcx.query_key_hash_verify_all());
-
-        if let Err((path, error)) = self.dep_graph.finish_encoding() {
-            self.sess.dcx().emit_fatal(crate::error::FailedWritingFile { path: &path, error });
-        }
-    }
 }
 
 /// This is used to get a reference to a `GlobalCtxt` if one is available.
@@ -1530,7 +1489,8 @@ impl<'tcx> TyCtxt<'tcx> {
     /// By only providing the `TyCtxt` inside of the closure we enforce that the type
     /// context and any interned value (types, args, etc.) can only be used while `ty::tls`
     /// has a valid reference to the context, to allow formatting values that need it.
-    pub fn create_global_ctxt(
+    pub fn create_global_ctxt<T>(
+        gcx_cell: &'tcx OnceLock<GlobalCtxt<'tcx>>,
         s: &'tcx Session,
         crate_types: Vec<CrateType>,
         stable_crate_id: StableCrateId,
@@ -1542,7 +1502,8 @@ impl<'tcx> TyCtxt<'tcx> {
         query_system: QuerySystem<'tcx>,
         hooks: crate::hooks::Providers,
         current_gcx: CurrentGcx,
-    ) -> GlobalCtxt<'tcx> {
+        f: impl FnOnce(TyCtxt<'tcx>) -> T,
+    ) -> T {
         let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| {
             s.dcx().emit_fatal(err);
         });
@@ -1551,7 +1512,7 @@ impl<'tcx> TyCtxt<'tcx> {
         let common_lifetimes = CommonLifetimes::new(&interners);
         let common_consts = CommonConsts::new(&interners, &common_types, s, &untracked);
 
-        GlobalCtxt {
+        let gcx = gcx_cell.get_or_init(|| GlobalCtxt {
             sess: s,
             crate_types,
             stable_crate_id,
@@ -1575,8 +1536,23 @@ impl<'tcx> TyCtxt<'tcx> {
             canonical_param_env_cache: Default::default(),
             data_layout,
             alloc_map: Lock::new(interpret::AllocMap::new()),
-            current_gcx,
+        });
+
+        let icx = tls::ImplicitCtxt::new(&gcx);
+
+        // Reset `current_gcx` to `None` when we exit.
+        let _on_drop = defer(|| {
+            *current_gcx.value.write() = None;
+        });
+
+        // Set this `GlobalCtxt` as the current one.
+        {
+            let mut guard = current_gcx.value.write();
+            assert!(guard.is_none(), "no `GlobalCtxt` is currently set");
+            *guard = Some(&gcx as *const _ as *const ());
         }
+
+        tls::enter_context(&icx, || f(icx.tcx))
     }
 
     /// Obtain all lang items of this crate and all dependencies (recursively)
@@ -1983,7 +1959,7 @@ impl<'tcx> TyCtxt<'tcx> {
         generic_param_scope: LocalDefId,
         mut region: Region<'tcx>,
     ) -> Option<FreeRegionInfo> {
-        let (suitable_region_binding_scope, bound_region) = loop {
+        let (suitable_region_binding_scope, region_def_id) = loop {
             let def_id =
                 region.opt_param_def_id(self, generic_param_scope.to_def_id())?.as_local()?;
             let scope = self.local_parent(def_id);
@@ -1993,10 +1969,7 @@ impl<'tcx> TyCtxt<'tcx> {
                 region = self.map_opaque_lifetime_to_parent_lifetime(def_id);
                 continue;
             }
-            break (
-                scope,
-                ty::BoundRegionKind::Named(def_id.into(), self.item_name(def_id.into())),
-            );
+            break (scope, def_id.into());
         };
 
         let is_impl_item = match self.hir_node_by_def_id(suitable_region_binding_scope) {
@@ -2005,7 +1978,7 @@ impl<'tcx> TyCtxt<'tcx> {
             _ => false,
         };
 
-        Some(FreeRegionInfo { def_id: suitable_region_binding_scope, bound_region, is_impl_item })
+        Some(FreeRegionInfo { scope: suitable_region_binding_scope, region_def_id, is_impl_item })
     }
 
     /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type.
@@ -2120,6 +2093,19 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn local_opaque_ty_origin(self, def_id: LocalDefId) -> hir::OpaqueTyOrigin<LocalDefId> {
         self.hir().expect_opaque_ty(def_id).origin
     }
+
+    pub fn finish(self) {
+        // We assume that no queries are run past here. If there are new queries
+        // after this point, they'll show up as "<unknown>" in self-profiling data.
+        self.alloc_self_profile_query_strings();
+
+        self.save_dep_graph();
+        self.query_key_hash_verify_all();
+
+        if let Err((path, error)) = self.dep_graph.finish_encoding() {
+            self.sess.dcx().emit_fatal(crate::error::FailedWritingFile { path: &path, error });
+        }
+    }
 }
 
 macro_rules! nop_lift {
@@ -2521,7 +2507,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// that is, a `fn` type that is equivalent in every way for being
     /// unsafe.
     pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> {
-        assert_eq!(sig.safety(), hir::Safety::Safe);
+        assert!(sig.safety().is_safe());
         Ty::new_fn_ptr(self, sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Unsafe, ..sig }))
     }
 
@@ -3239,12 +3225,16 @@ pub fn provide(providers: &mut Providers) {
     providers.extern_mod_stmt_cnum =
         |tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned();
     providers.is_panic_runtime =
-        |tcx, LocalCrate| attr::contains_name(tcx.hir().krate_attrs(), sym::panic_runtime);
+        |tcx, LocalCrate| contains_name(tcx.hir().krate_attrs(), sym::panic_runtime);
     providers.is_compiler_builtins =
-        |tcx, LocalCrate| attr::contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins);
+        |tcx, LocalCrate| contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins);
     providers.has_panic_handler = |tcx, LocalCrate| {
         // We want to check if the panic handler was defined in this crate
         tcx.lang_items().panic_impl().is_some_and(|did| did.is_local())
     };
     providers.source_span = |tcx, def_id| tcx.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP);
 }
+
+pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {
+    attrs.iter().any(|x| x.has_name(name))
+}
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 604f1da26c6..406e732744b 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -309,7 +309,7 @@ pub fn suggest_constraining_type_params<'a>(
         let Some(param) = param else { return false };
 
         {
-            let mut sized_constraints = constraints.extract_if(|(_, def_id, _)| {
+            let mut sized_constraints = constraints.extract_if(.., |(_, def_id, _)| {
                 def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))
             });
             if let Some((_, def_id, _)) = sized_constraints.next() {
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index ce40ab18261..85d9db7ee74 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -2,8 +2,7 @@ use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::DefId;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, kw};
+use rustc_span::{Span, Symbol, kw};
 use tracing::instrument;
 
 use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt};
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 1dd564d9798..49b5588e261 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -202,7 +202,7 @@ impl<'tcx> Instance<'tcx> {
         if !tcx.sess.opts.share_generics()
             // However, if the def_id is marked inline(never), then it's fine to just reuse the
             // upstream monomorphization.
-            && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr::InlineAttr::Never
+            && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr_parsing::InlineAttr::Never
         {
             return None;
         }
@@ -277,7 +277,7 @@ impl<'tcx> InstanceKind<'tcx> {
         &self,
         tcx: TyCtxt<'tcx>,
         attr: Symbol,
-    ) -> impl Iterator<Item = &'tcx rustc_ast::Attribute> {
+    ) -> impl Iterator<Item = &'tcx hir::Attribute> {
         tcx.get_attrs(self.def_id(), attr)
     }
 
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 07573a79260..ad1680ed3a2 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -16,8 +16,7 @@ use rustc_hir::def_id::DefId;
 use rustc_index::IndexVec;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension};
 use rustc_session::config::OptLevel;
-use rustc_span::symbol::{Symbol, sym};
-use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
+use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
 use rustc_target::callconv::FnAbi;
 use rustc_target::spec::{
     HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, PanicStrategy, Target, WasmCAbi, X86Abi,
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index e47b0cf0b3b..25d0d7b71da 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -33,9 +33,9 @@ use rustc_data_structures::intern::Interned;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::steal::Steal;
 use rustc_errors::{Diag, ErrorGuaranteed, StashKey};
+use rustc_hir::LangItem;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
-use rustc_hir::{LangItem, Safety};
 use rustc_index::IndexVec;
 use rustc_macros::{
     Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable,
@@ -46,13 +46,12 @@ use rustc_serialize::{Decodable, Encodable};
 use rustc_session::lint::LintBuffer;
 pub use rustc_session::lint::RegisteredTools;
 use rustc_span::hygiene::MacroKind;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{ExpnId, ExpnKind, Span};
+use rustc_span::{ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym};
 pub use rustc_type_ir::relate::VarianceDiagInfo;
 pub use rustc_type_ir::*;
 use tracing::{debug, instrument};
 pub use vtable::*;
-use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
+use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir};
 
 pub use self::closure::{
     BorrowKind, CAPTURE_STRUCT_LOCAL, CaptureInfo, CapturedPlace, ClosureTypeInfo,
@@ -1281,7 +1280,7 @@ impl VariantDef {
 
     /// Returns whether this variant has unsafe fields.
     pub fn has_unsafe_fields(&self) -> bool {
-        self.fields.iter().any(|x| x.safety == Safety::Unsafe)
+        self.fields.iter().any(|x| x.safety.is_unsafe())
     }
 }
 
@@ -1745,11 +1744,11 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     // FIXME(@lcnr): Remove this function.
-    pub fn get_attrs_unchecked(self, did: DefId) -> &'tcx [ast::Attribute] {
+    pub fn get_attrs_unchecked(self, did: DefId) -> &'tcx [hir::Attribute] {
         if let Some(did) = did.as_local() {
             self.hir().attrs(self.local_def_id_to_hir_id(did))
         } else {
-            self.item_attrs(did)
+            self.attrs_for_def(did)
         }
     }
 
@@ -1758,14 +1757,14 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         did: impl Into<DefId>,
         attr: Symbol,
-    ) -> impl Iterator<Item = &'tcx ast::Attribute> {
+    ) -> impl Iterator<Item = &'tcx hir::Attribute> {
         let did: DefId = did.into();
-        let filter_fn = move |a: &&ast::Attribute| a.has_name(attr);
+        let filter_fn = move |a: &&hir::Attribute| a.has_name(attr);
         if let Some(did) = did.as_local() {
             self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn)
         } else {
             debug_assert!(rustc_feature::encode_cross_crate(attr));
-            self.item_attrs(did).iter().filter(filter_fn)
+            self.attrs_for_def(did).iter().filter(filter_fn)
         }
     }
 
@@ -1781,7 +1780,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         did: impl Into<DefId>,
         attr: Symbol,
-    ) -> Option<&'tcx ast::Attribute> {
+    ) -> Option<&'tcx hir::Attribute> {
         let did: DefId = did.into();
         if did.as_local().is_some() {
             // it's a crate local item, we need to check feature flags
@@ -1794,7 +1793,7 @@ impl<'tcx> TyCtxt<'tcx> {
             // we filter out unstable diagnostic attributes before
             // encoding attributes
             debug_assert!(rustc_feature::encode_cross_crate(attr));
-            self.item_attrs(did)
+            self.attrs_for_def(did)
                 .iter()
                 .find(|a| matches!(a.path().as_ref(), [sym::diagnostic, a] if *a == attr))
         }
@@ -1804,19 +1803,19 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         did: DefId,
         attr: &'attr [Symbol],
-    ) -> impl Iterator<Item = &'tcx ast::Attribute> + 'attr
+    ) -> impl Iterator<Item = &'tcx hir::Attribute> + 'attr
     where
         'tcx: 'attr,
     {
-        let filter_fn = move |a: &&ast::Attribute| a.path_matches(attr);
+        let filter_fn = move |a: &&hir::Attribute| a.path_matches(attr);
         if let Some(did) = did.as_local() {
             self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn)
         } else {
-            self.item_attrs(did).iter().filter(filter_fn)
+            self.attrs_for_def(did).iter().filter(filter_fn)
         }
     }
 
-    pub fn get_attr(self, did: impl Into<DefId>, attr: Symbol) -> Option<&'tcx ast::Attribute> {
+    pub fn get_attr(self, did: impl Into<DefId>, attr: Symbol) -> Option<&'tcx hir::Attribute> {
         if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) {
             let did: DefId = did.into();
             bug!("get_attr: unexpected called with DefId `{:?}`, attr `{:?}`", did, attr);
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index f7322217aa3..86a95827e84 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -80,10 +80,10 @@ trivially_parameterized_over_tcx! {
     rustc_ast::Attribute,
     rustc_ast::DelimArgs,
     rustc_ast::expand::StrippedCfgItem<rustc_hir::def_id::DefIndex>,
-    rustc_attr::ConstStability,
-    rustc_attr::DefaultBodyStability,
-    rustc_attr::Deprecation,
-    rustc_attr::Stability,
+    rustc_attr_parsing::ConstStability,
+    rustc_attr_parsing::DefaultBodyStability,
+    rustc_attr_parsing::Deprecation,
+    rustc_attr_parsing::Stability,
     rustc_hir::Constness,
     rustc_hir::Defaultness,
     rustc_hir::Safety,
@@ -109,8 +109,9 @@ trivially_parameterized_over_tcx! {
     rustc_span::Symbol,
     rustc_span::def_id::DefPathHash,
     rustc_span::hygiene::SyntaxContextData,
-    rustc_span::symbol::Ident,
+    rustc_span::Ident,
     rustc_type_ir::Variance,
+    rustc_hir::Attribute,
 }
 
 // HACK(compiler-errors): This macro rule can only take a fake path,
@@ -140,5 +141,5 @@ parameterized_over_tcx! {
     ty::Predicate,
     ty::Clause,
     ty::ClauseKind,
-    ty::ImplTraitHeader
+    ty::ImplTraitHeader,
 }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 40e0fb0087f..83508d97cf8 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -16,8 +16,7 @@ use rustc_hir::definitions::{DefKey, DefPathDataName};
 use rustc_macros::{Lift, extension};
 use rustc_session::Limit;
 use rustc_session::cstore::{ExternCrate, ExternCrateSource};
-use rustc_span::symbol::{Ident, Symbol, kw};
-use rustc_span::{FileNameDisplayPreference, sym};
+use rustc_span::{FileNameDisplayPreference, Ident, Symbol, kw, sym};
 use rustc_type_ir::{Upcast as _, elaborate};
 use smallvec::SmallVec;
 
diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs
index 60c2c322d4f..0eb2aafdf2e 100644
--- a/compiler/rustc_middle/src/ty/region.rs
+++ b/compiler/rustc_middle/src/ty/region.rs
@@ -4,8 +4,7 @@ use rustc_data_structures::intern::Interned;
 use rustc_errors::MultiSpan;
 use rustc_hir::def_id::DefId;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
-use rustc_span::symbol::{Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, ErrorGuaranteed};
+use rustc_span::{DUMMY_SP, ErrorGuaranteed, Symbol, kw, sym};
 use rustc_type_ir::RegionKind as IrRegionKind;
 pub use rustc_type_ir::RegionVid;
 use tracing::debug;
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 0af0a5f170d..ec4fb93bdb3 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -224,7 +224,6 @@ TrivialTypeTraversalImpls! {
     ::rustc_ast::InlineAsmOptions,
     ::rustc_ast::InlineAsmTemplatePiece,
     ::rustc_ast::NodeId,
-    ::rustc_span::symbol::Symbol,
     ::rustc_hir::def::Res,
     ::rustc_hir::def_id::LocalDefId,
     ::rustc_hir::ByRef,
@@ -252,8 +251,9 @@ TrivialTypeTraversalImpls! {
     crate::ty::Placeholder<ty::BoundVar>,
     crate::ty::LateParamRegion,
     crate::ty::adjustment::PointerCoercion,
+    ::rustc_span::Ident,
     ::rustc_span::Span,
-    ::rustc_span::symbol::Ident,
+    ::rustc_span::Symbol,
     ty::BoundVar,
     ty::ValTree<'tcx>,
 }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 3fbc23924f5..045c483d6a5 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -15,8 +15,7 @@ use rustc_hir as hir;
 use rustc_hir::LangItem;
 use rustc_hir::def_id::DefId;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension};
-use rustc_span::symbol::{Symbol, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::visit::TypeVisitableExt;
 use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind};
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index fda9898ed56..fc3530e3dde 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1291,7 +1291,7 @@ impl<'tcx> Ty<'tcx> {
     /// Checks whether this type is an ADT that has unsafe fields.
     pub fn has_unsafe_fields(self) -> bool {
         if let ty::Adt(adt_def, ..) = self.kind() {
-            adt_def.all_fields().any(|x| x.safety == hir::Safety::Unsafe)
+            adt_def.all_fields().any(|x| x.safety.is_unsafe())
         } else {
             false
         }
diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_middle/src/util/call_kind.rs
index df5b73ac1bd..0e395331687 100644
--- a/compiler/rustc_middle/src/util/call_kind.rs
+++ b/compiler/rustc_middle/src/util/call_kind.rs
@@ -4,8 +4,7 @@
 
 use rustc_hir::def_id::DefId;
 use rustc_hir::{LangItem, lang_items};
-use rustc_span::symbol::Ident;
-use rustc_span::{DesugaringKind, Span, sym};
+use rustc_span::{DesugaringKind, Ident, Span, sym};
 use tracing::debug;
 
 use crate::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv};
diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/builder/block.rs
index 89e64015bc4..ba63a97de89 100644
--- a/compiler/rustc_mir_build/src/build/block.rs
+++ b/compiler/rustc_mir_build/src/builder/block.rs
@@ -5,9 +5,9 @@ use rustc_middle::{span_bug, ty};
 use rustc_span::Span;
 use tracing::debug;
 
-use crate::build::ForGuard::OutsideGuard;
-use crate::build::matches::{DeclareLetBindings, EmitStorageLive, ScheduleDrops};
-use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
+use crate::builder::ForGuard::OutsideGuard;
+use crate::builder::matches::{DeclareLetBindings, EmitStorageLive, ScheduleDrops};
+use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     pub(crate) fn ast_block(
diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs
index 9c5ee5b0996..cca309115ba 100644
--- a/compiler/rustc_mir_build/src/build/cfg.rs
+++ b/compiler/rustc_mir_build/src/builder/cfg.rs
@@ -4,7 +4,7 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 use tracing::debug;
 
-use crate::build::CFG;
+use crate::builder::CFG;
 
 impl<'tcx> CFG<'tcx> {
     pub(crate) fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> {
diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/builder/coverageinfo.rs
index 52a4a4b4b51..a80bd4f3c80 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo.rs
+++ b/compiler/rustc_mir_build/src/builder/coverageinfo.rs
@@ -8,8 +8,8 @@ use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::LocalDefId;
 
-use crate::build::coverageinfo::mcdc::MCDCInfoBuilder;
-use crate::build::{Builder, CFG};
+use crate::builder::coverageinfo::mcdc::MCDCInfoBuilder;
+use crate::builder::{Builder, CFG};
 
 mod mcdc;
 
diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/builder/coverageinfo/mcdc.rs
index 343d4000043..6b4871dc1fc 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
+++ b/compiler/rustc_mir_build/src/builder/coverageinfo/mcdc.rs
@@ -9,7 +9,7 @@ use rustc_middle::thir::LogicalOp;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
 
-use crate::build::Builder;
+use crate::builder::Builder;
 use crate::errors::MCDCExceedsConditionLimit;
 
 /// LLVM uses `i16` to represent condition id. Hence `i16::MAX` is the hard limit for number of
diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs
index e809c9a23f3..34cdc288f0b 100644
--- a/compiler/rustc_mir_build/src/build/custom/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs
@@ -17,10 +17,9 @@
 //! terminators, and everything below can be found in the `parse::instruction` submodule.
 //!
 
-use rustc_ast::Attribute;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::HirId;
 use rustc_hir::def_id::DefId;
+use rustc_hir::{Attribute, HirId};
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::mir::*;
 use rustc_middle::span_bug;
diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs
index 538068e1fac..538068e1fac 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse.rs
+++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs
index 67114efdff5..59f440432eb 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs
@@ -9,8 +9,8 @@ use rustc_span::Span;
 use rustc_span::source_map::Spanned;
 
 use super::{PResult, ParseCtxt, parse_by_kind};
-use crate::build::custom::ParseError;
-use crate::build::expr::as_constant::as_constant_inner;
+use crate::builder::custom::ParseError;
+use crate::builder::expr::as_constant::as_constant_inner;
 
 impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
     pub(crate) fn parse_statement(&self, expr_id: ExprId) -> PResult<StatementKind<'tcx>> {
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs
index 640408cb9c8..177c1e33a83 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs
@@ -14,7 +14,7 @@ use rustc_middle::ty::{
 use rustc_middle::{bug, mir, span_bug};
 use tracing::{instrument, trace};
 
-use crate::build::{Builder, parse_float_into_constval};
+use crate::builder::{Builder, parse_float_into_constval};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Compile `expr`, yielding a compile-time constant. Assumes that
diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs
index 777ff9e68f0..63e9b1dc6cd 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs
@@ -4,8 +4,8 @@ use rustc_middle::mir::*;
 use rustc_middle::thir::*;
 use tracing::{debug, instrument};
 
-use crate::build::expr::category::Category;
-use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
+use crate::builder::expr::category::Category;
+use crate::builder::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Construct a temporary lifetime restricted to just the local scope
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
index 70a74910a68..01aec70f437 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
@@ -14,9 +14,9 @@ use rustc_middle::{bug, span_bug};
 use rustc_span::{DesugaringKind, Span};
 use tracing::{debug, instrument, trace};
 
-use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
-use crate::build::expr::category::Category;
-use crate::build::{BlockAnd, BlockAndExtension, Builder, Capture, CaptureMap};
+use crate::builder::ForGuard::{OutsideGuard, RefWithinGuard};
+use crate::builder::expr::category::Category;
+use crate::builder::{BlockAnd, BlockAndExtension, Builder, Capture, CaptureMap};
 
 /// The "outermost" place that holds this value.
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -68,7 +68,7 @@ pub(crate) enum PlaceBase {
 /// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
 /// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
 #[derive(Clone, Debug, PartialEq)]
-pub(in crate::build) struct PlaceBuilder<'tcx> {
+pub(in crate::builder) struct PlaceBuilder<'tcx> {
     base: PlaceBase,
     projection: Vec<PlaceElem<'tcx>>,
 }
@@ -249,7 +249,7 @@ fn strip_prefix<'a, 'tcx>(
 }
 
 impl<'tcx> PlaceBuilder<'tcx> {
-    pub(in crate::build) fn to_place(&self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> {
+    pub(in crate::builder) fn to_place(&self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> {
         self.try_to_place(cx).unwrap_or_else(|| match self.base {
             PlaceBase::Local(local) => span_bug!(
                 cx.local_decls[local].source_info.span,
@@ -265,7 +265,7 @@ impl<'tcx> PlaceBuilder<'tcx> {
     }
 
     /// Creates a `Place` or returns `None` if an upvar cannot be resolved
-    pub(in crate::build) fn try_to_place(&self, cx: &Builder<'_, 'tcx>) -> Option<Place<'tcx>> {
+    pub(in crate::builder) fn try_to_place(&self, cx: &Builder<'_, 'tcx>) -> Option<Place<'tcx>> {
         let resolved = self.resolve_upvar(cx);
         let builder = resolved.as_ref().unwrap_or(self);
         let PlaceBase::Local(local) = builder.base else { return None };
@@ -283,7 +283,7 @@ impl<'tcx> PlaceBuilder<'tcx> {
     /// not captured. This can happen because the final mir that will be
     /// generated doesn't require a read for this place. Failures will only
     /// happen inside closures.
-    pub(in crate::build) fn resolve_upvar(
+    pub(in crate::builder) fn resolve_upvar(
         &self,
         cx: &Builder<'_, 'tcx>,
     ) -> Option<PlaceBuilder<'tcx>> {
@@ -647,13 +647,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         match place_ty.kind() {
             ty::Array(_elem_ty, len_const) => {
-                // We know how long an array is, so just use that as a constant
-                // directly -- no locals needed. We do need one statement so
-                // that borrow- and initialization-checking consider it used,
-                // though. FIXME: Do we really *need* to count this as a use?
-                // Could partial array tracking work off something else instead?
-                self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place);
-                let const_ = Const::from_ty_const(*len_const, usize_ty, self.tcx);
+                let ty_const = if let Some((_, len_ty)) = len_const.try_to_valtree()
+                    && len_ty != self.tcx.types.usize
+                {
+                    // Bad const generics can give us a constant from the type that's
+                    // not actually a `usize`, so in that case give an error instead.
+                    // FIXME: It'd be nice if the type checker made sure this wasn't
+                    // possible, instead.
+                    let err = self.tcx.dcx().span_delayed_bug(
+                        span,
+                        format!(
+                            "Array length should have already been a type error, as it's {len_ty:?}"
+                        ),
+                    );
+                    ty::Const::new_error(self.tcx, err)
+                } else {
+                    // We know how long an array is, so just use that as a constant
+                    // directly -- no locals needed. We do need one statement so
+                    // that borrow- and initialization-checking consider it used,
+                    // though. FIXME: Do we really *need* to count this as a use?
+                    // Could partial array tracking work off something else instead?
+                    self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place);
+                    *len_const
+                };
+
+                let const_ = Const::from_ty_const(ty_const, usize_ty, self.tcx);
                 Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_ }))
             }
             ty::Slice(_elem_ty) => {
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
index c66af118453..9961c2488ef 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
@@ -16,9 +16,9 @@ use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, Span};
 use tracing::debug;
 
-use crate::build::expr::as_place::PlaceBase;
-use crate::build::expr::category::{Category, RvalueFunc};
-use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
+use crate::builder::expr::as_place::PlaceBase;
+use crate::builder::expr::category::{Category, RvalueFunc};
+use crate::builder::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Returns an rvalue suitable for use until the end of the current
diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs
index 466f67b1ba4..5e3a24e18fb 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs
@@ -7,8 +7,8 @@ use rustc_middle::mir::*;
 use rustc_middle::thir::*;
 use tracing::{debug, instrument};
 
-use crate::build::scope::DropKind;
-use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::builder::scope::DropKind;
+use crate::builder::{BlockAnd, BlockAndExtension, Builder};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Compile `expr` into a fresh temporary. This is used when building
diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs
index e0349e3e3f6..e0349e3e3f6 100644
--- a/compiler/rustc_mir_build/src/build/expr/category.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/category.rs
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs
index a3d5376dcd4..88f63d4e22c 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/into.rs
@@ -11,9 +11,9 @@ use rustc_middle::ty::CanonicalUserTypeAnnotation;
 use rustc_span::source_map::Spanned;
 use tracing::{debug, instrument};
 
-use crate::build::expr::category::{Category, RvalueFunc};
-use crate::build::matches::DeclareLetBindings;
-use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
+use crate::builder::expr::category::{Category, RvalueFunc};
+use crate::builder::matches::DeclareLetBindings;
+use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Compile `expr`, storing the result into `destination`, which
@@ -367,14 +367,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             .collect()
                     }
                     AdtExprBase::DefaultFields(field_types) => {
-                        itertools::zip_eq(field_names, &**field_types)
-                            .map(|(n, ty)| match fields_map.get(&n) {
+                        itertools::zip_eq(field_names, field_types)
+                            .map(|(n, &ty)| match fields_map.get(&n) {
                                 Some(v) => v.clone(),
                                 None => match variant.fields[n].value {
                                     Some(def) => {
-                                        let value = Const::from_unevaluated(this.tcx, def)
-                                            .instantiate(this.tcx, args);
-                                        this.literal_operand(expr_span, value)
+                                        let value = Const::Unevaluated(
+                                            UnevaluatedConst::new(def, args),
+                                            ty,
+                                        );
+                                        Operand::Constant(Box::new(ConstOperand {
+                                            span: expr_span,
+                                            user_ty: None,
+                                            const_: value,
+                                        }))
                                     }
                                     None => {
                                         let name = variant.fields[n].name;
diff --git a/compiler/rustc_mir_build/src/build/expr/mod.rs b/compiler/rustc_mir_build/src/builder/expr/mod.rs
index 3de43a3370f..3de43a3370f 100644
--- a/compiler/rustc_mir_build/src/build/expr/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/mod.rs
diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
index 15ee6dd014c..4ae3536d9c2 100644
--- a/compiler/rustc_mir_build/src/build/expr/stmt.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
@@ -5,8 +5,8 @@ use rustc_middle::thir::*;
 use rustc_span::source_map::Spanned;
 use tracing::debug;
 
-use crate::build::scope::BreakableTarget;
-use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
+use crate::builder::scope::BreakableTarget;
+use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Builds a block of MIR statements to evaluate the THIR `expr`.
diff --git a/compiler/rustc_mir_build/src/build/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
index 33fbd7b1a3f..9d59ffc88ba 100644
--- a/compiler/rustc_mir_build/src/build/matches/match_pair.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
@@ -2,9 +2,9 @@ use rustc_middle::mir::*;
 use rustc_middle::thir::{self, *};
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
 
-use crate::build::Builder;
-use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
-use crate::build::matches::{FlatPat, MatchPairTree, TestCase};
+use crate::builder::Builder;
+use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
+use crate::builder::matches::{FlatPat, MatchPairTree, TestCase};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Builds and returns [`MatchPairTree`] subtrees, one for each pattern in
@@ -86,7 +86,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> {
     /// Recursively builds a match pair tree for the given pattern and its
     /// subpatterns.
-    pub(in crate::build) fn for_pattern(
+    pub(in crate::builder) fn for_pattern(
         mut place_builder: PlaceBuilder<'tcx>,
         pattern: &'pat Pat<'tcx>,
         cx: &mut Builder<'_, 'tcx>,
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs
index 5791460a6b1..b944d13fb0d 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs
@@ -14,14 +14,13 @@ use rustc_middle::middle::region;
 use rustc_middle::mir::{self, *};
 use rustc_middle::thir::{self, *};
 use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
-use rustc_span::symbol::Symbol;
-use rustc_span::{BytePos, Pos, Span};
+use rustc_span::{BytePos, Pos, Span, Symbol};
 use tracing::{debug, instrument};
 
-use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
-use crate::build::expr::as_place::PlaceBuilder;
-use crate::build::scope::DropKind;
-use crate::build::{
+use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard};
+use crate::builder::expr::as_place::PlaceBuilder;
+use crate::builder::scope::DropKind;
+use crate::builder::{
     BlockAnd, BlockAndExtension, Builder, GuardFrame, GuardFrameLocal, LocalsForNode,
 };
 
diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/builder/matches/simplify.rs
index 5b402604395..ebaed1e431b 100644
--- a/compiler/rustc_mir_build/src/build/matches/simplify.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/simplify.rs
@@ -16,8 +16,8 @@ use std::mem;
 
 use tracing::{debug, instrument};
 
-use crate::build::Builder;
-use crate::build::matches::{MatchPairTree, PatternExtraData, TestCase};
+use crate::builder::Builder;
+use crate::builder::matches::{MatchPairTree, PatternExtraData, TestCase};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Simplify a list of match pairs so they all require a test. Stores relevant bindings and
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs
index 4f7bbc4ce3e..8cca84d7fcc 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/test.rs
@@ -16,12 +16,11 @@ use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::DefId;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Symbol, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 use tracing::{debug, instrument};
 
-use crate::build::Builder;
-use crate::build::matches::{Candidate, MatchPairTree, Test, TestBranch, TestCase, TestKind};
+use crate::builder::Builder;
+use crate::builder::matches::{Candidate, MatchPairTree, Test, TestBranch, TestCase, TestKind};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Identifies what test is needed to decide if `match_pair` is applicable.
diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs
index 555684ded81..1bd399e511b 100644
--- a/compiler/rustc_mir_build/src/build/matches/util.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/util.rs
@@ -4,9 +4,9 @@ use rustc_middle::ty::Ty;
 use rustc_span::Span;
 use tracing::debug;
 
-use crate::build::Builder;
-use crate::build::expr::as_place::PlaceBase;
-use crate::build::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestCase};
+use crate::builder::Builder;
+use crate::builder::expr::as_place::PlaceBase;
+use crate::builder::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestCase};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Creates a false edge to `imaginary_target` and a real edge to
diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/builder/misc.rs
index a14dcad6573..a53ae05e84f 100644
--- a/compiler/rustc_mir_build/src/build/misc.rs
+++ b/compiler/rustc_mir_build/src/builder/misc.rs
@@ -7,7 +7,7 @@ use rustc_span::Span;
 use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::debug;
 
-use crate::build::Builder;
+use crate::builder::Builder;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Adds a new temporary value of type `ty` storing the result of
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index f43c29d8f5d..fdd951c8899 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -1,3 +1,8 @@
+//! This module used to be named `build`, but that was causing GitHub's
+//! "Go to file" feature to silently ignore all files in the module, probably
+//! because it assumes that "build" is a build-output directory.
+//! See <https://github.com/rust-lang/rust/pull/134365>.
+
 use itertools::Itertools;
 use rustc_abi::{ExternAbi, FieldIdx};
 use rustc_apfloat::Float;
@@ -19,12 +24,11 @@ use rustc_middle::query::TyCtxtAt;
 use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir};
 use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode};
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 use super::lints;
-use crate::build::expr::as_place::PlaceBuilder;
-use crate::build::scope::DropKind;
+use crate::builder::expr::as_place::PlaceBuilder;
+use crate::builder::scope::DropKind;
 
 pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs
index 636e47b7ad2..882e29de46d 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/builder/scope.rs
@@ -95,7 +95,7 @@ use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument};
 
-use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
+use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
 
 #[derive(Debug)]
 pub(crate) struct Scopes<'tcx> {
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index f37b3f977fa..f7071eb139f 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -4,7 +4,7 @@ use std::ops::Bound;
 
 use rustc_errors::DiagArgValue;
 use rustc_hir::def::DefKind;
-use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, Safety};
+use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability};
 use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
 use rustc_middle::mir::BorrowKind;
 use rustc_middle::span_bug;
@@ -15,10 +15,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::lint::Level;
 use rustc_session::lint::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
 use rustc_span::def_id::{DefId, LocalDefId};
-use rustc_span::symbol::Symbol;
-use rustc_span::{Span, sym};
+use rustc_span::{Span, Symbol, sym};
 
-use crate::build::ExprCategory;
+use crate::builder::ExprCategory;
 use crate::errors::*;
 
 struct UnsafetyVisitor<'a, 'tcx> {
@@ -342,7 +341,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
             PatKind::Leaf { subpatterns, .. } => {
                 if let ty::Adt(adt_def, ..) = pat.ty.kind() {
                     for pat in subpatterns {
-                        if adt_def.non_enum_variant().fields[pat.field].safety == Safety::Unsafe {
+                        if adt_def.non_enum_variant().fields[pat.field].safety.is_unsafe() {
                             self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
                         }
                     }
@@ -367,7 +366,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
             PatKind::Variant { adt_def, args: _, variant_index, subpatterns } => {
                 for pat in subpatterns {
                     let field = &pat.field;
-                    if adt_def.variant(*variant_index).fields[*field].safety == Safety::Unsafe {
+                    if adt_def.variant(*variant_index).fields[*field].safety.is_unsafe() {
                         self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
                     }
                 }
@@ -479,7 +478,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 return; // don't visit the whole expression
             }
             ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
-                if self.thir[fun].ty.fn_sig(self.tcx).safety() == hir::Safety::Unsafe {
+                if self.thir[fun].ty.fn_sig(self.tcx).safety().is_unsafe() {
                     let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
                         Some(*func_id)
                     } else {
@@ -623,7 +622,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
             ExprKind::Field { lhs, variant_index, name } => {
                 let lhs = &self.thir[lhs];
                 if let ty::Adt(adt_def, _) = lhs.ty.kind() {
-                    if adt_def.variant(variant_index).fields[name].safety == Safety::Unsafe {
+                    if adt_def.variant(variant_index).fields[name].safety.is_unsafe() {
                         self.requires_unsafe(expr.span, UseOfUnsafeField);
                     } else if adt_def.is_union() {
                         if let Some(assigned_ty) = self.assignment_info {
@@ -1112,11 +1111,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
 
     let hir_id = tcx.local_def_id_to_hir_id(def);
     let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
-        if fn_sig.header.safety == hir::Safety::Unsafe {
-            SafetyContext::UnsafeFn
-        } else {
-            SafetyContext::Safe
-        }
+        if fn_sig.header.safety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe }
     });
     let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
     let mut warnings = Vec::new();
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 3632da943e1..e77d2496168 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -7,8 +7,7 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{self, Ty};
 use rustc_pattern_analysis::errors::Uncovered;
 use rustc_pattern_analysis::rustc::RustcPatCtxt;
-use rustc_span::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::{Span, Symbol};
 
 use crate::fluent_generated as fluent;
 
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 833e5019865..467725841dc 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -11,7 +11,10 @@
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
-mod build;
+// The `builder` module used to be named `build`, but that was causing GitHub's
+// "Go to file" feature to silently ignore all files in the module, probably
+// because it assumes that "build" is a build-output directory. See #134365.
+mod builder;
 mod check_tail_calls;
 mod check_unsafety;
 mod errors;
@@ -25,9 +28,9 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 pub fn provide(providers: &mut Providers) {
     providers.check_match = thir::pattern::check_match;
     providers.lit_to_const = thir::constant::lit_to_const;
-    providers.hooks.build_mir = build::mir_build;
+    providers.hooks.build_mir = builder::mir_build;
     providers.closure_saved_names_of_captured_variables =
-        build::closure_saved_names_of_captured_variables;
+        builder::closure_saved_names_of_captured_variables;
     providers.check_unsafety = check_unsafety::check_unsafety;
     providers.check_tail_calls = check_tail_calls::check_tail_calls;
     providers.thir_body = thir::cx::thir_body;
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index 30b6718683b..ce1c635d1b9 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -5,7 +5,7 @@ use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
 use rustc_middle::ty::{self, ScalarInt, TyCtxt, TypeVisitableExt as _};
 use tracing::trace;
 
-use crate::build::parse_float_into_scalar;
+use crate::builder::parse_float_into_scalar;
 
 pub(crate) fn lit_to_const<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 6770e562d50..ae49b266153 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -25,6 +25,14 @@ use crate::thir::cx::Cx;
 use crate::thir::util::UserAnnotatedTyHelpers;
 
 impl<'tcx> Cx<'tcx> {
+    /// Create a THIR expression for the given HIR expression. This expands all
+    /// adjustments and directly adds the type information from the
+    /// `typeck_results`. See the [dev-guide] for more details.
+    ///
+    /// (The term "mirror" in this case does not refer to "flipped" or
+    /// "reversed".)
+    ///
+    /// [dev-guide]: https://rustc-dev-guide.rust-lang.org/thir.html
     pub(crate) fn mirror_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> ExprId {
         // `mirror_expr` is recursing very deep. Make sure the stack doesn't overflow.
         ensure_sufficient_stack(|| self.mirror_expr_inner(expr))
@@ -1189,7 +1197,7 @@ impl<'tcx> Cx<'tcx> {
             .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id);
         let var_ty = place.base_ty;
 
-        // The result of capture analysis in `rustc_hir_analysis/check/upvar.rs`represents a captured path
+        // The result of capture analysis in `rustc_hir_typeck/src/upvar.rs` represents a captured path
         // as it's seen for use within the closure and not at the time of closure creation.
         //
         // That is we see expect to see it start from a captured upvar and not something that is local
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index 5b2b128e035..60c97710c7f 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -16,7 +16,7 @@ use rustc_middle::mir::{
 };
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 use tracing::debug;
 use {rustc_ast as ast, rustc_graphviz as dot};
 
diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
index 85cf8ca2104..399141aa921 100644
--- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs
+++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
@@ -2,8 +2,7 @@ use rustc_ast::MetaItem;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{self, Body, Local, Location};
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use tracing::{debug, info};
 
 use crate::errors::{
diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml
index 4b648d21084..2f233f787f0 100644
--- a/compiler/rustc_mir_transform/Cargo.toml
+++ b/compiler/rustc_mir_transform/Cargo.toml
@@ -10,7 +10,7 @@ itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_const_eval = { path = "../rustc_const_eval" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 31d5245fb5c..a3320f99cc3 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -73,9 +73,8 @@ use rustc_mir_dataflow::impls::{
     always_storage_live_locals,
 };
 use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor};
-use rustc_span::Span;
 use rustc_span::def_id::{DefId, LocalDefId};
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 use rustc_target::spec::PanicStrategy;
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::infer::TyCtxtInferExt as _;
diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
index ef61866e902..9f5bb015fa3 100644
--- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
@@ -78,7 +78,7 @@ use rustc_middle::hir::place::{Projection, ProjectionKind};
 use rustc_middle::mir::visit::MutVisitor;
 use rustc_middle::mir::{self, dump_mir};
 use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt};
-use rustc_span::symbol::kw;
+use rustc_span::kw;
 
 pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
index 589be81558c..e1f1dd83f0d 100644
--- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs
+++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
@@ -1,4 +1,4 @@
-use rustc_attr::InlineAttr;
+use rustc_attr_parsing::InlineAttr;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::mir::visit::Visitor;
diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs
index 2945fc6f3d5..fb21bf9977f 100644
--- a/compiler/rustc_mir_transform/src/function_item_references.rs
+++ b/compiler/rustc_mir_transform/src/function_item_references.rs
@@ -5,9 +5,8 @@ use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt};
 use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
-use rustc_span::Span;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 
 use crate::errors;
 
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 79c38b50459..f0acbaf56b6 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -4,7 +4,7 @@ use std::iter;
 use std::ops::{Range, RangeFrom};
 
 use rustc_abi::{ExternAbi, FieldIdx};
-use rustc_attr::InlineAttr;
+use rustc_attr_parsing::InlineAttr;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_index::Idx;
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index adc3374df2e..1a65affe812 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -7,8 +7,7 @@ use rustc_middle::bug;
 use rustc_middle::mir::*;
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, layout};
-use rustc_span::symbol::Symbol;
-use rustc_span::{DUMMY_SP, sym};
+use rustc_span::{DUMMY_SP, Symbol, sym};
 
 use crate::simplify::simplify_duplicate_switch_targets;
 use crate::take_array;
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index 6d635606687..942c6144ea6 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -3,7 +3,7 @@
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 
 use crate::take_array;
 
diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml
index e18441ea7fc..9bdaeb015cd 100644
--- a/compiler/rustc_monomorphize/Cargo.toml
+++ b/compiler/rustc_monomorphize/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 rustc_abi = { path = "../rustc_abi" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 480d82c1a38..05d34715314 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -232,8 +232,7 @@ use rustc_middle::{bug, span_bug};
 use rustc_session::Limit;
 use rustc_session::config::EntryFnType;
 use rustc_span::source_map::{Spanned, dummy_spanned, respan};
-use rustc_span::symbol::sym;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, sym};
 use tracing::{debug, instrument, trace};
 
 use crate::errors::{self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit};
diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs
index 438d49fd7fb..02b9397f9a9 100644
--- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs
+++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs
@@ -6,9 +6,8 @@ use rustc_middle::mir::{self, Location, traversal};
 use rustc_middle::ty::{self, AssocKind, Instance, Ty, TyCtxt, TypeFoldable};
 use rustc_session::Limit;
 use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
-use rustc_span::Span;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Ident, sym};
+use rustc_span::{Ident, Span, sym};
 use tracing::{debug, trace};
 
 use crate::errors::LargeAssignmentsLint;
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index dabce72650a..7b179663430 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -117,7 +117,7 @@ use rustc_middle::ty::{self, InstanceKind, TyCtxt};
 use rustc_middle::util::Providers;
 use rustc_session::CodegenUnits;
 use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath};
-use rustc_span::symbol::Symbol;
+use rustc_span::Symbol;
 use rustc_target::spec::SymbolVisibility;
 use tracing::debug;
 
@@ -833,7 +833,8 @@ fn mono_item_visibility<'tcx>(
         return if is_generic
             && (always_export_generics
                 || (can_export_generics
-                    && tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never))
+                    && tcx.codegen_fn_attrs(def_id).inline
+                        == rustc_attr_parsing::InlineAttr::Never))
         {
             // If it is an upstream monomorphization and we export generics, we must make
             // it available to downstream crates.
@@ -847,7 +848,7 @@ fn mono_item_visibility<'tcx>(
     if is_generic {
         if always_export_generics
             || (can_export_generics
-                && tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never)
+                && tcx.codegen_fn_attrs(def_id).inline == rustc_attr_parsing::InlineAttr::Never)
         {
             if tcx.is_unreachable_local_definition(def_id) {
                 // This instance cannot be used from another crate.
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 4c4e03cdfa3..f78d9dc2bfc 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -13,8 +13,7 @@ use rustc_errors::{
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::edition::{Edition, LATEST_STABLE_EDITION};
-use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 use crate::fluent_generated as fluent;
 use crate::parser::{ForbiddenLetReason, TokenDescription};
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 443ddfc94ec..792e2cc26ef 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -14,8 +14,7 @@ use rustc_session::lint::builtin::{
     TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
 };
 use rustc_session::parse::ParseSess;
-use rustc_span::symbol::Symbol;
-use rustc_span::{BytePos, Pos, Span};
+use rustc_span::{BytePos, Pos, Span, Symbol};
 use tracing::debug;
 
 use crate::lexer::diagnostics::TokenTreeDiagInfo;
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index 42eef27803e..ef8d0f96b61 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -1,8 +1,7 @@
 //! Characters and their corresponding confusables were collected from
 //! <https://www.unicode.org/Public/security/10.0.0/confusables.txt>
 
-use rustc_span::symbol::kw;
-use rustc_span::{BytePos, Pos, Span};
+use rustc_span::{BytePos, Pos, Span, kw};
 
 use super::Lexer;
 use crate::errors::TokenSubstitution;
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index b6fa2099588..9da4ab5a788 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -1,10 +1,8 @@
-use rustc_ast as ast;
-use rustc_ast::attr;
 use rustc_ast::token::{self, Delimiter};
+use rustc_ast::{self as ast, Attribute, attr};
 use rustc_errors::codes::*;
 use rustc_errors::{Diag, PResult};
-use rustc_span::symbol::kw;
-use rustc_span::{BytePos, Span};
+use rustc_span::{BytePos, Span, kw};
 use thin_vec::ThinVec;
 use tracing::debug;
 
@@ -48,7 +46,7 @@ impl<'a> Parser<'a> {
         let start_pos = self.num_bump_calls;
         loop {
             let attr = if self.check(&token::Pound) {
-                let prev_outer_attr_sp = outer_attrs.last().map(|attr| attr.span);
+                let prev_outer_attr_sp = outer_attrs.last().map(|attr: &Attribute| attr.span);
 
                 let inner_error_reason = if just_parsed_doc_comment {
                     Some(InnerAttrForbiddenReason::AfterOuterDocComment {
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index e5edf605d82..d1a725e729a 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -22,8 +22,8 @@ use rustc_errors::{
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{AllKeywords, Ident, kw, sym};
-use rustc_span::{BytePos, DUMMY_SP, Span, SpanSnippetError, Symbol};
+use rustc_span::symbol::AllKeywords;
+use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 use tracing::{debug, trace};
 
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index a2136399b0c..5a377464223 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -26,8 +26,7 @@ use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error};
 use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
 use rustc_span::source_map::{self, Spanned};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{BytePos, ErrorGuaranteed, Pos, Span};
+use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Symbol, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 use tracing::instrument;
 
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 76ecb77d750..65e390c9a82 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -4,8 +4,7 @@ use rustc_ast::{
     WhereClause, token,
 };
 use rustc_errors::{Applicability, PResult};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, kw};
+use rustc_span::{Ident, Span, kw};
 use thin_vec::ThinVec;
 
 use super::{ForceCollect, Parser, Trailing, UsePreAttrPos};
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index e27fc963eb9..e6a8eda42e8 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -13,8 +13,7 @@ use rustc_errors::codes::*;
 use rustc_errors::{Applicability, PResult, StashKey, struct_span_code_err};
 use rustc_span::edit_distance::edit_distance;
 use rustc_span::edition::Edition;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, source_map};
+use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, source_map, sym};
 use thin_vec::{ThinVec, thin_vec};
 use tracing::debug;
 
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 37556c064d8..0d220e74c0e 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -29,9 +29,9 @@ use rustc_ast::tokenstream::{
 };
 use rustc_ast::util::case::Case;
 use rustc_ast::{
-    self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind,
-    DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered,
-    Safety, StrLit, Visibility, VisibilityKind,
+    self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID,
+    DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit,
+    Visibility, VisibilityKind,
 };
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
@@ -39,8 +39,7 @@ use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, Diag, FatalError, MultiSpan, PResult};
 use rustc_index::interval::IntervalSet;
 use rustc_session::parse::ParseSess;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use thin_vec::ThinVec;
 use tracing::debug;
 
@@ -1376,7 +1375,7 @@ impl<'a> Parser<'a> {
             AttrArgs::Delimited(args)
         } else if self.eat(&token::Eq) {
             let eq_span = self.prev_token.span;
-            AttrArgs::Eq { eq_span, value: AttrArgsEq::Ast(self.parse_expr_force_collect()?) }
+            AttrArgs::Eq { eq_span, expr: self.parse_expr_force_collect()? }
         } else {
             AttrArgs::Empty
         })
diff --git a/compiler/rustc_parse/src/parser/mut_visit/tests.rs b/compiler/rustc_parse/src/parser/mut_visit/tests.rs
index b82c295732d..46c678c3902 100644
--- a/compiler/rustc_parse/src/parser/mut_visit/tests.rs
+++ b/compiler/rustc_parse/src/parser/mut_visit/tests.rs
@@ -1,8 +1,7 @@
 use rustc_ast as ast;
 use rustc_ast::mut_visit::MutVisitor;
 use rustc_ast_pretty::pprust;
-use rustc_span::create_default_session_globals_then;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, create_default_session_globals_then};
 
 use crate::parser::tests::{matches_codepattern, string_to_crate};
 
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 752a52b382b..67cabb757e9 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -9,7 +9,7 @@ use rustc_ast::token::{
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::PResult;
-use rustc_span::symbol::{Ident, kw};
+use rustc_span::{Ident, kw};
 
 use crate::errors::UnexpectedNonterminal;
 use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 4cda887a02b..76101914bbd 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -13,8 +13,7 @@ use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::{Spanned, respan};
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{BytePos, ErrorGuaranteed, Span};
+use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos};
@@ -1371,10 +1370,10 @@ impl<'a> Parser<'a> {
         self.bump();
         let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| {
             e.span_label(path.span, "while parsing the fields for this pattern");
-            e.emit();
+            let guar = e.emit();
             self.recover_stmt();
             // When recovering, pretend we had `Foo { .. }`, to avoid cascading errors.
-            (ThinVec::new(), PatFieldsRest::Rest)
+            (ThinVec::new(), PatFieldsRest::Recovered(guar))
         });
         self.bump();
         Ok(PatKind::Struct(qself, path, fields, etc))
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 6a7029a8f1c..f2f0c6dfad5 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -9,8 +9,7 @@ use rustc_ast::{
     Path, PathSegment, QSelf,
 };
 use rustc_errors::{Applicability, Diag, PResult};
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{BytePos, Span};
+use rustc_span::{BytePos, Ident, Span, kw, sym};
 use thin_vec::ThinVec;
 use tracing::debug;
 
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 5fa2e01fc86..0f32e396273 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -12,8 +12,7 @@ use rustc_ast::{
     LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind,
 };
 use rustc_errors::{Applicability, Diag, PResult};
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{BytePos, ErrorGuaranteed, Span};
+use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use super::attr::InnerAttrForbiddenReason;
diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs
index 1813960dad0..ca9e78be201 100644
--- a/compiler/rustc_parse/src/parser/tests.rs
+++ b/compiler/rustc_parse/src/parser/tests.rs
@@ -18,8 +18,9 @@ use rustc_errors::emitter::{HumanEmitter, OutputTheme};
 use rustc_errors::{DiagCtxt, MultiSpan, PResult};
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::{FilePathMapping, SourceMap};
-use rustc_span::symbol::{Symbol, kw, sym};
-use rustc_span::{BytePos, FileName, Pos, Span, create_default_session_globals_then};
+use rustc_span::{
+    BytePos, FileName, Pos, Span, Symbol, create_default_session_globals_then, kw, sym,
+};
 use termcolor::WriteColor;
 
 use crate::parser::{ForceCollect, Parser};
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index f696074e66a..6e720db4edf 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -8,8 +8,7 @@ use rustc_ast::{
     TyKind, UnsafeBinderTy,
 };
 use rustc_errors::{Applicability, PResult};
-use rustc_span::symbol::{Ident, kw, sym};
-use rustc_span::{ErrorGuaranteed, Span};
+use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 
 use super::{Parser, PathStyle, SeqSep, TokenType, Trailing};
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index aab3f10bc66..8b6b37c0f8f 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -3,8 +3,7 @@
 use rustc_ast::token::Delimiter;
 use rustc_ast::tokenstream::DelimSpan;
 use rustc_ast::{
-    self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind,
-    Safety,
+    self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety,
 };
 use rustc_errors::{Applicability, FatalError, PResult};
 use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
@@ -70,7 +69,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met
                     parse_in(psess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?;
                 MetaItemKind::List(nmis)
             }
-            AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => {
+            AttrArgs::Eq { expr, .. } => {
                 if let ast::ExprKind::Lit(token_lit) = expr.kind {
                     let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span);
                     let res = match res {
@@ -116,9 +115,6 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met
                     return Err(err);
                 }
             }
-            AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => {
-                MetaItemKind::NameValue(lit.clone())
-            }
         },
     })
 }
diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml
index ed5991459ac..2b8a3b9ce23 100644
--- a/compiler/rustc_passes/Cargo.toml
+++ b/compiler/rustc_passes/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_expand = { path = "../rustc_expand" }
@@ -16,7 +16,6 @@ rustc_feature = { path = "../rustc_feature" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
-rustc_lexer = { path = "../rustc_lexer" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_privacy = { path = "../rustc_privacy" }
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 7a0a518bb51..fd133ba878e 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -211,8 +211,9 @@ passes_doc_invalid =
 passes_doc_keyword_empty_mod =
     `#[doc(keyword = "...")]` should be used on empty modules
 
-passes_doc_keyword_invalid_ident =
-    `{$doc_keyword}` is not a valid identifier
+passes_doc_keyword_not_keyword =
+    nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]`
+    .help = only existing keywords are allowed in core/std
 
 passes_doc_keyword_not_mod =
     `#[doc(keyword = "...")]` should be used on modules
diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs
index 4db8584b884..75a1bb39a74 100644
--- a/compiler/rustc_passes/src/abi_test.rs
+++ b/compiler/rustc_passes/src/abi_test.rs
@@ -1,11 +1,11 @@
-use rustc_ast::Attribute;
+use rustc_hir::Attribute;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::span_bug;
 use rustc_middle::ty::layout::{FnAbiError, LayoutError};
 use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt};
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 use rustc_target::abi::call::FnAbi;
 
 use super::layout_test::ensure_wf;
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 074fe77324f..ce7947ad3ec 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -7,17 +7,15 @@
 use std::cell::Cell;
 use std::collections::hash_map::Entry;
 
-use rustc_ast::{
-    AttrKind, AttrStyle, Attribute, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast,
-};
+use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
 use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
 use rustc_hir::def_id::LocalModDefId;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{
-    self as hir, self, AssocItemKind, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId,
-    Item, ItemKind, MethodKind, Safety, Target, TraitItem,
+    self as hir, self, AssocItemKind, AttrKind, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig,
+    ForeignItem, HirId, Item, ItemKind, MethodKind, Safety, Target, TraitItem,
 };
 use rustc_macros::LintDiagnostic;
 use rustc_middle::hir::nested_filter;
@@ -32,8 +30,7 @@ use rustc_session::lint::builtin::{
     UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
 };
 use rustc_session::parse::feature_err;
-use rustc_span::symbol::{Symbol, kw, sym};
-use rustc_span::{BytePos, DUMMY_SP, Span};
+use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, kw, sym};
 use rustc_target::abi::Size;
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
@@ -715,7 +712,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         attrs: &[Attribute],
     ) {
         match target {
-            Target::Fn => {
+            Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
+            | Target::Fn => {
                 // `#[target_feature]` is not allowed in lang items.
                 if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
                     // Calling functions with `#[target_feature]` is
@@ -732,7 +730,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     });
                 }
             }
-            Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {}
             // FIXME: #[target_feature] was previously erroneously allowed on statements and some
             // crates used this, so only emit a warning.
             Target::Statement => {
@@ -914,6 +911,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     fn check_doc_keyword(&self, meta: &MetaItemInner, hir_id: HirId) {
+        fn is_doc_keyword(s: Symbol) -> bool {
+            // FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
+            // can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
+            // `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
+            s <= kw::Union || s == sym::SelfTy
+        }
+
         let doc_keyword = meta.value_str().unwrap_or(kw::Empty);
         if doc_keyword == kw::Empty {
             self.doc_attr_str_error(meta, "keyword");
@@ -935,10 +939,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 return;
             }
         }
-        if !rustc_lexer::is_ident(doc_keyword.as_str()) {
-            self.dcx().emit_err(errors::DocKeywordInvalidIdent {
+        if !is_doc_keyword(doc_keyword) {
+            self.dcx().emit_err(errors::DocKeywordNotKeyword {
                 span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
-                doc_keyword,
+                keyword: doc_keyword,
             });
         }
     }
@@ -1176,10 +1180,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         specified_inline: &mut Option<(bool, Span)>,
         aliases: &mut FxHashMap<String, Span>,
     ) {
-        if let Some(mi) = attr.meta()
-            && let Some(list) = mi.meta_item_list()
-        {
-            for meta in list {
+        if let Some(list) = attr.meta_item_list() {
+            for meta in &list {
                 if let Some(i_meta) = meta.meta_item() {
                     match i_meta.name_or_empty() {
                         sym::alias => {
@@ -1279,7 +1281,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                                             AttrStyle::Inner => "!",
                                             AttrStyle::Outer => "",
                                         },
-                                        sugg: (attr.meta().unwrap().span, applicability),
+                                        sugg: (attr.span, applicability),
                                     },
                                 );
                             } else if i_meta.has_name(sym::passes)
@@ -2141,10 +2143,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     fn check_confusables(&self, attr: &Attribute, target: Target) {
         match target {
             Target::Method(MethodKind::Inherent) => {
-                let Some(meta) = attr.meta() else {
-                    return;
-                };
-                let ast::MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else {
+                let Some(metas) = attr.meta_item_list() else {
                     return;
                 };
 
@@ -2602,7 +2601,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
 
                     if let AttrKind::Normal(ref p) = attr.kind {
                         tcx.dcx().try_steal_replace_and_emit_err(
-                            p.item.path.span,
+                            p.path.span,
                             StashKey::UndeterminedMacroResolution,
                             err,
                         );
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index ecf8d34ad84..c5dc8e68fe8 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -22,7 +22,7 @@ use rustc_middle::ty::{self, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
 use rustc_session::lint::builtin::DEAD_CODE;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 use crate::errors::{
     ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment,
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index 1c3d3bf3dea..7b02aecdfae 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -9,13 +9,12 @@
 //!
 //! * Compiler internal types like `Ty` and `TyCtxt`
 
-use rustc_ast as ast;
-use rustc_hir::OwnerId;
 use rustc_hir::diagnostic_items::DiagnosticItems;
+use rustc_hir::{Attribute, OwnerId};
 use rustc_middle::query::{LocalCrate, Providers};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 use crate::errors::DuplicateDiagnosticItemInCrate;
 
@@ -55,7 +54,7 @@ fn report_duplicate_item(
 }
 
 /// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.
-fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> {
+fn extract(attrs: &[Attribute]) -> Option<Symbol> {
     attrs.iter().find_map(|attr| {
         if attr.has_name(sym::rustc_diagnostic_item) { attr.value_str() } else { None }
     })
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index 690808e7527..4949a4316a7 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -8,8 +8,7 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::RemapFileNameExt;
 use rustc_session::config::{CrateType, EntryFnType, RemapPathScopeComponents, sigpipe};
-use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 use crate::errors::{
     AttrOnlyInFunctions, ExternMain, MultipleRustcMain, MultipleStartFunctions, NoMainErr,
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index fdc7e1bba2f..f71d5284052 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -216,18 +216,19 @@ pub(crate) struct DocKeywordEmptyMod {
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_doc_keyword_not_mod)]
-pub(crate) struct DocKeywordNotMod {
+#[diag(passes_doc_keyword_not_keyword)]
+#[help]
+pub(crate) struct DocKeywordNotKeyword {
     #[primary_span]
     pub span: Span,
+    pub keyword: Symbol,
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_doc_keyword_invalid_ident)]
-pub(crate) struct DocKeywordInvalidIdent {
+#[diag(passes_doc_keyword_not_mod)]
+pub(crate) struct DocKeywordNotMod {
     #[primary_span]
     pub span: Span,
-    pub doc_keyword: Symbol,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs
index 7ccbc7bdc57..f9cb8c9b927 100644
--- a/compiler/rustc_passes/src/input_stats.rs
+++ b/compiler/rustc_passes/src/input_stats.rs
@@ -22,6 +22,10 @@ impl NodeStats {
     fn new() -> NodeStats {
         NodeStats { count: 0, size: 0 }
     }
+
+    fn accum_size(&self) -> usize {
+        self.count * self.size
+    }
 }
 
 struct Node {
@@ -121,11 +125,9 @@ impl<'k> StatCollector<'k> {
         // We will soon sort, so the initial order does not matter.
         #[allow(rustc::potential_query_instability)]
         let mut nodes: Vec<_> = self.nodes.iter().collect();
-        nodes.sort_by_cached_key(|(label, node)| {
-            (node.stats.count * node.stats.size, label.to_owned())
-        });
+        nodes.sort_by_cached_key(|(label, node)| (node.stats.accum_size(), label.to_owned()));
 
-        let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum();
+        let total_size = nodes.iter().map(|(_, node)| node.stats.accum_size()).sum();
         let total_count = nodes.iter().map(|(_, node)| node.stats.count).sum();
 
         eprintln!("{prefix} {title}");
@@ -138,7 +140,7 @@ impl<'k> StatCollector<'k> {
         let percent = |m, n| (m * 100) as f64 / n as f64;
 
         for (label, node) in nodes {
-            let size = node.stats.count * node.stats.size;
+            let size = node.stats.accum_size();
             eprintln!(
                 "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}",
                 prefix,
@@ -152,10 +154,12 @@ impl<'k> StatCollector<'k> {
                 // We will soon sort, so the initial order does not matter.
                 #[allow(rustc::potential_query_instability)]
                 let mut subnodes: Vec<_> = node.subnodes.iter().collect();
-                subnodes.sort_by_key(|(_, subnode)| subnode.count * subnode.size);
+                subnodes.sort_by_cached_key(|(label, subnode)| {
+                    (subnode.accum_size(), label.to_owned())
+                });
 
                 for (label, subnode) in subnodes {
-                    let size = subnode.count * subnode.size;
+                    let size = subnode.accum_size();
                     eprintln!(
                         "{} - {:<18}{:>10} ({:4.1}%){:>14}",
                         prefix,
@@ -500,7 +504,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
         hir_visit::walk_assoc_item_constraint(self, constraint)
     }
 
-    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
+    fn visit_attribute(&mut self, attr: &'v hir::Attribute) {
         self.record("Attribute", None, attr);
     }
 
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 19f00aa3c1b..fe1140d893e 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -16,8 +16,7 @@ use rustc_hir::{LangItem, LanguageItems, MethodKind, Target};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
 use rustc_session::cstore::ExternCrate;
-use rustc_span::Span;
-use rustc_span::symbol::kw::Empty;
+use rustc_span::{Span, kw};
 
 use crate::errors::{
     DuplicateLangItem, IncorrectTarget, LangItemOnIncorrectTarget, UnknownLangItem,
@@ -99,7 +98,7 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
         {
             let lang_item_name = lang_item.name();
             let crate_name = self.tcx.crate_name(item_def_id.krate);
-            let mut dependency_of = Empty;
+            let mut dependency_of = kw::Empty;
             let is_local = item_def_id.is_local();
             let path = if is_local {
                 String::new()
@@ -113,8 +112,8 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
             };
 
             let first_defined_span = self.item_spans.get(&original_def_id).copied();
-            let mut orig_crate_name = Empty;
-            let mut orig_dependency_of = Empty;
+            let mut orig_crate_name = kw::Empty;
+            let mut orig_dependency_of = kw::Empty;
             let orig_is_local = original_def_id.is_local();
             let orig_path = if orig_is_local {
                 String::new()
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index bb90b5a1e31..1133cf93304 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -1,13 +1,12 @@
 use rustc_abi::{HasDataLayout, TargetDataLayout};
-use rustc_ast::Attribute;
+use rustc_hir::Attribute;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::span_bug;
 use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers};
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::Span;
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::infer::TyCtxtInferExt;
 use rustc_trait_selection::traits;
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 8a360c017ad..600c46eb3d0 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -4,15 +4,14 @@
 //! but are not declared in one single location (unlike lang features), which means we need to
 //! collect them instead.
 
-use rustc_ast::Attribute;
-use rustc_attr::VERSION_PLACEHOLDER;
+use rustc_attr_parsing::VERSION_PLACEHOLDER;
+use rustc_hir::Attribute;
 use rustc_hir::intravisit::Visitor;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
 use rustc_middle::query::{LocalCrate, Providers};
 use rustc_middle::ty::TyCtxt;
-use rustc_span::symbol::Symbol;
-use rustc_span::{Span, sym};
+use rustc_span::{Span, Symbol, sym};
 
 use crate::errors::{FeaturePreviouslyDeclared, FeatureStableTwice};
 
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 034f7308c4a..b85a987c641 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -96,8 +96,7 @@ use rustc_middle::query::Providers;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt};
 use rustc_session::lint;
-use rustc_span::symbol::{Symbol, kw, sym};
-use rustc_span::{BytePos, Span};
+use rustc_span::{BytePos, Span, Symbol, kw, sym};
 use tracing::{debug, instrument};
 
 use self::LiveNodeKind::*;
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index 766a9e1bdad..cad488bd71d 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -10,8 +10,7 @@ use rustc_middle::query::Providers;
 use rustc_middle::span_bug;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI;
-use rustc_span::Span;
-use rustc_span::symbol::sym;
+use rustc_span::{Span, sym};
 use rustc_target::spec::abi::Abi;
 
 use crate::errors::{
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 2809ad453ff..d3637dac6b7 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -4,9 +4,9 @@
 use std::mem::replace;
 use std::num::NonZero;
 
-use rustc_attr::{
+use rustc_attr_parsing::{
     self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince,
-    Unstable, UnstableReason, VERSION_PLACEHOLDER,
+    UnstableReason, VERSION_PLACEHOLDER,
 };
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
@@ -25,8 +25,7 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::lint;
 use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Span, Symbol, sym};
 use tracing::{debug, info};
 
 use crate::errors;
@@ -199,7 +198,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
             // this is *almost surely* an accident.
             if let (
                 &Some(DeprecatedSince::RustcVersion(dep_since)),
-                &attr::Stable { since: stab_since, .. },
+                &attr::StabilityLevel::Stable { since: stab_since, .. },
             ) = (&depr.as_ref().map(|(d, _)| d.since), &stab.level)
             {
                 match stab_since {
@@ -224,15 +223,17 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
 
             // Stable *language* features shouldn't be used as unstable library features.
             // (Not doing this for stable library features is checked by tidy.)
-            if let Stability { level: Unstable { .. }, feature } = stab {
+            if let Stability { level: StabilityLevel::Unstable { .. }, feature } = stab {
                 if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() {
                     self.tcx
                         .dcx()
                         .emit_err(errors::UnstableAttrForAlreadyStableFeature { span, item_sp });
                 }
             }
-            if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } =
-                stab
+            if let Stability {
+                level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. },
+                feature,
+            } = stab
             {
                 self.index.implications.insert(implied_by, feature);
             }
@@ -278,8 +279,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
 
         // Stable *language* features shouldn't be used as unstable library features.
         // (Not doing this for stable library features is checked by tidy.)
-        if let Some((ConstStability { level: Unstable { .. }, feature, .. }, const_span)) =
-            const_stab
+        if let Some((
+            ConstStability { level: StabilityLevel::Unstable { .. }, feature, .. },
+            const_span,
+        )) = const_stab
         {
             if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() {
                 self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature {
@@ -314,7 +317,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
         });
 
         if let Some(ConstStability {
-            level: Unstable { implied_by: Some(implied_by), .. },
+            level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. },
             feature,
             ..
         }) = const_stab
@@ -780,7 +783,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                     // error if all involved types and traits are stable, because
                     // it will have no effect.
                     // See: https://github.com/rust-lang/rust/issues/55436
-                    if let Some((Stability { level: attr::Unstable { .. }, .. }, span)) = stab {
+                    if let Some((
+                        Stability { level: attr::StabilityLevel::Unstable { .. }, .. },
+                        span,
+                    )) = stab
+                    {
                         let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
                         c.visit_ty(self_ty);
                         c.visit_trait_ref(t);
diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml
index f998e0ff154..eb48155919f 100644
--- a/compiler/rustc_privacy/Cargo.toml
+++ b/compiler/rustc_privacy/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 rustc_ast = { path = "../rustc_ast" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 3057f13e3a7..c50c9007a01 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -37,11 +37,10 @@ use rustc_middle::ty::{
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
-use rustc_span::Span;
 use rustc_span::hygiene::Transparency;
-use rustc_span::symbol::{Ident, kw, sym};
+use rustc_span::{Ident, Span, kw, sym};
 use tracing::debug;
-use {rustc_attr as attr, rustc_hir as hir};
+use {rustc_attr_parsing as attr, rustc_hir as hir};
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs
index ad4b194eb50..6b737ceb50a 100644
--- a/compiler/rustc_query_system/src/ich/hcx.rs
+++ b/compiler/rustc_query_system/src/ich/hcx.rs
@@ -6,8 +6,7 @@ use rustc_hir::definitions::DefPathHash;
 use rustc_session::Session;
 use rustc_session::cstore::Untracked;
 use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::Symbol;
-use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, SourceFile, Span, SpanData};
+use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, SourceFile, Span, SpanData, Symbol};
 
 use crate::ich;
 
diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs
index 5a72e80a0a5..480fd497728 100644
--- a/compiler/rustc_query_system/src/ich/impls_syntax.rs
+++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs
@@ -1,18 +1,17 @@
 //! This module contains `HashStable` implementations for various data types
-//! from `rustc_ast` in no particular order.
+//! from various crates in no particular order.
 
-use std::assert_matches::assert_matches;
-
-use rustc_ast as ast;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_hir as hir;
 use rustc_span::SourceFile;
 use smallvec::SmallVec;
 
 use crate::ich::StableHashingContext;
 
 impl<'ctx> rustc_target::HashStableContext for StableHashingContext<'ctx> {}
+impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> {}
 
-impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] {
+impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         if self.is_empty() {
             self.len().hash_stable(hcx, hasher);
@@ -20,7 +19,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] {
         }
 
         // Some attributes are always ignored during hashing.
-        let filtered: SmallVec<[&ast::Attribute; 8]> = self
+        let filtered: SmallVec<[&hir::Attribute; 8]> = self
             .iter()
             .filter(|attr| {
                 !attr.is_doc_comment()
@@ -35,30 +34,23 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] {
     }
 }
 
-impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> {
-    fn hash_attr(&mut self, attr: &ast::Attribute, hasher: &mut StableHasher) {
+impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> {
+    fn hash_attr(&mut self, attr: &hir::Attribute, hasher: &mut StableHasher) {
         // Make sure that these have been filtered out.
         debug_assert!(!attr.ident().is_some_and(|ident| self.is_ignored_attr(ident.name)));
         debug_assert!(!attr.is_doc_comment());
 
-        let ast::Attribute { kind, id: _, style, span } = attr;
-        if let ast::AttrKind::Normal(normal) = kind {
-            normal.item.hash_stable(self, hasher);
+        let hir::Attribute { kind, id: _, style, span } = attr;
+        if let hir::AttrKind::Normal(item) = kind {
+            item.hash_stable(self, hasher);
             style.hash_stable(self, hasher);
             span.hash_stable(self, hasher);
-            assert_matches!(
-                normal.tokens.as_ref(),
-                None,
-                "Tokens should have been removed during lowering!"
-            );
         } else {
             unreachable!();
         }
     }
 }
 
-impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> {}
-
 impl<'a> HashStable<StableHashingContext<'a>> for SourceFile {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         let SourceFile {
diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_query_system/src/ich/mod.rs
index 8f28b1d4c80..852d93b711f 100644
--- a/compiler/rustc_query_system/src/ich/mod.rs
+++ b/compiler/rustc_query_system/src/ich/mod.rs
@@ -1,6 +1,6 @@
 //! ICH - Incremental Compilation Hash
 
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 pub use self::hcx::StableHashingContext;
 
diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml
index b71853b871d..309227176d4 100644
--- a/compiler/rustc_resolve/Cargo.toml
+++ b/compiler/rustc_resolve/Cargo.toml
@@ -10,7 +10,7 @@ pulldown-cmark = { version = "0.11", features = ["html"], default-features = fal
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_expand = { path = "../rustc_expand" }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 924b8afa329..3fe1eed243f 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -12,7 +12,7 @@ use rustc_ast::{
     self as ast, AssocItem, AssocItemKind, Block, ForeignItem, ForeignItemKind, Impl, Item,
     ItemKind, MetaItemKind, NodeId, StmtKind,
 };
-use rustc_attr as attr;
+use rustc_attr_parsing as attr;
 use rustc_data_structures::sync::Lrc;
 use rustc_expand::base::ResolverExpand;
 use rustc_expand::expand::AstFragment;
@@ -22,9 +22,8 @@ use rustc_metadata::creader::LoadedMacro;
 use rustc_middle::metadata::ModChild;
 use rustc_middle::ty::Feed;
 use rustc_middle::{bug, ty};
-use rustc_span::Span;
 use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
+use rustc_span::{Ident, Span, Symbol, kw, sym};
 use tracing::debug;
 
 use crate::Namespace::{MacroNS, TypeNS, ValueNS};
@@ -634,7 +633,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             }
             ast::UseTreeKind::Glob => {
                 let kind = ImportKind::Glob {
-                    is_prelude: attr::contains_name(&item.attrs, sym::prelude_import),
+                    is_prelude: ast::attr::contains_name(&item.attrs, sym::prelude_import),
                     max_vis: Cell::new(None),
                     id,
                 };
@@ -777,7 +776,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
                     expansion.to_expn_id(),
                     item.span,
                     parent.no_implicit_prelude
-                        || attr::contains_name(&item.attrs, sym::no_implicit_prelude),
+                        || ast::attr::contains_name(&item.attrs, sym::no_implicit_prelude),
                 );
                 self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
 
@@ -835,7 +834,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
                     // If the structure is marked as non_exhaustive then lower the visibility
                     // to within the crate.
                     let mut ctor_vis = if vis.is_public()
-                        && attr::contains_name(&item.attrs, sym::non_exhaustive)
+                        && ast::attr::contains_name(&item.attrs, sym::non_exhaustive)
                     {
                         ty::Visibility::Restricted(CRATE_DEF_ID)
                     } else {
@@ -1176,11 +1175,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
     }
 
     fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
-        if attr::contains_name(&item.attrs, sym::proc_macro) {
+        if ast::attr::contains_name(&item.attrs, sym::proc_macro) {
             return Some((MacroKind::Bang, item.ident, item.span));
-        } else if attr::contains_name(&item.attrs, sym::proc_macro_attribute) {
+        } else if ast::attr::contains_name(&item.attrs, sym::proc_macro_attribute) {
             return Some((MacroKind::Attr, item.ident, item.span));
-        } else if let Some(attr) = attr::find_by_name(&item.attrs, sym::proc_macro_derive) {
+        } else if let Some(attr) = ast::attr::find_by_name(&item.attrs, sym::proc_macro_derive) {
             if let Some(meta_item_inner) =
                 attr.meta_item_list().and_then(|list| list.get(0).cloned())
             {
@@ -1233,7 +1232,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
         if macro_rules {
             let ident = ident.normalize_to_macros_2_0();
             self.r.macro_names.insert(ident);
-            let is_macro_export = attr::contains_name(&item.attrs, sym::macro_export);
+            let is_macro_export = ast::attr::contains_name(&item.attrs, sym::macro_export);
             let vis = if is_macro_export {
                 ty::Visibility::Public
             } else {
@@ -1507,7 +1506,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
 
         // If the variant is marked as non_exhaustive then lower the visibility to within the crate.
         let ctor_vis =
-            if vis.is_public() && attr::contains_name(&variant.attrs, sym::non_exhaustive) {
+            if vis.is_public() && ast::attr::contains_name(&variant.attrs, sym::non_exhaustive) {
                 ty::Visibility::Restricted(CRATE_DEF_ID)
             } else {
                 vis
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index e36055e8575..5a605df88c2 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -33,8 +33,7 @@ use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::{
     MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS, UNUSED_QUALIFICATIONS,
 };
-use rustc_span::symbol::{Ident, kw};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, kw};
 
 use crate::imports::{Import, ImportKind};
 use crate::{LexicalScopeBinding, NameBindingKind, Resolver, module_to_string};
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 66492842581..87024c487df 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -7,9 +7,8 @@ use rustc_expand::expand::AstFragment;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind};
 use rustc_hir::def_id::LocalDefId;
-use rustc_span::Span;
 use rustc_span::hygiene::LocalExpnId;
-use rustc_span::symbol::{Symbol, kw, sym};
+use rustc_span::{Span, Symbol, kw, sym};
 use tracing::debug;
 
 use crate::{ImplTraitContext, InvocationParent, Resolver};
@@ -356,6 +355,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
                 let kind = match self.impl_trait_context {
                     ImplTraitContext::Universal => DefKind::TyParam,
                     ImplTraitContext::Existential => DefKind::OpaqueTy,
+                    ImplTraitContext::InBinding => return visit::walk_ty(self, ty),
                 };
                 let id = self.create_def(*id, name, kind, ty.span);
                 match self.impl_trait_context {
@@ -365,6 +365,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
                     ImplTraitContext::Existential => {
                         self.with_parent(id, |this| visit::walk_ty(this, ty))
                     }
+                    ImplTraitContext::InBinding => unreachable!(),
                 };
             }
             _ => visit::walk_ty(self, ty),
@@ -374,6 +375,13 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
     fn visit_stmt(&mut self, stmt: &'a Stmt) {
         match stmt.kind {
             StmtKind::MacCall(..) => self.visit_macro_invoc(stmt.id),
+            // FIXME(impl_trait_in_bindings): We don't really have a good way of
+            // introducing the right `ImplTraitContext` here for all the cases we
+            // care about, in case we want to introduce ITIB to other positions
+            // such as turbofishes (e.g. `foo::<impl Fn()>(|| {})`).
+            StmtKind::Let(ref local) => self.with_impl_trait(ImplTraitContext::InBinding, |this| {
+                visit::walk_local(this, local)
+            }),
             _ => visit::walk_stmt(self, stmt),
         }
     }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 368eb3c26c7..9795299ed6d 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -28,8 +28,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{BytePos, Span, SyntaxContext};
+use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym};
 use thin_vec::{ThinVec, thin_vec};
 use tracing::debug;
 
@@ -2817,11 +2816,11 @@ fn show_candidates(
         path_strings.sort_by(|a, b| a.0.cmp(&b.0));
         path_strings.dedup_by(|a, b| a.0 == b.0);
         let core_path_strings =
-            path_strings.extract_if(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
+            path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::<Vec<_>>();
         let std_path_strings =
-            path_strings.extract_if(|p| p.0.starts_with("std::")).collect::<Vec<_>>();
+            path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::<Vec<_>>();
         let foreign_crate_path_strings =
-            path_strings.extract_if(|p| !p.0.starts_with("crate::")).collect::<Vec<_>>();
+            path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::<Vec<_>>();
 
         // We list the `crate` local paths first.
         // Then we list the `std`/`core` paths.
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index 24f5a812a82..3bfe98f7091 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -1,8 +1,7 @@
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, ElidedLifetimeInPathSubdiag, MultiSpan};
 use rustc_macros::{Diagnostic, Subdiagnostic};
-use rustc_span::Span;
-use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::{Ident, Span, Symbol};
 
 use crate::Res;
 use crate::late::PatternSource;
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 5906a682f3e..45e87edc53e 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -9,8 +9,7 @@ use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK;
 use rustc_session::parse::feature_err;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext};
-use rustc_span::symbol::{Ident, kw};
-use rustc_span::{Span, sym};
+use rustc_span::{Ident, Span, kw, sym};
 use tracing::{debug, instrument};
 
 use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst};
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 2ed3f4d2c4f..5b1d8d622bd 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -17,10 +17,9 @@ use rustc_session::lint::builtin::{
     AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE,
     REDUNDANT_IMPORTS, UNUSED_IMPORTS,
 };
-use rustc_span::Span;
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::hygiene::LocalExpnId;
-use rustc_span::symbol::{Ident, Symbol, kw};
+use rustc_span::{Ident, Span, Symbol, kw};
 use smallvec::SmallVec;
 use tracing::debug;
 
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index bde11428d40..a48a2865228 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -19,7 +19,9 @@ use rustc_ast::visit::{
 use rustc_ast::*;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_errors::codes::*;
-use rustc_errors::{Applicability, DiagArgValue, IntoDiagArg, StashKey, Suggestions};
+use rustc_errors::{
+    Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
+};
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
@@ -31,8 +33,7 @@ use rustc_session::config::{CrateType, ResolveDocLinks};
 use rustc_session::lint::{self, BuiltinLintDiag};
 use rustc_session::parse::feature_err;
 use rustc_span::source_map::{Spanned, respan};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{BytePos, Span, SyntaxContext};
+use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym};
 use smallvec::{SmallVec, smallvec};
 use tracing::{debug, instrument, trace};
 
@@ -264,12 +265,17 @@ impl RibKind<'_> {
 #[derive(Debug)]
 pub(crate) struct Rib<'ra, R = Res> {
     pub bindings: IdentMap<R>,
+    pub patterns_with_skipped_bindings: FxHashMap<DefId, Vec<(Span, Result<(), ErrorGuaranteed>)>>,
     pub kind: RibKind<'ra>,
 }
 
 impl<'ra, R> Rib<'ra, R> {
     fn new(kind: RibKind<'ra>) -> Rib<'ra, R> {
-        Rib { bindings: Default::default(), kind }
+        Rib {
+            bindings: Default::default(),
+            patterns_with_skipped_bindings: Default::default(),
+            kind,
+        }
     }
 }
 
@@ -3775,6 +3781,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
     /// When a whole or-pattern has been dealt with, the thing happens.
     ///
     /// See the implementation and `fresh_binding` for more details.
+    #[tracing::instrument(skip(self, bindings), level = "debug")]
     fn resolve_pattern_inner(
         &mut self,
         pat: &Pat,
@@ -3783,7 +3790,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
     ) {
         // Visit all direct subpatterns of this pattern.
         pat.walk(&mut |pat| {
-            debug!("resolve_pattern pat={:?} node={:?}", pat, pat.kind);
             match pat.kind {
                 PatKind::Ident(bmode, ident, ref sub) => {
                     // First try to resolve the identifier as some existing entity,
@@ -3809,8 +3815,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 PatKind::Path(ref qself, ref path) => {
                     self.smart_resolve_path(pat.id, qself, path, PathSource::Pat);
                 }
-                PatKind::Struct(ref qself, ref path, ..) => {
+                PatKind::Struct(ref qself, ref path, ref _fields, ref rest) => {
                     self.smart_resolve_path(pat.id, qself, path, PathSource::Struct);
+                    self.record_patterns_with_skipped_bindings(pat, rest);
                 }
                 PatKind::Or(ref ps) => {
                     // Add a new set of bindings to the stack. `Or` here records that when a
@@ -3843,6 +3850,30 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         });
     }
 
+    fn record_patterns_with_skipped_bindings(&mut self, pat: &Pat, rest: &ast::PatFieldsRest) {
+        match rest {
+            ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) => {
+                // Record that the pattern doesn't introduce all the bindings it could.
+                if let Some(partial_res) = self.r.partial_res_map.get(&pat.id)
+                    && let Some(res) = partial_res.full_res()
+                    && let Some(def_id) = res.opt_def_id()
+                {
+                    self.ribs[ValueNS]
+                        .last_mut()
+                        .unwrap()
+                        .patterns_with_skipped_bindings
+                        .entry(def_id)
+                        .or_default()
+                        .push((pat.span, match rest {
+                            ast::PatFieldsRest::Recovered(guar) => Err(*guar),
+                            _ => Ok(()),
+                        }));
+                }
+            }
+            ast::PatFieldsRest::None => {}
+        }
+    }
+
     fn fresh_binding(
         &mut self,
         ident: Ident,
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 09f3e848766..6ee02e9f47f 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -27,8 +27,7 @@ use rustc_session::{Session, lint};
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::MacroKind;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use thin_vec::ThinVec;
 use tracing::debug;
 
@@ -430,6 +429,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());
         err.code(code);
 
+        self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);
         self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);
         self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
 
@@ -628,7 +628,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         // Try to filter out intrinsics candidates, as long as we have
         // some other candidates to suggest.
         let intrinsic_candidates: Vec<_> = candidates
-            .extract_if(|sugg| {
+            .extract_if(.., |sugg| {
                 let path = path_names_to_string(&sugg.path);
                 path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")
             })
@@ -1120,6 +1120,48 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         true
     }
 
+    fn detect_missing_binding_available_from_pattern(
+        &mut self,
+        err: &mut Diag<'_>,
+        path: &[Segment],
+        following_seg: Option<&Segment>,
+    ) {
+        let [segment] = path else { return };
+        let None = following_seg else { return };
+        for rib in self.ribs[ValueNS].iter().rev() {
+            for (def_id, spans) in &rib.patterns_with_skipped_bindings {
+                if let Some(fields) = self.r.field_idents(*def_id) {
+                    for field in fields {
+                        if field.name == segment.ident.name {
+                            if spans.iter().all(|(_, had_error)| had_error.is_err()) {
+                                // This resolution error will likely be fixed by fixing a
+                                // syntax error in a pattern, so it is irrelevant to the user.
+                                let multispan: MultiSpan =
+                                    spans.iter().map(|(s, _)| *s).collect::<Vec<_>>().into();
+                                err.span_note(
+                                    multispan,
+                                    "this pattern had a recovered parse error which likely lost \
+                                     the expected fields",
+                                );
+                                err.downgrade_to_delayed_bug();
+                            }
+                            let ty = self.r.tcx.item_name(*def_id);
+                            for (span, _) in spans {
+                                err.span_label(
+                                    *span,
+                                    format!(
+                                        "this pattern doesn't include `{field}`, which is \
+                                         available in `{ty}`",
+                                    ),
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     fn suggest_at_operator_in_slice_pat_with_range(
         &mut self,
         err: &mut Diag<'_>,
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 94adfcd3b91..7b950b97d30 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -70,8 +70,7 @@ use rustc_query_system::ich::StableHashingContext;
 use rustc_session::lint::builtin::PRIVATE_MACRO_USE;
 use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
 use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use smallvec::{SmallVec, smallvec};
 use tracing::debug;
 
@@ -190,6 +189,7 @@ impl InvocationParent {
 enum ImplTraitContext {
     Existential,
     Universal,
+    InBinding,
 }
 
 /// Used for tracking import use types which will be used for redundant import checking.
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 669a9c2428f..edee19c280a 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -4,10 +4,11 @@
 use std::cell::Cell;
 use std::mem;
 
+use rustc_ast::attr::AttributeExt;
 use rustc_ast::expand::StrippedCfgItem;
 use rustc_ast::{self as ast, Crate, Inline, ItemKind, ModKind, NodeId, attr};
 use rustc_ast_pretty::pprust;
-use rustc_attr::StabilityLevel;
+use rustc_attr_parsing::StabilityLevel;
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, StashKey};
@@ -32,8 +33,7 @@ use rustc_session::parse::feature_err;
 use rustc_span::edit_distance::edit_distance;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::{self, AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind};
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 
 use crate::Namespace::*;
 use crate::errors::{
@@ -1126,7 +1126,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         &mut self,
         macro_def: &ast::MacroDef,
         ident: Ident,
-        attrs: &[ast::Attribute],
+        attrs: &[impl AttributeExt],
         span: Span,
         node_id: NodeId,
         edition: Edition,
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index 65128ceb866..84e43d0e016 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -5,12 +5,13 @@ use pulldown_cmark::{
     BrokenLink, BrokenLinkCallback, CowStr, Event, LinkType, Options, Parser, Tag,
 };
 use rustc_ast as ast;
+use rustc_ast::attr::AttributeExt;
 use rustc_ast::util::comments::beautify_doc_string;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::DefId;
-use rustc_span::symbol::{Symbol, kw, sym};
-use rustc_span::{DUMMY_SP, InnerSpan, Span};
+use rustc_span::{DUMMY_SP, InnerSpan, Span, Symbol, kw, sym};
+use thin_vec::ThinVec;
 use tracing::{debug, trace};
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -192,19 +193,24 @@ pub fn add_doc_fragment(out: &mut String, frag: &DocFragment) {
     }
 }
 
-pub fn attrs_to_doc_fragments<'a>(
-    attrs: impl Iterator<Item = (&'a ast::Attribute, Option<DefId>)>,
+pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>(
+    attrs: impl Iterator<Item = (&'a A, Option<DefId>)>,
     doc_only: bool,
-) -> (Vec<DocFragment>, ast::AttrVec) {
+) -> (Vec<DocFragment>, ThinVec<A>) {
     let mut doc_fragments = Vec::new();
-    let mut other_attrs = ast::AttrVec::new();
+    let mut other_attrs = ThinVec::<A>::new();
     for (attr, item_id) in attrs {
         if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() {
             let doc = beautify_doc_string(doc_str, comment_kind);
             let (span, kind) = if attr.is_doc_comment() {
-                (attr.span, DocFragmentKind::SugaredDoc)
+                (attr.span(), DocFragmentKind::SugaredDoc)
             } else {
-                (span_for_value(attr), DocFragmentKind::RawDoc)
+                (
+                    attr.value_span()
+                        .map(|i| i.with_ctxt(attr.span().ctxt()))
+                        .unwrap_or(attr.span()),
+                    DocFragmentKind::RawDoc,
+                )
             };
             let fragment = DocFragment { span, doc, kind, item_id, indent: 0 };
             doc_fragments.push(fragment);
@@ -218,16 +224,6 @@ pub fn attrs_to_doc_fragments<'a>(
     (doc_fragments, other_attrs)
 }
 
-fn span_for_value(attr: &ast::Attribute) -> Span {
-    if let ast::AttrKind::Normal(normal) = &attr.kind
-        && let ast::AttrArgs::Eq { value, .. } = &normal.item.args
-    {
-        value.span().with_ctxt(attr.span.ctxt())
-    } else {
-        attr.span
-    }
-}
-
 /// Return the doc-comments on this item, grouped by the module they came from.
 /// The module can be different if this is a re-export with added documentation.
 ///
@@ -353,12 +349,15 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGen
 ///
 //// If there are no doc-comments, return true.
 /// FIXME(#78591): Support both inner and outer attributes on the same item.
-pub fn inner_docs(attrs: &[ast::Attribute]) -> bool {
-    attrs.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == ast::AttrStyle::Inner)
+pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool {
+    attrs
+        .iter()
+        .find(|a| a.doc_str().is_some())
+        .map_or(true, |a| a.style() == ast::AttrStyle::Inner)
 }
 
 /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`.
-pub fn has_primitive_or_keyword_docs(attrs: &[ast::Attribute]) -> bool {
+pub fn has_primitive_or_keyword_docs(attrs: &[impl AttributeExt]) -> bool {
     for attr in attrs {
         if attr.has_name(sym::rustc_doc_primitive) {
             return true;
@@ -408,7 +407,7 @@ pub fn may_be_doc_link(link_type: LinkType) -> bool {
 
 /// Simplified version of `preprocessed_markdown_links` from rustdoc.
 /// Must return at least the same links as it, but may add some more links on top of that.
-pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<Box<str>> {
+pub(crate) fn attrs_to_preprocessed_links<A: AttributeExt + Clone>(attrs: &[A]) -> Vec<Box<str>> {
     let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
     let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap();
 
diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs
index 43cf1324edd..3426858495b 100644
--- a/compiler/rustc_session/src/config/cfg.rs
+++ b/compiler/rustc_session/src/config/cfg.rs
@@ -28,7 +28,7 @@ use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_lint_defs::BuiltinLintDiag;
 use rustc_lint_defs::builtin::EXPLICIT_BUILTIN_CFGS_IN_FLAGS;
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, TARGETS, Target, TargetTuple};
 
 use crate::Session;
diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs
index 3e2cbc18933..beae9dc278c 100644
--- a/compiler/rustc_session/src/cstore.rs
+++ b/compiler/rustc_session/src/cstore.rs
@@ -13,8 +13,7 @@ use rustc_hir::def_id::{
 };
 use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, Definitions};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
-use rustc_span::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::{Span, Symbol};
 
 use crate::search_paths::PathKind;
 use crate::utils::NativeLibKind;
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 0b4470b2b0f..dcf86d1a408 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -29,9 +29,6 @@ pub mod output;
 
 pub use getopts;
 
-mod version;
-pub use version::RustcVersion;
-
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
 /// Requirements for a `StableHashingContext` to be used in this crate.
diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs
index 2b2ba50d3fb..bd103e86e26 100644
--- a/compiler/rustc_session/src/output.rs
+++ b/compiler/rustc_session/src/output.rs
@@ -4,8 +4,7 @@ use std::path::Path;
 
 use rustc_ast::{self as ast, attr};
 use rustc_errors::FatalError;
-use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 use crate::Session;
 use crate::config::{self, CrateType, Input, OutFileName, OutputFilenames, OutputType};
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 21c11655110..90361efed84 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -79,7 +79,7 @@ impl SymbolGallery {
 
 // todo: this function now accepts `Session` instead of `ParseSess` and should be relocated
 /// Construct a diagnostic for a language feature error due to the given `span`.
-/// The `feature`'s `Symbol` is the one you used in `unstable.rs` and `rustc_span::symbols`.
+/// The `feature`'s `Symbol` is the one you used in `unstable.rs` and `rustc_span::symbol`.
 #[track_caller]
 pub fn feature_err(
     sess: &Session,
diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml
index 1230667ee91..29ce24e8b78 100644
--- a/compiler/rustc_smir/Cargo.toml
+++ b/compiler/rustc_smir/Cargo.toml
@@ -7,9 +7,9 @@ edition = "2021"
 # tidy-alphabetical-start
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
-rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_hir = { path = "../rustc_hir" }
+rustc_hir_pretty = { path = "../rustc_hir_pretty" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index 64d241067a8..dc63ea1999e 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -318,7 +318,7 @@ macro_rules! run_driver {
     ($args:expr, $callback:expr $(, $with_tcx:ident)?) => {{
         use rustc_driver::{Callbacks, Compilation, RunCompiler};
         use rustc_middle::ty::TyCtxt;
-        use rustc_interface::{interface, Queries};
+        use rustc_interface::interface;
         use stable_mir::CompilerError;
         use std::ops::ControlFlow;
 
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 9025b47c422..9793a4d4162 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -251,11 +251,10 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let mut tables = self.0.borrow_mut();
         let tcx = tables.tcx;
         let did = tables[def_id];
-        let attr_name: Vec<_> =
-            attr.iter().map(|seg| rustc_span::symbol::Symbol::intern(&seg)).collect();
+        let attr_name: Vec<_> = attr.iter().map(|seg| rustc_span::Symbol::intern(&seg)).collect();
         tcx.get_attrs_by_path(did, &attr_name)
             .map(|attribute| {
-                let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute);
+                let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
                 let span = attribute.span;
                 stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables))
             })
@@ -266,17 +265,16 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let mut tables = self.0.borrow_mut();
         let tcx = tables.tcx;
         let did = tables[def_id];
-        let filter_fn = move |a: &&rustc_ast::ast::Attribute| {
-            matches!(a.kind, rustc_ast::ast::AttrKind::Normal(_))
-        };
+        let filter_fn =
+            move |a: &&rustc_hir::Attribute| matches!(a.kind, rustc_hir::AttrKind::Normal(_));
         let attrs_iter = if let Some(did) = did.as_local() {
             tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter().filter(filter_fn)
         } else {
-            tcx.item_attrs(did).iter().filter(filter_fn)
+            tcx.attrs_for_def(did).iter().filter(filter_fn)
         };
         attrs_iter
             .map(|attribute| {
-                let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute);
+                let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute);
                 let span = attribute.span;
                 stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables))
             })
diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs
index 4f120db48f0..8a2baaa42e2 100644
--- a/compiler/rustc_span/src/edit_distance.rs
+++ b/compiler/rustc_span/src/edit_distance.rs
@@ -11,7 +11,7 @@
 
 use std::{cmp, mem};
 
-use crate::symbol::Symbol;
+use crate::Symbol;
 
 #[cfg(test)]
 mod tests;
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index bd8b93bb4d1..d5c2a337b4c 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -25,7 +25,6 @@
 #![feature(hash_set_entry)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
-#![feature(min_specialization)]
 #![feature(negative_impls)]
 #![feature(read_buf)]
 #![feature(round_char_boundary)]
@@ -68,7 +67,7 @@ mod span_encoding;
 pub use span_encoding::{DUMMY_SP, Span};
 
 pub mod symbol;
-pub use symbol::{Symbol, sym};
+pub use symbol::{Ident, MacroRulesNormalizedIdent, Symbol, kw, sym};
 
 mod analyze_source_file;
 pub mod fatal_error;
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index e3708896ba9..7d99ca5a31e 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -306,6 +306,7 @@ symbols! {
         RwLockWriteGuard,
         Saturating,
         SeekFrom,
+        SelfTy,
         Send,
         SeqCst,
         Sized,
@@ -1190,6 +1191,7 @@ symbols! {
         loongarch_target_feature,
         loop_break_value,
         lt,
+        m68k_target_feature,
         macro_at_most_once_rep,
         macro_attributes_in_derive_output,
         macro_escape,
@@ -2476,13 +2478,6 @@ impl fmt::Display for Symbol {
     }
 }
 
-// takes advantage of `str::to_string` specialization
-impl ToString for Symbol {
-    fn to_string(&self) -> String {
-        self.as_str().to_string()
-    }
-}
-
 impl<CTX> HashStable<CTX> for Symbol {
     #[inline]
     fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs
index 73bd8d7555b..7fda81126c4 100644
--- a/compiler/rustc_symbol_mangling/src/test.rs
+++ b/compiler/rustc_symbol_mangling/src/test.rs
@@ -7,7 +7,7 @@
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{GenericArgs, Instance, TyCtxt};
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 use crate::errors::{Kind, TestOutput};
 
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index def741e3546..a801b3e53a1 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::{
     self, EarlyBinder, FloatTy, GenericArg, GenericArgKind, Instance, IntTy, ReifyReason, Ty,
     TyCtxt, TypeVisitable, TypeVisitableExt, UintTy,
 };
-use rustc_span::symbol::kw;
+use rustc_span::kw;
 
 pub(super) fn mangle<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -439,7 +439,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
                 let sig = sig_tys.with(hdr);
                 self.push("F");
                 self.in_binder(&sig, |cx, sig| {
-                    if sig.safety == hir::Safety::Unsafe {
+                    if sig.safety.is_unsafe() {
                         cx.push("U");
                     }
                     match sig.abi {
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
index 204d5d53361..1292f46f0c9 100644
--- a/compiler/rustc_target/src/asm/mod.rs
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -35,7 +35,7 @@ macro_rules! def_reg_class {
         impl $arch_regclass {
             pub fn name(self) -> rustc_span::Symbol {
                 match self {
-                    $(Self::$class => rustc_span::symbol::sym::$class,)*
+                    $(Self::$class => rustc_span::sym::$class,)*
                 }
             }
 
@@ -511,7 +511,7 @@ impl InlineAsmRegClass {
             Self::Msp430(r) => r.name(),
             Self::M68k(r) => r.name(),
             Self::CSKY(r) => r.name(),
-            Self::Err => rustc_span::symbol::sym::reg,
+            Self::Err => rustc_span::sym::reg,
         }
     }
 
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 7d308c6c662..ad746d3f26b 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -46,7 +46,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_fs_util::try_canonicalize;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
-use rustc_span::symbol::{Symbol, kw, sym};
+use rustc_span::{Symbol, kw, sym};
 use serde_json::Value;
 use tracing::debug;
 
@@ -2213,6 +2213,10 @@ pub struct TargetOptions {
     /// `-Ctarget-cpu` but can be overwritten with `-Ctarget-features`.
     /// Corresponds to `llc -mattr=$features`.
     /// Note that these are LLVM feature names, not Rust feature names!
+    ///
+    /// Generally it is a bad idea to use negative target features because they often interact very
+    /// poorly with how `-Ctarget-cpu` works. Instead, try to use a lower "base CPU" and enable the
+    /// features you want to use.
     pub features: StaticCow<str>,
     /// Direct or use GOT indirect to reference external data symbols
     pub direct_access_external_data: Option<bool>,
@@ -2605,15 +2609,11 @@ impl TargetOptions {
     }
 
     pub(crate) fn has_feature(&self, search_feature: &str) -> bool {
-        self.features.split(',').any(|f| {
-            if let Some(f) = f.strip_prefix('+')
-                && f == search_feature
-            {
-                true
-            } else {
-                false
-            }
-        })
+        self.features.split(',').any(|f| f.strip_prefix('+').is_some_and(|f| f == search_feature))
+    }
+
+    pub(crate) fn has_neg_feature(&self, search_feature: &str) -> bool {
+        self.features.split(',').any(|f| f.strip_prefix('-').is_some_and(|f| f == search_feature))
     }
 }
 
@@ -3166,7 +3166,7 @@ impl Target {
                 // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI.
                 check_matches!(
                     &*self.llvm_abiname,
-                    "lp64" | "lp64f" | "lp64d" | "lp64q" | "lp64e",
+                    "lp64" | "lp64f" | "lp64d" | "lp64e",
                     "invalid RISC-V ABI name"
                 );
             }
diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs
index 952a00c52a2..f763c37f535 100644
--- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs
@@ -13,8 +13,8 @@ pub(crate) fn target() -> Target {
         llvm_target: "powerpc64le-unknown-linux-musl".into(),
         metadata: crate::spec::TargetMetadata {
             description: Some("64-bit PowerPC Linux with musl 1.2.3, Little Endian".into()),
-            tier: Some(3),
-            host_tools: Some(false),
+            tier: Some(2),
+            host_tools: Some(true),
             std: Some(true),
         },
         pointer_width: 64,
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index 29d3f826a15..5372437b0d2 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -3,7 +3,7 @@
 //! and Rust adds some features that do not correspond to LLVM features at all.
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::{Symbol, sym};
 
 use crate::spec::Target;
 
@@ -20,7 +20,7 @@ pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"];
 /// `Toggleability` is the type storing whether (un)stable features can be toggled:
 /// this is initially a function since it can depend on `Target`, but for stable hashing
 /// it needs to be something hashable to we have to make the type generic.
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
 pub enum Stability<Toggleability> {
     /// This target feature is stable, it can be used in `#[target_feature]` and
     /// `#[cfg(target_feature)]`.
@@ -39,16 +39,26 @@ pub enum Stability<Toggleability> {
         allow_toggle: Toggleability,
     },
     /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be
-    /// set in the basic target definition. It is never set in `cfg(target_feature)`. Used in
+    /// set in the target spec. It is never set in `cfg(target_feature)`. Used in
     /// particular for features that change the floating-point ABI.
     Forbidden { reason: &'static str },
 }
 
-/// `Stability` where `allow_toggle` has not been computed yet.
 /// Returns `Ok` if the toggle is allowed, `Err` with an explanation of not.
-pub type StabilityUncomputed = Stability<fn(&Target) -> Result<(), &'static str>>;
+/// The `bool` indicates whether the feature is being enabled (`true`) or disabled.
+pub type AllowToggleUncomputed = fn(&Target, bool) -> Result<(), &'static str>;
+
+/// The computed result of whether a feature can be enabled/disabled on the current target.
+#[derive(Debug, Clone)]
+pub struct AllowToggleComputed {
+    enable: Result<(), &'static str>,
+    disable: Result<(), &'static str>,
+}
+
+/// `Stability` where `allow_toggle` has not been computed yet.
+pub type StabilityUncomputed = Stability<AllowToggleUncomputed>;
 /// `Stability` where `allow_toggle` has already been computed.
-pub type StabilityComputed = Stability<Result<(), &'static str>>;
+pub type StabilityComputed = Stability<AllowToggleComputed>;
 
 impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleability> {
     #[inline]
@@ -69,11 +79,20 @@ impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleab
     }
 }
 
+impl<CTX> HashStable<CTX> for AllowToggleComputed {
+    #[inline]
+    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
+        let AllowToggleComputed { enable, disable } = self;
+        enable.hash_stable(hcx, hasher);
+        disable.hash_stable(hcx, hasher);
+    }
+}
+
 impl<Toggleability> Stability<Toggleability> {
     /// Returns whether the feature can be used in `cfg(target_feature)` ever.
     /// (It might still be nightly-only even if this returns `true`, so make sure to also check
     /// `requires_nightly`.)
-    pub fn in_cfg(self) -> bool {
+    pub fn in_cfg(&self) -> bool {
         !matches!(self, Stability::Forbidden { .. })
     }
 
@@ -85,8 +104,8 @@ impl<Toggleability> Stability<Toggleability> {
     /// Before calling this, ensure the feature is even permitted for this use:
     /// - for `#[target_feature]`/`-Ctarget-feature`, check `allow_toggle()`
     /// - for `cfg(target_feature)`, check `in_cfg`
-    pub fn requires_nightly(self) -> Option<Symbol> {
-        match self {
+    pub fn requires_nightly(&self) -> Option<Symbol> {
+        match *self {
             Stability::Unstable { nightly_feature, .. } => Some(nightly_feature),
             Stability::Stable { .. } => None,
             Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"),
@@ -95,35 +114,49 @@ impl<Toggleability> Stability<Toggleability> {
 }
 
 impl StabilityUncomputed {
-    pub fn compute_toggleability(self, target: &Target) -> StabilityComputed {
+    pub fn compute_toggleability(&self, target: &Target) -> StabilityComputed {
         use Stability::*;
-        match self {
-            Stable { allow_toggle } => Stable { allow_toggle: allow_toggle(target) },
+        let compute = |f: AllowToggleUncomputed| AllowToggleComputed {
+            enable: f(target, true),
+            disable: f(target, false),
+        };
+        match *self {
+            Stable { allow_toggle } => Stable { allow_toggle: compute(allow_toggle) },
             Unstable { nightly_feature, allow_toggle } => {
-                Unstable { nightly_feature, allow_toggle: allow_toggle(target) }
+                Unstable { nightly_feature, allow_toggle: compute(allow_toggle) }
             }
             Forbidden { reason } => Forbidden { reason },
         }
     }
+
+    pub fn toggle_allowed(&self, target: &Target, enable: bool) -> Result<(), &'static str> {
+        use Stability::*;
+        match *self {
+            Stable { allow_toggle } => allow_toggle(target, enable),
+            Unstable { allow_toggle, .. } => allow_toggle(target, enable),
+            Forbidden { reason } => Err(reason),
+        }
+    }
 }
 
 impl StabilityComputed {
     /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
     /// (It might still be nightly-only even if this returns `true`, so make sure to also check
     /// `requires_nightly`.)
-    pub fn allow_toggle(self) -> Result<(), &'static str> {
-        match self {
+    pub fn toggle_allowed(&self, enable: bool) -> Result<(), &'static str> {
+        let allow_toggle = match self {
             Stability::Stable { allow_toggle } => allow_toggle,
             Stability::Unstable { allow_toggle, .. } => allow_toggle,
-            Stability::Forbidden { reason } => Err(reason),
-        }
+            Stability::Forbidden { reason } => return Err(reason),
+        };
+        if enable { allow_toggle.enable } else { allow_toggle.disable }
     }
 }
 
 // Constructors for the list below, defaulting to "always allow toggle".
-const STABLE: StabilityUncomputed = Stability::Stable { allow_toggle: |_target| Ok(()) };
+const STABLE: StabilityUncomputed = Stability::Stable { allow_toggle: |_target, _enable| Ok(()) };
 const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed {
-    Stability::Unstable { nightly_feature, allow_toggle: |_target| Ok(()) }
+    Stability::Unstable { nightly_feature, allow_toggle: |_target, _enable| Ok(()) }
 }
 
 // Here we list target features that rustc "understands": they can be used in `#[target_feature]`
@@ -184,7 +217,7 @@ const ARM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[
         "fpregs",
         Stability::Unstable {
             nightly_feature: sym::arm_target_feature,
-            allow_toggle: |target: &Target| {
+            allow_toggle: |target: &Target, _enable| {
                 // Only allow toggling this if the target has `soft-float` set. With `soft-float`,
                 // `fpregs` isn't needed so changing it cannot affect the ABI.
                 if target.has_feature("soft-float") {
@@ -257,6 +290,7 @@ const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[
     ("flagm", STABLE, &[]),
     // FEAT_FLAGM2
     ("flagm2", unstable(sym::aarch64_unstable_target_feature), &[]),
+    ("fp-armv8", Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`" }, &[]),
     // FEAT_FP16
     // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608
     ("fp16", STABLE, &["neon"]),
@@ -292,7 +326,28 @@ const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[
     // FEAT_MTE & FEAT_MTE2
     ("mte", STABLE, &[]),
     // FEAT_AdvSimd & FEAT_FP
-    ("neon", STABLE, &[]),
+    (
+        "neon",
+        Stability::Stable {
+            allow_toggle: |target, enable| {
+                if target.abi == "softfloat" {
+                    // `neon` has no ABI implications for softfloat targets, we can allow this.
+                    Ok(())
+                } else if enable
+                    && !target.has_neg_feature("fp-armv8")
+                    && !target.has_neg_feature("neon")
+                {
+                    // neon is enabled by default, and has not been disabled, so enabling it again
+                    // is redundant and we can permit it. Forbidding this would be a breaking change
+                    // since this feature is stable.
+                    Ok(())
+                } else {
+                    Err("unsound on hard-float targets because it changes float ABI")
+                }
+            },
+        },
+        &[],
+    ),
     // FEAT_PAUTH (address authentication)
     ("paca", STABLE, &[]),
     // FEAT_PAUTH (generic authentication)
@@ -481,7 +536,7 @@ const X86_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[
         "x87",
         Stability::Unstable {
             nightly_feature: sym::x87_target_feature,
-            allow_toggle: |target: &Target| {
+            allow_toggle: |target: &Target, _enable| {
                 // Only allow toggling this if the target has `soft-float` set. With `soft-float`,
                 // `fpregs` isn't needed so changing it cannot affect the ABI.
                 if target.has_feature("soft-float") {
@@ -535,9 +590,76 @@ const RISCV_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[
     // tidy-alphabetical-start
     ("a", STABLE, &["zaamo", "zalrsc"]),
     ("c", STABLE, &[]),
-    ("d", unstable(sym::riscv_target_feature), &["f"]),
-    ("e", unstable(sym::riscv_target_feature), &[]),
-    ("f", unstable(sym::riscv_target_feature), &[]),
+    (
+        "d",
+        Stability::Unstable {
+            nightly_feature: sym::riscv_target_feature,
+            allow_toggle: |target, enable| match &*target.llvm_abiname {
+                "ilp32d" | "lp64d" if !enable => {
+                    // The ABI requires the `d` feature, so it cannot be disabled.
+                    Err("feature is required by ABI")
+                }
+                "ilp32e" if enable => {
+                    // ilp32e is incompatible with features that need aligned load/stores > 32 bits,
+                    // like `d`.
+                    Err("feature is incompatible with ABI")
+                }
+                _ => Ok(()),
+            },
+        },
+        &["f"],
+    ),
+    (
+        "e",
+        Stability::Unstable {
+            // Given that this is a negative feature, consider this before stabilizing:
+            // does it really make sense to enable this feature in an individual
+            // function with `#[target_feature]`?
+            nightly_feature: sym::riscv_target_feature,
+            allow_toggle: |target, enable| {
+                match &*target.llvm_abiname {
+                    _ if !enable => {
+                        // Disabling this feature means we can use more registers (x16-x31).
+                        // The "e" ABIs treat them as caller-save, so it is safe to use them only
+                        // in some parts of a program while the rest doesn't know they even exist.
+                        // On other ABIs, the feature is already disabled anyway.
+                        Ok(())
+                    }
+                    "ilp32e" | "lp64e" => {
+                        // Embedded ABIs should already have the feature anyway, it's fine to enable
+                        // it again from an ABI perspective.
+                        Ok(())
+                    }
+                    _ => {
+                        // *Not* an embedded ABI. Enabling `e` is invalid.
+                        Err("feature is incompatible with ABI")
+                    }
+                }
+            },
+        },
+        &[],
+    ),
+    (
+        "f",
+        Stability::Unstable {
+            nightly_feature: sym::riscv_target_feature,
+            allow_toggle: |target, enable| {
+                match &*target.llvm_abiname {
+                    "ilp32f" | "ilp32d" | "lp64f" | "lp64d" if !enable => {
+                        // The ABI requires the `f` feature, so it cannot be disabled.
+                        Err("feature is required by ABI")
+                    }
+                    _ => Ok(()),
+                }
+            },
+        },
+        &[],
+    ),
+    (
+        "forced-atomics",
+        Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" },
+        &[],
+    ),
     ("m", STABLE, &[]),
     ("relax", unstable(sym::riscv_target_feature), &[]),
     ("unaligned-scalar-mem", unstable(sym::riscv_target_feature), &[]),
@@ -668,6 +790,20 @@ const SPARC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[
     // tidy-alphabetical-end
 ];
 
+const M68K_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[
+    // tidy-alphabetical-start
+    ("isa-68000", unstable(sym::m68k_target_feature), &[]),
+    ("isa-68010", unstable(sym::m68k_target_feature), &["isa-68000"]),
+    ("isa-68020", unstable(sym::m68k_target_feature), &["isa-68010"]),
+    ("isa-68030", unstable(sym::m68k_target_feature), &["isa-68020"]),
+    ("isa-68040", unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]),
+    ("isa-68060", unstable(sym::m68k_target_feature), &["isa-68040"]),
+    // FPU
+    ("isa-68881", unstable(sym::m68k_target_feature), &[]),
+    ("isa-68882", unstable(sym::m68k_target_feature), &["isa-68881"]),
+    // tidy-alphabetical-end
+];
+
 /// When rustdoc is running, provide a list of all known features so that all their respective
 /// primitives may be documented.
 ///
@@ -687,6 +823,7 @@ pub fn all_rust_features() -> impl Iterator<Item = (&'static str, StabilityUncom
         .chain(LOONGARCH_FEATURES)
         .chain(IBMZ_FEATURES)
         .chain(SPARC_FEATURES)
+        .chain(M68K_FEATURES)
         .cloned()
         .map(|(f, s, _)| (f, s))
 }
@@ -734,6 +871,7 @@ impl Target {
             "loongarch64" => LOONGARCH_FEATURES,
             "s390x" => IBMZ_FEATURES,
             "sparc" | "sparc64" => SPARC_FEATURES,
+            "m68k" => M68K_FEATURES,
             _ => &[],
         }
     }
@@ -751,7 +889,7 @@ impl Target {
             "sparc" | "sparc64" => SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI,
             "hexagon" => HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI,
             "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI,
-            "bpf" => &[], // no vector ABI
+            "bpf" | "m68k" => &[], // no vector ABI
             "csky" => CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI,
             // FIXME: for some tier3 targets, we are overly cautious and always give warnings
             // when passing args in vector registers.
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index e29ed9a4b56..b13a753c4ed 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -9,7 +9,7 @@ itertools = "0.12"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_ir = { path = "../rustc_ast_ir" }
-rustc_attr = { path = "../rustc_attr" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index 5a62a4c3bd5..ee5ce19cb4d 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -824,7 +824,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn cmp_fn_sig(
         &self,
         sig1: &ty::PolyFnSig<'tcx>,
+        fn_def1: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>,
         sig2: &ty::PolyFnSig<'tcx>,
+        fn_def2: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>,
     ) -> (DiagStyledString, DiagStyledString) {
         let sig1 = &(self.normalize_fn_sig)(*sig1);
         let sig2 = &(self.normalize_fn_sig)(*sig2);
@@ -930,6 +932,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             (values.1).0.extend(x2.0);
         }
 
+        let fmt = |(did, args)| format!(" {{{}}}", self.tcx.def_path_str_with_args(did, args));
+
+        match (fn_def1, fn_def2) {
+            (None, None) => {}
+            (Some(fn_def1), Some(fn_def2)) => {
+                let path1 = fmt(fn_def1);
+                let path2 = fmt(fn_def2);
+                let same_path = path1 == path2;
+                values.0.push(path1, !same_path);
+                values.1.push(path2, !same_path);
+            }
+            (Some(fn_def1), None) => {
+                values.0.push_highlighted(fmt(fn_def1));
+            }
+            (None, Some(fn_def2)) => {
+                values.1.push_highlighted(fmt(fn_def2));
+            }
+        }
+
         values
     }
 
@@ -1318,36 +1339,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
                 let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1);
                 let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2);
-                let mut values = self.cmp_fn_sig(&sig1, &sig2);
-                let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_args(*did1, args1));
-                let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2));
-                let same_path = path1 == path2;
-                values.0.push(path1, !same_path);
-                values.1.push(path2, !same_path);
-                values
+                self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig2, Some((*did2, args2)))
             }
 
             (ty::FnDef(did1, args1), ty::FnPtr(sig_tys2, hdr2)) => {
                 let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1);
-                let mut values = self.cmp_fn_sig(&sig1, &sig_tys2.with(*hdr2));
-                values.0.push_highlighted(format!(
-                    " {{{}}}",
-                    self.tcx.def_path_str_with_args(*did1, args1)
-                ));
-                values
+                self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig_tys2.with(*hdr2), None)
             }
 
             (ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => {
                 let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2);
-                let mut values = self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig2);
-                values
-                    .1
-                    .push_normal(format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2)));
-                values
+                self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig2, Some((*did2, args2)))
             }
 
             (ty::FnPtr(sig_tys1, hdr1), ty::FnPtr(sig_tys2, hdr2)) => {
-                self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig_tys2.with(*hdr2))
+                self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig_tys2.with(*hdr2), None)
             }
 
             _ => {
@@ -2102,7 +2108,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 if exp_found.references_error() {
                     return None;
                 }
-                let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found);
+                let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, None, &exp_found.found, None);
                 Some((exp, fnd, None))
             }
         }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
index af3b5e0d5d4..2dcf3760d2f 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
@@ -17,10 +17,10 @@ use rustc_middle::ty::{
     self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt,
     TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults,
 };
-use rustc_span::symbol::{Ident, sym};
-use rustc_span::{BytePos, DUMMY_SP, FileName, Span};
+use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym};
 use tracing::{debug, instrument, warn};
 
+use super::nice_region_error::placeholder_error::Highlighted;
 use crate::error_reporting::TypeErrCtxt;
 use crate::errors::{
     AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError,
@@ -279,8 +279,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     pub fn extract_inference_diagnostics_data(
         &self,
         arg: GenericArg<'tcx>,
-        highlight: Option<ty::print::RegionHighlightMode<'tcx>>,
+        highlight: ty::print::RegionHighlightMode<'tcx>,
     ) -> InferenceDiagnosticsData {
+        let tcx = self.tcx;
         match arg.unpack() {
             GenericArgKind::Type(ty) => {
                 if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() {
@@ -300,13 +301,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     }
                 }
 
-                let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
-                if let Some(highlight) = highlight {
-                    printer.region_highlight_mode = highlight;
-                }
-                ty.print(&mut printer).unwrap();
                 InferenceDiagnosticsData {
-                    name: printer.into_buffer(),
+                    name: Highlighted { highlight, ns: Namespace::TypeNS, tcx, value: ty }
+                        .to_string(),
                     span: None,
                     kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) },
                     parent: None,
@@ -325,13 +322,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     }
 
                     debug_assert!(!origin.span.is_dummy());
-                    let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS);
-                    if let Some(highlight) = highlight {
-                        printer.region_highlight_mode = highlight;
-                    }
-                    ct.print(&mut printer).unwrap();
                     InferenceDiagnosticsData {
-                        name: printer.into_buffer(),
+                        name: Highlighted { highlight, ns: Namespace::ValueNS, tcx, value: ct }
+                            .to_string(),
                         span: Some(origin.span),
                         kind: UnderspecifiedArgKind::Const { is_parameter: false },
                         parent: None,
@@ -343,13 +336,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     // FIXME: Ideally we should look into the generic constant
                     // to figure out which inference var is actually unresolved so that
                     // this path is unreachable.
-                    let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS);
-                    if let Some(highlight) = highlight {
-                        printer.region_highlight_mode = highlight;
-                    }
-                    ct.print(&mut printer).unwrap();
                     InferenceDiagnosticsData {
-                        name: printer.into_buffer(),
+                        name: Highlighted { highlight, ns: Namespace::ValueNS, tcx, value: ct }
+                            .to_string(),
                         span: None,
                         kind: UnderspecifiedArgKind::Const { is_parameter: false },
                         parent: None,
@@ -422,7 +411,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         should_label_span: bool,
     ) -> Diag<'a> {
         let arg = self.resolve_vars_if_possible(arg);
-        let arg_data = self.extract_inference_diagnostics_data(arg, None);
+        let arg_data =
+            self.extract_inference_diagnostics_data(arg, ty::print::RegionHighlightMode::default());
 
         let Some(typeck_results) = &self.typeck_results else {
             // If we don't have any typeck results we're outside
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs
index 7f847253439..9cb58a9d45b 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs
@@ -63,26 +63,16 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         }
 
         // Determine whether the sub and sup consist of both anonymous (elided) regions.
-        let anon_reg_sup = self.tcx().is_suitable_region(self.generic_param_scope, sup)?;
+        let sup_info = self.tcx().is_suitable_region(self.generic_param_scope, sup)?;
 
-        let anon_reg_sub = self.tcx().is_suitable_region(self.generic_param_scope, sub)?;
-        let scope_def_id_sup = anon_reg_sup.def_id;
-        let bregion_sup = anon_reg_sup.bound_region;
-        let scope_def_id_sub = anon_reg_sub.def_id;
-        let bregion_sub = anon_reg_sub.bound_region;
+        let sub_info = self.tcx().is_suitable_region(self.generic_param_scope, sub)?;
 
-        let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup, &bregion_sup)?;
+        let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup)?;
 
-        let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub, &bregion_sub)?;
+        let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub)?;
 
-        debug!(
-            "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}",
-            ty_sub, sup, bregion_sup
-        );
-        debug!(
-            "try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}",
-            ty_sup, sub, bregion_sub
-        );
+        debug!("try_report_anon_anon_conflict: found_param1={:?} sup={:?}", ty_sub, sup);
+        debug!("try_report_anon_anon_conflict: found_param2={:?} sub={:?}", ty_sup, sub);
 
         let (ty_sup, ty_fndecl_sup) = ty_sup;
         let (ty_sub, ty_fndecl_sub) = ty_sub;
@@ -93,9 +83,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             self.find_param_with_region(sub, sub)?;
 
         let sup_is_ret_type =
-            self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup);
+            self.is_return_type_anon(sup_info.scope, sup_info.region_def_id, ty_fndecl_sup);
         let sub_is_ret_type =
-            self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
+            self.is_return_type_anon(sub_info.scope, sub_info.region_def_id, ty_fndecl_sub);
 
         debug!(
             "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}",
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
index cd621fd1a39..2a487a48a8e 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
@@ -1,7 +1,7 @@
 use core::ops::ControlFlow;
 
 use rustc_hir as hir;
-use rustc_hir::def_id::LocalDefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
@@ -28,16 +28,15 @@ pub fn find_anon_type<'tcx>(
     tcx: TyCtxt<'tcx>,
     generic_param_scope: LocalDefId,
     region: Region<'tcx>,
-    br: &ty::BoundRegionKind,
 ) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> {
     let anon_reg = tcx.is_suitable_region(generic_param_scope, region)?;
-    let fn_sig = tcx.hir_node_by_def_id(anon_reg.def_id).fn_sig()?;
+    let fn_sig = tcx.hir_node_by_def_id(anon_reg.scope).fn_sig()?;
 
     fn_sig
         .decl
         .inputs
         .iter()
-        .find_map(|arg| find_component_for_bound_region(tcx, arg, br))
+        .find_map(|arg| find_component_for_bound_region(tcx, arg, anon_reg.region_def_id))
         .map(|ty| (ty, fn_sig))
 }
 
@@ -46,9 +45,9 @@ pub fn find_anon_type<'tcx>(
 fn find_component_for_bound_region<'tcx>(
     tcx: TyCtxt<'tcx>,
     arg: &'tcx hir::Ty<'tcx>,
-    br: &ty::BoundRegionKind,
+    region_def_id: DefId,
 ) -> Option<&'tcx hir::Ty<'tcx>> {
-    FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST }
+    FindNestedTypeVisitor { tcx, region_def_id, current_index: ty::INNERMOST }
         .visit_ty(arg)
         .break_value()
 }
@@ -62,9 +61,8 @@ fn find_component_for_bound_region<'tcx>(
 // specific part of the type in the error message.
 struct FindNestedTypeVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
-    // The bound_region corresponding to the Refree(freeregion)
-    // associated with the anonymous region we are looking for.
-    bound_region: ty::BoundRegionKind,
+    // The `DefId` of the region we're looking for.
+    region_def_id: DefId,
     current_index: ty::DebruijnIndex,
 }
 
@@ -96,16 +94,13 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
             hir::TyKind::Ref(lifetime, _) => {
                 // the lifetime of the Ref
                 let hir_id = lifetime.hir_id;
-                match (self.tcx.named_bound_var(hir_id), self.bound_region) {
+                match self.tcx.named_bound_var(hir_id) {
                     // Find the index of the named region that was part of the
                     // error. We will then search the function parameters for a bound
                     // region at the right depth with the same index
-                    (
-                        Some(rbv::ResolvedArg::EarlyBound(id)),
-                        ty::BoundRegionKind::Named(def_id, _),
-                    ) => {
-                        debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
-                        if id.to_def_id() == def_id {
+                    Some(rbv::ResolvedArg::EarlyBound(id)) => {
+                        debug!("EarlyBound id={:?}", id);
+                        if id.to_def_id() == self.region_def_id {
                             return ControlFlow::Break(arg);
                         }
                     }
@@ -113,31 +108,25 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
                     // Find the index of the named region that was part of the
                     // error. We will then search the function parameters for a bound
                     // region at the right depth with the same index
-                    (
-                        Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)),
-                        ty::BoundRegionKind::Named(def_id, _),
-                    ) => {
+                    Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)) => {
                         debug!(
                             "FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
                             debruijn_index
                         );
-                        debug!("LateBound id={:?} def_id={:?}", id, def_id);
-                        if debruijn_index == self.current_index && id.to_def_id() == def_id {
+                        debug!("LateBound id={:?}", id);
+                        if debruijn_index == self.current_index
+                            && id.to_def_id() == self.region_def_id
+                        {
                             return ControlFlow::Break(arg);
                         }
                     }
 
-                    (
-                        Some(
-                            rbv::ResolvedArg::StaticLifetime
-                            | rbv::ResolvedArg::Free(_, _)
-                            | rbv::ResolvedArg::EarlyBound(_)
-                            | rbv::ResolvedArg::LateBound(_, _, _)
-                            | rbv::ResolvedArg::Error(_),
-                        )
-                        | None,
-                        _,
-                    ) => {
+                    Some(
+                        rbv::ResolvedArg::StaticLifetime
+                        | rbv::ResolvedArg::Free(_, _)
+                        | rbv::ResolvedArg::Error(_),
+                    )
+                    | None => {
                         debug!("no arg found");
                     }
                 }
@@ -151,7 +140,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
                 return if intravisit::walk_ty(
                     &mut TyPathVisitor {
                         tcx: self.tcx,
-                        bound_region: self.bound_region,
+                        region_def_id: self.region_def_id,
                         current_index: self.current_index,
                     },
                     arg,
@@ -179,7 +168,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
 // specific part of the type in the error message.
 struct TyPathVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
-    bound_region: ty::BoundRegionKind,
+    region_def_id: DefId,
     current_index: ty::DebruijnIndex,
 }
 
@@ -192,38 +181,29 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
     }
 
     fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) -> Self::Result {
-        match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) {
+        match self.tcx.named_bound_var(lifetime.hir_id) {
             // the lifetime of the TyPath!
-            (Some(rbv::ResolvedArg::EarlyBound(id)), ty::BoundRegionKind::Named(def_id, _)) => {
-                debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
-                if id.to_def_id() == def_id {
+            Some(rbv::ResolvedArg::EarlyBound(id)) => {
+                debug!("EarlyBound id={:?}", id);
+                if id.to_def_id() == self.region_def_id {
                     return ControlFlow::Break(());
                 }
             }
 
-            (
-                Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)),
-                ty::BoundRegionKind::Named(def_id, _),
-            ) => {
+            Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)) => {
                 debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,);
                 debug!("id={:?}", id);
-                debug!("def_id={:?}", def_id);
-                if debruijn_index == self.current_index && id.to_def_id() == def_id {
+                if debruijn_index == self.current_index && id.to_def_id() == self.region_def_id {
                     return ControlFlow::Break(());
                 }
             }
 
-            (
-                Some(
-                    rbv::ResolvedArg::StaticLifetime
-                    | rbv::ResolvedArg::EarlyBound(_)
-                    | rbv::ResolvedArg::LateBound(_, _, _)
-                    | rbv::ResolvedArg::Free(_, _)
-                    | rbv::ResolvedArg::Error(_),
-                )
-                | None,
-                _,
-            ) => {
+            Some(
+                rbv::ResolvedArg::StaticLifetime
+                | rbv::ResolvedArg::Free(_, _)
+                | rbv::ResolvedArg::Error(_),
+            )
+            | None => {
                 debug!("no arg found");
             }
         }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs
index 9fa5a8ac637..5befd81467b 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs
@@ -3,7 +3,7 @@
 
 use rustc_errors::Diag;
 use rustc_middle::ty;
-use rustc_span::symbol::kw;
+use rustc_span::kw;
 use tracing::debug;
 
 use crate::error_reporting::infer::nice_region_error::NiceRegionError;
@@ -54,9 +54,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         let param = anon_param_info.param;
         let new_ty = anon_param_info.param_ty;
         let new_ty_span = anon_param_info.param_ty_span;
-        let br = anon_param_info.bound_region;
+        let br = anon_param_info.br;
         let is_first = anon_param_info.is_first;
-        let scope_def_id = region_info.def_id;
+        let scope_def_id = region_info.scope;
         let is_impl_item = region_info.is_impl_item;
 
         match br {
@@ -73,7 +73,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             return None;
         }
 
-        if find_anon_type(self.tcx(), self.generic_param_scope, anon, &br).is_some()
+        if find_anon_type(self.tcx(), self.generic_param_scope, anon).is_some()
             && self.is_self_anon(is_first, scope_def_id)
         {
             return None;
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs
index 4398af76ab2..aaaefd81d19 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs
@@ -21,9 +21,10 @@ use crate::traits::{ObligationCause, ObligationCauseCode};
 // HACK(eddyb) maybe move this in a more central location.
 #[derive(Copy, Clone)]
 pub struct Highlighted<'tcx, T> {
-    tcx: TyCtxt<'tcx>,
-    highlight: RegionHighlightMode<'tcx>,
-    value: T,
+    pub tcx: TyCtxt<'tcx>,
+    pub highlight: RegionHighlightMode<'tcx>,
+    pub value: T,
+    pub ns: Namespace,
 }
 
 impl<'tcx, T> IntoDiagArg for Highlighted<'tcx, T>
@@ -37,7 +38,7 @@ where
 
 impl<'tcx, T> Highlighted<'tcx, T> {
     fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {
-        Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) }
+        Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value), ns: self.ns }
     }
 }
 
@@ -46,7 +47,7 @@ where
     T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
+        let mut printer = ty::print::FmtPrinter::new(self.tcx, self.ns);
         printer.region_highlight_mode = self.highlight;
 
         self.value.print(&mut printer)?;
@@ -381,6 +382,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
             tcx: self.tcx(),
             highlight: RegionHighlightMode::default(),
             value: trait_ref,
+            ns: Namespace::TypeNS,
         };
 
         let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
index 9a7bdaa5b57..3416a17624e 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
@@ -11,9 +11,8 @@ use rustc_hir::{
 use rustc_middle::ty::{
     self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
 };
-use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::Ident;
+use rustc_span::{Ident, Span};
 use tracing::debug;
 
 use crate::error_reporting::infer::nice_region_error::NiceRegionError;
@@ -50,7 +49,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                     // This may have a closure and it would cause ICE
                     // through `find_param_with_region` (#78262).
                     let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
-                    let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
+                    let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.scope);
                     if fn_returns.is_empty() {
                         return None;
                     }
@@ -196,7 +195,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 
         let mut err = self.tcx().dcx().create_err(diag);
 
-        let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
+        let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.scope);
 
         let mut override_error_code = None;
         if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin
@@ -250,7 +249,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             Some(arg),
             captures,
             Some((param.param_ty_span, param.param_ty.to_string())),
-            Some(anon_reg_sup.def_id),
+            Some(anon_reg_sup.scope),
         );
 
         let reported = err.emit();
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
index 592ade8ede2..95dd1b28a39 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
@@ -2,18 +2,19 @@
 
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
-use rustc_hir::def::Res;
+use rustc_hir::def::{Namespace, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::ExpectedFound;
 use rustc_middle::ty::print::RegionHighlightMode;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
+use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
 use rustc_span::Span;
 use tracing::debug;
 
 use crate::error_reporting::infer::nice_region_error::NiceRegionError;
+use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted;
 use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff};
 use crate::infer::{RegionResolutionError, Subtype, ValuePairs};
 
@@ -32,19 +33,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             _,
         ) = error.clone()
             && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin)
-            && let ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } =
+            && let &ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } =
                 sub_trace.cause.code()
             && sub_trace.values == sup_trace.values
             && let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values
         {
             // FIXME(compiler-errors): Don't like that this needs `Ty`s, but
             // all of the region highlighting machinery only deals with those.
-            let guar = self.emit_err(
-                var_origin.span(),
-                Ty::new_fn_ptr(self.cx.tcx, expected),
-                Ty::new_fn_ptr(self.cx.tcx, found),
-                *trait_item_def_id,
-            );
+            let guar = self.emit_err(var_origin.span(), expected, found, trait_item_def_id);
             return Some(guar);
         }
         None
@@ -53,11 +49,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     fn emit_err(
         &self,
         sp: Span,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-        trait_def_id: DefId,
+        expected: ty::PolyFnSig<'tcx>,
+        found: ty::PolyFnSig<'tcx>,
+        trait_item_def_id: DefId,
     ) -> ErrorGuaranteed {
-        let trait_sp = self.tcx().def_span(trait_def_id);
+        let trait_sp = self.tcx().def_span(trait_item_def_id);
 
         // Mark all unnamed regions in the type with a number.
         // This diagnostic is called in response to lifetime errors, so be informative.
@@ -67,10 +63,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         }
 
         impl<'tcx> HighlightBuilder<'tcx> {
-            fn build(ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> {
+            fn build(sig: ty::PolyFnSig<'tcx>) -> RegionHighlightMode<'tcx> {
                 let mut builder =
                     HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 };
-                builder.visit_ty(ty);
+                sig.visit_with(&mut builder);
                 builder.highlight
             }
         }
@@ -85,16 +81,21 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         }
 
         let expected_highlight = HighlightBuilder::build(expected);
-        let expected = self
-            .cx
-            .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight))
-            .name;
+        let tcx = self.cx.tcx;
+        let expected = Highlighted {
+            highlight: expected_highlight,
+            ns: Namespace::TypeNS,
+            tcx,
+            value: expected,
+        }
+        .to_string();
         let found_highlight = HighlightBuilder::build(found);
         let found =
-            self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name;
+            Highlighted { highlight: found_highlight, ns: Namespace::TypeNS, tcx, value: found }
+                .to_string();
 
         // Get the span of all the used type parameters in the method.
-        let assoc_item = self.tcx().associated_item(trait_def_id);
+        let assoc_item = self.tcx().associated_item(trait_item_def_id);
         let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
         match assoc_item.kind {
             ty::AssocKind::Fn => {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
index 218d2e753ef..a2150dc7c8c 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
@@ -2,7 +2,7 @@
 //! anonymous regions.
 
 use rustc_hir as hir;
-use rustc_hir::def_id::LocalDefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::fold::fold_regions;
 use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable};
 use rustc_span::Span;
@@ -17,8 +17,8 @@ pub struct AnonymousParamInfo<'tcx> {
     pub param: &'tcx hir::Param<'tcx>,
     /// The type corresponding to the anonymous region parameter.
     pub param_ty: Ty<'tcx>,
-    /// The ty::BoundRegionKind corresponding to the anonymous region.
-    pub bound_region: ty::BoundRegionKind,
+    /// The `ty::BoundRegionKind` corresponding to the anonymous region.
+    pub br: ty::BoundRegionKind,
     /// The `Span` of the parameter type.
     pub param_ty_span: Span,
     /// Signals that the argument is the first parameter in the declaration.
@@ -43,7 +43,7 @@ pub fn find_param_with_region<'tcx>(
     anon_region: Region<'tcx>,
     replace_region: Region<'tcx>,
 ) -> Option<AnonymousParamInfo<'tcx>> {
-    let (id, bound_region) = match *anon_region {
+    let (id, br) = match *anon_region {
         ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region),
         ty::ReEarlyParam(ebr) => {
             let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id;
@@ -96,13 +96,7 @@ pub fn find_param_with_region<'tcx>(
                 let ty_hir_id = fn_decl.inputs[index].hir_id;
                 let param_ty_span = hir.span(ty_hir_id);
                 let is_first = index == 0;
-                AnonymousParamInfo {
-                    param,
-                    param_ty: new_param_ty,
-                    param_ty_span,
-                    bound_region,
-                    is_first,
-                }
+                AnonymousParamInfo { param, param_ty: new_param_ty, param_ty_span, br, is_first }
             })
         })
 }
@@ -122,7 +116,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     pub(super) fn is_return_type_anon(
         &self,
         scope_def_id: LocalDefId,
-        br: ty::BoundRegionKind,
+        region_def_id: DefId,
         hir_sig: &hir::FnSig<'_>,
     ) -> Option<Span> {
         let fn_ty = self.tcx().type_of(scope_def_id).instantiate_identity();
@@ -135,8 +129,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                 None
             };
             return match future_output {
-                Some(output) if self.includes_region(output, br) => Some(span),
-                None if self.includes_region(ret_ty, br) => Some(span),
+                Some(output) if self.includes_region(output, region_def_id) => Some(span),
+                None if self.includes_region(ret_ty, region_def_id) => Some(span),
                 _ => None,
             };
         }
@@ -146,12 +140,15 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     fn includes_region(
         &self,
         ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>,
-        region: ty::BoundRegionKind,
+        region_def_id: DefId,
     ) -> bool {
         let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty);
         // We are only checking is any region meets the condition so order doesn't matter
         #[allow(rustc::potential_query_instability)]
-        late_bound_regions.iter().any(|r| *r == region)
+        late_bound_regions.iter().any(|r| match *r {
+            ty::BoundRegionKind::Named(def_id, _) => def_id == region_def_id,
+            _ => false,
+        })
     }
 
     // Here we check for the case where anonymous region
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs
index c4dac1a936c..beae9962f7f 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs
@@ -3,7 +3,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::{self, IsSuggestable, Region, Ty};
-use rustc_span::symbol::kw;
+use rustc_span::kw;
 use tracing::debug;
 
 use super::ObligationCauseAsDiagArg;
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
index 9fd7dccc57c..babf3ebc5a3 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
@@ -12,8 +12,7 @@ use rustc_middle::bug;
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _};
-use rustc_span::symbol::kw;
-use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol};
+use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol, kw};
 use rustc_type_ir::Upcast as _;
 use tracing::{debug, instrument};
 
@@ -790,7 +789,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 let lifetime_scope = match sub.kind() {
                     ty::ReStatic => hir::def_id::CRATE_DEF_ID,
                     _ => match self.tcx.is_suitable_region(generic_param_scope, sub) {
-                        Some(info) => info.def_id,
+                        Some(info) => info.scope,
                         None => generic_param_scope,
                     },
                 };
@@ -864,13 +863,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
         }
 
-        let (lifetime_def_id, lifetime_scope) =
-            match self.tcx.is_suitable_region(generic_param_scope, lifetime) {
-                Some(info) if !lifetime.has_name() => {
-                    (info.bound_region.get_id().unwrap().expect_local(), info.def_id)
-                }
-                _ => return lifetime.get_name_or_anon().to_string(),
-            };
+        let (lifetime_def_id, lifetime_scope) = match self
+            .tcx
+            .is_suitable_region(generic_param_scope, lifetime)
+        {
+            Some(info) if !lifetime.has_name() => (info.region_def_id.expect_local(), info.scope),
+            _ => return lifetime.get_name_or_anon().to_string(),
+        };
 
         let new_lt = {
             let generics = self.tcx.generics_of(lifetime_scope);
@@ -1097,8 +1096,7 @@ fn msg_span_from_named_region<'tcx>(
         }
         ty::ReLateParam(ref fr) => {
             if !fr.bound_region.is_named()
-                && let Some((ty, _)) =
-                    find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
+                && let Some((ty, _)) = find_anon_type(tcx, generic_param_scope, region)
             {
                 ("the anonymous lifetime defined here".to_string(), Some(ty.span))
             } else {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 4fb02f60943..d41f8f46c17 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -27,8 +27,7 @@ use rustc_middle::ty::{
     self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast,
 };
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::sym;
-use rustc_span::{BytePos, DUMMY_SP, Span, Symbol};
+use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, sym};
 use tracing::{debug, instrument};
 
 use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote};
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index f10314c1c9e..a401fcf3505 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -1,21 +1,21 @@
 use std::iter;
 use std::path::PathBuf;
 
-use rustc_ast::{AttrArgs, AttrKind, Attribute, MetaItemInner};
+use rustc_ast::MetaItemInner;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::codes::*;
 use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{AttrArgs, AttrKind, Attribute};
 use rustc_macros::LintDiagnostic;
 use rustc_middle::bug;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
 use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TyCtxt};
 use rustc_parse_format::{ParseMode, Parser, Piece, Position};
 use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
-use rustc_span::Span;
-use rustc_span::symbol::{Symbol, kw, sym};
+use rustc_span::{Span, Symbol, kw, sym};
 use tracing::{debug, info};
-use {rustc_attr as attr, rustc_hir as hir};
+use {rustc_attr_parsing as attr, rustc_hir as hir};
 
 use super::{ObligationCauseCode, PredicateObligation};
 use crate::error_reporting::TypeErrCtxt;
@@ -639,7 +639,7 @@ impl<'tcx> OnUnimplementedDirective {
                 let report_span = match &item.args {
                     AttrArgs::Empty => item.path.span,
                     AttrArgs::Delimited(args) => args.dspan.entire(),
-                    AttrArgs::Eq { eq_span, value } => eq_span.to(value.span()),
+                    AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span),
                 };
 
                 if let Some(item_def_id) = item_def_id.as_local() {
@@ -654,7 +654,7 @@ impl<'tcx> OnUnimplementedDirective {
             }
         } else if is_diagnostic_namespace_variant {
             match &attr.kind {
-                AttrKind::Normal(p) if !matches!(p.item.args, AttrArgs::Empty) => {
+                AttrKind::Normal(p) if !matches!(p.args, AttrArgs::Empty) => {
                     if let Some(item_def_id) = item_def_id.as_local() {
                         tcx.emit_node_span_lint(
                             UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index cc8941b9224..6a8f7f4ee35 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -38,8 +38,9 @@ use rustc_middle::ty::{
 };
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::{Ident, Symbol, kw, sym};
-use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, ExpnKind, MacroKind, Span};
+use rustc_span::{
+    BytePos, DUMMY_SP, DesugaringKind, ExpnKind, Ident, MacroKind, Span, Symbol, kw, sym,
+};
 use tracing::{debug, instrument};
 
 use super::{
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 68fe90f0de2..700c79a7065 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -14,8 +14,7 @@ use rustc_hir::{FnRetTy, GenericParamKind, Node};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
 use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt};
-use rustc_span::symbol::{Ident, Symbol, kw};
-use rustc_span::{BytePos, Span};
+use rustc_span::{BytePos, Ident, Span, Symbol, kw};
 
 use crate::error_reporting::infer::ObligationCauseAsDiagArg;
 use crate::error_reporting::infer::need_type_info::UnderspecifiedArgKind;
@@ -517,7 +516,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
                 return false;
             };
 
-            let node = self.tcx.hir_node_by_def_id(anon_reg.def_id);
+            let node = self.tcx.hir_node_by_def_id(anon_reg.scope);
             let is_impl = matches!(&node, hir::Node::ImplItem(_));
             let (generics, parent_generics) = match node {
                 hir::Node::Item(&hir::Item {
@@ -527,7 +526,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
                 | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. })
                 | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => (
                     generics,
-                    match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.def_id))
+                    match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.scope))
                     {
                         hir::Node::Item(hir::Item {
                             kind: hir::ItemKind::Trait(_, _, ref generics, ..),
diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
index cc0a637a78e..1ce5e6ba917 100644
--- a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
+++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
@@ -2,8 +2,7 @@ use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdi
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::bug;
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_span::Span;
-use rustc_span::symbol::kw;
+use rustc_span::{Span, kw};
 
 use crate::error_reporting::infer::nice_region_error::find_anon_type;
 use crate::fluent_generated as fluent;
@@ -41,8 +40,7 @@ impl<'a> DescriptionCtx<'a> {
             }
             ty::ReLateParam(ref fr) => {
                 if !fr.bound_region.is_named()
-                    && let Some((ty, _)) =
-                        find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
+                    && let Some((ty, _)) = find_anon_type(tcx, generic_param_scope, region)
                 {
                     (Some(ty.span), "defined_here", String::new())
                 } else {
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 11d72106b22..1af383e9200 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -23,6 +23,7 @@
 #![feature(extract_if)]
 #![feature(if_let_guard)]
 #![feature(iter_intersperse)]
+#![feature(iterator_try_reduce)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index a98871b2d60..971d3a81102 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -21,8 +21,7 @@ use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableEx
 use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode};
 pub use rustc_next_trait_solver::coherence::*;
 use rustc_next_trait_solver::solve::SolverDelegateEvalExt;
-use rustc_span::symbol::sym;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, sym};
 use tracing::{debug, instrument, warn};
 
 use super::ObligationCtxt;
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index d216ae72913..7a67b943e94 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -84,7 +84,7 @@ pub fn type_allowed_to_implement_copy<'tcx>(
         return Err(CopyImplementationError::HasDestructor);
     }
 
-    if impl_safety == hir::Safety::Safe && self_type.has_unsafe_fields() {
+    if impl_safety.is_safe() && self_type.has_unsafe_fields() {
         return Err(CopyImplementationError::HasUnsafeFields);
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 069fab6a6e6..069d42d4018 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -447,7 +447,7 @@ pub fn normalize_param_env_or_error<'tcx>(
     // This works fairly well because trait matching does not actually care about param-env
     // TypeOutlives predicates - these are normally used by regionck.
     let outlives_predicates: Vec<_> = predicates
-        .extract_if(|predicate| {
+        .extract_if(.., |predicate| {
             matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..))
         })
         .collect();
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 5f1dbfeedfb..4bccd3450bc 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, Term, Ty, TyCtxt, TypingMode, Upcast};
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::sym;
+use rustc_span::sym;
 use thin_vec::thin_vec;
 use tracing::{debug, instrument};
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 25fe43e3a0e..0462b1d9ee7 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -31,8 +31,7 @@ use rustc_middle::ty::{
     self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt,
     TypingMode, Upcast,
 };
-use rustc_span::Symbol;
-use rustc_span::symbol::sym;
+use rustc_span::{Symbol, sym};
 use tracing::{debug, instrument, trace};
 
 use self::EvaluationResult::*;
@@ -445,7 +444,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         // Winnow, but record the exact outcome of evaluation, which
         // is needed for specialization. Propagate overflow if it occurs.
-        let mut candidates = candidates
+        let candidates = candidates
             .into_iter()
             .map(|c| match self.evaluate_candidate(stack, &c) {
                 Ok(eval) if eval.may_apply() => {
@@ -458,40 +457,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             .flat_map(Result::transpose)
             .collect::<Result<Vec<_>, _>>()?;
 
-        debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len());
-
-        let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
-
-        // If there are STILL multiple candidates, we can further
-        // reduce the list by dropping duplicates -- including
-        // resolving specializations.
-        if candidates.len() > 1 {
-            let mut i = 0;
-            while i < candidates.len() {
-                let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
-                    self.candidate_should_be_dropped_in_favor_of(
-                        &candidates[i],
-                        &candidates[j],
-                        has_non_region_infer,
-                    ) == DropVictim::Yes
-                });
-                if should_drop_i {
-                    debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
-                    candidates.swap_remove(i);
-                } else {
-                    debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
-                    i += 1;
-
-                    // If there are *STILL* multiple candidates, give up
-                    // and report ambiguity.
-                    if i > 1 {
-                        debug!("multiple matches, ambig");
-                        return Ok(None);
-                    }
-                }
-            }
-        }
-
+        debug!(?stack, ?candidates, "{} potentially applicable candidates", candidates.len());
         // If there are *NO* candidates, then there are no impls --
         // that we know of, anyway. Note that in the case where there
         // are unbound type variables within the obligation, it might
@@ -508,13 +474,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // to have emitted at least one.
             if stack.obligation.predicate.references_error() {
                 debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous");
-                return Ok(None);
+                Ok(None)
+            } else {
+                Err(Unimplemented)
+            }
+        } else {
+            let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
+            if let Some(candidate) = self.winnow_candidates(has_non_region_infer, candidates) {
+                self.filter_reservation_impls(candidate)
+            } else {
+                Ok(None)
             }
-            return Err(Unimplemented);
         }
-
-        // Just one candidate left.
-        self.filter_reservation_impls(candidates.pop().unwrap().candidate)
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -1803,18 +1774,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-enum DropVictim {
-    Yes,
-    No,
-}
-
-impl DropVictim {
-    fn drop_if(should_drop: bool) -> DropVictim {
-        if should_drop { DropVictim::Yes } else { DropVictim::No }
-    }
-}
-
 /// ## Winnowing
 ///
 /// Winnowing is the process of attempting to resolve ambiguity by
@@ -1822,131 +1781,149 @@ impl DropVictim {
 /// type variables and then we also attempt to evaluate recursive
 /// bounds to see if they are satisfied.
 impl<'tcx> SelectionContext<'_, 'tcx> {
-    /// Returns `DropVictim::Yes` if `victim` should be dropped in favor of
-    /// `other`. Generally speaking we will drop duplicate
-    /// candidates and prefer where-clause candidates.
+    /// If there are multiple ways to prove a trait goal, we make some
+    /// *fairly arbitrary* choices about which candidate is actually used.
     ///
-    /// See the comment for "SelectionCandidate" for more details.
-    #[instrument(level = "debug", skip(self))]
-    fn candidate_should_be_dropped_in_favor_of(
+    /// For more details, look at the implementation of this method :)
+    #[instrument(level = "debug", skip(self), ret)]
+    fn winnow_candidates(
         &mut self,
-        victim: &EvaluatedCandidate<'tcx>,
-        other: &EvaluatedCandidate<'tcx>,
         has_non_region_infer: bool,
-    ) -> DropVictim {
-        if victim.candidate == other.candidate {
-            return DropVictim::Yes;
+        mut candidates: Vec<EvaluatedCandidate<'tcx>>,
+    ) -> Option<SelectionCandidate<'tcx>> {
+        if candidates.len() == 1 {
+            return Some(candidates.pop().unwrap().candidate);
+        }
+
+        // We prefer trivial builtin candidates, i.e. builtin impls without any nested
+        // requirements, over all others. This is a fix for #53123 and prevents winnowing
+        // from accidentally extending the lifetime of a variable.
+        let mut trivial_builtin = candidates
+            .iter()
+            .filter(|c| matches!(c.candidate, BuiltinCandidate { has_nested: false }));
+        if let Some(_trivial) = trivial_builtin.next() {
+            // There should only ever be a single trivial builtin candidate
+            // as they would otherwise overlap.
+            debug_assert_eq!(trivial_builtin.next(), None);
+            return Some(BuiltinCandidate { has_nested: false });
         }
 
-        // Check if a bound would previously have been removed when normalizing
-        // the param_env so that it can be given the lowest priority. See
-        // #50825 for the motivation for this.
-        let is_global =
-            |cand: ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars();
+        // Before we consider where-bounds, we have to deduplicate them here and also
+        // drop where-bounds in case the same where-bound exists without bound vars.
+        // This is necessary as elaborating super-trait bounds may result in duplicates.
+        'search_victim: loop {
+            for (i, this) in candidates.iter().enumerate() {
+                let ParamCandidate(this) = this.candidate else { continue };
+                for (j, other) in candidates.iter().enumerate() {
+                    if i == j {
+                        continue;
+                    }
 
-        // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`,
-        // or `DiscriminantKindCandidate` to anything else.
-        //
-        // This is a fix for #53123 and prevents winnowing from accidentally extending the
-        // lifetime of a variable.
-        match (&other.candidate, &victim.candidate) {
-            // FIXME(@jswrenn): this should probably be more sophisticated
-            (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No,
-
-            // (*)
-            (BuiltinCandidate { has_nested: false }, _) => DropVictim::Yes,
-            (_, BuiltinCandidate { has_nested: false }) => DropVictim::No,
-
-            (ParamCandidate(other), ParamCandidate(victim)) => {
-                let same_except_bound_vars = other.skip_binder().trait_ref
-                    == victim.skip_binder().trait_ref
-                    && other.skip_binder().polarity == victim.skip_binder().polarity
-                    && !other.skip_binder().trait_ref.has_escaping_bound_vars();
-                if same_except_bound_vars {
-                    // See issue #84398. In short, we can generate multiple ParamCandidates which are
-                    // the same except for unused bound vars. Just pick the one with the fewest bound vars
-                    // or the current one if tied (they should both evaluate to the same answer). This is
-                    // probably best characterized as a "hack", since we might prefer to just do our
-                    // best to *not* create essentially duplicate candidates in the first place.
-                    DropVictim::drop_if(other.bound_vars().len() <= victim.bound_vars().len())
-                } else {
-                    DropVictim::No
+                    let ParamCandidate(other) = other.candidate else { continue };
+                    if this == other {
+                        candidates.remove(j);
+                        continue 'search_victim;
+                    }
+
+                    if this.skip_binder().trait_ref == other.skip_binder().trait_ref
+                        && this.skip_binder().polarity == other.skip_binder().polarity
+                        && !this.skip_binder().trait_ref.has_escaping_bound_vars()
+                    {
+                        candidates.remove(j);
+                        continue 'search_victim;
+                    }
                 }
             }
 
-            (
-                ParamCandidate(other_cand),
-                ImplCandidate(..)
-                | AutoImplCandidate
-                | ClosureCandidate { .. }
-                | AsyncClosureCandidate
-                | AsyncFnKindHelperCandidate
-                | CoroutineCandidate
-                | FutureCandidate
-                | IteratorCandidate
-                | AsyncIteratorCandidate
-                | FnPointerCandidate { .. }
-                | BuiltinObjectCandidate
-                | BuiltinUnsizeCandidate
-                | TraitUpcastingUnsizeCandidate(_)
-                | BuiltinCandidate { .. }
-                | TraitAliasCandidate
-                | ObjectCandidate(_)
-                | ProjectionCandidate(_),
-            ) => {
-                // We have a where clause so don't go around looking
-                // for impls. Arbitrarily give param candidates priority
-                // over projection and object candidates.
-                //
-                // Global bounds from the where clause should be ignored
-                // here (see issue #50825).
-                DropVictim::drop_if(!is_global(*other_cand))
-            }
-            (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(victim_cand)) => {
-                // Prefer these to a global where-clause bound
-                // (see issue #50825).
-                if is_global(*victim_cand) { DropVictim::Yes } else { DropVictim::No }
+            break;
+        }
+
+        // The next highest priority is for non-global where-bounds. However, while we don't
+        // prefer global where-clauses here, we do bail with ambiguity when encountering both
+        // a global and a non-global where-clause.
+        //
+        // Our handling of where-bounds is generally fairly messy but necessary for backwards
+        // compatability, see #50825 for why we need to handle global where-bounds like this.
+        let is_global = |c: ty::PolyTraitPredicate<'tcx>| c.is_global() && !c.has_bound_vars();
+        let param_candidates = candidates
+            .iter()
+            .filter_map(|c| if let ParamCandidate(p) = c.candidate { Some(p) } else { None });
+        let mut has_global_bounds = false;
+        let mut param_candidate = None;
+        for c in param_candidates {
+            if is_global(c) {
+                has_global_bounds = true;
+            } else if param_candidate.replace(c).is_some() {
+                // Ambiguity, two potentially different where-clauses
+                return None;
             }
-            (
-                ImplCandidate(_)
-                | AutoImplCandidate
-                | ClosureCandidate { .. }
-                | AsyncClosureCandidate
-                | AsyncFnKindHelperCandidate
-                | CoroutineCandidate
-                | FutureCandidate
-                | IteratorCandidate
-                | AsyncIteratorCandidate
-                | FnPointerCandidate { .. }
-                | BuiltinObjectCandidate
-                | BuiltinUnsizeCandidate
-                | TraitUpcastingUnsizeCandidate(_)
-                | BuiltinCandidate { has_nested: true }
-                | TraitAliasCandidate,
-                ParamCandidate(victim_cand),
-            ) => {
-                // Prefer these to a global where-clause bound
-                // (see issue #50825).
-                DropVictim::drop_if(
-                    is_global(*victim_cand) && other.evaluation.must_apply_modulo_regions(),
-                )
+        }
+        if let Some(predicate) = param_candidate {
+            // Ambiguity, a global and a non-global where-bound.
+            if has_global_bounds {
+                return None;
+            } else {
+                return Some(ParamCandidate(predicate));
             }
+        }
+
+        // Prefer alias-bounds over blanket impls for rigid associated types. This is
+        // fairly arbitrary but once again necessary for backwards compatibility.
+        // If there are multiple applicable candidates which don't affect type inference,
+        // choose the one with the lowest index.
+        let alias_bound = candidates
+            .iter()
+            .filter_map(|c| if let ProjectionCandidate(i) = c.candidate { Some(i) } else { None })
+            .try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
+        match alias_bound {
+            Some(Some(index)) => return Some(ProjectionCandidate(index)),
+            Some(None) => {}
+            None => return None,
+        }
+
+        // Need to prioritize builtin trait object impls as `<dyn Any as Any>::type_id`
+        // should use the vtable method and not the method provided by the user-defined
+        // impl `impl<T: ?Sized> Any for T { .. }`. This really shouldn't exist but is
+        // necessary due to #57893. We again arbitrarily prefer the applicable candidate
+        // with the lowest index.
+        let object_bound = candidates
+            .iter()
+            .filter_map(|c| if let ObjectCandidate(i) = c.candidate { Some(i) } else { None })
+            .try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
+        match object_bound {
+            Some(Some(index)) => return Some(ObjectCandidate(index)),
+            Some(None) => {}
+            None => return None,
+        }
 
-            (ProjectionCandidate(i), ProjectionCandidate(j))
-            | (ObjectCandidate(i), ObjectCandidate(j)) => {
-                // Arbitrarily pick the lower numbered candidate for backwards
-                // compatibility reasons. Don't let this affect inference.
-                DropVictim::drop_if(i < j && !has_non_region_infer)
+        // Finally, handle overlapping user-written impls.
+        let impls = candidates.iter().filter_map(|c| {
+            if let ImplCandidate(def_id) = c.candidate {
+                Some((def_id, c.evaluation))
+            } else {
+                None
             }
-            (ObjectCandidate(_), ProjectionCandidate(_))
-            | (ProjectionCandidate(_), ObjectCandidate(_)) => {
-                bug!("Have both object and projection candidate")
+        });
+        let mut impl_candidate = None;
+        for c in impls {
+            if let Some(prev) = impl_candidate.replace(c) {
+                if self.prefer_lhs_over_victim(has_non_region_infer, c, prev) {
+                    // Ok, prefer `c` over the previous entry
+                } else if self.prefer_lhs_over_victim(has_non_region_infer, prev, c) {
+                    // Ok, keep `prev` instead of the new entry
+                    impl_candidate = Some(prev);
+                } else {
+                    // Ambiguity, two potentially different where-clauses
+                    return None;
+                }
             }
-
-            // Arbitrarily give projection and object candidates priority.
-            (
-                ObjectCandidate(_) | ProjectionCandidate(_),
-                ImplCandidate(..)
+        }
+        if let Some((def_id, _evaluation)) = impl_candidate {
+            // Don't use impl candidates which overlap with other candidates.
+            // This should pretty much only ever happen with malformed impls.
+            if candidates.iter().all(|c| match c.candidate {
+                BuiltinCandidate { has_nested: _ }
+                | TransmutabilityCandidate
                 | AutoImplCandidate
                 | ClosureCandidate { .. }
                 | AsyncClosureCandidate
@@ -1955,155 +1932,113 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 | FutureCandidate
                 | IteratorCandidate
                 | AsyncIteratorCandidate
-                | FnPointerCandidate { .. }
-                | BuiltinObjectCandidate
-                | BuiltinUnsizeCandidate
+                | FnPointerCandidate
+                | TraitAliasCandidate
                 | TraitUpcastingUnsizeCandidate(_)
-                | BuiltinCandidate { .. }
-                | TraitAliasCandidate,
-            ) => DropVictim::Yes,
-
-            (
-                ImplCandidate(..)
-                | AutoImplCandidate
-                | ClosureCandidate { .. }
-                | AsyncClosureCandidate
-                | AsyncFnKindHelperCandidate
-                | CoroutineCandidate
-                | FutureCandidate
-                | IteratorCandidate
-                | AsyncIteratorCandidate
-                | FnPointerCandidate { .. }
                 | BuiltinObjectCandidate
-                | BuiltinUnsizeCandidate
-                | TraitUpcastingUnsizeCandidate(_)
-                | BuiltinCandidate { .. }
-                | TraitAliasCandidate,
-                ObjectCandidate(_) | ProjectionCandidate(_),
-            ) => DropVictim::No,
-
-            (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => {
-                // See if we can toss out `victim` based on specialization.
-                // While this requires us to know *for sure* that the `other` impl applies
-                // we still use modulo regions here.
-                //
-                // This is fine as specialization currently assumes that specializing
-                // impls have to be always applicable, meaning that the only allowed
-                // region constraints may be constraints also present on the default impl.
-                let tcx = self.tcx();
-                if other.evaluation.must_apply_modulo_regions()
-                    && tcx.specializes((other_def, victim_def))
-                {
-                    return DropVictim::Yes;
-                }
-
-                match tcx.impls_are_allowed_to_overlap(other_def, victim_def) {
-                    // For #33140 the impl headers must be exactly equal, the trait must not have
-                    // any associated items and there are no where-clauses.
-                    //
-                    // We can just arbitrarily drop one of the impls.
-                    Some(ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects) => {
-                        assert_eq!(other.evaluation, victim.evaluation);
-                        DropVictim::Yes
-                    }
-                    // For candidates which already reference errors it doesn't really
-                    // matter what we do 🤷
-                    Some(ty::ImplOverlapKind::Permitted { marker: false }) => {
-                        DropVictim::drop_if(other.evaluation.must_apply_considering_regions())
-                    }
-                    Some(ty::ImplOverlapKind::Permitted { marker: true }) => {
-                        // Subtle: If the predicate we are evaluating has inference
-                        // variables, do *not* allow discarding candidates due to
-                        // marker trait impls.
-                        //
-                        // Without this restriction, we could end up accidentally
-                        // constraining inference variables based on an arbitrarily
-                        // chosen trait impl.
-                        //
-                        // Imagine we have the following code:
-                        //
-                        // ```rust
-                        // #[marker] trait MyTrait {}
-                        // impl MyTrait for u8 {}
-                        // impl MyTrait for bool {}
-                        // ```
-                        //
-                        // And we are evaluating the predicate `<_#0t as MyTrait>`.
-                        //
-                        // During selection, we will end up with one candidate for each
-                        // impl of `MyTrait`. If we were to discard one impl in favor
-                        // of the other, we would be left with one candidate, causing
-                        // us to "successfully" select the predicate, unifying
-                        // _#0t with (for example) `u8`.
-                        //
-                        // However, we have no reason to believe that this unification
-                        // is correct - we've essentially just picked an arbitrary
-                        // *possibility* for _#0t, and required that this be the *only*
-                        // possibility.
-                        //
-                        // Eventually, we will either:
-                        // 1) Unify all inference variables in the predicate through
-                        // some other means (e.g. type-checking of a function). We will
-                        // then be in a position to drop marker trait candidates
-                        // without constraining inference variables (since there are
-                        // none left to constrain)
-                        // 2) Be left with some unconstrained inference variables. We
-                        // will then correctly report an inference error, since the
-                        // existence of multiple marker trait impls tells us nothing
-                        // about which one should actually apply.
-                        DropVictim::drop_if(
-                            !has_non_region_infer
-                                && other.evaluation.must_apply_considering_regions(),
-                        )
-                    }
-                    None => DropVictim::No,
-                }
+                | BuiltinUnsizeCandidate => false,
+                // Non-global param candidates have already been handled, global
+                // where-bounds get ignored.
+                ParamCandidate(_) | ImplCandidate(_) => true,
+                ProjectionCandidate(_) | ObjectCandidate(_) => unreachable!(),
+            }) {
+                return Some(ImplCandidate(def_id));
+            } else {
+                return None;
             }
+        }
 
-            (AutoImplCandidate, ImplCandidate(_)) | (ImplCandidate(_), AutoImplCandidate) => {
-                DropVictim::No
-            }
+        if candidates.len() == 1 {
+            Some(candidates.pop().unwrap().candidate)
+        } else {
+            // Also try ignoring all global where-bounds and check whether we end
+            // with a unique candidate in this case.
+            let mut not_a_global_where_bound = candidates
+                .into_iter()
+                .filter(|c| !matches!(c.candidate, ParamCandidate(p) if is_global(p)));
+            not_a_global_where_bound
+                .next()
+                .map(|c| c.candidate)
+                .filter(|_| not_a_global_where_bound.next().is_none())
+        }
+    }
 
-            (AutoImplCandidate, _) | (_, AutoImplCandidate) => {
-                bug!(
-                    "default implementations shouldn't be recorded \
-                    when there are other global candidates: {:?} {:?}",
-                    other,
-                    victim
-                );
+    fn prefer_lhs_over_victim(
+        &self,
+        has_non_region_infer: bool,
+        (lhs, lhs_evaluation): (DefId, EvaluationResult),
+        (victim, victim_evaluation): (DefId, EvaluationResult),
+    ) -> bool {
+        let tcx = self.tcx();
+        // See if we can toss out `victim` based on specialization.
+        //
+        // While this requires us to know *for sure* that the `lhs` impl applies
+        // we still use modulo regions here. This is fine as specialization currently
+        // assumes that specializing impls have to be always applicable, meaning that
+        // the only allowed region constraints may be constraints also present on the default impl.
+        if lhs_evaluation.must_apply_modulo_regions() {
+            if tcx.specializes((lhs, victim)) {
+                return true;
             }
+        }
 
-            // Everything else is ambiguous
-            (
-                ImplCandidate(_)
-                | ClosureCandidate { .. }
-                | AsyncClosureCandidate
-                | AsyncFnKindHelperCandidate
-                | CoroutineCandidate
-                | FutureCandidate
-                | IteratorCandidate
-                | AsyncIteratorCandidate
-                | FnPointerCandidate { .. }
-                | BuiltinObjectCandidate
-                | BuiltinUnsizeCandidate
-                | TraitUpcastingUnsizeCandidate(_)
-                | BuiltinCandidate { has_nested: true }
-                | TraitAliasCandidate,
-                ImplCandidate(_)
-                | ClosureCandidate { .. }
-                | AsyncClosureCandidate
-                | AsyncFnKindHelperCandidate
-                | CoroutineCandidate
-                | FutureCandidate
-                | IteratorCandidate
-                | AsyncIteratorCandidate
-                | FnPointerCandidate { .. }
-                | BuiltinObjectCandidate
-                | BuiltinUnsizeCandidate
-                | TraitUpcastingUnsizeCandidate(_)
-                | BuiltinCandidate { has_nested: true }
-                | TraitAliasCandidate,
-            ) => DropVictim::No,
+        match tcx.impls_are_allowed_to_overlap(lhs, victim) {
+            // For #33140 the impl headers must be exactly equal, the trait must not have
+            // any associated items and there are no where-clauses.
+            //
+            // We can just arbitrarily drop one of the impls.
+            Some(ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects) => {
+                assert_eq!(lhs_evaluation, victim_evaluation);
+                true
+            }
+            // For candidates which already reference errors it doesn't really
+            // matter what we do 🤷
+            Some(ty::ImplOverlapKind::Permitted { marker: false }) => {
+                lhs_evaluation.must_apply_considering_regions()
+            }
+            Some(ty::ImplOverlapKind::Permitted { marker: true }) => {
+                // Subtle: If the predicate we are evaluating has inference
+                // variables, do *not* allow discarding candidates due to
+                // marker trait impls.
+                //
+                // Without this restriction, we could end up accidentally
+                // constraining inference variables based on an arbitrarily
+                // chosen trait impl.
+                //
+                // Imagine we have the following code:
+                //
+                // ```rust
+                // #[marker] trait MyTrait {}
+                // impl MyTrait for u8 {}
+                // impl MyTrait for bool {}
+                // ```
+                //
+                // And we are evaluating the predicate `<_#0t as MyTrait>`.
+                //
+                // During selection, we will end up with one candidate for each
+                // impl of `MyTrait`. If we were to discard one impl in favor
+                // of the other, we would be left with one candidate, causing
+                // us to "successfully" select the predicate, unifying
+                // _#0t with (for example) `u8`.
+                //
+                // However, we have no reason to believe that this unification
+                // is correct - we've essentially just picked an arbitrary
+                // *possibility* for _#0t, and required that this be the *only*
+                // possibility.
+                //
+                // Eventually, we will either:
+                // 1) Unify all inference variables in the predicate through
+                // some other means (e.g. type-checking of a function). We will
+                // then be in a position to drop marker trait candidates
+                // without constraining inference variables (since there are
+                // none left to constrain)
+                // 2) Be left with some unconstrained inference variables. We
+                // will then correctly report an inference error, since the
+                // existence of multiple marker trait impls tells us nothing
+                // about which one should actually apply.
+                !has_non_region_infer && lhs_evaluation.must_apply_considering_regions()
+            }
+            None => false,
         }
     }
 }
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index f7651e49cdd..ad86813c87e 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -131,7 +131,7 @@ mod rustc {
             c: Const<'tcx>,
         ) -> Option<Self> {
             use rustc_middle::ty::ScalarInt;
-            use rustc_span::symbol::sym;
+            use rustc_span::sym;
 
             let Some((cv, ty)) = c.try_to_valtree() else {
                 return None;
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index f97e8d48c8e..8f9f66db1bd 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -6,7 +6,7 @@ use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt};
 use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::kw;
+use rustc_span::kw;
 
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 0d656f1b63b..7c7c3803ad9 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -23,8 +23,7 @@ use rustc_middle::ty::{
     TypeVisitableExt,
 };
 use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
-use rustc_span::sym;
-use rustc_span::symbol::Symbol;
+use rustc_span::{Symbol, sym};
 use tracing::{debug, instrument, trace};
 use {rustc_abi as abi, rustc_hir as hir};