about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-05-21 10:19:04 +0000
committerbors <bors@rust-lang.org>2025-05-21 10:19:04 +0000
commitc43786c9b7b8d8dcc3f9c604e0e3074c16ed69d3 (patch)
tree2b836335dfd46af2703cc0758f24c342ae22b1ad
parentbbd3a5ada41e0d4678de15d10404a4733dba4087 (diff)
parent5b150e312202cdceb6e0f6cb7ef58b3ac9780aea (diff)
downloadrust-c43786c9b7b8d8dcc3f9c604e0e3074c16ed69d3.tar.gz
rust-c43786c9b7b8d8dcc3f9c604e0e3074c16ed69d3.zip
Auto merge of #141331 - matthiaskrgr:rollup-k0loxj6, r=matthiaskrgr
Rollup of 7 pull requests

Successful merges:

 - #137759 (Add `std::os::unix::process::CommandExt::chroot` to safely chroot a child process)
 - #140994 (replace `cc_detect::cc2ar` with `cc::try_get_archiver`)
 - #141213 (Suggest use "{}", self.x instead of {self.x} when resolve x as field of `self`)
 - #141283 (Allow `x perf` to find rustc.exe on Windows)
 - #141284 (Allow trailing comma after argument in query definition)
 - #141317 (typeck: catch `continue`s pointing to blocks)
 - #141318 (Avoid creating an empty identifer in `Symbol::to_ident_string`.)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs34
-rw-r--r--compiler/rustc_macros/src/query.rs1
-rw-r--r--compiler/rustc_mir_build/src/builder/mod.rs3
-rw-r--r--compiler/rustc_passes/src/liveness.rs10
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs24
-rw-r--r--compiler/rustc_span/src/symbol.rs3
-rw-r--r--library/std/src/os/unix/process.rs18
-rw-r--r--library/std/src/sys/process/unix/common.rs13
-rw-r--r--library/std/src/sys/process/unix/unix.rs10
-rw-r--r--library/std/src/sys/process/unix/vxworks.rs6
-rw-r--r--src/bootstrap/Cargo.lock4
-rw-r--r--src/bootstrap/Cargo.toml2
-rw-r--r--src/bootstrap/src/core/build_steps/perf.rs6
-rw-r--r--src/bootstrap/src/utils/cc_detect.rs34
-rw-r--r--src/bootstrap/src/utils/cc_detect/tests.rs113
-rw-r--r--src/ci/docker/host-x86_64/arm-android/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/dist-android/Dockerfile2
-rw-r--r--tests/crashes/113379.rs7
-rw-r--r--tests/crashes/121623.rs8
-rw-r--r--tests/crashes/140884.rs6
-rw-r--r--tests/ui/extern/extern-empty-string-issue-140884.rs3
-rw-r--r--tests/ui/extern/extern-empty-string-issue-140884.stderr15
-rw-r--r--tests/ui/label/continue-pointing-to-block-ice-113379.rs12
-rw-r--r--tests/ui/label/continue-pointing-to-block-ice-113379.stderr43
-rw-r--r--tests/ui/label/continue-pointing-to-block-ice-121623.rs11
-rw-r--r--tests/ui/label/continue-pointing-to-block-ice-121623.stderr14
-rw-r--r--tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs15
-rw-r--r--tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr48
-rw-r--r--tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr12
29 files changed, 277 insertions, 201 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 2c28ffd1fe3..a1a33885b94 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -532,14 +532,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ExprKind::Break(destination, ref expr_opt) => {
                 self.check_expr_break(destination, expr_opt.as_deref(), expr)
             }
-            ExprKind::Continue(destination) => {
-                if destination.target_id.is_ok() {
-                    tcx.types.never
-                } else {
-                    // There was an error; make type-check fail.
-                    Ty::new_misc_error(tcx)
-                }
-            }
+            ExprKind::Continue(destination) => self.check_expr_continue(destination, expr),
             ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
             ExprKind::Become(call) => self.check_expr_become(call, expr),
             ExprKind::Let(let_expr) => self.check_expr_let(let_expr, expr.hir_id),
@@ -989,6 +982,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    fn check_expr_continue(
+        &self,
+        destination: hir::Destination,
+        expr: &'tcx hir::Expr<'tcx>,
+    ) -> Ty<'tcx> {
+        if let Ok(target_id) = destination.target_id {
+            if let hir::Node::Expr(hir::Expr { kind: ExprKind::Loop(..), .. }) =
+                self.tcx.hir_node(target_id)
+            {
+                self.tcx.types.never
+            } else {
+                // Liveness linting assumes `continue`s all point to loops. We'll report an error
+                // in `check_mod_loops`, but make sure we don't run liveness (#113379, #121623).
+                let guar = self.dcx().span_delayed_bug(
+                    expr.span,
+                    "found `continue` not pointing to loop, but no error reported",
+                );
+                Ty::new_error(self.tcx, guar)
+            }
+        } else {
+            // There was an error; make type-check fail.
+            Ty::new_misc_error(self.tcx)
+        }
+    }
+
     fn check_expr_return(
         &self,
         expr_opt: Option<&'tcx hir::Expr<'tcx>>,
diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs
index ee377277017..2196f71299a 100644
--- a/compiler/rustc_macros/src/query.rs
+++ b/compiler/rustc_macros/src/query.rs
@@ -51,6 +51,7 @@ impl Parse for Query {
         let key = Pat::parse_single(&arg_content)?;
         arg_content.parse::<Token![:]>()?;
         let arg = arg_content.parse()?;
+        let _ = arg_content.parse::<Option<Token![,]>>()?;
         let result = input.parse()?;
 
         // Parse the query modifiers
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index 9cf051a8760..8400709740f 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -66,8 +66,7 @@ pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> {
                 }
             };
 
-            // this must run before MIR dump, because
-            // "not all control paths return a value" is reported here.
+            // Checking liveness after building the THIR ensures there were no typeck errors.
             //
             // maybe move the check to a MIR pass?
             tcx.ensure_ok().check_liveness(def);
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 4e9b7fd44d4..763d9fda804 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -122,7 +122,6 @@ enum LiveNodeKind {
     VarDefNode(Span, HirId),
     ClosureNode,
     ExitNode,
-    ErrNode,
 }
 
 fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
@@ -133,7 +132,6 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
         VarDefNode(s, _) => format!("Var def node [{}]", sm.span_to_diagnostic_string(s)),
         ClosureNode => "Closure node".to_owned(),
         ExitNode => "Exit node".to_owned(),
-        ErrNode => "Error node".to_owned(),
     }
 }
 
@@ -492,6 +490,9 @@ struct Liveness<'a, 'tcx> {
 impl<'a, 'tcx> Liveness<'a, 'tcx> {
     fn new(ir: &'a mut IrMaps<'tcx>, body_owner: LocalDefId) -> Liveness<'a, 'tcx> {
         let typeck_results = ir.tcx.typeck(body_owner);
+        // Liveness linting runs after building the THIR. We make several assumptions based on
+        // typeck succeeding, e.g. that breaks and continues are well-formed.
+        assert!(typeck_results.tainted_by_errors.is_none());
         // FIXME(#132279): we're in a body here.
         let typing_env = ty::TypingEnv::non_body_analysis(ir.tcx, body_owner);
         let closure_min_captures = typeck_results.closure_min_captures.get(&body_owner);
@@ -976,8 +977,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                 // Now that we know the label we're going to,
                 // look it up in the continue loop nodes table
                 self.cont_ln.get(&sc).cloned().unwrap_or_else(|| {
-                    self.ir.tcx.dcx().span_delayed_bug(expr.span, "continue to unknown label");
-                    self.ir.add_live_node(ErrNode)
+                    // Liveness linting happens after building the THIR. Bad labels should already
+                    // have been caught.
+                    span_bug!(expr.span, "continue to unknown label");
                 })
             }
 
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 6768907adde..ca25cdc9563 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -765,12 +765,24 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                 match candidate {
                     AssocSuggestion::Field(field_span) => {
                         if self_is_available {
-                            err.span_suggestion_verbose(
-                                span.shrink_to_lo(),
-                                "you might have meant to use the available field",
-                                format!("{pre}self."),
-                                Applicability::MachineApplicable,
-                            );
+                            let source_map = self.r.tcx.sess.source_map();
+                            // check if the field is used in a format string, such as `"{x}"`
+                            let field_is_format_named_arg = source_map
+                                .span_to_source(span, |s, start, _| {
+                                    Ok(s.get(start - 1..start) == Some("{"))
+                                });
+                            if let Ok(true) = field_is_format_named_arg {
+                                err.help(
+                                    format!("you might have meant to use the available field in a format string: `\"{{}}\", self.{}`", segment.ident.name),
+                                );
+                            } else {
+                                err.span_suggestion_verbose(
+                                    span.shrink_to_lo(),
+                                    "you might have meant to use the available field",
+                                    format!("{pre}self."),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
                         } else {
                             err.span_label(field_span, "a field by that name exists in `Self`");
                         }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index efae6250b07..fbe3b4ca6f5 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -2592,7 +2592,8 @@ impl Symbol {
     /// (`token_to_string`, `Ident::to_string`), except that symbols don't keep the rawness flag
     /// or edition, so we have to guess the rawness using the global edition.
     pub fn to_ident_string(self) -> String {
-        Ident::with_dummy_span(self).to_string()
+        // Avoid creating an empty identifier, because that asserts in debug builds.
+        if self == kw::Empty { String::new() } else { Ident::with_dummy_span(self).to_string() }
     }
 }
 
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index 7c3fa7d6507..57ce3c5a4bf 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -8,6 +8,7 @@ use cfg_if::cfg_if;
 
 use crate::ffi::OsStr;
 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+use crate::path::Path;
 use crate::sealed::Sealed;
 use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
 use crate::{io, process, sys};
@@ -197,6 +198,18 @@ pub trait CommandExt: Sealed {
     /// ```
     #[stable(feature = "process_set_process_group", since = "1.64.0")]
     fn process_group(&mut self, pgroup: i32) -> &mut process::Command;
+
+    /// Set the root of the child process. This calls `chroot` in the child process before executing
+    /// the command.
+    ///
+    /// This happens before changing to the directory specified with
+    /// [`process::Command::current_dir`], and that directory will be relative to the new root.
+    ///
+    /// If no directory has been specified with [`process::Command::current_dir`], this will set the
+    /// directory to `/`, to avoid leaving the current directory outside the chroot. (This is an
+    /// intentional difference from the underlying `chroot` system call.)
+    #[unstable(feature = "process_chroot", issue = "141298")]
+    fn chroot<P: AsRef<Path>>(&mut self, dir: P) -> &mut process::Command;
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -242,6 +255,11 @@ impl CommandExt for process::Command {
         self.as_inner_mut().pgroup(pgroup);
         self
     }
+
+    fn chroot<P: AsRef<Path>>(&mut self, dir: P) -> &mut process::Command {
+        self.as_inner_mut().chroot(dir.as_ref());
+        self
+    }
 }
 
 /// Unix-specific extensions to [`process::ExitStatus`] and
diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs
index a9c2510e6d4..e205a839005 100644
--- a/library/std/src/sys/process/unix/common.rs
+++ b/library/std/src/sys/process/unix/common.rs
@@ -88,6 +88,7 @@ pub struct Command {
 
     program_kind: ProgramKind,
     cwd: Option<CString>,
+    chroot: Option<CString>,
     uid: Option<uid_t>,
     gid: Option<gid_t>,
     saw_nul: bool,
@@ -182,6 +183,7 @@ impl Command {
             program_kind,
             env: Default::default(),
             cwd: None,
+            chroot: None,
             uid: None,
             gid: None,
             saw_nul,
@@ -206,6 +208,7 @@ impl Command {
             program_kind,
             env: Default::default(),
             cwd: None,
+            chroot: None,
             uid: None,
             gid: None,
             saw_nul,
@@ -254,6 +257,12 @@ impl Command {
     pub fn pgroup(&mut self, pgroup: pid_t) {
         self.pgroup = Some(pgroup);
     }
+    pub fn chroot(&mut self, dir: &Path) {
+        self.chroot = Some(os2c(dir.as_os_str(), &mut self.saw_nul));
+        if self.cwd.is_none() {
+            self.cwd(&OsStr::new("/"));
+        }
+    }
 
     #[cfg(target_os = "linux")]
     pub fn create_pidfd(&mut self, val: bool) {
@@ -326,6 +335,10 @@ impl Command {
     pub fn get_pgroup(&self) -> Option<pid_t> {
         self.pgroup
     }
+    #[allow(dead_code)]
+    pub fn get_chroot(&self) -> Option<&CStr> {
+        self.chroot.as_deref()
+    }
 
     pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> {
         &mut self.closures
diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs
index 1b3bd2de265..4f595ac9a1c 100644
--- a/library/std/src/sys/process/unix/unix.rs
+++ b/library/std/src/sys/process/unix/unix.rs
@@ -323,6 +323,15 @@ impl Command {
                 cvt(libc::setuid(u as uid_t))?;
             }
         }
+        if let Some(chroot) = self.get_chroot() {
+            #[cfg(not(target_os = "fuchsia"))]
+            cvt(libc::chroot(chroot.as_ptr()))?;
+            #[cfg(target_os = "fuchsia")]
+            return Err(io::const_error!(
+                io::ErrorKind::Unsupported,
+                "chroot not supported by fuchsia"
+            ));
+        }
         if let Some(cwd) = self.get_cwd() {
             cvt(libc::chdir(cwd.as_ptr()))?;
         }
@@ -447,6 +456,7 @@ impl Command {
             || (self.env_saw_path() && !self.program_is_path())
             || !self.get_closures().is_empty()
             || self.get_groups().is_some()
+            || self.get_chroot().is_some()
         {
             return Ok(None);
         }
diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs
index fab3b36ebf3..f33b4a375da 100644
--- a/library/std/src/sys/process/unix/vxworks.rs
+++ b/library/std/src/sys/process/unix/vxworks.rs
@@ -27,6 +27,12 @@ impl Command {
                 "nul byte found in provided data",
             ));
         }
+        if self.get_chroot().is_some() {
+            return Err(io::const_error!(
+                ErrorKind::Unsupported,
+                "chroot not supported by vxworks",
+            ));
+        }
         let (ours, theirs) = self.setup_io(default, needs_stdin)?;
         let mut p = Process { pid: 0, status: None };
 
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index d10d2d9bf8c..0c8e6633560 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -89,9 +89,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.17"
+version = "1.2.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
+checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
 dependencies = [
  "shlex",
 ]
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 9652d18f1a6..b12b3dfc7b2 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -32,7 +32,7 @@ test = false
 # Most of the time updating these dependencies requires modifications to the
 # bootstrap codebase(e.g., https://github.com/rust-lang/rust/issues/124565);
 # otherwise, some targets will fail. That's why these dependencies are explicitly pinned.
-cc = "=1.2.17"
+cc = "=1.2.23"
 cmake = "=0.1.54"
 
 build_helper = { path = "../build_helper" }
diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs
index 7f4e88bd73c..71cdb665ed4 100644
--- a/src/bootstrap/src/core/build_steps/perf.rs
+++ b/src/bootstrap/src/core/build_steps/perf.rs
@@ -1,3 +1,4 @@
+use std::env::consts::EXE_EXTENSION;
 use std::fmt::{Display, Formatter};
 
 use crate::core::build_steps::compile::{Std, Sysroot};
@@ -160,7 +161,10 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#);
     }
 
     let sysroot = builder.ensure(Sysroot::new(compiler));
-    let rustc = sysroot.join("bin/rustc");
+    let mut rustc = sysroot.clone();
+    rustc.push("bin");
+    rustc.push("rustc");
+    rustc.set_extension(EXE_EXTENSION);
 
     let rustc_perf_dir = builder.build.tempdir().join("rustc-perf");
     let results_dir = rustc_perf_dir.join("results");
diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs
index ceac24d4315..5c9e30706ee 100644
--- a/src/bootstrap/src/utils/cc_detect.rs
+++ b/src/bootstrap/src/utils/cc_detect.rs
@@ -22,43 +22,13 @@
 //! everything.
 
 use std::collections::HashSet;
+use std::iter;
 use std::path::{Path, PathBuf};
-use std::{env, iter};
 
 use crate::core::config::TargetSelection;
 use crate::utils::exec::{BootstrapCommand, command};
 use crate::{Build, CLang, GitRepo};
 
-/// Finds archiver tool for the given target if possible.
-/// FIXME(onur-ozkan): This logic should be replaced by calling into the `cc` crate.
-fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option<PathBuf> {
-    if let Some(ar) = env::var_os(format!("AR_{}", target.triple.replace('-', "_"))) {
-        Some(PathBuf::from(ar))
-    } else if let Some(ar) = env::var_os("AR") {
-        Some(PathBuf::from(ar))
-    } else if target.is_msvc() {
-        None
-    } else if target.contains("musl") || target.contains("openbsd") {
-        Some(PathBuf::from("ar"))
-    } else if target.contains("vxworks") {
-        Some(PathBuf::from("wr-ar"))
-    } else if target.contains("-nto-") {
-        if target.starts_with("i586") {
-            Some(PathBuf::from("ntox86-ar"))
-        } else if target.starts_with("aarch64") {
-            Some(PathBuf::from("ntoaarch64-ar"))
-        } else if target.starts_with("x86_64") {
-            Some(PathBuf::from("ntox86_64-ar"))
-        } else {
-            panic!("Unknown architecture, cannot determine archiver for Neutrino QNX");
-        }
-    } else if target.contains("android") || target.contains("-wasi") {
-        Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar")))
-    } else {
-        Some(default_ar)
-    }
-}
-
 /// Creates and configures a new [`cc::Build`] instance for the given target.
 fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build {
     let mut cfg = cc::Build::new();
@@ -140,7 +110,7 @@ pub fn find_target(build: &Build, target: TargetSelection) {
     let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
         ar
     } else {
-        cc2ar(compiler.path(), target, PathBuf::from(cfg.get_archiver().get_program()))
+        cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok()
     };
 
     build.cc.borrow_mut().insert(target, compiler.clone());
diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs
index 43d61ce02c5..225fb7619b5 100644
--- a/src/bootstrap/src/utils/cc_detect/tests.rs
+++ b/src/bootstrap/src/utils/cc_detect/tests.rs
@@ -6,119 +6,6 @@ use crate::core::config::{Target, TargetSelection};
 use crate::{Build, Config, Flags};
 
 #[test]
-fn test_cc2ar_env_specific() {
-    let triple = "x86_64-unknown-linux-gnu";
-    let key = "AR_x86_64_unknown_linux_gnu";
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::set_var(key, "custom-ar") };
-    let target = TargetSelection::from_user(triple);
-    let cc = Path::new("/usr/bin/clang");
-    let default_ar = PathBuf::from("default-ar");
-    let result = cc2ar(cc, target, default_ar);
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var(key) };
-    assert_eq!(result, Some(PathBuf::from("custom-ar")));
-}
-
-#[test]
-fn test_cc2ar_musl() {
-    let triple = "x86_64-unknown-linux-musl";
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR_x86_64_unknown_linux_musl") };
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR") };
-    let target = TargetSelection::from_user(triple);
-    let cc = Path::new("/usr/bin/clang");
-    let default_ar = PathBuf::from("default-ar");
-    let result = cc2ar(cc, target, default_ar);
-    assert_eq!(result, Some(PathBuf::from("ar")));
-}
-
-#[test]
-fn test_cc2ar_openbsd() {
-    let triple = "x86_64-unknown-openbsd";
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR_x86_64_unknown_openbsd") };
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR") };
-    let target = TargetSelection::from_user(triple);
-    let cc = Path::new("/usr/bin/cc");
-    let default_ar = PathBuf::from("default-ar");
-    let result = cc2ar(cc, target, default_ar);
-    assert_eq!(result, Some(PathBuf::from("ar")));
-}
-
-#[test]
-fn test_cc2ar_vxworks() {
-    let triple = "armv7-wrs-vxworks";
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR_armv7_wrs_vxworks") };
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR") };
-    let target = TargetSelection::from_user(triple);
-    let cc = Path::new("/usr/bin/clang");
-    let default_ar = PathBuf::from("default-ar");
-    let result = cc2ar(cc, target, default_ar);
-    assert_eq!(result, Some(PathBuf::from("wr-ar")));
-}
-
-#[test]
-fn test_cc2ar_nto_i586() {
-    let triple = "i586-unknown-nto-something";
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR_i586_unknown_nto_something") };
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR") };
-    let target = TargetSelection::from_user(triple);
-    let cc = Path::new("/usr/bin/clang");
-    let default_ar = PathBuf::from("default-ar");
-    let result = cc2ar(cc, target, default_ar);
-    assert_eq!(result, Some(PathBuf::from("ntox86-ar")));
-}
-
-#[test]
-fn test_cc2ar_nto_aarch64() {
-    let triple = "aarch64-unknown-nto-something";
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR_aarch64_unknown_nto_something") };
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR") };
-    let target = TargetSelection::from_user(triple);
-    let cc = Path::new("/usr/bin/clang");
-    let default_ar = PathBuf::from("default-ar");
-    let result = cc2ar(cc, target, default_ar);
-    assert_eq!(result, Some(PathBuf::from("ntoaarch64-ar")));
-}
-
-#[test]
-fn test_cc2ar_nto_x86_64() {
-    let triple = "x86_64-unknown-nto-something";
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR_x86_64_unknown_nto_something") };
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR") };
-    let target = TargetSelection::from_user(triple);
-    let cc = Path::new("/usr/bin/clang");
-    let default_ar = PathBuf::from("default-ar");
-    let result = cc2ar(cc, target, default_ar);
-    assert_eq!(result, Some(PathBuf::from("ntox86_64-ar")));
-}
-
-#[test]
-#[should_panic(expected = "Unknown architecture, cannot determine archiver for Neutrino QNX")]
-fn test_cc2ar_nto_unknown() {
-    let triple = "powerpc-unknown-nto-something";
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR_powerpc_unknown_nto_something") };
-    // SAFETY: bootstrap tests run on a single thread
-    unsafe { env::remove_var("AR") };
-    let target = TargetSelection::from_user(triple);
-    let cc = Path::new("/usr/bin/clang");
-    let default_ar = PathBuf::from("default-ar");
-    let _ = cc2ar(cc, target, default_ar);
-}
-
-#[test]
 fn test_ndk_compiler_c() {
     let ndk_path = PathBuf::from("/ndk");
     let target_triple = "arm-unknown-linux-android";
diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile
index aade9588268..bc311be0580 100644
--- a/src/ci/docker/host-x86_64/arm-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile
@@ -28,6 +28,7 @@ RUN /scripts/android-sdk.sh
 ENV PATH=$PATH:/android/sdk/emulator
 ENV PATH=$PATH:/android/sdk/tools
 ENV PATH=$PATH:/android/sdk/platform-tools
+ENV PATH=$PATH:/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin
 
 ENV TARGETS=arm-linux-androideabi
 
diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile
index 95fed6ee767..7b73326e359 100644
--- a/src/ci/docker/host-x86_64/dist-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile
@@ -22,6 +22,8 @@ ENV RUST_CONFIGURE_ARGS \
       --android-ndk=/android/ndk/ \
       --disable-docs
 
+ENV PATH=$PATH:/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin
+
 ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS
 
 COPY scripts/sccache.sh /scripts/
diff --git a/tests/crashes/113379.rs b/tests/crashes/113379.rs
deleted file mode 100644
index 7163cbc3934..00000000000
--- a/tests/crashes/113379.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//@ known-bug: #113379
-
-async fn f999() -> Vec<usize> {
-    'b: {
-        continue 'b;
-    }
-}
diff --git a/tests/crashes/121623.rs b/tests/crashes/121623.rs
deleted file mode 100644
index 3c01a7f452c..00000000000
--- a/tests/crashes/121623.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//@ known-bug: #121623
-fn main() {
-    match () {
-        _ => 'b: {
-            continue 'b;
-        }
-    }
-}
diff --git a/tests/crashes/140884.rs b/tests/crashes/140884.rs
deleted file mode 100644
index 6840760933a..00000000000
--- a/tests/crashes/140884.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-//@ known-bug: #140884
-//@ needs-rustc-debug-assertions
-
-fn a() {
-    extern "" {}
-}
diff --git a/tests/ui/extern/extern-empty-string-issue-140884.rs b/tests/ui/extern/extern-empty-string-issue-140884.rs
new file mode 100644
index 00000000000..10291513d34
--- /dev/null
+++ b/tests/ui/extern/extern-empty-string-issue-140884.rs
@@ -0,0 +1,3 @@
+extern "" {} //~ ERROR invalid ABI: found ``
+
+fn main() {}
diff --git a/tests/ui/extern/extern-empty-string-issue-140884.stderr b/tests/ui/extern/extern-empty-string-issue-140884.stderr
new file mode 100644
index 00000000000..accae0c0f7c
--- /dev/null
+++ b/tests/ui/extern/extern-empty-string-issue-140884.stderr
@@ -0,0 +1,15 @@
+error[E0703]: invalid ABI: found ``
+  --> $DIR/extern-empty-string-issue-140884.rs:1:8
+   |
+LL | extern "" {}
+   |        ^^ invalid ABI
+   |
+   = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
+help: there's a similarly named valid ABI `C`
+   |
+LL | extern "C" {}
+   |         +
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0703`.
diff --git a/tests/ui/label/continue-pointing-to-block-ice-113379.rs b/tests/ui/label/continue-pointing-to-block-ice-113379.rs
new file mode 100644
index 00000000000..8a6a9cc8409
--- /dev/null
+++ b/tests/ui/label/continue-pointing-to-block-ice-113379.rs
@@ -0,0 +1,12 @@
+//! Regression test for ICE #113379. Liveness linting assumes that `continue`s all point to loops.
+//! This tests that if a `continue` points to a block, we don't run liveness lints.
+
+async fn f999() -> Vec<usize> {
+    //~^ ERROR `async fn` is not permitted in Rust 2015
+    'b: {
+        //~^ ERROR mismatched types
+        continue 'b;
+        //~^ ERROR `continue` pointing to a labeled block
+    }
+}
+//~^ ERROR `main` function not found
diff --git a/tests/ui/label/continue-pointing-to-block-ice-113379.stderr b/tests/ui/label/continue-pointing-to-block-ice-113379.stderr
new file mode 100644
index 00000000000..ada6305ec99
--- /dev/null
+++ b/tests/ui/label/continue-pointing-to-block-ice-113379.stderr
@@ -0,0 +1,43 @@
+error[E0670]: `async fn` is not permitted in Rust 2015
+  --> $DIR/continue-pointing-to-block-ice-113379.rs:4:1
+   |
+LL | async fn f999() -> Vec<usize> {
+   | ^^^^^ to use `async fn`, switch to Rust 2018 or later
+   |
+   = help: pass `--edition 2024` to `rustc`
+   = note: for more on editions, read https://doc.rust-lang.org/edition-guide
+
+error[E0601]: `main` function not found in crate `continue_pointing_to_block_ice_113379`
+  --> $DIR/continue-pointing-to-block-ice-113379.rs:11:2
+   |
+LL | }
+   |  ^ consider adding a `main` function to `$DIR/continue-pointing-to-block-ice-113379.rs`
+
+error[E0696]: `continue` pointing to a labeled block
+  --> $DIR/continue-pointing-to-block-ice-113379.rs:8:9
+   |
+LL | /     'b: {
+LL | |
+LL | |         continue 'b;
+   | |         ^^^^^^^^^^^ labeled blocks cannot be `continue`'d
+LL | |
+LL | |     }
+   | |_____- labeled block the `continue` points to
+
+error[E0308]: mismatched types
+  --> $DIR/continue-pointing-to-block-ice-113379.rs:6:5
+   |
+LL | /     'b: {
+LL | |
+LL | |         continue 'b;
+LL | |
+LL | |     }
+   | |_____^ expected `Vec<usize>`, found `()`
+   |
+   = note: expected struct `Vec<usize>`
+           found unit type `()`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0308, E0601, E0670, E0696.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/label/continue-pointing-to-block-ice-121623.rs b/tests/ui/label/continue-pointing-to-block-ice-121623.rs
new file mode 100644
index 00000000000..7047f7a309a
--- /dev/null
+++ b/tests/ui/label/continue-pointing-to-block-ice-121623.rs
@@ -0,0 +1,11 @@
+//! Regression test for ICE #121623. Liveness linting assumes that `continue`s all point to loops.
+//! This tests that if a `continue` points to a block, we don't run liveness lints.
+
+fn main() {
+    match () {
+        _ => 'b: {
+            continue 'b;
+            //~^ ERROR `continue` pointing to a labeled block
+        }
+    }
+}
diff --git a/tests/ui/label/continue-pointing-to-block-ice-121623.stderr b/tests/ui/label/continue-pointing-to-block-ice-121623.stderr
new file mode 100644
index 00000000000..a484fb629d1
--- /dev/null
+++ b/tests/ui/label/continue-pointing-to-block-ice-121623.stderr
@@ -0,0 +1,14 @@
+error[E0696]: `continue` pointing to a labeled block
+  --> $DIR/continue-pointing-to-block-ice-121623.rs:7:13
+   |
+LL |           _ => 'b: {
+   |  ______________-
+LL | |             continue 'b;
+   | |             ^^^^^^^^^^^ labeled blocks cannot be `continue`'d
+LL | |
+LL | |         }
+   | |_________- labeled block the `continue` points to
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0696`.
diff --git a/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs
new file mode 100644
index 00000000000..d2aa61186bc
--- /dev/null
+++ b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.rs
@@ -0,0 +1,15 @@
+struct Foo {
+    x: i32
+}
+
+impl Foo {
+    fn foo(&self) {
+        let _ = format!("{x}"); //~ ERROR cannot find value `x` in this scope [E0425]
+        let _ = format!("{x }"); //~ ERROR cannot find value `x` in this scope [E0425]
+        let _ = format!("{ x}"); //~ ERROR invalid format string: expected `}`, found `x`
+        let _ = format!("{}", x); //~ ERROR cannot find value `x` in this scope [E0425]
+        println!("{x}"); //~ ERROR cannot find value `x` in this scope [E0425]
+    }
+}
+
+fn main(){}
diff --git a/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr
new file mode 100644
index 00000000000..0a84848081d
--- /dev/null
+++ b/tests/ui/resolve/suggestions/sugg-field-in-format-string-issue-141136.stderr
@@ -0,0 +1,48 @@
+error: invalid format string: expected `}`, found `x`
+  --> $DIR/sugg-field-in-format-string-issue-141136.rs:9:28
+   |
+LL |         let _ = format!("{ x}");
+   |                          - ^ expected `}` in format string
+   |                          |
+   |                          because of this opening brace
+   |
+   = note: if you intended to print `{`, you can escape it using `{{`
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/sugg-field-in-format-string-issue-141136.rs:7:27
+   |
+LL |         let _ = format!("{x}");
+   |                           ^
+   |
+   = help: you might have meant to use the available field in a format string: `"{}", self.x`
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/sugg-field-in-format-string-issue-141136.rs:8:27
+   |
+LL |         let _ = format!("{x }");
+   |                           ^^
+   |
+   = help: you might have meant to use the available field in a format string: `"{}", self.x`
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/sugg-field-in-format-string-issue-141136.rs:10:31
+   |
+LL |         let _ = format!("{}", x);
+   |                               ^
+   |
+help: you might have meant to use the available field
+   |
+LL |         let _ = format!("{}", self.x);
+   |                               +++++
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/sugg-field-in-format-string-issue-141136.rs:11:20
+   |
+LL |         println!("{x}");
+   |                    ^
+   |
+   = help: you might have meant to use the available field in a format string: `"{}", self.x`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr
index 5832cb69a3d..1ecbfee17bc 100644
--- a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr
+++ b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr
@@ -20,17 +20,9 @@ error[E0425]: cannot find value `config` in this scope
   --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:15:20
    |
 LL |         println!("{config}");
-   |                    ^^^^^^
-   |
-help: you might have meant to use the available field
-   |
-LL |         println!("{self.config}");
-   |                    +++++
-help: a local variable with a similar name exists
-   |
-LL -         println!("{config}");
-LL +         println!("{cofig}");
+   |                    ^^^^^^ help: a local variable with a similar name exists: `cofig`
    |
+   = help: you might have meant to use the available field in a format string: `"{}", self.config`
 
 error[E0425]: cannot find value `bah` in this scope
   --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:33:9