diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 20a3482aaa27f..ce1a200886478 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -146,13 +146,12 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } fn const_str(&self, s: &str) -> (RValue<'gcc>, RValue<'gcc>) { - let str_global = *self - .const_str_cache - .borrow_mut() - .raw_entry_mut() - .from_key(s) - .or_insert_with(|| (s.to_owned(), self.global_string(s))) - .1; + let mut const_str_cache = self.const_str_cache.borrow_mut(); + let str_global = const_str_cache.get(s).copied().unwrap_or_else(|| { + let g = self.global_string(s); + const_str_cache.insert(s.to_owned(), g); + g + }); let len = s.len(); let cs = self.const_ptrcast( str_global.get_address(None), diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 9d91aab72ab3c..f090597f95335 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -16,7 +16,7 @@ #![allow(internal_features)] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![feature(rustc_private, decl_macro, never_type, trusted_len, hash_raw_entry, let_chains)] +#![feature(rustc_private, decl_macro, never_type, trusted_len, let_chains)] #![allow(broken_intra_doc_links)] #![recursion_limit = "256"] #![warn(rust_2018_idioms)] diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 0621b893e7550..6eaecfa87a9c2 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -209,28 +209,24 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn const_str(&self, s: &str) -> (&'ll Value, &'ll Value) { - let str_global = *self - .const_str_cache - .borrow_mut() - .raw_entry_mut() - .from_key(s) - .or_insert_with(|| { - let sc = self.const_bytes(s.as_bytes()); - let sym = self.generate_local_symbol_name("str"); - let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| { - bug!("symbol `{}` is already defined", sym); - }); - llvm::set_initializer(g, sc); - unsafe { - llvm::LLVMSetGlobalConstant(g, True); - llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); - } - llvm::set_linkage(g, llvm::Linkage::InternalLinkage); - // Cast to default address space if globals are in a different addrspace - let g = self.const_pointercast(g, self.type_ptr()); - (s.to_owned(), g) - }) - .1; + let mut const_str_cache = self.const_str_cache.borrow_mut(); + let str_global = const_str_cache.get(s).copied().unwrap_or_else(|| { + let sc = self.const_bytes(s.as_bytes()); + let sym = self.generate_local_symbol_name("str"); + let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + llvm::set_initializer(g, sc); + unsafe { + llvm::LLVMSetGlobalConstant(g, True); + llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); + } + llvm::set_linkage(g, llvm::Linkage::InternalLinkage); + // Cast to default address space if globals are in a different addrspace + let g = self.const_pointercast(g, self.type_ptr()); + const_str_cache.insert(s.to_owned(), g); + g + }); let len = s.len(); (str_global, self.const_usize(len as u64)) } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index c88372db4913b..8f72307eeba3d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -12,7 +12,6 @@ #![feature(exact_size_is_empty)] #![feature(extern_types)] #![feature(file_buffered)] -#![feature(hash_raw_entry)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 5054ae561c043..d1732e5057f29 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3438,6 +3438,32 @@ fn add_lld_args( // this, `wasm-component-ld`, which is overridden if this option is passed. if !sess.target.is_like_wasm { cmd.cc_arg("-fuse-ld=lld"); + + // GNU ld and LLD have opposite defaults on some section garbage-collection features. For + // example, the somewhat popular `linkme` crate and its dependents rely in practice on this + // difference: when using lld, they need `-z nostart-stop-gc` to prevent encapsulation + // symbols and sections from being garbage-collected. + // + // More information about all this can be found in: + // - https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order + // - https://lld.llvm.org/ELF/start-stop-gc + // + // So when using lld, we restore, for now, the traditional behavior to help migration, but + // will remove it in the future. + // Since this only disables an optimization, it shouldn't create issues, but is in theory + // slightly suboptimal. However, it: + // - doesn't have any visible impact on our benchmarks + // - reduces the need to disable lld for the crates that depend on this + // + // Note that lld can detect some cases where this difference is relied on, and emits a + // dedicated error to add this link arg. We could make use of this error to emit an FCW. As + // of writing this, we don't do it, because lld is already enabled by default on nightly + // without this mitigation: no working project would see the FCW, so we do this to help + // stabilization. + // + // FIXME: emit an FCW if linking fails due its absence, and then remove this link-arg in the + // future. + cmd.link_arg("-znostart-stop-gc"); } if !flavor.is_gnu() { diff --git a/compiler/rustc_data_structures/src/captures.rs b/compiler/rustc_data_structures/src/captures.rs deleted file mode 100644 index 677ccb31454ea..0000000000000 --- a/compiler/rustc_data_structures/src/captures.rs +++ /dev/null @@ -1,8 +0,0 @@ -/// "Signaling" trait used in impl trait to tag lifetimes that you may -/// need to capture but don't really need for other reasons. -/// Basically a workaround; see [this comment] for details. -/// -/// [this comment]: https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999 -pub trait Captures<'a> {} - -impl<'a, T: ?Sized> Captures<'a> for T {} diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 66d3834d85784..a3b62b469196a 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -48,7 +48,6 @@ pub use rustc_index::static_assert_size; pub mod aligned; pub mod base_n; pub mod binary_search_util; -pub mod captures; pub mod fingerprint; pub mod flat_map_in_place; pub mod flock; diff --git a/config.example.toml b/config.example.toml index 2e26c024865db..d27633423a935 100644 --- a/config.example.toml +++ b/config.example.toml @@ -425,6 +425,10 @@ # a specific version. #ccache = false +# List of paths to exclude from the build and test processes. +# For example, exclude = ["tests/ui", "src/tools/tidy"]. +#exclude = [] + # ============================================================================= # General install configuration options # ============================================================================= diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 4a071b4e1faec..e62aeb2ede0ad 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -641,11 +641,6 @@ impl Error for JoinPathsError { /// None => println!("Impossible to get your home dir!"), /// } /// ``` -#[deprecated( - since = "1.29.0", - note = "This function's behavior may be unexpected on Windows. \ - Consider using a crate from crates.io instead." -)] #[must_use] #[stable(feature = "env", since = "1.0.0")] pub fn home_dir() -> Option { diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 57e235c3efe1d..4314c8a0b1825 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2857,9 +2857,11 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// /// See [`fs::remove_file`] and [`fs::remove_dir`]. /// -/// `remove_dir_all` will fail if `remove_dir` or `remove_file` fail on any constituent paths, including the root `path`. -/// As a result, the directory you are deleting must exist, meaning that this function is not idempotent. -/// Additionally, `remove_dir_all` will also fail if the `path` is not a directory. +/// [`remove_dir_all`] will fail if [`remove_dir`] or [`remove_file`] fail on *any* constituent +/// paths, *including* the root `path`. Consequently, +/// +/// - The directory you are deleting *must* exist, meaning that this function is *not idempotent*. +/// - [`remove_dir_all`] will fail if the `path` is *not* a directory. /// /// Consider ignoring the error if validating the removal is not required for your use case. /// diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 980ea1478e084..7f610bc88bfd7 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2534,7 +2534,7 @@ pub trait BufRead: Read { fn read_line(&mut self, buf: &mut String) -> Result { // Note that we are not calling the `.read_until` method here, but // rather our hardcoded implementation. For more details as to why, see - // the comments in `read_to_end`. + // the comments in `default_read_to_string`. unsafe { append_to_string(buf, |b| read_until(self, b'\n', b)) } } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index d0e0ed50ad89f..0791604dc034d 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -942,6 +942,7 @@ define_config! { jobs: Option = "jobs", compiletest_diff_tool: Option = "compiletest-diff-tool", ccache: Option = "ccache", + exclude: Option> = "exclude", } } @@ -1372,22 +1373,6 @@ impl Config { "flags.exclude" = ?flags.exclude ); - config.skip = flags - .skip - .into_iter() - .chain(flags.exclude) - .map(|p| { - // Never return top-level path here as it would break `--skip` - // logic on rustc's internal test framework which is utilized - // by compiletest. - if cfg!(windows) { - PathBuf::from(p.to_str().unwrap().replace('/', "\\")) - } else { - p - } - }) - .collect(); - #[cfg(feature = "tracing")] span!( target: "CONFIG_HANDLING", @@ -1632,8 +1617,29 @@ impl Config { jobs, compiletest_diff_tool, mut ccache, + exclude, } = toml.build.unwrap_or_default(); + let mut paths: Vec = flags.skip.into_iter().chain(flags.exclude).collect(); + + if let Some(exclude) = exclude { + paths.extend(exclude); + } + + config.skip = paths + .into_iter() + .map(|p| { + // Never return top-level path here as it would break `--skip` + // logic on rustc's internal test framework which is utilized + // by compiletest. + if cfg!(windows) { + PathBuf::from(p.to_str().unwrap().replace('/', "\\")) + } else { + p + } + }) + .collect(); + config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0)))); if let Some(file_build) = build { diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index eff5e0337428c..27968bea31869 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -515,3 +515,17 @@ fn test_explicit_stage() { assert!(!config.explicit_stage_from_config); assert!(!config.is_explicit_stage()); } + +#[test] +fn test_exclude() { + let config = parse("build.exclude=[\"test/codegen\"]"); + + let first_excluded = config + .skip + .first() + .expect("Expected at least one excluded path") + .to_str() + .expect("Failed to convert excluded path to string"); + + assert_eq!(first_excluded, "test/codegen"); +} diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 5f49c50c5ad3a..b9f57898f4d98 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -365,4 +365,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "`rust.channel` now supports \"auto-detect\" to load the channel from `src/ci/channel`", }, + ChangeInfo { + change_id: 137147, + severity: ChangeSeverity::Info, + summary: "New option `build.exclude` that adds support for excluding test.", + }, ]; diff --git a/src/etc/test-float-parse/src/gen/exhaustive.rs b/src/etc/test-float-parse/src/gen/exhaustive.rs index 5d4b6df8e59b9..01458fb0b6084 100644 --- a/src/etc/test-float-parse/src/gen/exhaustive.rs +++ b/src/etc/test-float-parse/src/gen/exhaustive.rs @@ -13,13 +13,12 @@ impl Generator for Exhaustive where RangeInclusive: Iterator, { - const NAME: &'static str = "exhaustive"; const SHORT_NAME: &'static str = "exhaustive"; type WriteCtx = F; fn total_tests() -> u64 { - F::Int::MAX.try_into().unwrap_or(u64::MAX) + 1u64.checked_shl(F::Int::BITS).expect("More than u64::MAX tests") } fn new() -> Self { diff --git a/src/etc/test-float-parse/src/gen/fuzz.rs b/src/etc/test-float-parse/src/gen/fuzz.rs index 0c63e8aae26ea..7fc999d167113 100644 --- a/src/etc/test-float-parse/src/gen/fuzz.rs +++ b/src/etc/test-float-parse/src/gen/fuzz.rs @@ -49,7 +49,6 @@ impl Generator for Fuzz where Standard: Distribution<::Int>, { - const NAME: &'static str = "fuzz"; const SHORT_NAME: &'static str = "fuzz"; type WriteCtx = F; diff --git a/src/etc/test-float-parse/src/gen/sparse.rs b/src/etc/test-float-parse/src/gen/sparse.rs index 389b71056a3e5..72b65d4ce7f10 100644 --- a/src/etc/test-float-parse/src/gen/sparse.rs +++ b/src/etc/test-float-parse/src/gen/sparse.rs @@ -35,7 +35,6 @@ impl Generator for FewOnesInt where >::Error: std::fmt::Debug, { - const NAME: &'static str = "few ones int"; const SHORT_NAME: &'static str = "few ones int"; type WriteCtx = F::Int; diff --git a/src/etc/test-float-parse/src/lib.rs b/src/etc/test-float-parse/src/lib.rs index 3c71b0dc32e74..5a0c160312295 100644 --- a/src/etc/test-float-parse/src/lib.rs +++ b/src/etc/test-float-parse/src/lib.rs @@ -2,19 +2,19 @@ mod traits; mod ui; mod validate; -use std::any::{TypeId, type_name}; +use std::any::type_name; use std::cmp::min; use std::ops::RangeInclusive; use std::process::ExitCode; +use std::sync::OnceLock; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::{OnceLock, mpsc}; use std::{fmt, time}; -use indicatif::{MultiProgress, ProgressBar}; use rand::distributions::{Distribution, Standard}; use rayon::prelude::*; use time::{Duration, Instant}; use traits::{Float, Generator, Int}; +use validate::CheckError; /// Test generators. mod gen { @@ -43,7 +43,7 @@ const HUGE_TEST_CUTOFF: u64 = 5_000_000; /// Seed for tests that use a deterministic RNG. const SEED: [u8; 32] = *b"3.141592653589793238462643383279"; -/// Global configuration +/// Global configuration. #[derive(Debug)] pub struct Config { pub timeout: Duration, @@ -106,7 +106,7 @@ pub fn run(cfg: Config, include: &[String], exclude: &[String]) -> ExitCode { println!("launching"); let elapsed = launch_tests(&mut tests, &cfg); - ui::finish(&tests, elapsed, &cfg) + ui::finish_all(&tests, elapsed, &cfg) } /// Enumerate tests to run but don't actually run them. @@ -160,18 +160,18 @@ where #[derive(Debug)] pub struct TestInfo { pub name: String, - /// Tests are identified by the type ID of `(F, G)` (tuple of the float and generator type). - /// This gives an easy way to associate messages with tests. - id: TypeId, float_name: &'static str, + float_bits: u32, gen_name: &'static str, /// Name for display in the progress bar. short_name: String, + /// Pad the short name to a common width for progress bar use. + short_name_padded: String, total_tests: u64, /// Function to launch this test. - launch: fn(&mpsc::Sender, &TestInfo, &Config), + launch: fn(&TestInfo, &Config), /// Progress bar to be updated. - pb: Option, + progress: Option, /// Once completed, this will be set. completed: OnceLock, } @@ -187,14 +187,18 @@ impl TestInfo { let f_name = type_name::(); let gen_name = G::NAME; let gen_short_name = G::SHORT_NAME; + let name = format!("{f_name} {gen_name}"); + let short_name = format!("{f_name} {gen_short_name}"); + let short_name_padded = format!("{short_name:18}"); let info = TestInfo { - id: TypeId::of::<(F, G)>(), float_name: f_name, + float_bits: F::BITS, gen_name, - pb: None, - name: format!("{f_name} {gen_name}"), - short_name: format!("{f_name} {gen_short_name}"), + progress: None, + name, + short_name_padded, + short_name, launch: test_runner::, total_tests: G::total_tests(), completed: OnceLock::new(), @@ -202,106 +206,18 @@ impl TestInfo { v.push(info); } - /// Pad the short name to a common width for progress bar use. - fn short_name_padded(&self) -> String { - format!("{:18}", self.short_name) - } - - /// Create a progress bar for this test within a multiprogress bar. - fn register_pb(&mut self, mp: &MultiProgress, drop_bars: &mut Vec) { - self.pb = Some(ui::create_pb(mp, self.total_tests, &self.short_name_padded(), drop_bars)); - } - - /// When the test is finished, update progress bar messages and finalize. - fn finalize_pb(&self, c: &Completed) { - let pb = self.pb.as_ref().unwrap(); - ui::finalize_pb(pb, &self.short_name_padded(), c); - } - /// True if this should be run after all others. fn is_huge_test(&self) -> bool { self.total_tests >= HUGE_TEST_CUTOFF } -} - -/// A message sent from test runner threads to the UI/log thread. -#[derive(Clone, Debug)] -struct Msg { - id: TypeId, - update: Update, -} -impl Msg { - /// Wrap an `Update` into a message for the specified type. We use the `TypeId` of `(F, G)` to - /// identify which test a message in the channel came from. - fn new>(u: Update) -> Self { - Self { id: TypeId::of::<(F, G)>(), update: u } - } - - /// Get the matching test from a list. Panics if not found. - fn find_test<'a>(&self, tests: &'a [TestInfo]) -> &'a TestInfo { - tests.iter().find(|t| t.id == self.id).unwrap() - } - - /// Update UI as needed for a single message received from the test runners. - fn handle(self, tests: &[TestInfo], mp: &MultiProgress) { - let test = self.find_test(tests); - let pb = test.pb.as_ref().unwrap(); - - match self.update { - Update::Started => { - mp.println(format!("Testing '{}'", test.name)).unwrap(); - } - Update::Progress { executed, failures } => { - pb.set_message(format! {"{failures}"}); - pb.set_position(executed); - } - Update::Failure { fail, input, float_res } => { - mp.println(format!( - "Failure in '{}': {fail}. parsing '{input}'. Parsed as: {float_res}", - test.name - )) - .unwrap(); - } - Update::Completed(c) => { - test.finalize_pb(&c); - - let prefix = match c.result { - Ok(FinishedAll) => "Completed tests for", - Err(EarlyExit::Timeout) => "Timed out", - Err(EarlyExit::MaxFailures) => "Max failures reached for", - }; - - mp.println(format!( - "{prefix} generator '{}' in {:?}. {} tests run, {} failures", - test.name, c.elapsed, c.executed, c.failures - )) - .unwrap(); - test.completed.set(c).unwrap(); - } - }; + /// When the test is finished, update progress bar messages and finalize. + fn complete(&self, c: Completed) { + self.progress.as_ref().unwrap().complete(&c, 0); + self.completed.set(c).unwrap(); } } -/// Status sent with a message. -#[derive(Clone, Debug)] -enum Update { - /// Starting a new test runner. - Started, - /// Completed a out of b tests. - Progress { executed: u64, failures: u64 }, - /// Received a failed test. - Failure { - fail: CheckFailure, - /// String for which parsing was attempted. - input: Box, - /// The parsed & decomposed `FloatRes`, already stringified so we don't need generics here. - float_res: Box, - }, - /// Exited with an unexpected condition. - Completed(Completed), -} - /// Result of an input did not parsing successfully. #[derive(Clone, Debug)] enum CheckFailure { @@ -398,55 +314,21 @@ enum EarlyExit { /// This launches a main thread that receives messages and handlees UI updates, and uses the /// rest of the thread pool to execute the tests. fn launch_tests(tests: &mut [TestInfo], cfg: &Config) -> Duration { - // Run shorter tests first - tests.sort_unstable_by_key(|test| test.total_tests); + // Run shorter tests and smaller float types first. + tests.sort_unstable_by_key(|test| (test.total_tests, test.float_bits)); for test in tests.iter() { println!("Launching test '{}'", test.name); } - // Configure progress bars let mut all_progress_bars = Vec::new(); - let mp = MultiProgress::new(); - mp.set_move_cursor(true); - for test in tests.iter_mut() { - test.register_pb(&mp, &mut all_progress_bars); - } - - ui::set_panic_hook(all_progress_bars); - - let (tx, rx) = mpsc::channel::(); let start = Instant::now(); - rayon::scope(|scope| { - // Thread that updates the UI - scope.spawn(|_scope| { - let rx = rx; // move rx - - loop { - if tests.iter().all(|t| t.completed.get().is_some()) { - break; - } - - let msg = rx.recv().unwrap(); - msg.handle(tests, &mp); - } - - // All tests completed; finish things up - drop(mp); - assert_eq!(rx.try_recv().unwrap_err(), mpsc::TryRecvError::Empty); - }); - - // Don't let the thread pool be starved by huge tests. Run faster tests first in parallel, - // then parallelize only within the rest of the tests. - let (huge_tests, normal_tests): (Vec<_>, Vec<_>) = - tests.iter().partition(|t| t.is_huge_test()); - - // Run the actual tests - normal_tests.par_iter().for_each(|test| ((test.launch)(&tx, test, cfg))); - - huge_tests.par_iter().for_each(|test| ((test.launch)(&tx, test, cfg))); - }); + for test in tests.iter_mut() { + test.progress = Some(ui::Progress::new(test, &mut all_progress_bars)); + ui::set_panic_hook(&all_progress_bars); + ((test.launch)(test, cfg)); + } start.elapsed() } @@ -454,15 +336,12 @@ fn launch_tests(tests: &mut [TestInfo], cfg: &Config) -> Duration { /// Test runer for a single generator. /// /// This calls the generator's iterator multiple times (in parallel) and validates each output. -fn test_runner>(tx: &mpsc::Sender, _info: &TestInfo, cfg: &Config) { - tx.send(Msg::new::(Update::Started)).unwrap(); - - let total = G::total_tests(); +fn test_runner>(test: &TestInfo, cfg: &Config) { let gen = G::new(); let executed = AtomicU64::new(0); let failures = AtomicU64::new(0); - let checks_per_update = min(total, 1000); + let checks_per_update = min(test.total_tests, 1000); let started = Instant::now(); // Function to execute for a single test iteration. @@ -474,7 +353,12 @@ fn test_runner>(tx: &mpsc::Sender, _info: &TestIn match validate::validate::(buf) { Ok(()) => (), Err(e) => { - tx.send(Msg::new::(e)).unwrap(); + let CheckError { fail, input, float_res } = e; + test.progress.as_ref().unwrap().println(&format!( + "Failure in '{}': {fail}. parsing '{input}'. Parsed as: {float_res}", + test.name + )); + let f = failures.fetch_add(1, Ordering::Relaxed); // End early if the limit is exceeded. if f >= cfg.max_failures { @@ -486,9 +370,7 @@ fn test_runner>(tx: &mpsc::Sender, _info: &TestIn // Send periodic updates if executed % checks_per_update == 0 { let failures = failures.load(Ordering::Relaxed); - - tx.send(Msg::new::(Update::Progress { executed, failures })).unwrap(); - + test.progress.as_ref().unwrap().update(executed, failures); if started.elapsed() > cfg.timeout { return Err(EarlyExit::Timeout); } @@ -499,15 +381,19 @@ fn test_runner>(tx: &mpsc::Sender, _info: &TestIn // Run the test iterations in parallel. Each thread gets a string buffer to write // its check values to. - let res = gen.par_bridge().try_for_each_init(|| String::with_capacity(100), check_one); + let res = gen.par_bridge().try_for_each_init(String::new, check_one); let elapsed = started.elapsed(); let executed = executed.into_inner(); let failures = failures.into_inner(); // Warn about bad estimates if relevant. - let warning = if executed != total && res.is_ok() { - let msg = format!("executed tests != estimated ({executed} != {total}) for {}", G::NAME); + let warning = if executed != test.total_tests && res.is_ok() { + let msg = format!( + "executed tests != estimated ({executed} != {}) for {}", + test.total_tests, + G::NAME + ); Some(msg.into()) } else { @@ -515,12 +401,5 @@ fn test_runner>(tx: &mpsc::Sender, _info: &TestIn }; let result = res.map(|()| FinishedAll); - tx.send(Msg::new::(Update::Completed(Completed { - executed, - failures, - result, - warning, - elapsed, - }))) - .unwrap(); + test.complete(Completed { executed, failures, result, warning, elapsed }); } diff --git a/src/etc/test-float-parse/src/traits.rs b/src/etc/test-float-parse/src/traits.rs index f5333d63b3693..c8407ba7cc50a 100644 --- a/src/etc/test-float-parse/src/traits.rs +++ b/src/etc/test-float-parse/src/traits.rs @@ -177,7 +177,7 @@ impl_float!(f32, u32, 32; f64, u64, 64); /// allocations (which otherwise turn out to be a pretty expensive part of these tests). pub trait Generator: Iterator + Send + 'static { /// Full display and filtering name - const NAME: &'static str; + const NAME: &'static str = Self::SHORT_NAME; /// Name for display with the progress bar const SHORT_NAME: &'static str; diff --git a/src/etc/test-float-parse/src/ui.rs b/src/etc/test-float-parse/src/ui.rs index f333bd4a55dc9..1ee57723e6a62 100644 --- a/src/etc/test-float-parse/src/ui.rs +++ b/src/etc/test-float-parse/src/ui.rs @@ -1,67 +1,92 @@ //! Progress bars and such. +use std::any::type_name; +use std::fmt; use std::io::{self, Write}; use std::process::ExitCode; use std::time::Duration; -use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use indicatif::{ProgressBar, ProgressStyle}; use crate::{Completed, Config, EarlyExit, FinishedAll, TestInfo}; /// Templates for progress bars. -const PB_TEMPLATE: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME ({pos}/{len}, {msg} f, {per_sec}, eta {eta})"; -const PB_TEMPLATE_FINAL: &str = - "[{elapsed:3} {percent:3}%] NAME ({pos}/{len}, {msg:.COLOR}, {per_sec}, {elapsed_precise})"; - -/// Create a new progress bar within a multiprogress bar. -pub fn create_pb( - mp: &MultiProgress, - total_tests: u64, - short_name_padded: &str, - all_bars: &mut Vec, -) -> ProgressBar { - let pb = mp.add(ProgressBar::new(total_tests)); - let pb_style = ProgressStyle::with_template(&PB_TEMPLATE.replace("NAME", short_name_padded)) - .unwrap() - .progress_chars("##-"); - - pb.set_style(pb_style.clone()); - pb.set_message("0"); - all_bars.push(pb.clone()); - pb +const PB_TEMPLATE: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME \ + {human_pos:>8}/{human_len:8} {msg} f {per_sec:14} eta {eta:8}"; +const PB_TEMPLATE_FINAL: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME \ + {human_pos:>8}/{human_len:8} {msg:.COLOR} {per_sec:18} {elapsed_precise}"; + +/// Thin abstraction over our usage of a `ProgressBar`. +#[derive(Debug)] +pub struct Progress { + pb: ProgressBar, + make_final_style: NoDebug ProgressStyle + Sync>>, } -/// Removes the status bar and replace it with a message. -pub fn finalize_pb(pb: &ProgressBar, short_name_padded: &str, c: &Completed) { - let f = c.failures; +impl Progress { + /// Create a new progress bar within a multiprogress bar. + pub fn new(test: &TestInfo, all_bars: &mut Vec) -> Self { + let initial_template = PB_TEMPLATE.replace("NAME", &test.short_name_padded); + let final_template = PB_TEMPLATE_FINAL.replace("NAME", &test.short_name_padded); + let initial_style = + ProgressStyle::with_template(&initial_template).unwrap().progress_chars("##-"); + let make_final_style = move |color| { + ProgressStyle::with_template(&final_template.replace("COLOR", color)) + .unwrap() + .progress_chars("##-") + }; - // Use a tuple so we can use colors - let (color, msg, finish_pb): (&str, String, fn(&ProgressBar, String)) = match &c.result { - Ok(FinishedAll) if f > 0 => { - ("red", format!("{f} f (finished with errors)",), ProgressBar::finish_with_message) - } - Ok(FinishedAll) => { - ("green", format!("{f} f (finished successfully)",), ProgressBar::finish_with_message) - } - Err(EarlyExit::Timeout) => { - ("red", format!("{f} f (timed out)"), ProgressBar::abandon_with_message) + let pb = ProgressBar::new(test.total_tests); + pb.set_style(initial_style); + pb.set_length(test.total_tests); + pb.set_message("0"); + all_bars.push(pb.clone()); + + Progress { pb, make_final_style: NoDebug(Box::new(make_final_style)) } + } + + /// Completed a out of b tests. + pub fn update(&self, completed: u64, failures: u64) { + // Infrequently update the progress bar. + if completed % 5_000 == 0 || failures > 0 { + self.pb.set_position(completed); } - Err(EarlyExit::MaxFailures) => { - ("red", format!("{f} f (failure limit)"), ProgressBar::abandon_with_message) + + if failures > 0 { + self.pb.set_message(format! {"{failures}"}); } - }; + } + + /// Finalize the progress bar. + pub fn complete(&self, c: &Completed, real_total: u64) { + let f = c.failures; + let (color, msg, finish_fn): (&str, String, fn(&ProgressBar)) = match &c.result { + Ok(FinishedAll) if f > 0 => { + ("red", format!("{f} f (completed with errors)",), ProgressBar::finish) + } + Ok(FinishedAll) => { + ("green", format!("{f} f (completed successfully)",), ProgressBar::finish) + } + Err(EarlyExit::Timeout) => ("red", format!("{f} f (timed out)"), ProgressBar::abandon), + Err(EarlyExit::MaxFailures) => { + ("red", format!("{f} f (failure limit)"), ProgressBar::abandon) + } + }; - let pb_style = ProgressStyle::with_template( - &PB_TEMPLATE_FINAL.replace("NAME", short_name_padded).replace("COLOR", color), - ) - .unwrap(); + self.pb.set_position(real_total); + self.pb.set_style(self.make_final_style.0(color)); + self.pb.set_message(msg); + finish_fn(&self.pb); + } - pb.set_style(pb_style); - finish_pb(pb, msg); + /// Print a message to stdout above the current progress bar. + pub fn println(&self, msg: &str) { + self.pb.suspend(|| println!("{msg}")); + } } /// Print final messages after all tests are complete. -pub fn finish(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> ExitCode { +pub fn finish_all(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> ExitCode { println!("\n\nResults:"); let mut failed_generators = 0; @@ -118,8 +143,9 @@ pub fn finish(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> Exit /// indicatif likes to eat panic messages. This workaround isn't ideal, but it improves things. /// . -pub fn set_panic_hook(drop_bars: Vec) { +pub fn set_panic_hook(drop_bars: &[ProgressBar]) { let hook = std::panic::take_hook(); + let drop_bars = drop_bars.to_owned(); std::panic::set_hook(Box::new(move |info| { for bar in &drop_bars { bar.abandon(); @@ -130,3 +156,13 @@ pub fn set_panic_hook(drop_bars: Vec) { hook(info); })); } + +/// Allow non-Debug items in a `derive(Debug)` struct`. +#[derive(Clone)] +struct NoDebug(T); + +impl fmt::Debug for NoDebug { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(type_name::()) + } +} diff --git a/src/etc/test-float-parse/src/validate.rs b/src/etc/test-float-parse/src/validate.rs index 1eb3699cfb9f7..6ca94a9378f25 100644 --- a/src/etc/test-float-parse/src/validate.rs +++ b/src/etc/test-float-parse/src/validate.rs @@ -9,7 +9,7 @@ use std::sync::LazyLock; use num::bigint::ToBigInt; use num::{BigInt, BigRational, FromPrimitive, Signed, ToPrimitive}; -use crate::{CheckFailure, Float, Int, Update}; +use crate::{CheckFailure, Float, Int}; /// Powers of two that we store for constants. Account for binary128 which has a 15-bit exponent. const POWERS_OF_TWO_RANGE: RangeInclusive = (-(2 << 15))..=(2 << 15); @@ -89,7 +89,7 @@ impl Constants { } /// Validate that a string parses correctly -pub fn validate(input: &str) -> Result<(), Update> { +pub fn validate(input: &str) -> Result<(), CheckError> { let parsed: F = input .parse() .unwrap_or_else(|e| panic!("parsing failed for {}: {e}. Input: {input}", type_name::())); @@ -118,10 +118,19 @@ pub enum FloatRes { }, } +#[derive(Clone, Debug)] +pub struct CheckError { + pub fail: CheckFailure, + /// String for which parsing was attempted. + pub input: Box, + /// The parsed & decomposed `FloatRes`, already stringified so we don't need generics here. + pub float_res: Box, +} + impl FloatRes { /// Given a known exact rational, check that this representation is accurate within the /// limits of the float representation. If not, construct a failure `Update` to send. - fn check(self, expected: Rational, input: &str) -> Result<(), Update> { + fn check(self, expected: Rational, input: &str) -> Result<(), CheckError> { let consts = F::constants(); // let bool_helper = |cond: bool, err| cond.then_some(()).ok_or(err); @@ -173,7 +182,7 @@ impl FloatRes { (Rational::Finite(r), FloatRes::Real { sig, exp }) => Self::validate_real(r, sig, exp), }; - res.map_err(|fail| Update::Failure { + res.map_err(|fail| CheckError { fail, input: input.into(), float_res: format!("{self:?}").into(), diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 91cc408878826..909b81a723b48 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustdoc" version = "0.0.0" -edition = "2021" +edition = "2024" build = "build.rs" [lib] diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index bec7fbe8f52bd..ab169f3c2a4d5 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -48,12 +48,12 @@ impl Cfg { exclude: &FxHashSet, ) -> Result, InvalidCfgError> { match nested_cfg { - MetaItemInner::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude), + MetaItemInner::MetaItem(cfg) => Cfg::parse_without(cfg, exclude), MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => match *b { true => Ok(Some(Cfg::True)), false => Ok(Some(Cfg::False)), }, - MetaItemInner::Lit(ref lit) => { + MetaItemInner::Lit(lit) => { Err(InvalidCfgError { msg: "unexpected literal", span: lit.span }) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ceffe5e5ce04e..bb12e4a706e7d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -741,7 +741,7 @@ pub(crate) fn clean_generics<'tcx>( for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) { let mut p = clean_generic_param(cx, Some(gens), p); match &mut p.kind { - GenericParamDefKind::Lifetime { ref mut outlives } => { + GenericParamDefKind::Lifetime { outlives } => { if let Some(region_pred) = region_predicates.get_mut(&Lifetime(p.name)) { // We merge bounds in the `where` clause. for outlive in outlives.drain(..) { @@ -2688,7 +2688,7 @@ fn filter_doc_attr_ident(ident: Symbol, is_inline: bool) -> bool { /// Before calling this function, make sure `normal` is a `#[doc]` attribute. fn filter_doc_attr(args: &mut hir::AttrArgs, is_inline: bool) { match args { - hir::AttrArgs::Delimited(ref mut args) => { + hir::AttrArgs::Delimited(args) => { let tokens = filter_tokens_from_list(&args.tokens, |token| { !matches!( token, diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 178b6a60b41f7..5906a720e0fd3 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -502,7 +502,7 @@ impl Item { let Some(links) = cx.cache().intra_doc_links.get(&self.item_id) else { return vec![] }; links .iter() - .filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| { + .filter_map(|ItemLink { link: s, link_text, page_id: id, fragment }| { debug!(?id); if let Ok((mut href, ..)) = href(*id, cx) { debug!(?href); @@ -1150,7 +1150,7 @@ pub(crate) struct Attributes { } impl Attributes { - pub(crate) fn lists(&self, name: Symbol) -> impl Iterator + '_ { + pub(crate) fn lists(&self, name: Symbol) -> impl Iterator { hir_attr_lists(&self.other_attrs[..], name) } @@ -1864,7 +1864,7 @@ impl PrimitiveType { .copied() } - pub(crate) fn all_impls(tcx: TyCtxt<'_>) -> impl Iterator + '_ { + pub(crate) fn all_impls(tcx: TyCtxt<'_>) -> impl Iterator { Self::simplified_types() .values() .flatten() @@ -2259,7 +2259,7 @@ impl GenericArgs { GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(), } } - pub(crate) fn constraints<'a>(&'a self) -> Box + 'a> { + pub(crate) fn constraints(&self) -> Box + '_> { match self { GenericArgs::AngleBracketed { constraints, .. } => { Box::new(constraints.iter().cloned()) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 34656b26ce28c..a284de5229a21 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -60,7 +60,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { let primitives = local_crate.primitives(cx.tcx); let keywords = local_crate.keywords(cx.tcx); { - let ItemKind::ModuleItem(ref mut m) = &mut module.inner.kind else { unreachable!() }; + let ItemKind::ModuleItem(m) = &mut module.inner.kind else { unreachable!() }; m.items.extend(primitives.iter().map(|&(def_id, prim)| { Item::from_def_id_and_parts( def_id, @@ -302,7 +302,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { use rustc_hir::*; debug!("trying to get a name from pattern: {p:?}"); - Symbol::intern(&match p.kind { + Symbol::intern(&match &p.kind { // FIXME(never_patterns): does this make sense? PatKind::Wild | PatKind::Err(_) @@ -313,8 +313,9 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { } PatKind::Binding(_, _, ident, _) => return ident.name, PatKind::Box(p) | PatKind::Ref(p, _) | PatKind::Guard(p, _) => return name_from_pat(p), - PatKind::TupleStruct(ref p, ..) - | PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref p), .. }) => qpath_to_string(p), + PatKind::TupleStruct(p, ..) | PatKind::Expr(PatExpr { kind: PatExprKind::Path(p), .. }) => { + qpath_to_string(p) + } PatKind::Or(pats) => { fmt::from_fn(|f| pats.iter().map(|p| name_from_pat(p)).joined(" | ", f)).to_string() } @@ -329,7 +330,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { return Symbol::intern("()"); } PatKind::Slice(begin, mid, end) => { - fn print_pat<'a>(pat: &'a Pat<'a>, wild: bool) -> impl Display + 'a { + fn print_pat(pat: &Pat<'_>, wild: bool) -> impl Display { fmt::from_fn(move |f| { if wild { f.write_str("..")?; @@ -493,7 +494,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { pub(crate) fn synthesize_auto_trait_and_blanket_impls( cx: &mut DocContext<'_>, item_def_id: DefId, -) -> impl Iterator { +) -> impl Iterator + use<> { let auto_impls = cx .sess() .prof diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 91b4b3ba1ebac..3b481acd410ae 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -15,7 +15,6 @@ use std::iter::{self, once}; use itertools::Either; use rustc_abi::ExternAbi; use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince}; -use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -41,10 +40,10 @@ pub(crate) fn write_str(s: &mut String, f: fmt::Arguments<'_>) { s.write_fmt(f).unwrap(); } -pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( - bounds: &'a [clean::GenericBound], - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +pub(crate) fn print_generic_bounds( + bounds: &[clean::GenericBound], + cx: &Context<'_>, +) -> impl Display { fmt::from_fn(move |f| { let mut bounds_dup = FxHashSet::default(); @@ -57,10 +56,7 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( } impl clean::GenericParamDef { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match &self.kind { clean::GenericParamDefKind::Lifetime { outlives } => { write!(f, "{}", self.name)?; @@ -80,7 +76,7 @@ impl clean::GenericParamDef { print_generic_bounds(bounds, cx).fmt(f)?; } - if let Some(ref ty) = default { + if let Some(ty) = default { f.write_str(" = ")?; ty.print(cx).fmt(f)?; } @@ -107,10 +103,7 @@ impl clean::GenericParamDef { } impl clean::Generics { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); if real_params.peek().is_none() { @@ -134,10 +127,7 @@ pub(crate) enum Ending { NoNewline, } -fn print_where_predicate<'a, 'tcx: 'a>( - predicate: &'a clean::WherePredicate, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +fn print_where_predicate(predicate: &clean::WherePredicate, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { match predicate { clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { @@ -173,12 +163,12 @@ fn print_where_predicate<'a, 'tcx: 'a>( /// * The Generics from which to emit a where-clause. /// * The number of spaces to indent each line with. /// * Whether the where-clause needs to add a comma and newline after the last bound. -pub(crate) fn print_where_clause<'a, 'tcx: 'a>( - gens: &'a clean::Generics, - cx: &'a Context<'tcx>, +pub(crate) fn print_where_clause( + gens: &clean::Generics, + cx: &Context<'_>, indent: usize, ending: Ending, -) -> impl Display + 'a + Captures<'tcx> { +) -> impl Display { fmt::from_fn(move |f| { if gens.where_predicates.is_empty() { return Ok(()); @@ -250,13 +240,13 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( } impl clean::Lifetime { - pub(crate) fn print(&self) -> impl Display + '_ { + pub(crate) fn print(&self) -> impl Display { self.0.as_str() } } impl clean::ConstantKind { - pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display + '_ { + pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display { let expr = self.expr(tcx); fmt::from_fn(move |f| { if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) } @@ -265,7 +255,7 @@ impl clean::ConstantKind { } impl clean::PolyTrait { - fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { + fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { print_higher_ranked_params_with_space(&self.generic_params, cx, "for").fmt(f)?; self.trait_.print(cx).fmt(f) @@ -274,10 +264,7 @@ impl clean::PolyTrait { } impl clean::GenericBound { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), clean::GenericBound::TraitBound(ty, modifiers) => { @@ -304,7 +291,7 @@ impl clean::GenericBound { } impl clean::GenericArgs { - fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { + fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { match self { clean::GenericArgs::AngleBracketed { args, constraints } => { @@ -809,11 +796,11 @@ fn primitive_link_fragment( Ok(()) } -fn tybounds<'a, 'tcx: 'a>( - bounds: &'a [clean::PolyTrait], - lt: &'a Option, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +fn tybounds( + bounds: &[clean::PolyTrait], + lt: &Option, + cx: &Context<'_>, +) -> impl Display { fmt::from_fn(move |f| { bounds.iter().map(|bound| bound.print(cx)).joined(" + ", f)?; if let Some(lt) = lt { @@ -825,11 +812,11 @@ fn tybounds<'a, 'tcx: 'a>( }) } -fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>( - params: &'a [clean::GenericParamDef], - cx: &'a Context<'tcx>, +fn print_higher_ranked_params_with_space( + params: &[clean::GenericParamDef], + cx: &Context<'_>, keyword: &'static str, -) -> impl Display + 'a + Captures<'tcx> { +) -> impl Display { fmt::from_fn(move |f| { if !params.is_empty() { f.write_str(keyword)?; @@ -841,11 +828,7 @@ fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>( }) } -pub(crate) fn anchor<'a: 'cx, 'cx>( - did: DefId, - text: Symbol, - cx: &'cx Context<'a>, -) -> impl Display + Captures<'a> + 'cx { +pub(crate) fn anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let parts = href(did, cx); if let Ok((url, short_ty, fqp)) = parts { @@ -1121,29 +1104,19 @@ fn fmt_type( } impl clean::Type { - pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'b + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| fmt_type(self, f, false, cx)) } } impl clean::Path { - pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'b + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) } } impl clean::Impl { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - use_absolute: bool, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, use_absolute: bool, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { f.write_str("impl")?; self.generics.print(cx).fmt(f)?; @@ -1182,12 +1155,12 @@ impl clean::Impl { print_where_clause(&self.generics, cx, 0, Ending::Newline).fmt(f) }) } - fn print_type<'a, 'tcx: 'a>( + fn print_type( &self, type_: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool, - cx: &'a Context<'tcx>, + cx: &Context<'_>, ) -> Result<(), fmt::Error> { if let clean::Type::Tuple(types) = type_ && let [clean::Type::Generic(name)] = &types[..] @@ -1258,10 +1231,7 @@ impl clean::Impl { } impl clean::Arguments { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { self.values .iter() @@ -1301,10 +1271,7 @@ impl Display for Indent { } impl clean::FnDecl { - pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'b + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; if f.alternate() { @@ -1333,12 +1300,12 @@ impl clean::FnDecl { /// are preserved. /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is /// necessary. - pub(crate) fn full_print<'a, 'tcx: 'a>( - &'a self, + pub(crate) fn full_print( + &self, header_len: usize, indent: usize, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + cx: &Context<'_>, + ) -> impl Display { fmt::from_fn(move |f| { // First, generate the text form of the declaration, with no line wrapping, and count the bytes. let mut counter = WriteCounter(0); @@ -1420,10 +1387,7 @@ impl clean::FnDecl { self.print_output(cx).fmt(f) } - fn print_output<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + fn print_output(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match &self.output { clean::Tuple(tys) if tys.is_empty() => Ok(()), ty if f.alternate() => { @@ -1434,10 +1398,7 @@ impl clean::FnDecl { } } -pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>( - item: &clean::Item, - cx: &'a Context<'tcx>, -) -> impl Display + 'a + Captures<'tcx> { +pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) -> impl Display { use std::fmt::Write as _; let vis: Cow<'static, str> = match item.visibility(cx.tcx()) { None => "".into(), @@ -1546,10 +1507,7 @@ pub(crate) fn print_constness_with_space( } impl clean::Import { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { @@ -1570,10 +1528,7 @@ impl clean::Import { } impl clean::ImportSource { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self.did { Some(did) => resolved_path(f, did, &self.path, true, false, cx), _ => { @@ -1593,10 +1548,7 @@ impl clean::ImportSource { } impl clean::AssocItemConstraint { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { f.write_str(self.assoc.name.as_str())?; self.assoc.args.print(cx).fmt(f)?; @@ -1627,15 +1579,12 @@ pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display { }) } -pub(crate) fn print_default_space<'a>(v: bool) -> &'a str { +pub(crate) fn print_default_space(v: bool) -> &'static str { if v { "default " } else { "" } } impl clean::GenericArg { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self { clean::GenericArg::Lifetime(lt) => lt.print().fmt(f), clean::GenericArg::Type(ty) => ty.print(cx).fmt(f), @@ -1646,10 +1595,7 @@ impl clean::GenericArg { } impl clean::Term { - pub(crate) fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl Display + 'a + Captures<'tcx> { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match self { clean::Term::Type(ty) => ty.print(cx).fmt(f), clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f), diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 204631063a23a..620135d37abb3 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -47,7 +47,6 @@ use rinja::Template; use rustc_attr_parsing::{ ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, }; -use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::Mutability; use rustc_hir::def_id::{DefId, DefIdSet}; @@ -82,7 +81,7 @@ use crate::html::{highlight, sources}; use crate::scrape_examples::{CallData, CallLocation}; use crate::{DOC_RUST_LANG_ORG_VERSION, try_none}; -pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { +pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display { fmt::from_fn(move |f| { if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) } }) @@ -310,7 +309,7 @@ impl ItemEntry { } impl ItemEntry { - pub(crate) fn print(&self) -> impl fmt::Display + '_ { + pub(crate) fn print(&self) -> impl fmt::Display { fmt::from_fn(move |f| write!(f, "{}", self.url, Escape(&self.name))) } } @@ -505,12 +504,12 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String { ) } -fn document<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - item: &'a clean::Item, - parent: Option<&'a clean::Item>, +fn document( + cx: &Context<'_>, + item: &clean::Item, + parent: Option<&clean::Item>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { if let Some(ref name) = item.name { info!("Documenting {name}"); } @@ -526,12 +525,12 @@ fn document<'a, 'cx: 'a>( } /// Render md_text as markdown. -fn render_markdown<'a, 'cx: 'a>( - cx: &'a Context<'cx>, - md_text: &'a str, +fn render_markdown( + cx: &Context<'_>, + md_text: &str, links: Vec, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { fmt::from_fn(move |f| { write!( f, @@ -552,13 +551,13 @@ fn render_markdown<'a, 'cx: 'a>( /// Writes a documentation block containing only the first paragraph of the documentation. If the /// docs are longer, a "Read more" link is appended to the end. -fn document_short<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, - link: AssocItemLink<'a>, - parent: &'a clean::Item, +fn document_short( + item: &clean::Item, + cx: &Context<'_>, + link: AssocItemLink<'_>, + parent: &clean::Item, show_def_docs: bool, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { fmt::from_fn(move |f| { document_item_info(cx, item, Some(parent)).render_into(f).unwrap(); if !show_def_docs { @@ -595,28 +594,28 @@ fn document_short<'a, 'cx: 'a>( }) } -fn document_full_collapsible<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, +fn document_full_collapsible( + item: &clean::Item, + cx: &Context<'_>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { document_full_inner(item, cx, true, heading_offset) } -fn document_full<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, +fn document_full( + item: &clean::Item, + cx: &Context<'_>, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { document_full_inner(item, cx, false, heading_offset) } -fn document_full_inner<'a, 'cx: 'a>( - item: &'a clean::Item, - cx: &'a Context<'cx>, +fn document_full_inner( + item: &clean::Item, + cx: &Context<'_>, is_collapsible: bool, heading_offset: HeadingOffset, -) -> impl fmt::Display + 'a + Captures<'cx> { +) -> impl fmt::Display { fmt::from_fn(move |f| { if let Some(s) = item.opt_doc_value() { debug!("Doc block: =====\n{s}\n====="); @@ -799,11 +798,11 @@ pub(crate) fn render_impls( } /// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item. -fn assoc_href_attr<'a, 'tcx>( +fn assoc_href_attr( it: &clean::Item, - link: AssocItemLink<'a>, - cx: &Context<'tcx>, -) -> Option> { + link: AssocItemLink<'_>, + cx: &Context<'_>, +) -> Option { let name = it.name.unwrap(); let item_type = it.type_(); @@ -814,7 +813,7 @@ fn assoc_href_attr<'a, 'tcx>( } let href = match link { - AssocItemLink::Anchor(Some(ref id)) => Href::AnchorId(id), + AssocItemLink::Anchor(Some(id)) => Href::AnchorId(id), AssocItemLink::Anchor(None) => Href::Anchor(item_type), AssocItemLink::GotoSource(did, provided_methods) => { // We're creating a link from the implementation of an associated item to its @@ -1168,7 +1167,7 @@ fn render_assoc_item( if parent == ItemType::Trait { 4 } else { 0 }, cx, ), - clean::RequiredAssocTypeItem(ref generics, ref bounds) => assoc_type( + clean::RequiredAssocTypeItem(generics, bounds) => assoc_type( w, item, generics, @@ -1178,7 +1177,7 @@ fn render_assoc_item( if parent == ItemType::Trait { 4 } else { 0 }, cx, ), - clean::AssocTypeItem(ref ty, ref bounds) => assoc_type( + clean::AssocTypeItem(ty, bounds) => assoc_type( w, item, &ty.generics, @@ -1194,11 +1193,7 @@ fn render_assoc_item( // When an attribute is rendered inside a `
` tag, it is formatted using
 // a whitespace prefix and newline.
-fn render_attributes_in_pre<'a, 'tcx: 'a>(
-    it: &'a clean::Item,
-    prefix: &'a str,
-    cx: &'a Context<'tcx>,
-) -> impl fmt::Display + Captures<'a> + Captures<'tcx> {
+fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         for a in it.attributes(cx.tcx(), cx.cache(), false) {
             writeln!(f, "{prefix}{a}")?;
@@ -1292,12 +1287,12 @@ pub(crate) fn render_all_impls(
     }
 }
 
-fn render_assoc_items<'a, 'cx: 'a>(
-    cx: &'a Context<'cx>,
-    containing_item: &'a clean::Item,
+fn render_assoc_items(
+    cx: &Context<'_>,
+    containing_item: &clean::Item,
     it: DefId,
-    what: AssocItemRender<'a>,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+    what: AssocItemRender<'_>,
+) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         let mut derefs = DefIdSet::default();
         derefs.insert(it);
@@ -1468,10 +1463,10 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
     }
 }
 
-pub(crate) fn notable_traits_button<'a, 'tcx>(
-    ty: &'a clean::Type,
-    cx: &'a Context<'tcx>,
-) -> Option> {
+pub(crate) fn notable_traits_button(
+    ty: &clean::Type,
+    cx: &Context<'_>,
+) -> Option {
     let mut has_notable_trait = false;
 
     if ty.is_unit() {
@@ -1773,7 +1768,7 @@ fn render_impl(
                     w.push_str("");
                 }
             }
-            clean::RequiredAssocConstItem(ref generics, ref ty) => {
+            clean::RequiredAssocConstItem(generics, ty) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
                 write_str(
@@ -1827,7 +1822,7 @@ fn render_impl(
                 );
                 w.push_str("");
             }
-            clean::RequiredAssocTypeItem(ref generics, ref bounds) => {
+            clean::RequiredAssocTypeItem(generics, bounds) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
                 write_str(
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index f320114703996..f04fbd992accc 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -4,7 +4,6 @@ use std::fmt::Display;
 
 use rinja::Template;
 use rustc_abi::VariantIdx;
-use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
@@ -92,44 +91,32 @@ macro_rules! item_template {
 macro_rules! item_template_methods {
     () => {};
     (document $($rest:tt)*) => {
-        fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let (item, cx) = self.item_and_cx();
-                let v = document(cx, item, None, HeadingOffset::H2);
-                write!(f, "{v}")
-            })
+        fn document(&self) -> impl fmt::Display {
+            let (item, cx) = self.item_and_cx();
+            document(cx, item, None, HeadingOffset::H2)
         }
         item_template_methods!($($rest)*);
     };
     (document_type_layout $($rest:tt)*) => {
-        fn document_type_layout<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let (item, cx) = self.item_and_cx();
-                let def_id = item.item_id.expect_def_id();
-                let v = document_type_layout(cx, def_id);
-                write!(f, "{v}")
-            })
+        fn document_type_layout(&self) -> impl fmt::Display {
+            let (item, cx) = self.item_and_cx();
+            let def_id = item.item_id.expect_def_id();
+            document_type_layout(cx, def_id)
         }
         item_template_methods!($($rest)*);
     };
     (render_attributes_in_pre $($rest:tt)*) => {
-        fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let (item, cx) = self.item_and_cx();
-                let v = render_attributes_in_pre(item, "", cx);
-                write!(f, "{v}")
-            })
+        fn render_attributes_in_pre(&self) -> impl fmt::Display {
+            let (item, cx) = self.item_and_cx();
+            render_attributes_in_pre(item, "", cx)
         }
         item_template_methods!($($rest)*);
     };
     (render_assoc_items $($rest:tt)*) => {
-        fn render_assoc_items<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let (item, cx) = self.item_and_cx();
-                let def_id = item.item_id.expect_def_id();
-                let v = render_assoc_items(cx, item, def_id, AssocItemRender::All);
-                write!(f, "{v}")
-            })
+        fn render_assoc_items(&self) -> impl fmt::Display {
+            let (item, cx) = self.item_and_cx();
+            let def_id = item.item_id.expect_def_id();
+            render_assoc_items(cx, item, def_id, AssocItemRender::All)
         }
         item_template_methods!($($rest)*);
     };
@@ -252,24 +239,24 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut String)
     item_vars.render_into(buf).unwrap();
 
     match &item.kind {
-        clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
-        clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f, _) => {
+        clean::ModuleItem(m) => item_module(buf, cx, item, &m.items),
+        clean::FunctionItem(f) | clean::ForeignFunctionItem(f, _) => {
             item_function(buf, cx, item, f)
         }
-        clean::TraitItem(ref t) => item_trait(buf, cx, item, t),
-        clean::StructItem(ref s) => item_struct(buf, cx, item, s),
-        clean::UnionItem(ref s) => item_union(buf, cx, item, s),
-        clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
-        clean::TypeAliasItem(ref t) => item_type_alias(buf, cx, item, t),
-        clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
-        clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
+        clean::TraitItem(t) => item_trait(buf, cx, item, t),
+        clean::StructItem(s) => item_struct(buf, cx, item, s),
+        clean::UnionItem(s) => item_union(buf, cx, item, s),
+        clean::EnumItem(e) => item_enum(buf, cx, item, e),
+        clean::TypeAliasItem(t) => item_type_alias(buf, cx, item, t),
+        clean::MacroItem(m) => item_macro(buf, cx, item, m),
+        clean::ProcMacroItem(m) => item_proc_macro(buf, cx, item, m),
         clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
-        clean::StaticItem(ref i) => item_static(buf, cx, item, i, None),
-        clean::ForeignStaticItem(ref i, safety) => item_static(buf, cx, item, i, Some(*safety)),
+        clean::StaticItem(i) => item_static(buf, cx, item, i, None),
+        clean::ForeignStaticItem(i, safety) => item_static(buf, cx, item, i, Some(*safety)),
         clean::ConstantItem(ci) => item_constant(buf, cx, item, &ci.generics, &ci.type_, &ci.kind),
         clean::ForeignTypeItem => item_foreign_type(buf, cx, item),
         clean::KeywordItem => item_keyword(buf, cx, item),
-        clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta),
+        clean::TraitAliasItem(ta) => item_trait_alias(buf, cx, item, ta),
         _ => {
             // We don't generate pages for any other type.
             unreachable!();
@@ -527,14 +514,14 @@ fn item_module(w: &mut String, cx: &Context<'_>, item: &clean::Item, items: &[cl
 
 /// Render the stability, deprecation and portability tags that are displayed in the item's summary
 /// at the module level.
-fn extra_info_tags<'a, 'tcx: 'a>(
-    tcx: TyCtxt<'tcx>,
-    item: &'a clean::Item,
-    parent: &'a clean::Item,
+fn extra_info_tags(
+    tcx: TyCtxt<'_>,
+    item: &clean::Item,
+    parent: &clean::Item,
     import_def_id: Option,
-) -> impl Display + 'a + Captures<'tcx> {
+) -> impl Display {
     fmt::from_fn(move |f| {
-        fn tag_html<'a>(class: &'a str, title: &'a str, contents: &'a str) -> impl Display + 'a {
+        fn tag_html(class: &str, title: &str, contents: &str) -> impl Display {
             fmt::from_fn(move |f| {
                 write!(
                     f,
@@ -973,7 +960,7 @@ fn item_trait(w: &mut String, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                 extern_crates.insert(did.krate);
             }
             match implementor.inner_impl().for_.without_borrowed_ref() {
-                clean::Type::Path { ref path } if !path.is_assoc_ty() => {
+                clean::Type::Path { path } if !path.is_assoc_ty() => {
                     let did = path.def_id();
                     let &mut (prev_did, ref mut has_duplicates) =
                         implementor_dups.entry(path.last()).or_insert((did, false));
@@ -1419,35 +1406,20 @@ fn item_union(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
     );
 
     impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
-        fn render_union<'b>(&'b self) -> impl Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let v = render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx);
-                write!(f, "{v}")
-            })
+        fn render_union(&self) -> impl Display {
+            render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx)
         }
 
-        fn document_field<'b>(
-            &'b self,
-            field: &'a clean::Item,
-        ) -> impl Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let v = document(self.cx, field, Some(self.it), HeadingOffset::H3);
-                write!(f, "{v}")
-            })
+        fn document_field(&self, field: &'a clean::Item) -> impl Display {
+            document(self.cx, field, Some(self.it), HeadingOffset::H3)
         }
 
         fn stability_field(&self, field: &clean::Item) -> Option {
             field.stability_class(self.cx.tcx())
         }
 
-        fn print_ty<'b>(
-            &'b self,
-            ty: &'a clean::Type,
-        ) -> impl Display + Captures<'a> + 'b + Captures<'cx> {
-            fmt::from_fn(move |f| {
-                let v = ty.print(self.cx);
-                write!(f, "{v}")
-            })
+        fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
+            ty.print(self.cx)
         }
 
         fn fields_iter(
@@ -1467,10 +1439,7 @@ fn item_union(w: &mut String, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
     ItemUnion { cx, it, s }.render_into(w).unwrap();
 }
 
-fn print_tuple_struct_fields<'a, 'cx: 'a>(
-    cx: &'a Context<'cx>,
-    s: &'a [clean::Item],
-) -> impl Display + 'a + Captures<'cx> {
+fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Display {
     fmt::from_fn(|f| {
         if !s.is_empty()
             && s.iter().all(|field| {
@@ -2111,18 +2080,14 @@ pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
     s
 }
 
-pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display + '_ {
+pub(super) fn item_path(ty: ItemType, name: &str) -> impl Display {
     fmt::from_fn(move |f| match ty {
         ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
         _ => write!(f, "{ty}.{name}.html"),
     })
 }
 
-fn bounds<'a, 'tcx>(
-    bounds: &'a [clean::GenericBound],
-    trait_alias: bool,
-    cx: &'a Context<'tcx>,
-) -> impl Display + 'a + Captures<'tcx> {
+fn bounds(bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> impl Display {
     (!bounds.is_empty())
         .then_some(fmt::from_fn(move |f| {
             let has_lots_of_bounds = bounds.len() > 2;
@@ -2208,12 +2173,12 @@ fn render_implementor(
     );
 }
 
-fn render_union<'a, 'cx: 'a>(
-    it: &'a clean::Item,
-    g: Option<&'a clean::Generics>,
-    fields: &'a [clean::Item],
-    cx: &'a Context<'cx>,
-) -> impl Display + 'a + Captures<'cx> {
+fn render_union(
+    it: &clean::Item,
+    g: Option<&clean::Generics>,
+    fields: &[clean::Item],
+    cx: &Context<'_>,
+) -> impl Display {
     fmt::from_fn(move |mut f| {
         write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
 
@@ -2410,7 +2375,7 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str {
     if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
 }
 
-fn document_non_exhaustive(item: &clean::Item) -> impl Display + '_ {
+fn document_non_exhaustive(item: &clean::Item) -> impl Display {
     fmt::from_fn(|f| {
         if item.is_non_exhaustive() {
             write!(
diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs
index 0f01db5f6bcc7..a1ee5c8c548b7 100644
--- a/src/librustdoc/html/render/type_layout.rs
+++ b/src/librustdoc/html/render/type_layout.rs
@@ -2,7 +2,6 @@ use std::fmt;
 
 use rinja::Template;
 use rustc_abi::{Primitive, TagEncoding, Variants};
-use rustc_data_structures::captures::Captures;
 use rustc_hir::def_id::DefId;
 use rustc_middle::span_bug;
 use rustc_middle::ty::layout::LayoutError;
@@ -26,10 +25,7 @@ struct TypeLayoutSize {
     size: u64,
 }
 
-pub(crate) fn document_type_layout<'a, 'cx: 'a>(
-    cx: &'a Context<'cx>,
-    ty_def_id: DefId,
-) -> impl fmt::Display + 'a + Captures<'cx> {
+pub(crate) fn document_type_layout(cx: &Context<'_>, ty_def_id: DefId) -> impl fmt::Display {
     fmt::from_fn(move |f| {
         if !cx.shared.show_type_layout {
             return Ok(());
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 78c86a27632b1..cbbd4b01d83ed 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -333,7 +333,7 @@ pub(crate) fn print_src(
     source_context: &SourceContext<'_>,
 ) {
     let mut lines = s.lines().count();
-    let line_info = if let SourceContext::Embedded(ref info) = source_context {
+    let line_info = if let SourceContext::Embedded(info) = source_context {
         highlight::LineInfo::new_scraped(lines as u32, info.offset as u32)
     } else {
         highlight::LineInfo::new(lines as u32)
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 97e6d3146427c..440d6331457b0 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -58,7 +58,7 @@ fn filter_assoc_items_by_name_and_namespace(
     assoc_items_of: DefId,
     ident: Ident,
     ns: Namespace,
-) -> impl Iterator + '_ {
+) -> impl Iterator {
     tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name).filter(move |item| {
         item.kind.namespace() == ns && tcx.hygienic_eq(ident, item.ident(tcx), assoc_items_of)
     })
diff --git a/src/tools/rustdoc/Cargo.toml b/src/tools/rustdoc/Cargo.toml
index c4101f72cc2da..d1682758d3626 100644
--- a/src/tools/rustdoc/Cargo.toml
+++ b/src/tools/rustdoc/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "rustdoc-tool"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 # Cargo adds a number of paths to the dylib search path on windows, which results in
 # the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
index 57fc601a2e0c0..91c83fa2f5ba9 100644
--- a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
+++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
@@ -19,7 +19,7 @@
 #![allow(incomplete_features)]
 #![feature(unsized_locals, unsized_fn_params)]
 
-// CHECK-LABEL: emptyfn:
+// CHECK-LABEL: emptyfn{{:|\[}}
 #[no_mangle]
 pub fn emptyfn() {
     // all: __stack_chk_fail
@@ -29,7 +29,7 @@ pub fn emptyfn() {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: array_char
+// CHECK-LABEL: array_char{{:|\[}}
 #[no_mangle]
 pub fn array_char(f: fn(*const char)) {
     let a = ['c'; 1];
@@ -47,7 +47,7 @@ pub fn array_char(f: fn(*const char)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: array_u8_1
+// CHECK-LABEL: array_u8_1{{:|\[}}
 #[no_mangle]
 pub fn array_u8_1(f: fn(*const u8)) {
     let a = [0u8; 1];
@@ -63,7 +63,7 @@ pub fn array_u8_1(f: fn(*const u8)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: array_u8_small:
+// CHECK-LABEL: array_u8_small{{:|\[}}
 #[no_mangle]
 pub fn array_u8_small(f: fn(*const u8)) {
     let a = [0u8; 2];
@@ -80,7 +80,7 @@ pub fn array_u8_small(f: fn(*const u8)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: array_u8_large:
+// CHECK-LABEL: array_u8_large{{:|\[}}
 #[no_mangle]
 pub fn array_u8_large(f: fn(*const u8)) {
     let a = [0u8; 9];
@@ -99,7 +99,7 @@ pub fn array_u8_large(f: fn(*const u8)) {
 #[derive(Copy, Clone)]
 pub struct ByteSizedNewtype(u8);
 
-// CHECK-LABEL: array_bytesizednewtype_9:
+// CHECK-LABEL: array_bytesizednewtype_9{{:|\[}}
 #[no_mangle]
 pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
     let a = [ByteSizedNewtype(0); 9];
@@ -115,7 +115,7 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: local_var_addr_used_indirectly
+// CHECK-LABEL: local_var_addr_used_indirectly{{:|\[}}
 #[no_mangle]
 pub fn local_var_addr_used_indirectly(f: fn(bool)) {
     let a = 5;
@@ -142,7 +142,7 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: local_string_addr_taken
+// CHECK-LABEL: local_string_addr_taken{{:|\[}}
 #[no_mangle]
 pub fn local_string_addr_taken(f: fn(&String)) {
     let x = String::new();
@@ -168,7 +168,7 @@ impl SelfByRef for i32 {
     }
 }
 
-// CHECK-LABEL: local_var_addr_taken_used_locally_only
+// CHECK-LABEL: local_var_addr_taken_used_locally_only{{:|\[}}
 #[no_mangle]
 pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32)) {
     let x = factory();
@@ -195,7 +195,7 @@ pub struct Gigastruct {
     members: u64,
 }
 
-// CHECK-LABEL: local_large_var_moved
+// CHECK-LABEL: local_large_var_moved{{:|\[}}
 #[no_mangle]
 pub fn local_large_var_moved(f: fn(Gigastruct)) {
     let x = Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 };
@@ -224,7 +224,7 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: local_large_var_cloned
+// CHECK-LABEL: local_large_var_cloned{{:|\[}}
 #[no_mangle]
 pub fn local_large_var_cloned(f: fn(Gigastruct)) {
     f(Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 });
@@ -281,7 +281,7 @@ extern "C" {
     fn alloca(size: usize) -> *mut ();
 }
 
-// CHECK-LABEL: alloca_small_compile_time_constant_arg
+// CHECK-LABEL: alloca_small_compile_time_constant_arg{{:|\[}}
 #[no_mangle]
 pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) {
     f(unsafe { alloca(8) });
@@ -293,7 +293,7 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: alloca_large_compile_time_constant_arg
+// CHECK-LABEL: alloca_large_compile_time_constant_arg{{:|\[}}
 #[no_mangle]
 pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) {
     f(unsafe { alloca(9) });
@@ -305,7 +305,7 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: alloca_dynamic_arg
+// CHECK-LABEL: alloca_dynamic_arg{{:|\[}}
 #[no_mangle]
 pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) {
     f(unsafe { alloca(n) });
@@ -324,7 +324,7 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) {
 // this is support for the "unsized locals" unstable feature:
 // https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html.
 
-// CHECK-LABEL: unsized_fn_param
+// CHECK-LABEL: unsized_fn_param{{:|\[}}
 #[no_mangle]
 pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
     let n = if l { 1 } else { 2 };
@@ -344,7 +344,7 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
     // missing-NOT: __stack_chk_fail
 }
 
-// CHECK-LABEL: unsized_local
+// CHECK-LABEL: unsized_local{{:|\[}}
 #[no_mangle]
 pub fn unsized_local(s: &[u8], l: bool, f: fn(&mut [u8])) {
     let n = if l { 1 } else { 2 };
diff --git a/x b/x
index e656d37c1e455..551cfe6efbf33 100755
--- a/x
+++ b/x
@@ -25,7 +25,13 @@ xpy=$(dirname "$(realpath "$0")")/x.py
 
 # On Windows, `py -3` sometimes works. We need to try it first because `python3`
 # sometimes tries to launch the app store on Windows.
-for SEARCH_PYTHON in py python3 python python2; do
+# On MacOS, `py` tries to install "Developer command line tools". Try `python3` first.
+# NOTE: running `bash -c ./x` from Windows doesn't set OSTYPE.
+case ${OSTYPE:-} in
+    cygwin*|msys*) SEARCH="py python3 python python2";;
+    *) SEARCH="python3 python py python2";;
+esac
+for SEARCH_PYTHON in $SEARCH; do
     if python=$(command -v $SEARCH_PYTHON) && [ -x "$python" ]; then
         if [ $SEARCH_PYTHON = py ]; then
             extra_arg="-3"