about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-06-24 21:58:31 +0000
committerbors <bors@rust-lang.org>2025-06-24 21:58:31 +0000
commit3de5b08ef6b260277dd4c77f7472fe6904bd6002 (patch)
treef55214c62cfdd5e3f435b0e1e337fa2d0a0fd065
parent28f1c807911c63f08d98e7b468cfcf15a441e34b (diff)
parentf299456597d36da11705f427102e782aafed38b1 (diff)
downloadrust-3de5b08ef6b260277dd4c77f7472fe6904bd6002.tar.gz
rust-3de5b08ef6b260277dd4c77f7472fe6904bd6002.zip
Auto merge of #142979 - matthiaskrgr:rollup-szqah4e, r=matthiaskrgr
Rollup of 9 pull requests

Successful merges:

 - rust-lang/rust#142645 (Also emit suggestions for usages in the `non_upper_case_globals` lint)
 - rust-lang/rust#142657 (mbe: Clean up code with non-optional `NonterminalKind`)
 - rust-lang/rust#142799 (rustc_session: Add a structure for keeping both explicit and default sysroots)
 - rust-lang/rust#142805 (Emit a single error when importing a path with `_`)
 - rust-lang/rust#142882 (Lazy init diagnostics-only local_names in borrowck)
 - rust-lang/rust#142883 (Add impl_trait_in_bindings tests from rust-lang/rust#61773)
 - rust-lang/rust#142943 (Don't include current rustc version string in feature removed help)
 - rust-lang/rust#142965 ([RTE-497] Ignore `c-link-to-rust-va-list-fn` test on SGX platform)
 - rust-lang/rust#142972 (Add a missing mailmap entry)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--.mailmap4
-rw-r--r--Cargo.lock2
-rw-r--r--compiler/rustc_ast/src/token.rs1
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs11
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs43
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs10
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs4
-rw-r--r--compiler/rustc_borrowck/src/lib.rs30
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs10
-rw-r--r--compiler/rustc_error_messages/Cargo.toml1
-rw-r--r--compiler/rustc_error_messages/src/lib.rs8
-rw-r--r--compiler/rustc_expand/messages.ftl2
-rw-r--r--compiler/rustc_expand/src/config.rs1
-rw-r--r--compiler/rustc_expand/src/errors.rs3
-rw-r--r--compiler/rustc_expand/src/mbe.rs10
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs1
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs12
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs53
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs49
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs128
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs4
-rw-r--r--compiler/rustc_interface/src/interface.rs13
-rw-r--r--compiler/rustc_interface/src/tests.rs9
-rw-r--r--compiler/rustc_interface/src/util.rs36
-rw-r--r--compiler/rustc_lint/src/context.rs14
-rw-r--r--compiler/rustc_lint/src/lints.rs19
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs107
-rw-r--r--compiler/rustc_metadata/src/locator.rs2
-rw-r--r--compiler/rustc_resolve/src/imports.rs18
-rw-r--r--compiler/rustc_session/Cargo.toml1
-rw-r--r--compiler/rustc_session/src/config.rs31
-rw-r--r--compiler/rustc_session/src/filesearch.rs18
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--compiler/rustc_session/src/session.rs16
-rw-r--r--src/librustdoc/config.rs14
-rw-r--r--src/librustdoc/doctest.rs7
-rw-r--r--tests/run-make/c-link-to-rust-va-list-fn/rmake.rs1
-rw-r--r--tests/ui-fulldeps/run-compiler-twice.rs6
-rw-r--r--tests/ui/deprecation/deprecated_no_stack_check.rs2
-rw-r--r--tests/ui/deprecation/deprecated_no_stack_check.stderr4
-rw-r--r--tests/ui/feature-gates/feature-gate-coverage-attribute.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-coverage-attribute.stderr6
-rw-r--r--tests/ui/feature-gates/gated-bad-feature.rs1
-rw-r--r--tests/ui/feature-gates/gated-bad-feature.stderr16
-rw-r--r--tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs2
-rw-r--r--tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr6
-rw-r--r--tests/ui/impl-trait/in-bindings/lifetime-equality.rs19
-rw-r--r--tests/ui/impl-trait/in-bindings/region-lifetimes.rs17
-rw-r--r--tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2015.stderr (renamed from tests/ui/imports/multiple-extern-by-macro-for-underscore.stderr)2
-rw-r--r--tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2021.stderr8
-rw-r--r--tests/ui/imports/multiple-extern-by-macro-for-underscore.rs4
-rw-r--r--tests/ui/lint/lint-non-uppercase-usages.fixed44
-rw-r--r--tests/ui/lint/lint-non-uppercase-usages.rs44
-rw-r--r--tests/ui/lint/lint-non-uppercase-usages.stderr39
-rw-r--r--tests/ui/macros/macro-match-nonterminal.rs3
-rw-r--r--tests/ui/macros/macro-match-nonterminal.stderr11
-rw-r--r--tests/ui/macros/macro-missing-fragment-deduplication.rs9
-rw-r--r--tests/ui/macros/macro-missing-fragment-deduplication.stderr58
-rw-r--r--tests/ui/macros/macro-missing-fragment.rs1
-rw-r--r--tests/ui/macros/macro-missing-fragment.stderr12
-rw-r--r--tests/ui/macros/macro-reexport-removed.rs1
-rw-r--r--tests/ui/macros/macro-reexport-removed.stderr6
-rw-r--r--tests/ui/parser/macro/issue-33569.rs3
-rw-r--r--tests/ui/parser/macro/issue-33569.stderr23
-rw-r--r--tests/ui/rustdoc/renamed-features-rustdoc_internals.rs2
-rw-r--r--tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr8
-rw-r--r--tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs1
-rw-r--r--tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr6
-rw-r--r--tests/ui/underscore-imports/issue-110164.ed2015.stderr (renamed from tests/ui/underscore-imports/issue-110164.stderr)36
-rw-r--r--tests/ui/underscore-imports/issue-110164.ed2021.stderr39
-rw-r--r--tests/ui/underscore-imports/issue-110164.rs7
-rw-r--r--tests/ui/underscore-imports/multiple-uses.ed2015.stderr49
-rw-r--r--tests/ui/underscore-imports/multiple-uses.ed2021.stderr49
-rw-r--r--tests/ui/underscore-imports/multiple-uses.rs16
-rw-r--r--tests/ui/unsized-locals/yote.rs2
-rw-r--r--tests/ui/unsized-locals/yote.stderr4
80 files changed, 881 insertions, 406 deletions
diff --git a/.mailmap b/.mailmap
index f55200c3fe9..2a53cbf9eff 100644
--- a/.mailmap
+++ b/.mailmap
@@ -162,8 +162,10 @@ David Carlier <devnexen@gmail.com>
 David Klein <david.klein@baesystemsdetica.com>
 David Manescu <david.manescu@gmail.com> <dman2626@uni.sydney.edu.au>
 David Ross <daboross@daboross.net>
-David Wood <david@davidtw.co> <david.wood@huawei.com>
+David Wood <david@davidtw.co> <Q0KPU0H1YOEPHRY1R2SN5B5RL@david.davidtw.co>
+David Wood <david@davidtw.co> <agile.lion3441@fuligin.ink>
 David Wood <david@davidtw.co> <david.wood2@arm.com>
+David Wood <david@davidtw.co> <david.wood@huawei.com>
 Deadbeef <ent3rm4n@gmail.com>
 Deadbeef <ent3rm4n@gmail.com> <fee1-dead-beef@protonmail.com>
 dependabot[bot] <dependabot[bot]@users.noreply.github.com> <27856297+dependabot-preview[bot]@users.noreply.github.com>
diff --git a/Cargo.lock b/Cargo.lock
index a170ece0a0d..7e798d6d261 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3655,7 +3655,6 @@ dependencies = [
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
- "smallvec",
  "tracing",
  "unic-langid",
 ]
@@ -4446,7 +4445,6 @@ dependencies = [
  "rustc_serialize",
  "rustc_span",
  "rustc_target",
- "smallvec",
  "termize",
  "tracing",
  "windows",
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 54781e8235e..9b4535dcfbc 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -1085,6 +1085,7 @@ pub enum NtExprKind {
     Expr2021 { inferred: bool },
 }
 
+/// A macro nonterminal, known in documentation as a fragment specifier.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)]
 pub enum NonterminalKind {
     Item,
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 095c0df98ac..f9e52239d6f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -71,7 +71,6 @@ impl<'tcx> BorrowExplanation<'tcx> {
     ) {
         let tcx = cx.infcx.tcx;
         let body = cx.body;
-        let local_names = &cx.local_names;
 
         if let Some(span) = borrow_span {
             let def_id = body.source.def_id();
@@ -220,7 +219,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
                     _ => ("destructor", format!("type `{}`", local_decl.ty)),
                 };
 
-                match local_names[dropped_local] {
+                match cx.local_name(dropped_local) {
                     Some(local_name) if !local_decl.from_compiler_desugaring() => {
                         let message = format!(
                             "{borrow_desc}borrow might be used here, when `{local_name}` is dropped \
@@ -670,10 +669,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
 
             Some(Cause::DropVar(local, location)) => {
                 let mut should_note_order = false;
-                if self.local_names[local].is_some()
+                if self.local_name(local).is_some()
                     && let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place
                     && let Some(borrowed_local) = place.as_local()
-                    && self.local_names[borrowed_local].is_some()
+                    && self.local_name(borrowed_local).is_some()
                     && local != borrowed_local
                 {
                     should_note_order = true;
@@ -748,7 +747,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                         Operand::Copy(place) | Operand::Move(place) => {
                             if let Some(l) = place.as_local() {
                                 let local_decl = &self.body.local_decls[l];
-                                if self.local_names[l].is_none() {
+                                if self.local_name(l).is_none() {
                                     local_decl.source_info.span
                                 } else {
                                     span
@@ -793,7 +792,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                             Operand::Copy(place) | Operand::Move(place) => {
                                 if let Some(l) = place.as_local() {
                                     let local_decl = &self.body.local_decls[l];
-                                    if self.local_names[l].is_none() {
+                                    if self.local_name(l).is_none() {
                                         local_decl.source_info.span
                                     } else {
                                         span
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 5e3f3ffa2ea..7b4e38969ee 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -7,17 +7,17 @@ use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::{self as hir, CoroutineKind, LangItem};
-use rustc_index::IndexSlice;
+use rustc_index::{IndexSlice, IndexVec};
 use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
 use rustc_infer::traits::SelectionError;
-use rustc_middle::bug;
 use rustc_middle::mir::{
     AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
     LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement,
-    StatementKind, Terminator, TerminatorKind, find_self_call,
+    StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, find_self_call,
 };
 use rustc_middle::ty::print::Print;
 use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::{bug, span_bug};
 use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Spanned;
@@ -190,6 +190,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     ) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
         self.diags_buffer.buffered_move_errors.get(move_out_indices)
     }
+
+    /// Uses `body.var_debug_info` to find the symbol
+    fn local_name(&self, index: Local) -> Option<Symbol> {
+        *self.local_names().get(index)?
+    }
+
+    fn local_names(&self) -> &IndexSlice<Local, Option<Symbol>> {
+        self.local_names.get_or_init(|| {
+            let mut local_names = IndexVec::from_elem(None, &self.body.local_decls);
+            for var_debug_info in &self.body.var_debug_info {
+                if let VarDebugInfoContents::Place(place) = var_debug_info.value {
+                    if let Some(local) = place.as_local() {
+                        if let Some(prev_name) = local_names[local]
+                            && var_debug_info.name != prev_name
+                        {
+                            span_bug!(
+                                var_debug_info.source_info.span,
+                                "local {:?} has many names (`{}` vs `{}`)",
+                                local,
+                                prev_name,
+                                var_debug_info.name
+                            );
+                        }
+                        local_names[local] = Some(var_debug_info.name);
+                    }
+                }
+            }
+            local_names
+        })
+    }
 }
 
 impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
@@ -430,7 +460,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     /// a name, or its name was generated by the compiler, then `Err` is returned
     fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
         let decl = &self.body.local_decls[local];
-        match self.local_names[local] {
+        match self.local_name(local) {
             Some(name) if !decl.from_compiler_desugaring() => {
                 buf.push_str(name.as_str());
                 Ok(())
@@ -1500,4 +1530,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
         }
     }
+
+    /// Skip over locals that begin with an underscore or have no name
+    pub(crate) fn local_excluded_from_unused_mut_lint(&self, index: Local) -> bool {
+        self.local_name(index).is_none_or(|name| name.as_str().starts_with('_'))
+    }
 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 0394a42ea9c..b21d348183f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -465,11 +465,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         if let PlaceRef { local, projection: [] } = deref_base {
             let decl = &self.body.local_decls[local];
+            let local_name = self.local_name(local).map(|sym| format!("`{sym}`"));
             if decl.is_ref_for_guard() {
                 return self
                     .cannot_move_out_of(
                         span,
-                        &format!("`{}` in pattern guard", self.local_names[local].unwrap()),
+                        &format!(
+                            "{} in pattern guard",
+                            local_name.as_deref().unwrap_or("the place")
+                        ),
                     )
                     .with_note(
                         "variables bound in patterns cannot be moved from \
@@ -825,7 +829,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
 
             if binds_to.len() == 1 {
-                let place_desc = &format!("`{}`", self.local_names[*local].unwrap());
+                let place_desc = self.local_name(*local).map(|sym| format!("`{sym}`"));
 
                 if let Some(expr) = self.find_expr(binding_span) {
                     self.suggest_cloning(err, bind_to.ty, expr, None);
@@ -834,7 +838,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
                     is_partial_move: false,
                     ty: bind_to.ty,
-                    place: place_desc,
+                    place: place_desc.as_deref().unwrap_or("the place"),
                     span: binding_span,
                 });
             }
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index a5c9bad3ac2..fd8a2a6bc35 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -60,7 +60,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 if access_place.as_local().is_some() {
                     reason = ", as it is not declared as mutable".to_string();
                 } else {
-                    let name = self.local_names[local].expect("immutable unnamed local");
+                    let name = self.local_name(local).expect("immutable unnamed local");
                     reason = format!(", as `{name}` is not declared as mutable");
                 }
             }
@@ -285,7 +285,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     .body
                     .local_decls
                     .get(local)
-                    .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) =>
+                    .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_name(local))) =>
             {
                 let decl = &self.body.local_decls[local];
                 err.span_label(span, format!("cannot {act}"));
@@ -481,7 +481,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 let (pointer_sigil, pointer_desc) =
                     if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
 
-                match self.local_names[local] {
+                match self.local_name(local) {
                     Some(name) if !local_decl.from_compiler_desugaring() => {
                         err.span_label(
                             span,
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 3bec07afa0f..d27e08573e0 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -664,14 +664,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
             self.infcx.tcx,
             self.body,
-            &self.local_names,
+            &self.local_names(),
             &self.upvars,
             errci.fr,
         );
         let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
             self.infcx.tcx,
             self.body,
-            &self.local_names,
+            &self.local_names(),
             &self.upvars,
             errci.outlived_fr,
         );
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 487f78058a8..1ad629ad167 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -399,7 +399,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             [implicit_inputs + argument_index];
         let (_, span) = self.regioncx.get_argument_name_and_span_for_region(
             self.body,
-            &self.local_names,
+            self.local_names(),
             argument_index,
         );
 
@@ -973,7 +973,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         {
             let (arg_name, arg_span) = self.regioncx.get_argument_name_and_span_for_region(
                 self.body,
-                &self.local_names,
+                self.local_names(),
                 arg_index,
             );
             let region_name = self.synthesize_region_name();
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 4d85f109020..82b300dcb17 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -16,7 +16,7 @@
 // tidy-alphabetical-end
 
 use std::borrow::Cow;
-use std::cell::RefCell;
+use std::cell::{OnceCell, RefCell};
 use std::marker::PhantomData;
 use std::ops::{ControlFlow, Deref};
 
@@ -391,7 +391,7 @@ fn do_mir_borrowck<'tcx>(
             used_mut_upvars: SmallVec::new(),
             borrow_set: &borrow_set,
             upvars: &[],
-            local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
+            local_names: OnceCell::from(IndexVec::from_elem(None, &promoted_body.local_decls)),
             region_names: RefCell::default(),
             next_region_name: RefCell::new(1),
             polonius_output: None,
@@ -414,26 +414,6 @@ fn do_mir_borrowck<'tcx>(
         promoted_mbcx.report_move_errors();
     }
 
-    let mut local_names = IndexVec::from_elem(None, &body.local_decls);
-    for var_debug_info in &body.var_debug_info {
-        if let VarDebugInfoContents::Place(place) = var_debug_info.value {
-            if let Some(local) = place.as_local() {
-                if let Some(prev_name) = local_names[local]
-                    && var_debug_info.name != prev_name
-                {
-                    span_bug!(
-                        var_debug_info.source_info.span,
-                        "local {:?} has many names (`{}` vs `{}`)",
-                        local,
-                        prev_name,
-                        var_debug_info.name
-                    );
-                }
-                local_names[local] = Some(var_debug_info.name);
-            }
-        }
-    }
-
     let mut mbcx = MirBorrowckCtxt {
         root_cx,
         infcx: &infcx,
@@ -450,7 +430,7 @@ fn do_mir_borrowck<'tcx>(
         used_mut_upvars: SmallVec::new(),
         borrow_set: &borrow_set,
         upvars: tcx.closure_captures(def),
-        local_names,
+        local_names: OnceCell::new(),
         region_names: RefCell::default(),
         next_region_name: RefCell::new(1),
         move_errors: Vec::new(),
@@ -682,7 +662,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
     upvars: &'tcx [&'tcx ty::CapturedPlace<'tcx>],
 
     /// Names of local (user) variables (extracted from `var_debug_info`).
-    local_names: IndexVec<Local, Option<Symbol>>,
+    local_names: OnceCell<IndexVec<Local, Option<Symbol>>>,
 
     /// Record the region names generated for each region in the given
     /// MIR def so that we can reuse them later in help/error messages.
@@ -2610,7 +2590,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
             };
 
             // Skip over locals that begin with an underscore or have no name
-            if self.local_names[local].is_none_or(|name| name.as_str().starts_with('_')) {
+            if self.local_excluded_from_unused_mut_lint(local) {
                 continue;
             }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 8882ba359b7..4a2425967e4 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -865,7 +865,7 @@ fn link_natively(
                     command: cmd,
                     escaped_output,
                     verbose: sess.opts.verbose,
-                    sysroot_dir: sess.sysroot.clone(),
+                    sysroot_dir: sess.opts.sysroot.path().to_owned(),
                 };
                 sess.dcx().emit_err(err);
                 // If MSVC's `link.exe` was expected but the return code
@@ -1249,10 +1249,10 @@ fn link_sanitizer_runtime(
         if path.exists() {
             sess.target_tlib_path.dir.clone()
         } else {
-            let default_sysroot = filesearch::get_or_default_sysroot();
-            let default_tlib =
-                filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.tuple());
-            default_tlib
+            filesearch::make_target_lib_path(
+                &sess.opts.sysroot.default,
+                sess.opts.target_triple.tuple(),
+            )
         }
     }
 
@@ -1758,7 +1758,7 @@ fn detect_self_contained_mingw(sess: &Session, linker: &Path) -> bool {
     for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
         let full_path = dir.join(&linker_with_extension);
         // If linker comes from sysroot assume self-contained mode
-        if full_path.is_file() && !full_path.starts_with(&sess.sysroot) {
+        if full_path.is_file() && !full_path.starts_with(sess.opts.sysroot.path()) {
             return false;
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index b6892bb63e8..cd08c0fc30f 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -1070,7 +1070,7 @@ impl<'a> Linker for MsvcLinker<'a> {
         self.link_arg("/PDBALTPATH:%_PDB%");
 
         // This will cause the Microsoft linker to embed .natvis info into the PDB file
-        let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc");
+        let natvis_dir_path = self.sess.opts.sysroot.path().join("lib\\rustlib\\etc");
         if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) {
             for entry in natvis_dir {
                 match entry {
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index daeca43169d..4855fc58d03 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -53,13 +53,13 @@ use rustc_metadata::locator;
 use rustc_middle::ty::TyCtxt;
 use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
 use rustc_session::config::{
-    CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType,
+    CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, Sysroot,
     UnstableOptions, Z_OPTIONS, nightly_options, parse_target_triple,
 };
 use rustc_session::getopts::{self, Matches};
 use rustc_session::lint::{Lint, LintId};
 use rustc_session::output::{CRATE_TYPES, collect_crate_types, invalid_output_for_target};
-use rustc_session::{EarlyDiagCtxt, Session, config, filesearch};
+use rustc_session::{EarlyDiagCtxt, Session, config};
 use rustc_span::FileName;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustc_target::json::ToJson;
@@ -662,7 +662,7 @@ fn print_crate_info(
                 println_info!("{}", targets.join("\n"));
             }
             HostTuple => println_info!("{}", rustc_session::config::host_tuple()),
-            Sysroot => println_info!("{}", sess.sysroot.display()),
+            Sysroot => println_info!("{}", sess.opts.sysroot.path().display()),
             TargetLibdir => println_info!("{}", sess.target_tlib_path.dir.display()),
             TargetSpecJson => {
                 println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
@@ -1114,8 +1114,8 @@ fn get_backend_from_raw_matches(
     let debug_flags = matches.opt_strs("Z");
     let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
     let target = parse_target_triple(early_dcx, matches);
-    let sysroot = filesearch::materialize_sysroot(matches.opt_str("sysroot").map(PathBuf::from));
-    let target = config::build_target_config(early_dcx, &target, &sysroot);
+    let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from));
+    let target = config::build_target_config(early_dcx, &target, sysroot.path());
 
     get_codegen_backend(early_dcx, &sysroot, backend_name, &target)
 }
diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml
index 5dc582b9c3a..0951859fa53 100644
--- a/compiler/rustc_error_messages/Cargo.toml
+++ b/compiler/rustc_error_messages/Cargo.toml
@@ -16,7 +16,6 @@ rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 unic-langid = { version = "0.9.0", features = ["macros"] }
 # tidy-alphabetical-end
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 194fc2450ba..4e4345cfe0f 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -8,7 +8,7 @@
 
 use std::borrow::Cow;
 use std::error::Error;
-use std::path::{Path, PathBuf};
+use std::path::Path;
 use std::sync::{Arc, LazyLock};
 use std::{fmt, fs, io};
 
@@ -21,7 +21,6 @@ use intl_memoizer::concurrent::IntlLangMemoizer;
 use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
 use rustc_macros::{Decodable, Encodable};
 use rustc_span::Span;
-use smallvec::SmallVec;
 use tracing::{instrument, trace};
 pub use unic_langid::{LanguageIdentifier, langid};
 
@@ -107,7 +106,7 @@ impl From<Vec<FluentError>> for TranslationBundleError {
 /// (overriding any conflicting messages).
 #[instrument(level = "trace")]
 pub fn fluent_bundle(
-    sysroot_candidates: SmallVec<[PathBuf; 2]>,
+    sysroot_candidates: &[&Path],
     requested_locale: Option<LanguageIdentifier>,
     additional_ftl_path: Option<&Path>,
     with_directionality_markers: bool,
@@ -141,7 +140,8 @@ pub fn fluent_bundle(
     // If the user requests the default locale then don't try to load anything.
     if let Some(requested_locale) = requested_locale {
         let mut found_resources = false;
-        for mut sysroot in sysroot_candidates {
+        for sysroot in sysroot_candidates {
+            let mut sysroot = sysroot.to_path_buf();
             sysroot.push("share");
             sysroot.push("locale");
             sysroot.push(requested_locale.to_string());
diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl
index 8b7c47dad99..b7555bba28e 100644
--- a/compiler/rustc_expand/messages.ftl
+++ b/compiler/rustc_expand/messages.ftl
@@ -62,7 +62,7 @@ expand_feature_not_allowed =
 expand_feature_removed =
     feature has been removed
     .label = feature has been removed
-    .note = removed in {$removed_rustc_version} (you are using {$current_rustc_version}){$pull_note}
+    .note = removed in {$removed_rustc_version}{$pull_note}
     .reason = {$reason}
 
 expand_glob_delegation_outside_impls =
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 9a359e9b031..170ac39d1ec 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -92,7 +92,6 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
                     span: mi.span(),
                     reason: f.reason.map(|reason| FeatureRemovedReason { reason }),
                     removed_rustc_version: f.feature.since,
-                    current_rustc_version: sess.cfg_version,
                     pull_note,
                 });
                 continue;
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs
index 714ba3bf0f4..fe4d2af97a0 100644
--- a/compiler/rustc_expand/src/errors.rs
+++ b/compiler/rustc_expand/src/errors.rs
@@ -162,7 +162,6 @@ pub(crate) struct FeatureRemoved<'a> {
     #[subdiagnostic]
     pub reason: Option<FeatureRemovedReason<'a>>,
     pub removed_rustc_version: &'a str,
-    pub current_rustc_version: &'a str,
     pub pull_note: String,
 }
 
@@ -444,7 +443,7 @@ pub(crate) struct InvalidFragmentSpecifier {
     #[primary_span]
     pub span: Span,
     pub fragment: Ident,
-    pub help: String,
+    pub help: &'static str,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs
index 4ff8c02bcdb..3082c881a7a 100644
--- a/compiler/rustc_expand/src/mbe.rs
+++ b/compiler/rustc_expand/src/mbe.rs
@@ -78,7 +78,13 @@ enum TokenTree {
     /// only covers the ident, e.g. `var`.)
     MetaVar(Span, Ident),
     /// e.g., `$var:expr`. Only appears on the LHS.
-    MetaVarDecl(Span, Ident /* name to bind */, Option<NonterminalKind>),
+    MetaVarDecl {
+        span: Span,
+        /// Name to bind.
+        name: Ident,
+        /// The fragment specifier.
+        kind: NonterminalKind,
+    },
     /// A meta-variable expression inside `${...}`.
     MetaVarExpr(DelimSpan, MetaVarExpr),
 }
@@ -102,7 +108,7 @@ impl TokenTree {
         match *self {
             TokenTree::Token(Token { span, .. })
             | TokenTree::MetaVar(span, _)
-            | TokenTree::MetaVarDecl(span, _, _) => span,
+            | TokenTree::MetaVarDecl { span, .. } => span,
             TokenTree::Delimited(span, ..)
             | TokenTree::MetaVarExpr(span, _)
             | TokenTree::Sequence(span, _) => span.entire(),
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index 698492f42e2..99aa376626d 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -24,6 +24,7 @@ pub(super) fn failed_to_match_macro(
     arg: TokenStream,
     lhses: &[Vec<MatcherLoc>],
 ) -> (Span, ErrorGuaranteed) {
+    debug!("failed to match macro");
     // An error occurred, try the expansion again, tracking the expansion closely for better
     // diagnostics.
     let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index 3cd803c3e84..dc2d46c4a14 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -117,7 +117,6 @@ use rustc_session::parse::ParseSess;
 use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw};
 use smallvec::SmallVec;
 
-use super::quoted::VALID_FRAGMENT_NAMES_MSG;
 use crate::errors;
 use crate::mbe::{KleeneToken, TokenTree};
 
@@ -263,14 +262,7 @@ fn check_binders(
             }
         }
         // Similarly, this can only happen when checking a toplevel macro.
-        TokenTree::MetaVarDecl(span, name, kind) => {
-            if kind.is_none() && node_id != DUMMY_NODE_ID {
-                psess.dcx().emit_err(errors::MissingFragmentSpecifier {
-                    span,
-                    add_span: span.shrink_to_hi(),
-                    valid: VALID_FRAGMENT_NAMES_MSG,
-                });
-            }
+        TokenTree::MetaVarDecl { span, name, .. } => {
             if !macros.is_empty() {
                 psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs");
             }
@@ -339,7 +331,7 @@ fn check_occurrences(
 ) {
     match *rhs {
         TokenTree::Token(..) => {}
-        TokenTree::MetaVarDecl(span, _name, _kind) => {
+        TokenTree::MetaVarDecl { span, .. } => {
             psess.dcx().span_bug(span, "unexpected MetaVarDecl in rhs")
         }
         TokenTree::MetaVar(span, name) => {
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index c78beb40688..802e43209a5 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -122,7 +122,7 @@ pub(crate) enum MatcherLoc {
     MetaVarDecl {
         span: Span,
         bind: Ident,
-        kind: Option<NonterminalKind>,
+        kind: NonterminalKind,
         next_metavar: usize,
         seq_depth: usize,
     },
@@ -151,12 +151,7 @@ impl Display for MatcherLoc {
                 write!(f, "{}", token_descr(token))
             }
             MatcherLoc::MetaVarDecl { bind, kind, .. } => {
-                write!(f, "meta-variable `${bind}")?;
-                if let Some(kind) = kind {
-                    write!(f, ":{kind}")?;
-                }
-                write!(f, "`")?;
-                Ok(())
+                write!(f, "meta-variable `${bind}:{kind}`")
             }
             MatcherLoc::Eof => f.write_str("end of macro"),
 
@@ -220,7 +215,7 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
                         seq_depth,
                     };
                 }
-                &TokenTree::MetaVarDecl(span, bind, kind) => {
+                &TokenTree::MetaVarDecl { span, name: bind, kind } => {
                     locs.push(MatcherLoc::MetaVarDecl {
                         span,
                         bind,
@@ -330,7 +325,7 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize {
     matcher
         .iter()
         .map(|tt| match tt {
-            TokenTree::MetaVarDecl(..) => 1,
+            TokenTree::MetaVarDecl { .. } => 1,
             TokenTree::Sequence(_, seq) => seq.num_captures,
             TokenTree::Delimited(.., delim) => count_metavar_decls(&delim.tts),
             TokenTree::Token(..) => 0,
@@ -551,18 +546,12 @@ impl TtParser {
                     mp.idx = idx_first;
                     self.cur_mps.push(mp);
                 }
-                &MatcherLoc::MetaVarDecl { span, kind, .. } => {
+                &MatcherLoc::MetaVarDecl { kind, .. } => {
                     // Built-in nonterminals never start with these tokens, so we can eliminate
                     // them from consideration. We use the span of the metavariable declaration
                     // to determine any edition-specific matching behavior for non-terminals.
-                    if let Some(kind) = kind {
-                        if Parser::nonterminal_may_begin_with(kind, token) {
-                            self.bb_mps.push(mp);
-                        }
-                    } else {
-                        // E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used.
-                        // Both this check and the one in `nameize` are necessary, surprisingly.
-                        return Some(Error(span, "missing fragment specifier".to_string()));
+                    if Parser::nonterminal_may_begin_with(kind, token) {
+                        self.bb_mps.push(mp);
                     }
                 }
                 MatcherLoc::Eof => {
@@ -666,11 +655,7 @@ impl TtParser {
                     let mut mp = self.bb_mps.pop().unwrap();
                     let loc = &matcher[mp.idx];
                     if let &MatcherLoc::MetaVarDecl {
-                        span,
-                        kind: Some(kind),
-                        next_metavar,
-                        seq_depth,
-                        ..
+                        span, kind, next_metavar, seq_depth, ..
                     } = loc
                     {
                         // We use the span of the metavariable declaration to determine any
@@ -715,7 +700,7 @@ impl TtParser {
             .bb_mps
             .iter()
             .map(|mp| match &matcher[mp.idx] {
-                MatcherLoc::MetaVarDecl { bind, kind: Some(kind), .. } => {
+                MatcherLoc::MetaVarDecl { bind, kind, .. } => {
                     format!("{kind} ('{bind}')")
                 }
                 _ => unreachable!(),
@@ -745,19 +730,13 @@ impl TtParser {
         // `NamedParseResult`. Otherwise, it's an error.
         let mut ret_val = FxHashMap::default();
         for loc in matcher {
-            if let &MatcherLoc::MetaVarDecl { span, bind, kind, .. } = loc {
-                if kind.is_some() {
-                    match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) {
-                        Vacant(spot) => spot.insert(res.next().unwrap()),
-                        Occupied(..) => {
-                            return Error(span, format!("duplicated bind name: {bind}"));
-                        }
-                    };
-                } else {
-                    // E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used.
-                    // Both this check and the one in `parse_tt_inner` are necessary, surprisingly.
-                    return Error(span, "missing fragment specifier".to_string());
-                }
+            if let &MatcherLoc::MetaVarDecl { span, bind, .. } = loc {
+                match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) {
+                    Vacant(spot) => spot.insert(res.next().unwrap()),
+                    Occupied(..) => {
+                        return Error(span, format!("duplicated bind name: {bind}"));
+                    }
+                };
             }
         }
         Success(ret_val)
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 783f061ec6c..432ab324740 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -392,7 +392,7 @@ pub fn compile_declarative_macro(
 
     let lhs_nm = Ident::new(sym::lhs, span);
     let rhs_nm = Ident::new(sym::rhs, span);
-    let tt_spec = Some(NonterminalKind::TT);
+    let tt_spec = NonterminalKind::TT;
     let macro_rules = macro_def.macro_rules;
 
     // Parse the macro_rules! invocation
@@ -407,9 +407,9 @@ pub fn compile_declarative_macro(
             DelimSpan::dummy(),
             mbe::SequenceRepetition {
                 tts: vec![
-                    mbe::TokenTree::MetaVarDecl(span, lhs_nm, tt_spec),
+                    mbe::TokenTree::MetaVarDecl { span, name: lhs_nm, kind: tt_spec },
                     mbe::TokenTree::token(token::FatArrow, span),
-                    mbe::TokenTree::MetaVarDecl(span, rhs_nm, tt_spec),
+                    mbe::TokenTree::MetaVarDecl { span, name: rhs_nm, kind: tt_spec },
                 ],
                 separator: Some(Token::new(
                     if macro_rules { token::Semi } else { token::Comma },
@@ -448,6 +448,7 @@ pub fn compile_declarative_macro(
         match tt_parser.parse_tt(&mut Cow::Owned(parser), &argument_gram, &mut NoopTracker) {
             Success(m) => m,
             Failure(()) => {
+                debug!("failed to parse macro tt");
                 // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it
                 // with another one that gives us the information we need.
                 // For this we need to reclone the macro body as the previous parser consumed it.
@@ -616,7 +617,7 @@ fn is_empty_token_tree(sess: &Session, seq: &mbe::SequenceRepetition) -> bool {
         let mut iter = seq.tts.iter().peekable();
         while let Some(tt) = iter.next() {
             match tt {
-                mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => {}
+                mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. } => {}
                 mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => {
                     let mut now = t;
                     while let Some(&mbe::TokenTree::Token(
@@ -651,7 +652,7 @@ fn check_redundant_vis_repetition(
 ) {
     let is_zero_or_one: bool = seq.kleene.op == KleeneOp::ZeroOrOne;
     let is_vis = seq.tts.first().map_or(false, |tt| {
-        matches!(tt, mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)))
+        matches!(tt, mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. })
     });
 
     if is_vis && is_zero_or_one {
@@ -678,7 +679,7 @@ fn check_lhs_no_empty_seq(sess: &Session, tts: &[mbe::TokenTree]) -> Result<(),
         match tt {
             TokenTree::Token(..)
             | TokenTree::MetaVar(..)
-            | TokenTree::MetaVarDecl(..)
+            | TokenTree::MetaVarDecl { .. }
             | TokenTree::MetaVarExpr(..) => (),
             TokenTree::Delimited(.., del) => check_lhs_no_empty_seq(sess, &del.tts)?,
             TokenTree::Sequence(span, seq) => {
@@ -777,7 +778,7 @@ impl<'tt> FirstSets<'tt> {
                 match tt {
                     TokenTree::Token(..)
                     | TokenTree::MetaVar(..)
-                    | TokenTree::MetaVarDecl(..)
+                    | TokenTree::MetaVarDecl { .. }
                     | TokenTree::MetaVarExpr(..) => {
                         first.replace_with(TtHandle::TtRef(tt));
                     }
@@ -845,7 +846,7 @@ impl<'tt> FirstSets<'tt> {
             match tt {
                 TokenTree::Token(..)
                 | TokenTree::MetaVar(..)
-                | TokenTree::MetaVarDecl(..)
+                | TokenTree::MetaVarDecl { .. }
                 | TokenTree::MetaVarExpr(..) => {
                     first.add_one(TtHandle::TtRef(tt));
                     return first;
@@ -1084,7 +1085,7 @@ fn check_matcher_core<'tt>(
         match token {
             TokenTree::Token(..)
             | TokenTree::MetaVar(..)
-            | TokenTree::MetaVarDecl(..)
+            | TokenTree::MetaVarDecl { .. }
             | TokenTree::MetaVarExpr(..) => {
                 if token_can_be_followed_by_any(token) {
                     // don't need to track tokens that work with any,
@@ -1152,7 +1153,7 @@ fn check_matcher_core<'tt>(
         // Now `last` holds the complete set of NT tokens that could
         // end the sequence before SUFFIX. Check that every one works with `suffix`.
         for tt in &last.tokens {
-            if let &TokenTree::MetaVarDecl(span, name, Some(kind)) = tt.get() {
+            if let &TokenTree::MetaVarDecl { span, name, kind } = tt.get() {
                 for next_token in &suffix_first.tokens {
                     let next_token = next_token.get();
 
@@ -1172,11 +1173,11 @@ fn check_matcher_core<'tt>(
                         )
                     {
                         // It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param.
-                        let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
+                        let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl {
                             span,
                             name,
-                            Some(NonterminalKind::Pat(PatParam { inferred: false })),
-                        ));
+                            kind: NonterminalKind::Pat(PatParam { inferred: false }),
+                        });
                         sess.psess.buffer_lint(
                             RUST_2021_INCOMPATIBLE_OR_PATTERNS,
                             span,
@@ -1212,11 +1213,11 @@ fn check_matcher_core<'tt>(
                                 && sess.psess.edition.at_least_rust_2021()
                                 && next_token.is_token(&token::Or)
                             {
-                                let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
+                                let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl {
                                     span,
                                     name,
-                                    Some(NonterminalKind::Pat(PatParam { inferred: false })),
-                                ));
+                                    kind: NonterminalKind::Pat(PatParam { inferred: false }),
+                                });
                                 err.span_suggestion(
                                     span,
                                     "try a `pat_param` fragment specifier instead",
@@ -1254,7 +1255,7 @@ fn check_matcher_core<'tt>(
 }
 
 fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool {
-    if let mbe::TokenTree::MetaVarDecl(_, _, Some(kind)) = *tok {
+    if let mbe::TokenTree::MetaVarDecl { kind, .. } = *tok {
         frag_can_be_followed_by_any(kind)
     } else {
         // (Non NT's can always be followed by anything in matchers.)
@@ -1367,7 +1368,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
                         }
                         _ => IsInFollow::No(TOKENS),
                     },
-                    TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Block)) => IsInFollow::Yes,
+                    TokenTree::MetaVarDecl { kind: NonterminalKind::Block, .. } => IsInFollow::Yes,
                     _ => IsInFollow::No(TOKENS),
                 }
             }
@@ -1400,11 +1401,10 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
                             }
                         }
                     },
-                    TokenTree::MetaVarDecl(
-                        _,
-                        _,
-                        Some(NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path),
-                    ) => IsInFollow::Yes,
+                    TokenTree::MetaVarDecl {
+                        kind: NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path,
+                        ..
+                    } => IsInFollow::Yes,
                     _ => IsInFollow::No(TOKENS),
                 }
             }
@@ -1416,8 +1416,7 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
     match tt {
         mbe::TokenTree::Token(token) => pprust::token_to_string(token).into(),
         mbe::TokenTree::MetaVar(_, name) => format!("${name}"),
-        mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${name}:{kind}"),
-        mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${name}:"),
+        mbe::TokenTree::MetaVarDecl { name, kind, .. } => format!("${name}:{kind}"),
         _ => panic!(
             "{}",
             "unexpected mbe::TokenTree::{Sequence or Delimited} \
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index 0c2362f23bc..2daa4e71558 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -54,66 +54,78 @@ pub(super) fn parse(
         // Given the parsed tree, if there is a metavar and we are expecting matchers, actually
         // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
         let tree = parse_tree(tree, &mut iter, parsing_patterns, sess, node_id, features, edition);
-        match tree {
-            TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
-                // Not consuming the next token immediately, as it may not be a colon
-                let span = match iter.peek() {
-                    Some(&tokenstream::TokenTree::Token(
-                        Token { kind: token::Colon, span: colon_span },
-                        _,
-                    )) => {
-                        // Consume the colon first
-                        iter.next();
-
-                        // It's ok to consume the next tree no matter how,
-                        // since if it's not a token then it will be an invalid declaration.
-                        match iter.next() {
-                            Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() {
-                                Some((fragment, _)) => {
-                                    let span = token.span.with_lo(start_sp.lo());
-                                    let edition = || {
-                                        // FIXME(#85708) - once we properly decode a foreign
-                                        // crate's `SyntaxContext::root`, then we can replace
-                                        // this with just `span.edition()`. A
-                                        // `SyntaxContext::root()` from the current crate will
-                                        // have the edition of the current crate, and a
-                                        // `SyntaxContext::root()` from a foreign crate will
-                                        // have the edition of that crate (which we manually
-                                        // retrieve via the `edition` parameter).
-                                        if !span.from_expansion() {
-                                            edition
-                                        } else {
-                                            span.edition()
-                                        }
-                                    };
-                                    let kind = NonterminalKind::from_symbol(fragment.name, edition)
-                                        .unwrap_or_else(|| {
-                                            sess.dcx().emit_err(errors::InvalidFragmentSpecifier {
-                                                span,
-                                                fragment,
-                                                help: VALID_FRAGMENT_NAMES_MSG.into(),
-                                            });
-                                            NonterminalKind::Ident
-                                        });
-                                    result.push(TokenTree::MetaVarDecl(span, ident, Some(kind)));
-                                    continue;
-                                }
-                                _ => token.span,
-                            },
-                            // Invalid, return a nice source location
-                            _ => colon_span.with_lo(start_sp.lo()),
-                        }
-                    }
-                    // Whether it's none or some other tree, it doesn't belong to
-                    // the current meta variable, returning the original span.
-                    _ => start_sp,
-                };
 
-                result.push(TokenTree::MetaVarDecl(span, ident, None));
-            }
+        if !parsing_patterns {
+            // No matchers allowed, nothing to process here
+            result.push(tree);
+            continue;
+        }
+
+        let TokenTree::MetaVar(start_sp, ident) = tree else {
+            // Not a metavariable, just return the tree
+            result.push(tree);
+            continue;
+        };
 
-            // Not a metavar or no matchers allowed, so just return the tree
-            _ => result.push(tree),
+        // Push a metavariable with no fragment specifier at the given span
+        let mut missing_fragment_specifier = |span| {
+            sess.dcx().emit_err(errors::MissingFragmentSpecifier {
+                span,
+                add_span: span.shrink_to_hi(),
+                valid: VALID_FRAGMENT_NAMES_MSG,
+            });
+
+            // Fall back to a `TokenTree` since that will match anything if we continue expanding.
+            result.push(TokenTree::MetaVarDecl { span, name: ident, kind: NonterminalKind::TT });
+        };
+
+        // Not consuming the next token immediately, as it may not be a colon
+        if let Some(peek) = iter.peek()
+            && let tokenstream::TokenTree::Token(token, _spacing) = peek
+            && let Token { kind: token::Colon, span: colon_span } = token
+        {
+            // Next token is a colon; consume it
+            iter.next();
+
+            // It's ok to consume the next tree no matter how,
+            // since if it's not a token then it will be an invalid declaration.
+            let Some(tokenstream::TokenTree::Token(token, _)) = iter.next() else {
+                // Invalid, return a nice source location as `var:`
+                missing_fragment_specifier(colon_span.with_lo(start_sp.lo()));
+                continue;
+            };
+
+            let Some((fragment, _)) = token.ident() else {
+                // No identifier for the fragment specifier;
+                missing_fragment_specifier(token.span);
+                continue;
+            };
+
+            let span = token.span.with_lo(start_sp.lo());
+            let edition = || {
+                // FIXME(#85708) - once we properly decode a foreign
+                // crate's `SyntaxContext::root`, then we can replace
+                // this with just `span.edition()`. A
+                // `SyntaxContext::root()` from the current crate will
+                // have the edition of the current crate, and a
+                // `SyntaxContext::root()` from a foreign crate will
+                // have the edition of that crate (which we manually
+                // retrieve via the `edition` parameter).
+                if !span.from_expansion() { edition } else { span.edition() }
+            };
+            let kind = NonterminalKind::from_symbol(fragment.name, edition).unwrap_or_else(|| {
+                sess.dcx().emit_err(errors::InvalidFragmentSpecifier {
+                    span,
+                    fragment,
+                    help: VALID_FRAGMENT_NAMES_MSG,
+                });
+                NonterminalKind::TT
+            });
+            result.push(TokenTree::MetaVarDecl { span, name: ident, kind });
+        } else {
+            // Whether it's none or some other tree, it doesn't belong to
+            // the current meta variable, returning the original span.
+            missing_fragment_specifier(start_sp);
         }
     }
     result
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 0520be5fbae..a8c4a9e4b1b 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -283,7 +283,7 @@ pub(super) fn transcribe<'a>(
             }
 
             // There should be no meta-var declarations in the invocation of a macro.
-            mbe::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl`"),
+            mbe::TokenTree::MetaVarDecl { .. } => panic!("unexpected `TokenTree::MetaVarDecl`"),
         }
     }
 }
@@ -776,7 +776,7 @@ fn lockstep_iter_size(
                 size.with(lockstep_iter_size(tt, interpolations, repeats))
             })
         }
-        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
+        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl { name, .. } => {
             let name = MacroRulesNormalizedIdent::new(*name);
             match lookup_cur_matched(name, interpolations, repeats) {
                 Some(matched) => match matched {
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index d62bf7f85e0..c46e879b976 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -18,7 +18,6 @@ use rustc_parse::parser::attr::AllowLeadingUnsafe;
 use rustc_query_impl::QueryCtxt;
 use rustc_query_system::query::print_query_stack;
 use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName};
-use rustc_session::filesearch::sysroot_with_fallback;
 use rustc_session::parse::ParseSess;
 use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint};
 use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs};
@@ -405,8 +404,11 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
 
     crate::callbacks::setup_callbacks();
 
-    let sysroot = config.opts.sysroot.clone();
-    let target = config::build_target_config(&early_dcx, &config.opts.target_triple, &sysroot);
+    let target = config::build_target_config(
+        &early_dcx,
+        &config.opts.target_triple,
+        config.opts.sysroot.path(),
+    );
     let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
     let path_mapping = config.opts.file_path_mapping();
     let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target);
@@ -426,7 +428,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
             let codegen_backend = match config.make_codegen_backend {
                 None => util::get_codegen_backend(
                     &early_dcx,
-                    &sysroot,
+                    &config.opts.sysroot,
                     config.opts.unstable_opts.codegen_backend.as_deref(),
                     &target,
                 ),
@@ -440,7 +442,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
             let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
 
             let bundle = match rustc_errors::fluent_bundle(
-                sysroot_with_fallback(&config.opts.sysroot),
+                &config.opts.sysroot.all_paths().collect::<Vec<_>>(),
                 config.opts.unstable_opts.translate_lang.clone(),
                 config.opts.unstable_opts.translate_additional_ftl.as_deref(),
                 config.opts.unstable_opts.translate_directionality_markers,
@@ -469,7 +471,6 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
                 locale_resources,
                 config.lint_caps,
                 target,
-                sysroot,
                 util::rustc_version_str().unwrap_or("unknown"),
                 config.ice_file,
                 config.using_internal_features,
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index a0012b04c4f..360b5629e9d 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -41,9 +41,11 @@ where
 
     let matches = optgroups().parse(args).unwrap();
     let sessopts = build_session_options(&mut early_dcx, &matches);
-    let sysroot = sessopts.sysroot.clone();
-    let target =
-        rustc_session::config::build_target_config(&early_dcx, &sessopts.target_triple, &sysroot);
+    let target = rustc_session::config::build_target_config(
+        &early_dcx,
+        &sessopts.target_triple,
+        sessopts.sysroot.path(),
+    );
     let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target);
     let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm();
     let sm_inputs = Some(SourceMapInputs {
@@ -72,7 +74,6 @@ where
             vec![],
             Default::default(),
             target,
-            sysroot,
             "",
             None,
             &USING_INTERNAL_FEATURES,
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 8a7d6117265..0ca4fcc66ca 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -11,7 +11,7 @@ use rustc_data_structures::sync;
 use rustc_metadata::{DylibError, load_symbol_from_dylib};
 use rustc_middle::ty::CurrentGcx;
 use rustc_parse::validate_attr;
-use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, host_tuple};
+use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple};
 use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer};
 use rustc_session::output::{CRATE_TYPES, categorize_crate_type};
 use rustc_session::{EarlyDiagCtxt, Session, filesearch};
@@ -305,7 +305,7 @@ fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBacken
 /// A name of `None` indicates that the default backend should be used.
 pub fn get_codegen_backend(
     early_dcx: &EarlyDiagCtxt,
-    sysroot: &Path,
+    sysroot: &Sysroot,
     backend_name: Option<&str>,
     target: &Target,
 ) -> Box<dyn CodegenBackend> {
@@ -336,25 +336,24 @@ pub fn get_codegen_backend(
 // This is used for rustdoc, but it uses similar machinery to codegen backend
 // loading, so we leave the code here. It is potentially useful for other tools
 // that want to invoke the rustc binary while linking to rustc as well.
-pub fn rustc_path<'a>() -> Option<&'a Path> {
+pub fn rustc_path<'a>(sysroot: &Sysroot) -> Option<&'a Path> {
     static RUSTC_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
 
-    const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR");
-
-    RUSTC_PATH.get_or_init(|| get_rustc_path_inner(BIN_PATH)).as_deref()
-}
-
-fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
-    let candidate = filesearch::get_or_default_sysroot()
-        .join(bin_path)
-        .join(if cfg!(target_os = "windows") { "rustc.exe" } else { "rustc" });
-    candidate.exists().then_some(candidate)
+    RUSTC_PATH
+        .get_or_init(|| {
+            let candidate = sysroot
+                .default
+                .join(env!("RUSTC_INSTALL_BINDIR"))
+                .join(if cfg!(target_os = "windows") { "rustc.exe" } else { "rustc" });
+            candidate.exists().then_some(candidate)
+        })
+        .as_deref()
 }
 
 #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
 fn get_codegen_sysroot(
     early_dcx: &EarlyDiagCtxt,
-    sysroot: &Path,
+    sysroot: &Sysroot,
     backend_name: &str,
 ) -> MakeBackendFn {
     // For now we only allow this function to be called once as it'll dlopen a
@@ -369,10 +368,9 @@ fn get_codegen_sysroot(
     );
 
     let target = host_tuple();
-    let sysroot_candidates = filesearch::sysroot_with_fallback(&sysroot);
 
-    let sysroot = sysroot_candidates
-        .iter()
+    let sysroot = sysroot
+        .all_paths()
         .map(|sysroot| {
             filesearch::make_target_lib_path(sysroot, target).with_file_name("codegen-backends")
         })
@@ -381,8 +379,8 @@ fn get_codegen_sysroot(
             f.exists()
         })
         .unwrap_or_else(|| {
-            let candidates = sysroot_candidates
-                .iter()
+            let candidates = sysroot
+                .all_paths()
                 .map(|p| p.display().to_string())
                 .collect::<Vec<_>>()
                 .join("\n* ");
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 297b8ef7e76..95663204ec3 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -524,6 +524,20 @@ pub trait LintContext {
         });
     }
 
+    /// Emit a lint at `span` from a lazily-constructed lint struct (some type that implements
+    /// `LintDiagnostic`, typically generated by `#[derive(LintDiagnostic)]`).
+    fn emit_span_lint_lazy<S: Into<MultiSpan>, L: for<'a> LintDiagnostic<'a, ()>>(
+        &self,
+        lint: &'static Lint,
+        span: S,
+        decorator: impl FnOnce() -> L,
+    ) {
+        self.opt_span_lint(lint, Some(span), |lint| {
+            let decorator = decorator();
+            decorator.decorate_lint(lint);
+        });
+    }
+
     /// Emit a lint at the appropriate level, with an associated span.
     ///
     /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index abdf8e3853b..4d405d915fb 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1353,6 +1353,8 @@ pub(crate) struct NonUpperCaseGlobal<'a> {
     pub name: &'a str,
     #[subdiagnostic]
     pub sub: NonUpperCaseGlobalSub,
+    #[subdiagnostic]
+    pub usages: Vec<NonUpperCaseGlobalSubTool>,
 }
 
 #[derive(Subdiagnostic)]
@@ -1362,14 +1364,29 @@ pub(crate) enum NonUpperCaseGlobalSub {
         #[primary_span]
         span: Span,
     },
-    #[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+    #[suggestion(lint_suggestion, code = "{replace}")]
     Suggestion {
         #[primary_span]
         span: Span,
+        #[applicability]
+        applicability: Applicability,
         replace: String,
     },
 }
 
+#[derive(Subdiagnostic)]
+#[suggestion(
+    lint_suggestion,
+    code = "{replace}",
+    applicability = "machine-applicable",
+    style = "tool-only"
+)]
+pub(crate) struct NonUpperCaseGlobalSubTool {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) replace: String,
+}
+
 // noop_method_call.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_noop_method_call)]
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index f39e1506390..97e627f2eb2 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -1,9 +1,12 @@
 use rustc_abi::ExternAbi;
 use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr};
 use rustc_attr_parsing::AttributeParser;
+use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::FnKind;
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{FnKind, Visitor};
 use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind};
+use rustc_middle::hir::nested_filter::All;
 use rustc_middle::ty;
 use rustc_session::config::CrateType;
 use rustc_session::{declare_lint, declare_lint_pass};
@@ -13,7 +16,7 @@ use {rustc_ast as ast, rustc_hir as hir};
 
 use crate::lints::{
     NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
-    NonUpperCaseGlobal, NonUpperCaseGlobalSub,
+    NonUpperCaseGlobal, NonUpperCaseGlobalSub, NonUpperCaseGlobalSubTool,
 };
 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 
@@ -493,22 +496,82 @@ declare_lint! {
 declare_lint_pass!(NonUpperCaseGlobals => [NON_UPPER_CASE_GLOBALS]);
 
 impl NonUpperCaseGlobals {
-    fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) {
+    fn check_upper_case(cx: &LateContext<'_>, sort: &str, did: Option<LocalDefId>, ident: &Ident) {
         let name = ident.name.as_str();
         if name.chars().any(|c| c.is_lowercase()) {
             let uc = NonSnakeCase::to_snake_case(name).to_uppercase();
+
+            // If the item is exported, suggesting changing it's name would be breaking-change
+            // and could break users without a "nice" applicable fix, so let's avoid it.
+            let can_change_usages = if let Some(did) = did {
+                !cx.tcx.effective_visibilities(()).is_exported(did)
+            } else {
+                false
+            };
+
             // We cannot provide meaningful suggestions
             // if the characters are in the category of "Lowercase Letter".
             let sub = if *name != uc {
-                NonUpperCaseGlobalSub::Suggestion { span: ident.span, replace: uc }
+                NonUpperCaseGlobalSub::Suggestion {
+                    span: ident.span,
+                    replace: uc.clone(),
+                    applicability: if can_change_usages {
+                        Applicability::MachineApplicable
+                    } else {
+                        Applicability::MaybeIncorrect
+                    },
+                }
             } else {
                 NonUpperCaseGlobalSub::Label { span: ident.span }
             };
-            cx.emit_span_lint(
-                NON_UPPER_CASE_GLOBALS,
-                ident.span,
-                NonUpperCaseGlobal { sort, name, sub },
-            );
+
+            struct UsageCollector<'a, 'tcx> {
+                cx: &'tcx LateContext<'a>,
+                did: DefId,
+                collected: Vec<Span>,
+            }
+
+            impl<'v, 'tcx> Visitor<'v> for UsageCollector<'v, 'tcx> {
+                type NestedFilter = All;
+
+                fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+                    self.cx.tcx
+                }
+
+                fn visit_path(
+                    &mut self,
+                    path: &rustc_hir::Path<'v>,
+                    _id: rustc_hir::HirId,
+                ) -> Self::Result {
+                    if let Some(final_seg) = path.segments.last()
+                        && final_seg.res.opt_def_id() == Some(self.did)
+                    {
+                        self.collected.push(final_seg.ident.span);
+                    }
+                }
+            }
+
+            cx.emit_span_lint_lazy(NON_UPPER_CASE_GLOBALS, ident.span, || {
+                // Compute usages lazily as it can expansive and useless when the lint is allowed.
+                // cf. https://github.com/rust-lang/rust/pull/142645#issuecomment-2993024625
+                let usages = if can_change_usages
+                    && *name != uc
+                    && let Some(did) = did
+                {
+                    let mut usage_collector =
+                        UsageCollector { cx, did: did.to_def_id(), collected: Vec::new() };
+                    cx.tcx.hir_walk_toplevel_module(&mut usage_collector);
+                    usage_collector
+                        .collected
+                        .into_iter()
+                        .map(|span| NonUpperCaseGlobalSubTool { span, replace: uc.clone() })
+                        .collect()
+                } else {
+                    vec![]
+                };
+
+                NonUpperCaseGlobal { sort, name, sub, usages }
+            });
         }
     }
 }
@@ -520,10 +583,20 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
             hir::ItemKind::Static(_, ident, ..)
                 if !find_attr!(attrs, AttributeKind::NoMangle(..)) =>
             {
-                NonUpperCaseGlobals::check_upper_case(cx, "static variable", &ident);
+                NonUpperCaseGlobals::check_upper_case(
+                    cx,
+                    "static variable",
+                    Some(it.owner_id.def_id),
+                    &ident,
+                );
             }
             hir::ItemKind::Const(ident, ..) => {
-                NonUpperCaseGlobals::check_upper_case(cx, "constant", &ident);
+                NonUpperCaseGlobals::check_upper_case(
+                    cx,
+                    "constant",
+                    Some(it.owner_id.def_id),
+                    &ident,
+                );
             }
             _ => {}
         }
@@ -531,7 +604,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
 
     fn check_trait_item(&mut self, cx: &LateContext<'_>, ti: &hir::TraitItem<'_>) {
         if let hir::TraitItemKind::Const(..) = ti.kind {
-            NonUpperCaseGlobals::check_upper_case(cx, "associated constant", &ti.ident);
+            NonUpperCaseGlobals::check_upper_case(cx, "associated constant", None, &ti.ident);
         }
     }
 
@@ -539,7 +612,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
         if let hir::ImplItemKind::Const(..) = ii.kind
             && !assoc_item_in_trait_impl(cx, ii)
         {
-            NonUpperCaseGlobals::check_upper_case(cx, "associated constant", &ii.ident);
+            NonUpperCaseGlobals::check_upper_case(cx, "associated constant", None, &ii.ident);
         }
     }
 
@@ -555,6 +628,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
                     NonUpperCaseGlobals::check_upper_case(
                         cx,
                         "constant in pattern",
+                        None,
                         &segment.ident,
                     );
                 }
@@ -564,7 +638,12 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
 
     fn check_generic_param(&mut self, cx: &LateContext<'_>, param: &hir::GenericParam<'_>) {
         if let GenericParamKind::Const { .. } = param.kind {
-            NonUpperCaseGlobals::check_upper_case(cx, "const parameter", &param.name.ident());
+            NonUpperCaseGlobals::check_upper_case(
+                cx,
+                "const parameter",
+                Some(param.def_id),
+                &param.name.ident(),
+            );
         }
     }
 }
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index 259bcb1b96d..941f16bd960 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -321,7 +321,7 @@ impl<'a> CrateLocator<'a> {
 
         CrateLocator {
             only_needs_metadata,
-            sysroot: &sess.sysroot,
+            sysroot: sess.opts.sysroot.path(),
             metadata_loader,
             cfg_version: sess.cfg_version,
             crate_name,
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index e989209e177..1b7a2c3bda0 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -608,7 +608,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             }
         }
 
-        self.throw_unresolved_import_error(errors, glob_error);
+        if !errors.is_empty() {
+            self.throw_unresolved_import_error(errors, glob_error);
+        }
     }
 
     pub(crate) fn check_hidden_glob_reexports(
@@ -688,14 +690,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false,
             _ => true,
         });
+        errors.retain(|(_import, err)| {
+            // If we've encountered something like `use _;`, we've already emitted an error stating
+            // that `_` is not a valid identifier, so we ignore that resolve error.
+            err.segment != Some(kw::Underscore)
+        });
+
         if errors.is_empty() {
+            self.tcx.dcx().delayed_bug("expected a parse or \"`_` can't be an identifier\" error");
             return;
         }
 
-        /// Upper limit on the number of `span_label` messages.
-        const MAX_LABEL_COUNT: usize = 10;
-
         let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect());
+
         let paths = errors
             .iter()
             .map(|(import, err)| {
@@ -715,6 +722,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             diag.note(note.clone());
         }
 
+        /// Upper limit on the number of `span_label` messages.
+        const MAX_LABEL_COUNT: usize = 10;
+
         for (import, err) in errors.into_iter().take(MAX_LABEL_COUNT) {
             if let Some(label) = err.label {
                 diag.span_label(err.span, label);
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index f0ee19e3c67..5b88a7017c5 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -22,7 +22,6 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-smallvec = "1.8.1"
 termize = "0.1.1"
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 87e4b0a17aa..c62e4ac30ea 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1296,6 +1296,28 @@ bitflags::bitflags! {
     }
 }
 
+#[derive(Clone, Debug)]
+pub struct Sysroot {
+    pub explicit: Option<PathBuf>,
+    pub default: PathBuf,
+}
+
+impl Sysroot {
+    pub fn new(explicit: Option<PathBuf>) -> Sysroot {
+        Sysroot { explicit, default: filesearch::default_sysroot() }
+    }
+
+    /// Return explicit sysroot if it was passed with `--sysroot`, or default sysroot otherwise.
+    pub fn path(&self) -> &Path {
+        self.explicit.as_deref().unwrap_or(&self.default)
+    }
+
+    /// Returns both explicit sysroot if it was passed with `--sysroot` and the default sysroot.
+    pub fn all_paths(&self) -> impl Iterator<Item = &Path> {
+        self.explicit.as_deref().into_iter().chain(iter::once(&*self.default))
+    }
+}
+
 pub fn host_tuple() -> &'static str {
     // Get the host triple out of the build environment. This ensures that our
     // idea of the host triple is the same as for the set of libraries we've
@@ -1342,7 +1364,7 @@ impl Default for Options {
             describe_lints: false,
             output_types: OutputTypes(BTreeMap::new()),
             search_paths: vec![],
-            sysroot: filesearch::materialize_sysroot(None),
+            sysroot: Sysroot::new(None),
             target_triple: TargetTuple::from_tuple(host_tuple()),
             test: false,
             incremental: None,
@@ -2673,7 +2695,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
     let cg = cg;
 
-    let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
     let target_triple = parse_target_triple(early_dcx, matches);
     let opt_level = parse_opt_level(early_dcx, matches, &cg);
     // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
@@ -2712,10 +2733,10 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
     let logical_env = parse_logical_env(early_dcx, matches);
 
-    let sysroot = filesearch::materialize_sysroot(sysroot_opt);
+    let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from));
 
     let real_source_base_dir = |suffix: &str, confirm: &str| {
-        let mut candidate = sysroot.join(suffix);
+        let mut candidate = sysroot.path().join(suffix);
         if let Ok(metadata) = candidate.symlink_metadata() {
             // Replace the symlink bootstrap creates, with its destination.
             // We could try to use `fs::canonicalize` instead, but that might
@@ -2742,7 +2763,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     let mut search_paths = vec![];
     for s in &matches.opt_strs("L") {
         search_paths.push(SearchPath::from_cli_opt(
-            &sysroot,
+            sysroot.path(),
             &target_triple,
             early_dcx,
             s,
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
index 4f8c3926207..f64fa86948c 100644
--- a/compiler/rustc_session/src/filesearch.rs
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -5,7 +5,6 @@ use std::{env, fs};
 
 use rustc_fs_util::try_canonicalize;
 use rustc_target::spec::Target;
-use smallvec::{SmallVec, smallvec};
 
 use crate::search_paths::{PathKind, SearchPath};
 
@@ -182,24 +181,9 @@ fn current_dll_path() -> Result<PathBuf, String> {
     Err("current_dll_path is not supported on WASI".to_string())
 }
 
-pub fn sysroot_with_fallback(sysroot: &Path) -> SmallVec<[PathBuf; 2]> {
-    let mut candidates = smallvec![sysroot.to_owned()];
-    let default_sysroot = get_or_default_sysroot();
-    if default_sysroot != sysroot {
-        candidates.push(default_sysroot);
-    }
-    candidates
-}
-
-/// Returns the provided sysroot or calls [`get_or_default_sysroot`] if it's none.
-/// Panics if [`get_or_default_sysroot`]  returns an error.
-pub fn materialize_sysroot(maybe_sysroot: Option<PathBuf>) -> PathBuf {
-    maybe_sysroot.unwrap_or_else(|| get_or_default_sysroot())
-}
-
 /// This function checks if sysroot is found using env::args().next(), and if it
 /// is not found, finds sysroot from current rustc_driver dll.
-pub fn get_or_default_sysroot() -> PathBuf {
+pub(crate) fn default_sysroot() -> PathBuf {
     fn default_from_rustc_driver_dll() -> Result<PathBuf, String> {
         let dll = current_dll_path()?;
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 7fef942525b..232531dc673 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -337,7 +337,7 @@ top_level_options!(
         output_types: OutputTypes [TRACKED],
         search_paths: Vec<SearchPath> [UNTRACKED],
         libs: Vec<NativeLib> [TRACKED],
-        sysroot: PathBuf [UNTRACKED],
+        sysroot: Sysroot [UNTRACKED],
 
         target_triple: TargetTuple [TRACKED],
 
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index ad58c3c8f7d..bad2581ae31 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -149,7 +149,6 @@ pub struct Session {
     pub opts: config::Options,
     pub target_tlib_path: Arc<SearchPath>,
     pub psess: ParseSess,
-    pub sysroot: PathBuf,
     /// Input, input file path and output file path to this compilation process.
     pub io: CompilerIO,
 
@@ -456,8 +455,10 @@ impl Session {
     /// directories are also returned, for example if `--sysroot` is used but tools are missing
     /// (#125246): we also add the bin directories to the sysroot where rustc is located.
     pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec<PathBuf> {
-        let search_paths = filesearch::sysroot_with_fallback(&self.sysroot)
-            .into_iter()
+        let search_paths = self
+            .opts
+            .sysroot
+            .all_paths()
             .map(|sysroot| filesearch::make_target_bin_path(&sysroot, config::host_tuple()));
 
         if self_contained {
@@ -1028,7 +1029,6 @@ pub fn build_session(
     fluent_resources: Vec<&'static str>,
     driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
     target: Target,
-    sysroot: PathBuf,
     cfg_version: &'static str,
     ice_file: Option<PathBuf>,
     using_internal_features: &'static AtomicBool,
@@ -1063,7 +1063,7 @@ pub fn build_session(
     }
 
     let host_triple = TargetTuple::from_tuple(config::host_tuple());
-    let (host, target_warnings) = Target::search(&host_triple, &sysroot)
+    let (host, target_warnings) = Target::search(&host_triple, sopts.sysroot.path())
         .unwrap_or_else(|e| dcx.handle().fatal(format!("Error loading host specification: {e}")));
     for warning in target_warnings.warning_messages() {
         dcx.handle().warn(warning)
@@ -1096,13 +1096,14 @@ pub fn build_session(
     let host_triple = config::host_tuple();
     let target_triple = sopts.target_triple.tuple();
     // FIXME use host sysroot?
-    let host_tlib_path = Arc::new(SearchPath::from_sysroot_and_triple(&sysroot, host_triple));
+    let host_tlib_path =
+        Arc::new(SearchPath::from_sysroot_and_triple(sopts.sysroot.path(), host_triple));
     let target_tlib_path = if host_triple == target_triple {
         // Use the same `SearchPath` if host and target triple are identical to avoid unnecessary
         // rescanning of the target lib path and an unnecessary allocation.
         Arc::clone(&host_tlib_path)
     } else {
-        Arc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple))
+        Arc::new(SearchPath::from_sysroot_and_triple(sopts.sysroot.path(), target_triple))
     };
 
     let prof = SelfProfilerRef::new(
@@ -1134,7 +1135,6 @@ pub fn build_session(
         opts: sopts,
         target_tlib_path,
         psess,
-        sysroot,
         io,
         incr_comp_session: RwLock::new(IncrCompSession::NotInitialized),
         prof,
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index f93aa8ffd0d..986390dbaa0 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::DiagCtxtHandle;
 use rustc_session::config::{
     self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns,
-    OptionsTargetModifiers, UnstableOptions, get_cmd_lint_options, nightly_options,
+    OptionsTargetModifiers, Sysroot, UnstableOptions, get_cmd_lint_options, nightly_options,
     parse_crate_types_from_list, parse_externs, parse_target_triple,
 };
 use rustc_session::lint::Level;
@@ -103,9 +103,7 @@ pub(crate) struct Options {
     /// compiling doctests from the crate.
     pub(crate) edition: Edition,
     /// The path to the sysroot. Used during the compilation process.
-    pub(crate) sysroot: PathBuf,
-    /// Has the same value as `sysroot` except is `None` when the user didn't pass `---sysroot`.
-    pub(crate) maybe_sysroot: Option<PathBuf>,
+    pub(crate) sysroot: Sysroot,
     /// Lint information passed over the command-line.
     pub(crate) lint_opts: Vec<(String, Level)>,
     /// Whether to ask rustc to describe the lints it knows.
@@ -201,7 +199,6 @@ impl fmt::Debug for Options {
             .field("target", &self.target)
             .field("edition", &self.edition)
             .field("sysroot", &self.sysroot)
-            .field("maybe_sysroot", &self.maybe_sysroot)
             .field("lint_opts", &self.lint_opts)
             .field("describe_lints", &self.describe_lints)
             .field("lint_cap", &self.lint_cap)
@@ -725,16 +722,14 @@ impl Options {
         }
 
         let target = parse_target_triple(early_dcx, matches);
-        let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
-
-        let sysroot = rustc_session::filesearch::materialize_sysroot(maybe_sysroot.clone());
+        let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from));
 
         let libs = matches
             .opt_strs("L")
             .iter()
             .map(|s| {
                 SearchPath::from_cli_opt(
-                    &sysroot,
+                    sysroot.path(),
                     &target,
                     early_dcx,
                     s,
@@ -827,7 +822,6 @@ impl Options {
             target,
             edition,
             sysroot,
-            maybe_sysroot,
             lint_opts,
             describe_lints,
             lint_cap,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 130fdff1afe..1b5c9fd4664 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -514,8 +514,9 @@ fn run_test(
 
     compiler_args.push(format!("@{}", doctest.global_opts.args_file.display()));
 
-    if let Some(sysroot) = &rustdoc_options.maybe_sysroot {
-        compiler_args.push(format!("--sysroot={}", sysroot.display()));
+    let sysroot = &rustdoc_options.sysroot;
+    if let Some(explicit_sysroot) = &sysroot.explicit {
+        compiler_args.push(format!("--sysroot={}", explicit_sysroot.display()));
     }
 
     compiler_args.extend_from_slice(&["--edition".to_owned(), doctest.edition.to_string()]);
@@ -574,7 +575,7 @@ fn run_test(
     let rustc_binary = rustdoc_options
         .test_builder
         .as_deref()
-        .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc"));
+        .unwrap_or_else(|| rustc_interface::util::rustc_path(sysroot).expect("found rustc"));
     let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary);
 
     compiler.args(&compiler_args);
diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
index 426d65b7af3..cca528c4252 100644
--- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
+++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
@@ -5,6 +5,7 @@
 
 //@ needs-target-std
 //@ ignore-android: FIXME(#142855)
+//@ ignore-sgx: (x86 machine code cannot be directly executed)
 
 use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name};
 
diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs
index fa651baa7bc..87504b8301f 100644
--- a/tests/ui-fulldeps/run-compiler-twice.rs
+++ b/tests/ui-fulldeps/run-compiler-twice.rs
@@ -18,7 +18,7 @@ extern crate rustc_span;
 use std::path::{Path, PathBuf};
 
 use rustc_interface::{Linker, interface};
-use rustc_session::config::{Input, Options, OutFileName, OutputType, OutputTypes};
+use rustc_session::config::{Input, Options, OutFileName, OutputType, OutputTypes, Sysroot};
 use rustc_span::FileName;
 
 fn main() {
@@ -32,7 +32,7 @@ fn main() {
         panic!("expected sysroot (and optional linker)");
     }
 
-    let sysroot = PathBuf::from(&args[1]);
+    let sysroot = Sysroot::new(Some(PathBuf::from(&args[1])));
     let linker = args.get(2).map(PathBuf::from);
 
     // compiletest sets the current dir to `output_base_dir` when running.
@@ -43,7 +43,7 @@ fn main() {
     compile(src.to_string(), tmpdir.join("out"), sysroot.clone(), linker.as_deref());
 }
 
-fn compile(code: String, output: PathBuf, sysroot: PathBuf, linker: Option<&Path>) {
+fn compile(code: String, output: PathBuf, sysroot: Sysroot, linker: Option<&Path>) {
     let mut opts = Options::default();
     opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]);
     opts.sysroot = sysroot;
diff --git a/tests/ui/deprecation/deprecated_no_stack_check.rs b/tests/ui/deprecation/deprecated_no_stack_check.rs
index 64635c5f5e8..8e1f5bbf045 100644
--- a/tests/ui/deprecation/deprecated_no_stack_check.rs
+++ b/tests/ui/deprecation/deprecated_no_stack_check.rs
@@ -1,5 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![deny(warnings)]
 #![feature(no_stack_check)]
 //~^ ERROR: feature has been removed [E0557]
diff --git a/tests/ui/deprecation/deprecated_no_stack_check.stderr b/tests/ui/deprecation/deprecated_no_stack_check.stderr
index 2d08b1b8db5..33788661d73 100644
--- a/tests/ui/deprecation/deprecated_no_stack_check.stderr
+++ b/tests/ui/deprecation/deprecated_no_stack_check.stderr
@@ -1,10 +1,10 @@
 error[E0557]: feature has been removed
-  --> $DIR/deprecated_no_stack_check.rs:4:12
+  --> $DIR/deprecated_no_stack_check.rs:2:12
    |
 LL | #![feature(no_stack_check)]
    |            ^^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.0.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/40110> for more information
+   = note: removed in 1.0.0; see <https://github.com/rust-lang/rust/pull/40110> for more information
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/feature-gates/feature-gate-coverage-attribute.rs b/tests/ui/feature-gates/feature-gate-coverage-attribute.rs
index 67ac866803c..0a463755f13 100644
--- a/tests/ui/feature-gates/feature-gate-coverage-attribute.rs
+++ b/tests/ui/feature-gates/feature-gate-coverage-attribute.rs
@@ -1,5 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![crate_type = "lib"]
 #![feature(no_coverage)] //~ ERROR feature has been removed [E0557]
 
diff --git a/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr b/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr
index 8c23544698d..68d0d9bc3c3 100644
--- a/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr
+++ b/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr
@@ -1,14 +1,14 @@
 error[E0557]: feature has been removed
-  --> $DIR/feature-gate-coverage-attribute.rs:4:12
+  --> $DIR/feature-gate-coverage-attribute.rs:2:12
    |
 LL | #![feature(no_coverage)]
    |            ^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.74.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/114656> for more information
+   = note: removed in 1.74.0; see <https://github.com/rust-lang/rust/pull/114656> for more information
    = note: renamed to `coverage_attribute`
 
 error[E0658]: the `#[coverage]` attribute is an experimental feature
-  --> $DIR/feature-gate-coverage-attribute.rs:12:1
+  --> $DIR/feature-gate-coverage-attribute.rs:10:1
    |
 LL | #[coverage(off)]
    | ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/feature-gates/gated-bad-feature.rs b/tests/ui/feature-gates/gated-bad-feature.rs
index aa094acba37..51f2db5556e 100644
--- a/tests/ui/feature-gates/gated-bad-feature.rs
+++ b/tests/ui/feature-gates/gated-bad-feature.rs
@@ -1,4 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
 #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
 //~^ ERROR malformed `feature`
 //~| ERROR malformed `feature`
diff --git a/tests/ui/feature-gates/gated-bad-feature.stderr b/tests/ui/feature-gates/gated-bad-feature.stderr
index 0e75dff14f8..e0e84d84235 100644
--- a/tests/ui/feature-gates/gated-bad-feature.stderr
+++ b/tests/ui/feature-gates/gated-bad-feature.stderr
@@ -1,43 +1,43 @@
 error[E0556]: malformed `feature` attribute input
-  --> $DIR/gated-bad-feature.rs:2:25
+  --> $DIR/gated-bad-feature.rs:1:25
    |
 LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
    |                         ^^^^^^^^ help: expected just one word: `foo`
 
 error[E0556]: malformed `feature` attribute input
-  --> $DIR/gated-bad-feature.rs:2:35
+  --> $DIR/gated-bad-feature.rs:1:35
    |
 LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
    |                                   ^^^^^^^^^^^ help: expected just one word: `foo`
 
 error[E0557]: feature has been removed
-  --> $DIR/gated-bad-feature.rs:9:12
+  --> $DIR/gated-bad-feature.rs:8:12
    |
 LL | #![feature(test_removed_feature)]
    |            ^^^^^^^^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.0.0 (you are using $RUSTC_VERSION)
+   = note: removed in 1.0.0
 
 error: malformed `feature` attribute input
-  --> $DIR/gated-bad-feature.rs:7:1
+  --> $DIR/gated-bad-feature.rs:6:1
    |
 LL | #![feature]
    | ^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name2, ...)]`
 
 error: malformed `feature` attribute input
-  --> $DIR/gated-bad-feature.rs:8:1
+  --> $DIR/gated-bad-feature.rs:7:1
    |
 LL | #![feature = "foo"]
    | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name2, ...)]`
 
 error[E0635]: unknown feature `foo_bar_baz`
-  --> $DIR/gated-bad-feature.rs:2:12
+  --> $DIR/gated-bad-feature.rs:1:12
    |
 LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
    |            ^^^^^^^^^^^
 
 error[E0635]: unknown feature `foo`
-  --> $DIR/gated-bad-feature.rs:2:48
+  --> $DIR/gated-bad-feature.rs:1:48
    |
 LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
    |                                                ^^^
diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs
index a217a7fda51..d8c5f48f9fd 100644
--- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs
+++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs
@@ -1,5 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![feature(external_doc)] //~ ERROR feature has been removed
 #![doc(include("README.md"))] //~ ERROR unknown `doc` attribute `include`
 
diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr
index 43205c7360b..bd8c56c61c3 100644
--- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr
+++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr
@@ -1,14 +1,14 @@
 error[E0557]: feature has been removed
-  --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:3:12
+  --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:1:12
    |
 LL | #![feature(external_doc)]
    |            ^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.54.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/85457> for more information
+   = note: removed in 1.54.0; see <https://github.com/rust-lang/rust/pull/85457> for more information
    = note: use #[doc = include_str!("filename")] instead, which handles macro invocations
 
 error: unknown `doc` attribute `include`
-  --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:4:8
+  --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:2:8
    |
 LL | #![doc(include("README.md"))]
    |        ^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/impl-trait/in-bindings/lifetime-equality.rs b/tests/ui/impl-trait/in-bindings/lifetime-equality.rs
new file mode 100644
index 00000000000..6cf48dccc7d
--- /dev/null
+++ b/tests/ui/impl-trait/in-bindings/lifetime-equality.rs
@@ -0,0 +1,19 @@
+//@ check-pass
+
+#![feature(impl_trait_in_bindings)]
+
+// A test for #61773 which would have been difficult to support if we
+// were to represent `impl_trait_in_bindings` using opaque types.
+
+trait Trait<'a, 'b> { }
+impl<T> Trait<'_, '_> for T { }
+
+
+fn bar<'a, 'b>(data0: &'a u32, data1: &'b u32) {
+  let x: impl Trait<'_, '_> = (data0, data1);
+  force_equal(x);
+}
+
+fn force_equal<'a>(t: impl Trait<'a, 'a>) { }
+
+fn main() { }
diff --git a/tests/ui/impl-trait/in-bindings/region-lifetimes.rs b/tests/ui/impl-trait/in-bindings/region-lifetimes.rs
new file mode 100644
index 00000000000..189ab85a276
--- /dev/null
+++ b/tests/ui/impl-trait/in-bindings/region-lifetimes.rs
@@ -0,0 +1,17 @@
+//@ check-pass
+
+#![feature(impl_trait_in_bindings)]
+
+// A test for #61773 which would have been difficult to support if we
+// were to represent `impl_trait_in_bindings` using opaque types.
+
+trait Foo<'a> { }
+impl Foo<'_> for &u32 { }
+
+fn bar<'a>(data: &'a u32) {
+  let x: impl Foo<'_> = data;
+}
+
+fn main() {
+  let _: impl Foo<'_> = &44;
+}
diff --git a/tests/ui/imports/multiple-extern-by-macro-for-underscore.stderr b/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2015.stderr
index 1da5aa87070..985cd654c39 100644
--- a/tests/ui/imports/multiple-extern-by-macro-for-underscore.stderr
+++ b/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2015.stderr
@@ -1,5 +1,5 @@
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/multiple-extern-by-macro-for-underscore.rs:16:11
+  --> $DIR/multiple-extern-by-macro-for-underscore.rs:18:11
    |
 LL |     use ::_;
    |           ^ expected identifier, found reserved identifier
diff --git a/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2021.stderr b/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2021.stderr
new file mode 100644
index 00000000000..985cd654c39
--- /dev/null
+++ b/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2021.stderr
@@ -0,0 +1,8 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-extern-by-macro-for-underscore.rs:18:11
+   |
+LL |     use ::_;
+   |           ^ expected identifier, found reserved identifier
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/imports/multiple-extern-by-macro-for-underscore.rs b/tests/ui/imports/multiple-extern-by-macro-for-underscore.rs
index ddf735d8947..ab877e06246 100644
--- a/tests/ui/imports/multiple-extern-by-macro-for-underscore.rs
+++ b/tests/ui/imports/multiple-extern-by-macro-for-underscore.rs
@@ -1,4 +1,6 @@
-//@ edition: 2021
+//@ revisions: ed2015 ed2021
+//@[ed2015] edition: 2015
+//@[ed2021] edition: 2021
 
 // issue#128813
 
diff --git a/tests/ui/lint/lint-non-uppercase-usages.fixed b/tests/ui/lint/lint-non-uppercase-usages.fixed
new file mode 100644
index 00000000000..231991dcae0
--- /dev/null
+++ b/tests/ui/lint/lint-non-uppercase-usages.fixed
@@ -0,0 +1,44 @@
+// Checks that the `non_upper_case_globals` emits suggestions for usages as well
+// <https://github.com/rust-lang/rust/issues/124061>
+
+//@ check-pass
+//@ run-rustfix
+
+#![allow(dead_code)]
+
+use std::cell::Cell;
+
+const MY_STATIC: u32 = 0;
+//~^ WARN constant `my_static` should have an upper case name
+//~| SUGGESTION MY_STATIC
+
+const LOL: u32 = MY_STATIC + 0;
+//~^ SUGGESTION MY_STATIC
+
+mod my_mod {
+    const INSIDE_MOD: u32 = super::MY_STATIC + 0;
+    //~^ SUGGESTION MY_STATIC
+}
+
+thread_local! {
+    static FOO_FOO: Cell<usize> = unreachable!();
+    //~^ WARN constant `fooFOO` should have an upper case name
+    //~| SUGGESTION FOO_FOO
+}
+
+fn foo<const FOO: u32>() {
+    //~^ WARN const parameter `foo` should have an upper case name
+    //~| SUGGESTION FOO
+    let _a = FOO + 1;
+    //~^ SUGGESTION FOO
+}
+
+fn main() {
+    let _a = crate::MY_STATIC;
+    //~^ SUGGESTION MY_STATIC
+
+    FOO_FOO.set(9);
+    //~^ SUGGESTION FOO_FOO
+    println!("{}", FOO_FOO.get());
+    //~^ SUGGESTION FOO_FOO
+}
diff --git a/tests/ui/lint/lint-non-uppercase-usages.rs b/tests/ui/lint/lint-non-uppercase-usages.rs
new file mode 100644
index 00000000000..9cdf5e47003
--- /dev/null
+++ b/tests/ui/lint/lint-non-uppercase-usages.rs
@@ -0,0 +1,44 @@
+// Checks that the `non_upper_case_globals` emits suggestions for usages as well
+// <https://github.com/rust-lang/rust/issues/124061>
+
+//@ check-pass
+//@ run-rustfix
+
+#![allow(dead_code)]
+
+use std::cell::Cell;
+
+const my_static: u32 = 0;
+//~^ WARN constant `my_static` should have an upper case name
+//~| SUGGESTION MY_STATIC
+
+const LOL: u32 = my_static + 0;
+//~^ SUGGESTION MY_STATIC
+
+mod my_mod {
+    const INSIDE_MOD: u32 = super::my_static + 0;
+    //~^ SUGGESTION MY_STATIC
+}
+
+thread_local! {
+    static fooFOO: Cell<usize> = unreachable!();
+    //~^ WARN constant `fooFOO` should have an upper case name
+    //~| SUGGESTION FOO_FOO
+}
+
+fn foo<const foo: u32>() {
+    //~^ WARN const parameter `foo` should have an upper case name
+    //~| SUGGESTION FOO
+    let _a = foo + 1;
+    //~^ SUGGESTION FOO
+}
+
+fn main() {
+    let _a = crate::my_static;
+    //~^ SUGGESTION MY_STATIC
+
+    fooFOO.set(9);
+    //~^ SUGGESTION FOO_FOO
+    println!("{}", fooFOO.get());
+    //~^ SUGGESTION FOO_FOO
+}
diff --git a/tests/ui/lint/lint-non-uppercase-usages.stderr b/tests/ui/lint/lint-non-uppercase-usages.stderr
new file mode 100644
index 00000000000..7c7e573a88e
--- /dev/null
+++ b/tests/ui/lint/lint-non-uppercase-usages.stderr
@@ -0,0 +1,39 @@
+warning: constant `my_static` should have an upper case name
+  --> $DIR/lint-non-uppercase-usages.rs:11:7
+   |
+LL | const my_static: u32 = 0;
+   |       ^^^^^^^^^
+   |
+   = note: `#[warn(non_upper_case_globals)]` on by default
+help: convert the identifier to upper case
+   |
+LL - const my_static: u32 = 0;
+LL + const MY_STATIC: u32 = 0;
+   |
+
+warning: constant `fooFOO` should have an upper case name
+  --> $DIR/lint-non-uppercase-usages.rs:24:12
+   |
+LL |     static fooFOO: Cell<usize> = unreachable!();
+   |            ^^^^^^
+   |
+help: convert the identifier to upper case
+   |
+LL -     static fooFOO: Cell<usize> = unreachable!();
+LL +     static FOO_FOO: Cell<usize> = unreachable!();
+   |
+
+warning: const parameter `foo` should have an upper case name
+  --> $DIR/lint-non-uppercase-usages.rs:29:14
+   |
+LL | fn foo<const foo: u32>() {
+   |              ^^^
+   |
+help: convert the identifier to upper case (notice the capitalization difference)
+   |
+LL - fn foo<const foo: u32>() {
+LL + fn foo<const FOO: u32>() {
+   |
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/macros/macro-match-nonterminal.rs b/tests/ui/macros/macro-match-nonterminal.rs
index fa2af945a1f..1643cddb192 100644
--- a/tests/ui/macros/macro-match-nonterminal.rs
+++ b/tests/ui/macros/macro-match-nonterminal.rs
@@ -2,11 +2,10 @@ macro_rules! test {
     ($a, $b) => {
         //~^ ERROR missing fragment
         //~| ERROR missing fragment
-        //~| ERROR missing fragment
         ()
     };
 }
 
 fn main() {
-    test!()
+    test!() //~ ERROR unexpected end of macro invocation
 }
diff --git a/tests/ui/macros/macro-match-nonterminal.stderr b/tests/ui/macros/macro-match-nonterminal.stderr
index 8196d795c4c..a92d099ca00 100644
--- a/tests/ui/macros/macro-match-nonterminal.stderr
+++ b/tests/ui/macros/macro-match-nonterminal.stderr
@@ -24,7 +24,16 @@ help: try adding a specifier here
 LL |     ($a, $b:spec) => {
    |            +++++
 
-error: missing fragment specifier
+error: unexpected end of macro invocation
+  --> $DIR/macro-match-nonterminal.rs:10:5
+   |
+LL | macro_rules! test {
+   | ----------------- when calling this macro
+...
+LL |     test!()
+   |     ^^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$a:tt`
   --> $DIR/macro-match-nonterminal.rs:2:6
    |
 LL |     ($a, $b) => {
diff --git a/tests/ui/macros/macro-missing-fragment-deduplication.rs b/tests/ui/macros/macro-missing-fragment-deduplication.rs
index 481f08fa111..fc81c713b4d 100644
--- a/tests/ui/macros/macro-missing-fragment-deduplication.rs
+++ b/tests/ui/macros/macro-missing-fragment-deduplication.rs
@@ -2,12 +2,11 @@
 
 macro_rules! m {
     ($name) => {}; //~ ERROR missing fragment
-                   //~| ERROR missing fragment
 }
 
 fn main() {
-    m!();
-    m!();
-    m!();
-    m!();
+    m!(); //~ ERROR unexpected end
+    m!(); //~ ERROR unexpected end
+    m!(); //~ ERROR unexpected end
+    m!(); //~ ERROR unexpected end
 }
diff --git a/tests/ui/macros/macro-missing-fragment-deduplication.stderr b/tests/ui/macros/macro-missing-fragment-deduplication.stderr
index 820f7eb3cf7..29d2ae0e16e 100644
--- a/tests/ui/macros/macro-missing-fragment-deduplication.stderr
+++ b/tests/ui/macros/macro-missing-fragment-deduplication.stderr
@@ -11,11 +11,65 @@ help: try adding a specifier here
 LL |     ($name:spec) => {};
    |           +++++
 
-error: missing fragment specifier
+error: unexpected end of macro invocation
+  --> $DIR/macro-missing-fragment-deduplication.rs:8:5
+   |
+LL | macro_rules! m {
+   | -------------- when calling this macro
+...
+LL |     m!();
+   |     ^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$name:tt`
+  --> $DIR/macro-missing-fragment-deduplication.rs:4:6
+   |
+LL |     ($name) => {};
+   |      ^^^^^
+
+error: unexpected end of macro invocation
+  --> $DIR/macro-missing-fragment-deduplication.rs:9:5
+   |
+LL | macro_rules! m {
+   | -------------- when calling this macro
+...
+LL |     m!();
+   |     ^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$name:tt`
+  --> $DIR/macro-missing-fragment-deduplication.rs:4:6
+   |
+LL |     ($name) => {};
+   |      ^^^^^
+
+error: unexpected end of macro invocation
+  --> $DIR/macro-missing-fragment-deduplication.rs:10:5
+   |
+LL | macro_rules! m {
+   | -------------- when calling this macro
+...
+LL |     m!();
+   |     ^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$name:tt`
+  --> $DIR/macro-missing-fragment-deduplication.rs:4:6
+   |
+LL |     ($name) => {};
+   |      ^^^^^
+
+error: unexpected end of macro invocation
+  --> $DIR/macro-missing-fragment-deduplication.rs:11:5
+   |
+LL | macro_rules! m {
+   | -------------- when calling this macro
+...
+LL |     m!();
+   |     ^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$name:tt`
   --> $DIR/macro-missing-fragment-deduplication.rs:4:6
    |
 LL |     ($name) => {};
    |      ^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/macros/macro-missing-fragment.rs b/tests/ui/macros/macro-missing-fragment.rs
index 533aa147bcb..7ed9074020e 100644
--- a/tests/ui/macros/macro-missing-fragment.rs
+++ b/tests/ui/macros/macro-missing-fragment.rs
@@ -2,7 +2,6 @@
 
 macro_rules! used_arm {
     ( $( any_token $field_rust_type )* ) => {}; //~ ERROR missing fragment
-                                                //~| ERROR missing fragment
 }
 
 macro_rules! used_macro_unused_arm {
diff --git a/tests/ui/macros/macro-missing-fragment.stderr b/tests/ui/macros/macro-missing-fragment.stderr
index 4a99d7d949c..886292378d1 100644
--- a/tests/ui/macros/macro-missing-fragment.stderr
+++ b/tests/ui/macros/macro-missing-fragment.stderr
@@ -12,7 +12,7 @@ LL |     ( $( any_token $field_rust_type:spec )* ) => {};
    |                                    +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:10:7
+  --> $DIR/macro-missing-fragment.rs:9:7
    |
 LL |     ( $name ) => {};
    |       ^^^^^
@@ -25,7 +25,7 @@ LL |     ( $name:spec ) => {};
    |            +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:14:7
+  --> $DIR/macro-missing-fragment.rs:13:7
    |
 LL |     ( $name ) => {};
    |       ^^^^^
@@ -37,11 +37,5 @@ help: try adding a specifier here
 LL |     ( $name:spec ) => {};
    |            +++++
 
-error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:4:20
-   |
-LL |     ( $( any_token $field_rust_type )* ) => {};
-   |                    ^^^^^^^^^^^^^^^^
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/macros/macro-reexport-removed.rs b/tests/ui/macros/macro-reexport-removed.rs
index 33e7cab4068..4a054686d77 100644
--- a/tests/ui/macros/macro-reexport-removed.rs
+++ b/tests/ui/macros/macro-reexport-removed.rs
@@ -1,5 +1,4 @@
 //@ aux-build:two_macros.rs
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
 
 #![feature(macro_reexport)] //~ ERROR feature has been removed
 
diff --git a/tests/ui/macros/macro-reexport-removed.stderr b/tests/ui/macros/macro-reexport-removed.stderr
index d4940eeb775..8130fe0c4bd 100644
--- a/tests/ui/macros/macro-reexport-removed.stderr
+++ b/tests/ui/macros/macro-reexport-removed.stderr
@@ -1,14 +1,14 @@
 error[E0557]: feature has been removed
-  --> $DIR/macro-reexport-removed.rs:4:12
+  --> $DIR/macro-reexport-removed.rs:3:12
    |
 LL | #![feature(macro_reexport)]
    |            ^^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.0.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/49982> for more information
+   = note: removed in 1.0.0; see <https://github.com/rust-lang/rust/pull/49982> for more information
    = note: subsumed by `pub use`
 
 error: cannot find attribute `macro_reexport` in this scope
-  --> $DIR/macro-reexport-removed.rs:6:3
+  --> $DIR/macro-reexport-removed.rs:5:3
    |
 LL | #[macro_reexport(macro_one)]
    |   ^^^^^^^^^^^^^^ help: a built-in attribute with a similar name exists: `macro_export`
diff --git a/tests/ui/parser/macro/issue-33569.rs b/tests/ui/parser/macro/issue-33569.rs
index e0a5352ab06..7288fa858db 100644
--- a/tests/ui/parser/macro/issue-33569.rs
+++ b/tests/ui/parser/macro/issue-33569.rs
@@ -1,11 +1,10 @@
 macro_rules! foo {
     { $+ } => { //~ ERROR expected identifier, found `+`
                 //~^ ERROR missing fragment specifier
-                //~| ERROR missing fragment specifier
         $(x)(y) //~ ERROR expected one of: `*`, `+`, or `?`
     }
 }
 
-foo!();
+foo!(); //~ ERROR unexpected end
 
 fn main() {}
diff --git a/tests/ui/parser/macro/issue-33569.stderr b/tests/ui/parser/macro/issue-33569.stderr
index 0d53c04c1c9..dd8e38f0d6e 100644
--- a/tests/ui/parser/macro/issue-33569.stderr
+++ b/tests/ui/parser/macro/issue-33569.stderr
@@ -4,12 +4,6 @@ error: expected identifier, found `+`
 LL |     { $+ } => {
    |        ^
 
-error: expected one of: `*`, `+`, or `?`
-  --> $DIR/issue-33569.rs:5:13
-   |
-LL |         $(x)(y)
-   |             ^^^
-
 error: missing fragment specifier
   --> $DIR/issue-33569.rs:2:8
    |
@@ -23,7 +17,22 @@ help: try adding a specifier here
 LL |     { $+:spec } => {
    |         +++++
 
-error: missing fragment specifier
+error: expected one of: `*`, `+`, or `?`
+  --> $DIR/issue-33569.rs:4:13
+   |
+LL |         $(x)(y)
+   |             ^^^
+
+error: unexpected end of macro invocation
+  --> $DIR/issue-33569.rs:8:1
+   |
+LL | macro_rules! foo {
+   | ---------------- when calling this macro
+...
+LL | foo!();
+   | ^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$<!dummy!>:tt`
   --> $DIR/issue-33569.rs:2:8
    |
 LL |     { $+ } => {
diff --git a/tests/ui/rustdoc/renamed-features-rustdoc_internals.rs b/tests/ui/rustdoc/renamed-features-rustdoc_internals.rs
index c0f3c190b65..739c624d0c6 100644
--- a/tests/ui/rustdoc/renamed-features-rustdoc_internals.rs
+++ b/tests/ui/rustdoc/renamed-features-rustdoc_internals.rs
@@ -1,5 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![feature(doc_keyword)] //~ ERROR
 #![feature(doc_primitive)] //~ ERROR
 #![crate_type = "lib"]
diff --git a/tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr b/tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr
index 9c664da8ee6..0608a8b58a2 100644
--- a/tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr
+++ b/tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr
@@ -1,19 +1,19 @@
 error[E0557]: feature has been removed
-  --> $DIR/renamed-features-rustdoc_internals.rs:3:12
+  --> $DIR/renamed-features-rustdoc_internals.rs:1:12
    |
 LL | #![feature(doc_keyword)]
    |            ^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.58.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/90420> for more information
+   = note: removed in 1.58.0; see <https://github.com/rust-lang/rust/pull/90420> for more information
    = note: merged into `#![feature(rustdoc_internals)]`
 
 error[E0557]: feature has been removed
-  --> $DIR/renamed-features-rustdoc_internals.rs:4:12
+  --> $DIR/renamed-features-rustdoc_internals.rs:2:12
    |
 LL | #![feature(doc_primitive)]
    |            ^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.58.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/90420> for more information
+   = note: removed in 1.58.0; see <https://github.com/rust-lang/rust/pull/90420> for more information
    = note: merged into `#![feature(rustdoc_internals)]`
 
 error: aborting due to 2 previous errors
diff --git a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs
index 9a41b9c8173..f90ff91aff4 100644
--- a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs
+++ b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs
@@ -6,7 +6,6 @@
 // Regression test for issue #125877.
 
 //@ compile-flags: -Znext-solver
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
 
 #![feature(const_trait_impl, effects)]
 //~^ ERROR feature has been removed
diff --git a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr
index a04f98e68a6..566961b27f3 100644
--- a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr
+++ b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr
@@ -1,14 +1,14 @@
 error[E0557]: feature has been removed
-  --> $DIR/const-trait-impl-parameter-mismatch.rs:11:30
+  --> $DIR/const-trait-impl-parameter-mismatch.rs:10:30
    |
 LL | #![feature(const_trait_impl, effects)]
    |                              ^^^^^^^ feature has been removed
    |
-   = note: removed in 1.84.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/132479> for more information
+   = note: removed in 1.84.0; see <https://github.com/rust-lang/rust/pull/132479> for more information
    = note: removed, redundant with `#![feature(const_trait_impl)]`
 
 error[E0049]: associated function `compute` has 0 type parameters but its trait declaration has 1 type parameter
-  --> $DIR/const-trait-impl-parameter-mismatch.rs:20:16
+  --> $DIR/const-trait-impl-parameter-mismatch.rs:19:16
    |
 LL |     fn compute<T: ~const Aux>() -> u32;
    |                - expected 1 type parameter
diff --git a/tests/ui/underscore-imports/issue-110164.stderr b/tests/ui/underscore-imports/issue-110164.ed2015.stderr
index d8a4b6bbb75..f34b5ab5dde 100644
--- a/tests/ui/underscore-imports/issue-110164.stderr
+++ b/tests/ui/underscore-imports/issue-110164.ed2015.stderr
@@ -1,17 +1,17 @@
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/issue-110164.rs:5:5
+  --> $DIR/issue-110164.rs:8:5
    |
 LL | use _::a;
    |     ^ expected identifier, found reserved identifier
 
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/issue-110164.rs:8:5
+  --> $DIR/issue-110164.rs:10:5
    |
 LL | use _::*;
    |     ^ expected identifier, found reserved identifier
 
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/issue-110164.rs:13:9
+  --> $DIR/issue-110164.rs:14:9
    |
 LL |     use _::a;
    |         ^ expected identifier, found reserved identifier
@@ -23,41 +23,17 @@ LL |     use _::*;
    |         ^ expected identifier, found reserved identifier
 
 error[E0432]: unresolved import `self::*`
-  --> $DIR/issue-110164.rs:1:5
+  --> $DIR/issue-110164.rs:4:5
    |
 LL | use self::*;
    |     ^^^^^^^ cannot glob-import a module into itself
 
 error[E0432]: unresolved import `crate::*`
-  --> $DIR/issue-110164.rs:3:5
+  --> $DIR/issue-110164.rs:6:5
    |
 LL | use crate::*;
    |     ^^^^^^^^ cannot glob-import a module into itself
 
-error[E0432]: unresolved import `_`
-  --> $DIR/issue-110164.rs:8:5
-   |
-LL | use _::*;
-   |     ^ `_` is not a valid crate or module name
-
-error[E0432]: unresolved import `_`
-  --> $DIR/issue-110164.rs:5:5
-   |
-LL | use _::a;
-   |     ^ `_` is not a valid crate or module name
-
-error[E0432]: unresolved import `_`
-  --> $DIR/issue-110164.rs:13:9
-   |
-LL |     use _::a;
-   |         ^ `_` is not a valid crate or module name
-
-error[E0432]: unresolved import `_`
-  --> $DIR/issue-110164.rs:16:9
-   |
-LL |     use _::*;
-   |         ^ `_` is not a valid crate or module name
-
-error: aborting due to 10 previous errors
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/underscore-imports/issue-110164.ed2021.stderr b/tests/ui/underscore-imports/issue-110164.ed2021.stderr
new file mode 100644
index 00000000000..f34b5ab5dde
--- /dev/null
+++ b/tests/ui/underscore-imports/issue-110164.ed2021.stderr
@@ -0,0 +1,39 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/issue-110164.rs:8:5
+   |
+LL | use _::a;
+   |     ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/issue-110164.rs:10:5
+   |
+LL | use _::*;
+   |     ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/issue-110164.rs:14:9
+   |
+LL |     use _::a;
+   |         ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/issue-110164.rs:16:9
+   |
+LL |     use _::*;
+   |         ^ expected identifier, found reserved identifier
+
+error[E0432]: unresolved import `self::*`
+  --> $DIR/issue-110164.rs:4:5
+   |
+LL | use self::*;
+   |     ^^^^^^^ cannot glob-import a module into itself
+
+error[E0432]: unresolved import `crate::*`
+  --> $DIR/issue-110164.rs:6:5
+   |
+LL | use crate::*;
+   |     ^^^^^^^^ cannot glob-import a module into itself
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/underscore-imports/issue-110164.rs b/tests/ui/underscore-imports/issue-110164.rs
index 6fd13414500..bb080c5e471 100644
--- a/tests/ui/underscore-imports/issue-110164.rs
+++ b/tests/ui/underscore-imports/issue-110164.rs
@@ -1,19 +1,18 @@
+//@ revisions: ed2015 ed2021
+//@[ed2015] edition: 2015
+//@[ed2021] edition: 2021
 use self::*;
 //~^ ERROR unresolved import `self::*`
 use crate::*;
 //~^ ERROR unresolved import `crate::*`
 use _::a;
 //~^ ERROR expected identifier, found reserved identifier `_`
-//~| ERROR unresolved import `_`
 use _::*;
 //~^ ERROR expected identifier, found reserved identifier `_`
-//~| ERROR unresolved import `_`
 
 fn main() {
     use _::a;
     //~^ ERROR expected identifier, found reserved identifier `_`
-    //~| ERROR unresolved import `_`
     use _::*;
     //~^ ERROR expected identifier, found reserved identifier `_`
-    //~| ERROR unresolved import `_`
 }
diff --git a/tests/ui/underscore-imports/multiple-uses.ed2015.stderr b/tests/ui/underscore-imports/multiple-uses.ed2015.stderr
new file mode 100644
index 00000000000..a295586fa16
--- /dev/null
+++ b/tests/ui/underscore-imports/multiple-uses.ed2015.stderr
@@ -0,0 +1,49 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:4:9
+   |
+LL | pub use _::{a, b};
+   |         ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:6:18
+   |
+LL | pub use std::{a, _};
+   |                  ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:9:18
+   |
+LL | pub use std::{b, _, c};
+   |                  ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:12:15
+   |
+LL | pub use std::{_, d};
+   |               ^ expected identifier, found reserved identifier
+
+error[E0432]: unresolved import `std::a`
+  --> $DIR/multiple-uses.rs:6:15
+   |
+LL | pub use std::{a, _};
+   |               ^ no `a` in the root
+
+error[E0432]: unresolved imports `std::b`, `std::c`
+  --> $DIR/multiple-uses.rs:9:15
+   |
+LL | pub use std::{b, _, c};
+   |               ^     ^
+   |               |     |
+   |               |     no `c` in the root
+   |               |     help: a similar name exists in the module: `rc`
+   |               no `b` in the root
+
+error[E0432]: unresolved import `std::d`
+  --> $DIR/multiple-uses.rs:12:18
+   |
+LL | pub use std::{_, d};
+   |                  ^ no `d` in the root
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/underscore-imports/multiple-uses.ed2021.stderr b/tests/ui/underscore-imports/multiple-uses.ed2021.stderr
new file mode 100644
index 00000000000..a295586fa16
--- /dev/null
+++ b/tests/ui/underscore-imports/multiple-uses.ed2021.stderr
@@ -0,0 +1,49 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:4:9
+   |
+LL | pub use _::{a, b};
+   |         ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:6:18
+   |
+LL | pub use std::{a, _};
+   |                  ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:9:18
+   |
+LL | pub use std::{b, _, c};
+   |                  ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:12:15
+   |
+LL | pub use std::{_, d};
+   |               ^ expected identifier, found reserved identifier
+
+error[E0432]: unresolved import `std::a`
+  --> $DIR/multiple-uses.rs:6:15
+   |
+LL | pub use std::{a, _};
+   |               ^ no `a` in the root
+
+error[E0432]: unresolved imports `std::b`, `std::c`
+  --> $DIR/multiple-uses.rs:9:15
+   |
+LL | pub use std::{b, _, c};
+   |               ^     ^
+   |               |     |
+   |               |     no `c` in the root
+   |               |     help: a similar name exists in the module: `rc`
+   |               no `b` in the root
+
+error[E0432]: unresolved import `std::d`
+  --> $DIR/multiple-uses.rs:12:18
+   |
+LL | pub use std::{_, d};
+   |                  ^ no `d` in the root
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/underscore-imports/multiple-uses.rs b/tests/ui/underscore-imports/multiple-uses.rs
new file mode 100644
index 00000000000..31dd1862429
--- /dev/null
+++ b/tests/ui/underscore-imports/multiple-uses.rs
@@ -0,0 +1,16 @@
+//@ revisions: ed2015 ed2021
+//@[ed2015] edition: 2015
+//@[ed2021] edition: 2021
+pub use _::{a, b};
+//~^ ERROR expected identifier, found reserved identifier `_`
+pub use std::{a, _};
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR unresolved import `std::a`
+pub use std::{b, _, c};
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR unresolved imports `std::b`, `std::c`
+pub use std::{_, d};
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR unresolved import `std::d`
+
+fn main() {}
diff --git a/tests/ui/unsized-locals/yote.rs b/tests/ui/unsized-locals/yote.rs
index f09db9dd006..1de75a6ce61 100644
--- a/tests/ui/unsized-locals/yote.rs
+++ b/tests/ui/unsized-locals/yote.rs
@@ -1,4 +1,2 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![feature(unsized_locals)] //~ERROR feature has been removed
 #![crate_type = "lib"]
diff --git a/tests/ui/unsized-locals/yote.stderr b/tests/ui/unsized-locals/yote.stderr
index 655aad5360c..8e7da64038a 100644
--- a/tests/ui/unsized-locals/yote.stderr
+++ b/tests/ui/unsized-locals/yote.stderr
@@ -1,10 +1,10 @@
 error[E0557]: feature has been removed
-  --> $DIR/yote.rs:3:12
+  --> $DIR/yote.rs:1:12
    |
 LL | #![feature(unsized_locals)]
    |            ^^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in CURRENT_RUSTC_VERSION (you are using $RUSTC_VERSION)
+   = note: removed in CURRENT_RUSTC_VERSION
    = note: removed due to implementation concerns; see https://github.com/rust-lang/rust/issues/111942
 
 error: aborting due to 1 previous error