diff --git a/Cargo.lock b/Cargo.lock index 8e35435e3f9b7..34ff71107eb06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -719,6 +719,7 @@ version = "0.0.0" dependencies = [ "anstyle-svg", "build_helper", + "camino", "colored", "diff", "getopts", @@ -1212,7 +1213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", - "miniz_oxide 0.8.7", + "miniz_oxide 0.8.8", ] [[package]] @@ -2282,9 +2283,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -4671,6 +4672,7 @@ name = "rustdoc-gui-test" version = "0.1.0" dependencies = [ "build_helper", + "camino", "compiletest", "getopts", "walkdir", diff --git a/library/Cargo.lock b/library/Cargo.lock index d035ca6c91f53..29e0e134da7f8 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -176,9 +176,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "compiler_builtins", diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index d525ab425e60d..2016ece007eba 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -61,52 +61,4 @@ impl bool { pub fn then T>(self, f: F) -> Option { if self { Some(f()) } else { None } } - - /// Returns either `true_val` or `false_val` depending on the value of - /// `self`, with a hint to the compiler that `self` is unlikely - /// to be correctly predicted by a CPU’s branch predictor. - /// - /// This method is functionally equivalent to - /// ```ignore (this is just for illustrative purposes) - /// fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { - /// if b { true_val } else { false_val } - /// } - /// ``` - /// but might generate different assembly. In particular, on platforms with - /// a conditional move or select instruction (like `cmov` on x86 or `csel` - /// on ARM) the optimizer might use these instructions to avoid branches, - /// which can benefit performance if the branch predictor is struggling - /// with predicting `condition`, such as in an implementation of binary - /// search. - /// - /// Note however that this lowering is not guaranteed (on any platform) and - /// should not be relied upon when trying to write constant-time code. Also - /// be aware that this lowering might *decrease* performance if `condition` - /// is well-predictable. It is advisable to perform benchmarks to tell if - /// this function is useful. - /// - /// # Examples - /// - /// Distribute values evenly between two buckets: - /// ``` - /// #![feature(select_unpredictable)] - /// - /// use std::hash::BuildHasher; - /// - /// fn append(hasher: &H, v: i32, bucket_one: &mut Vec, bucket_two: &mut Vec) { - /// let hash = hasher.hash_one(&v); - /// let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two); - /// bucket.push(v); - /// } - /// # let hasher = std::collections::hash_map::RandomState::new(); - /// # let mut bucket_one = Vec::new(); - /// # let mut bucket_two = Vec::new(); - /// # append(&hasher, 42, &mut bucket_one, &mut bucket_two); - /// # assert_eq!(bucket_one.len() + bucket_two.len(), 1); - /// ``` - #[inline(always)] - #[unstable(feature = "select_unpredictable", issue = "133962")] - pub fn select_unpredictable(self, true_val: T, false_val: T) -> T { - crate::intrinsics::select_unpredictable(self, true_val, false_val) - } } diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 5ce282b05de73..f6708cc4bc912 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -734,3 +734,52 @@ pub const fn unlikely(b: bool) -> bool { pub const fn cold_path() { crate::intrinsics::cold_path() } + +/// Returns either `true_val` or `false_val` depending on the value of `b`, +/// with a hint to the compiler that `b` is unlikely to be correctly +/// predicted by a CPU’s branch predictor. +/// +/// This method is functionally equivalent to +/// ```ignore (this is just for illustrative purposes) +/// fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { +/// if b { true_val } else { false_val } +/// } +/// ``` +/// but might generate different assembly. In particular, on platforms with +/// a conditional move or select instruction (like `cmov` on x86 or `csel` +/// on ARM) the optimizer might use these instructions to avoid branches, +/// which can benefit performance if the branch predictor is struggling +/// with predicting `condition`, such as in an implementation of binary +/// search. +/// +/// Note however that this lowering is not guaranteed (on any platform) and +/// should not be relied upon when trying to write constant-time code. Also +/// be aware that this lowering might *decrease* performance if `condition` +/// is well-predictable. It is advisable to perform benchmarks to tell if +/// this function is useful. +/// +/// # Examples +/// +/// Distribute values evenly between two buckets: +/// ``` +/// #![feature(select_unpredictable)] +/// +/// use std::hash::BuildHasher; +/// use std::hint; +/// +/// fn append(hasher: &H, v: i32, bucket_one: &mut Vec, bucket_two: &mut Vec) { +/// let hash = hasher.hash_one(&v); +/// let bucket = hint::select_unpredictable(hash % 2 == 0, bucket_one, bucket_two); +/// bucket.push(v); +/// } +/// # let hasher = std::collections::hash_map::RandomState::new(); +/// # let mut bucket_one = Vec::new(); +/// # let mut bucket_two = Vec::new(); +/// # append(&hasher, 42, &mut bucket_one, &mut bucket_two); +/// # assert_eq!(bucket_one.len() + bucket_two.len(), 1); +/// ``` +#[inline(always)] +#[unstable(feature = "select_unpredictable", issue = "133962")] +pub fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { + crate::intrinsics::select_unpredictable(b, true_val, false_val) +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 86856b26a4b30..077aaeddfa170 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1326,7 +1326,7 @@ pub const fn unlikely(b: bool) -> bool { /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The public form of this instrinsic is [`bool::select_unpredictable`]. +/// The public form of this instrinsic is [`core::hint::select_unpredictable`]. #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 59f8020f0c38f..7b2a3fac38a32 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2822,7 +2822,7 @@ impl [T] { // Binary search interacts poorly with branch prediction, so force // the compiler to use conditional moves if supported by the target // architecture. - base = (cmp == Greater).select_unpredictable(base, mid); + base = hint::select_unpredictable(cmp == Greater, base, mid); // This is imprecise in the case where `size` is odd and the // comparison returns Greater: the mid element still gets included diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 95f196a40d01c..4280f7570db4c 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -2,7 +2,7 @@ use crate::mem::{self, ManuallyDrop, MaybeUninit}; use crate::slice::sort::shared::FreezeMarker; -use crate::{intrinsics, ptr, slice}; +use crate::{hint, intrinsics, ptr, slice}; // It's important to differentiate between SMALL_SORT_THRESHOLD performance for // small slices and small-sort performance sorting small sub-slices as part of @@ -408,8 +408,8 @@ where // } // The goal is to generate cmov instructions here. - let v_a_swap = should_swap.select_unpredictable(v_b, v_a); - let v_b_swap = should_swap.select_unpredictable(v_a, v_b); + let v_a_swap = hint::select_unpredictable(should_swap, v_b, v_a); + let v_b_swap = hint::select_unpredictable(should_swap, v_a, v_b); let v_b_swap_tmp = ManuallyDrop::new(ptr::read(v_b_swap)); ptr::copy(v_a_swap, v_a, 1); @@ -640,15 +640,15 @@ pub unsafe fn sort4_stable bool>( // 1, 1 | c b a d let c3 = is_less(&*c, &*a); let c4 = is_less(&*d, &*b); - let min = c3.select_unpredictable(c, a); - let max = c4.select_unpredictable(b, d); - let unknown_left = c3.select_unpredictable(a, c4.select_unpredictable(c, b)); - let unknown_right = c4.select_unpredictable(d, c3.select_unpredictable(b, c)); + let min = hint::select_unpredictable(c3, c, a); + let max = hint::select_unpredictable(c4, b, d); + let unknown_left = hint::select_unpredictable(c3, a, hint::select_unpredictable(c4, c, b)); + let unknown_right = hint::select_unpredictable(c4, d, hint::select_unpredictable(c3, b, c)); // Sort the last two unknown elements. let c5 = is_less(&*unknown_right, &*unknown_left); - let lo = c5.select_unpredictable(unknown_right, unknown_left); - let hi = c5.select_unpredictable(unknown_left, unknown_right); + let lo = hint::select_unpredictable(c5, unknown_right, unknown_left); + let hi = hint::select_unpredictable(c5, unknown_left, unknown_right); ptr::copy_nonoverlapping(min, dst, 1); ptr::copy_nonoverlapping(lo, dst.add(1), 1); diff --git a/library/profiler_builtins/build.rs b/library/profiler_builtins/build.rs index dd85239fa8cfd..fc1a9ecc1ec32 100644 --- a/library/profiler_builtins/build.rs +++ b/library/profiler_builtins/build.rs @@ -9,8 +9,14 @@ use std::path::PathBuf; fn main() { if let Ok(rt) = tracked_env_var("LLVM_PROFILER_RT_LIB") { - println!("cargo::rustc-link-lib=static:+verbatim={rt}"); - return; + let rt = PathBuf::from(rt); + if let Some(lib) = rt.file_name() { + if let Some(dir) = rt.parent() { + println!("cargo::rustc-link-search=native={}", dir.display()); + } + println!("cargo::rustc-link-lib=static:+verbatim={}", lib.to_str().unwrap()); + return; + } } let target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set"); diff --git a/library/std/src/sys/args/common.rs b/library/std/src/sys/args/common.rs new file mode 100644 index 0000000000000..43ac5e9592348 --- /dev/null +++ b/library/std/src/sys/args/common.rs @@ -0,0 +1,43 @@ +use crate::ffi::OsString; +use crate::{fmt, vec}; + +pub struct Args { + iter: vec::IntoIter, +} + +impl !Send for Args {} +impl !Sync for Args {} + +impl Args { + pub(super) fn new(args: Vec) -> Self { + Args { iter: args.into_iter() } + } +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.as_slice().fmt(f) + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} diff --git a/library/std/src/sys/pal/hermit/args.rs b/library/std/src/sys/args/hermit.rs similarity index 59% rename from library/std/src/sys/pal/hermit/args.rs rename to library/std/src/sys/args/hermit.rs index 4402426027730..ddd644a554044 100644 --- a/library/std/src/sys/pal/hermit/args.rs +++ b/library/std/src/sys/args/hermit.rs @@ -1,8 +1,12 @@ use crate::ffi::{CStr, OsString, c_char}; use crate::os::hermit::ffi::OsStringExt; +use crate::ptr; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sync::atomic::{AtomicIsize, AtomicPtr}; -use crate::{fmt, ptr, vec}; + +#[path = "common.rs"] +mod common; +pub use common::Args; static ARGC: AtomicIsize = AtomicIsize::new(0); static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); @@ -27,40 +31,5 @@ pub fn args() -> Args { }) .collect(); - Args { iter: args.into_iter() } -} - -pub struct Args { - iter: vec::IntoIter, -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.iter.as_slice().fmt(f) - } -} - -impl !Send for Args {} -impl !Sync for Args {} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.iter.next_back() - } + Args::new(args) } diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs new file mode 100644 index 0000000000000..f24d6eb123e03 --- /dev/null +++ b/library/std/src/sys/args/mod.rs @@ -0,0 +1,34 @@ +//! Platform-dependent command line arguments abstraction. + +#![forbid(unsafe_op_in_unsafe_fn)] + +cfg_if::cfg_if! { + if #[cfg(all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))))] { + mod unix; + pub use unix::*; + } else if #[cfg(target_family = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else if #[cfg(target_os = "xous")] { + mod xous; + pub use xous::*; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + pub use zkvm::*; + } else { + mod unsupported; + pub use unsupported::*; + } +} diff --git a/library/std/src/sys/pal/sgx/args.rs b/library/std/src/sys/args/sgx.rs similarity index 89% rename from library/std/src/sys/pal/sgx/args.rs rename to library/std/src/sys/args/sgx.rs index e62bf383954eb..efc4b79185227 100644 --- a/library/std/src/sys/pal/sgx/args.rs +++ b/library/std/src/sys/args/sgx.rs @@ -1,8 +1,10 @@ -use super::abi::usercalls::alloc; -use super::abi::usercalls::raw::ByteBuffer; +#![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers + use crate::ffi::OsString; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::os_str::Buf; +use crate::sys::pal::abi::usercalls::alloc; +use crate::sys::pal::abi::usercalls::raw::ByteBuffer; use crate::sys_common::FromInner; use crate::{fmt, slice}; diff --git a/library/std/src/sys/pal/uefi/args.rs b/library/std/src/sys/args/uefi.rs similarity index 79% rename from library/std/src/sys/pal/uefi/args.rs rename to library/std/src/sys/args/uefi.rs index 0c29caf2db676..84406c7f69df2 100644 --- a/library/std/src/sys/pal/uefi/args.rs +++ b/library/std/src/sys/args/uefi.rs @@ -1,14 +1,13 @@ use r_efi::protocols::loaded_image; -use super::helpers; use crate::env::current_exe; use crate::ffi::OsString; use crate::iter::Iterator; -use crate::{fmt, vec}; +use crate::sys::pal::helpers; -pub struct Args { - parsed_args_list: vec::IntoIter, -} +#[path = "common.rs"] +mod common; +pub use common::Args; pub fn args() -> Args { let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]); @@ -22,51 +21,17 @@ pub fn args() -> Args { let lp_size = unsafe { (*protocol.as_ptr()).load_options_size } as usize; // Break if we are sure that it cannot be UTF-16 if lp_size < size_of::() || lp_size % size_of::() != 0 { - return Args { parsed_args_list: lazy_current_exe().into_iter() }; + return Args::new(lazy_current_exe()); } let lp_size = lp_size / size_of::(); let lp_cmd_line = unsafe { (*protocol.as_ptr()).load_options as *const u16 }; if !lp_cmd_line.is_aligned() { - return Args { parsed_args_list: lazy_current_exe().into_iter() }; + return Args::new(lazy_current_exe()); } let lp_cmd_line = unsafe { crate::slice::from_raw_parts(lp_cmd_line, lp_size) }; - Args { - parsed_args_list: parse_lp_cmd_line(lp_cmd_line) - .unwrap_or_else(lazy_current_exe) - .into_iter(), - } -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.parsed_args_list.as_slice().fmt(f) - } -} - -impl Iterator for Args { - type Item = OsString; - - fn next(&mut self) -> Option { - self.parsed_args_list.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.parsed_args_list.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.parsed_args_list.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.parsed_args_list.next_back() - } + Args::new(parse_lp_cmd_line(lp_cmd_line).unwrap_or_else(lazy_current_exe)) } /// Implements the UEFI command-line argument parsing algorithm. diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/args/unix.rs similarity index 85% rename from library/std/src/sys/pal/unix/args.rs rename to library/std/src/sys/args/unix.rs index 0bb7b64007aba..7d7815c6dff23 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/args/unix.rs @@ -5,13 +5,16 @@ #![allow(dead_code)] // runtime init functions not used during testing -use crate::ffi::{CStr, OsString}; +use crate::ffi::CStr; use crate::os::unix::ffi::OsStringExt; -use crate::{fmt, vec}; + +#[path = "common.rs"] +mod common; +pub use common::Args; /// One-time global initialization. pub unsafe fn init(argc: isize, argv: *const *const u8) { - imp::init(argc, argv) + unsafe { imp::init(argc, argv) } } /// Returns the command line arguments @@ -55,42 +58,7 @@ pub fn args() -> Args { vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); } - Args { iter: vec.into_iter() } -} - -pub struct Args { - iter: vec::IntoIter, -} - -impl !Send for Args {} -impl !Sync for Args {} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.iter.as_slice().fmt(f) - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.iter.next_back() - } + Args::new(vec) } #[cfg(any( @@ -141,7 +109,7 @@ mod imp { pub unsafe fn init(argc: isize, argv: *const *const u8) { // on GNU/Linux if we are main then we will init argv and argc twice, it "duplicates work" // BUT edge-cases are real: only using .init_array can break most emulators, dlopen, etc. - really_init(argc, argv); + unsafe { really_init(argc, argv) }; } /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. @@ -159,9 +127,7 @@ mod imp { argv: *const *const u8, _envp: *const *const u8, ) { - unsafe { - really_init(argc as isize, argv); - } + unsafe { really_init(argc as isize, argv) }; } init_wrapper }; @@ -228,16 +194,3 @@ mod imp { (argc as isize, argv.cast()) } } - -#[cfg(any(target_os = "espidf", target_os = "vita"))] -mod imp { - use crate::ffi::c_char; - use crate::ptr; - - #[inline(always)] - pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} - - pub fn argc_argv() -> (isize, *const *const c_char) { - (0, ptr::null()) - } -} diff --git a/library/std/src/sys/pal/unsupported/args.rs b/library/std/src/sys/args/unsupported.rs similarity index 100% rename from library/std/src/sys/pal/unsupported/args.rs rename to library/std/src/sys/args/unsupported.rs diff --git a/library/std/src/sys/args/wasi.rs b/library/std/src/sys/args/wasi.rs new file mode 100644 index 0000000000000..4795789e4c717 --- /dev/null +++ b/library/std/src/sys/args/wasi.rs @@ -0,0 +1,29 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::ffi::{CStr, OsStr, OsString}; +use crate::os::wasi::ffi::OsStrExt; + +#[path = "common.rs"] +mod common; +pub use common::Args; + +/// Returns the command line arguments +pub fn args() -> Args { + Args::new(maybe_args().unwrap_or(Vec::new())) +} + +fn maybe_args() -> Option> { + unsafe { + let (argc, buf_size) = wasi::args_sizes_get().ok()?; + let mut argv = Vec::with_capacity(argc); + let mut buf = Vec::with_capacity(buf_size); + wasi::args_get(argv.as_mut_ptr(), buf.as_mut_ptr()).ok()?; + argv.set_len(argc); + let mut ret = Vec::with_capacity(argc); + for ptr in argv { + let s = CStr::from_ptr(ptr.cast()); + ret.push(OsStr::from_bytes(s.to_bytes()).to_owned()); + } + Some(ret) + } +} diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/args/windows.rs similarity index 94% rename from library/std/src/sys/pal/windows/args.rs rename to library/std/src/sys/args/windows.rs index d973743639ab0..47f0e5f2d05f8 100644 --- a/library/std/src/sys/pal/windows/args.rs +++ b/library/std/src/sys/args/windows.rs @@ -6,17 +6,21 @@ #[cfg(test)] mod tests; -use super::ensure_no_nuls; -use super::os::current_exe; use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; +use crate::sys::pal::os::current_exe; +use crate::sys::pal::{ensure_no_nuls, fill_utf16_buf}; use crate::sys::path::get_long_path; use crate::sys::{c, to_u16s}; use crate::sys_common::AsInner; use crate::sys_common::wstr::WStrUnits; -use crate::{fmt, io, iter, vec}; +use crate::{io, iter, ptr}; + +#[path = "common.rs"] +mod common; +pub use common::Args; pub fn args() -> Args { // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16 @@ -27,7 +31,7 @@ pub fn args() -> Args { current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new()) }); - Args { parsed_args_list: parsed_args_list.into_iter() } + Args::new(parsed_args_list) } } @@ -153,38 +157,6 @@ fn parse_lp_cmd_line<'a, F: Fn() -> OsString>( ret_val } -pub struct Args { - parsed_args_list: vec::IntoIter, -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.parsed_args_list.as_slice().fmt(f) - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.parsed_args_list.next() - } - fn size_hint(&self) -> (usize, Option) { - self.parsed_args_list.size_hint() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.parsed_args_list.next_back() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.parsed_args_list.len() - } -} - #[derive(Debug)] pub(crate) enum Arg { /// Add quotes (if needed) @@ -384,9 +356,6 @@ pub(crate) fn to_user_path(path: &Path) -> io::Result> { from_wide_to_user_path(to_u16s(path)?) } pub(crate) fn from_wide_to_user_path(mut path: Vec) -> io::Result> { - use super::fill_utf16_buf; - use crate::ptr; - // UTF-16 encoded code points, used in parsing and building UTF-16 paths. // All of these are in the ASCII range so they can be cast directly to `u16`. const SEP: u16 = b'\\' as _; diff --git a/library/std/src/sys/pal/windows/args/tests.rs b/library/std/src/sys/args/windows/tests.rs similarity index 100% rename from library/std/src/sys/pal/windows/args/tests.rs rename to library/std/src/sys/args/windows/tests.rs diff --git a/library/std/src/sys/args/xous.rs b/library/std/src/sys/args/xous.rs new file mode 100644 index 0000000000000..09a47283d6573 --- /dev/null +++ b/library/std/src/sys/args/xous.rs @@ -0,0 +1,23 @@ +use crate::sys::pal::os::get_application_parameters; +use crate::sys::pal::os::params::ArgumentList; + +#[path = "common.rs"] +mod common; +pub use common::Args; + +pub fn args() -> Args { + let Some(params) = get_application_parameters() else { + return Args::new(vec![]); + }; + + for param in params { + if let Ok(args) = ArgumentList::try_from(¶m) { + let mut parsed_args = vec![]; + for arg in args { + parsed_args.push(arg.into()); + } + return Args::new(parsed_args); + } + } + Args::new(vec![]) +} diff --git a/library/std/src/sys/pal/zkvm/args.rs b/library/std/src/sys/args/zkvm.rs similarity index 98% rename from library/std/src/sys/pal/zkvm/args.rs rename to library/std/src/sys/args/zkvm.rs index 47857f6c448bc..194ba7159d459 100644 --- a/library/std/src/sys/pal/zkvm/args.rs +++ b/library/std/src/sys/args/zkvm.rs @@ -1,7 +1,7 @@ -use super::{WORD_SIZE, abi}; use crate::ffi::OsString; use crate::fmt; use crate::sys::os_str; +use crate::sys::pal::{WORD_SIZE, abi}; use crate::sys_common::FromInner; pub struct Args { diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index 2042ea2c73d00..13fefb4031d08 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -71,9 +71,11 @@ const fn max_iov() -> usize { target_os = "android", target_os = "dragonfly", target_os = "emscripten", + target_os = "espidf", target_os = "freebsd", target_os = "linux", target_os = "netbsd", + target_os = "nuttx", target_os = "nto", target_os = "openbsd", target_os = "horizon", diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 3b176d0d16c44..4c5e36ce67a3b 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -20,6 +20,7 @@ cfg_if::cfg_if! { mod windows; use windows as imp; pub use windows::{symlink_inner, junction_point}; + use crate::sys::path::with_native_path; } else if #[cfg(target_os = "hermit")] { mod hermit; use hermit as imp; @@ -39,7 +40,7 @@ cfg_if::cfg_if! { } // FIXME: Replace this with platform-specific path conversion functions. -#[cfg(not(target_family = "unix"))] +#[cfg(not(any(target_family = "unix", target_os = "windows")))] #[inline] pub fn with_native_path(path: &Path, f: &dyn Fn(&Path) -> io::Result) -> io::Result { f(path) @@ -51,7 +52,7 @@ pub use imp::{ }; pub fn read_dir(path: &Path) -> io::Result { - // FIXME: use with_native_path + // FIXME: use with_native_path on all platforms imp::readdir(path) } @@ -68,8 +69,11 @@ pub fn remove_dir(path: &Path) -> io::Result<()> { } pub fn remove_dir_all(path: &Path) -> io::Result<()> { - // FIXME: use with_native_path - imp::remove_dir_all(path) + // FIXME: use with_native_path on all platforms + #[cfg(not(windows))] + return imp::remove_dir_all(path); + #[cfg(windows)] + with_native_path(path, &imp::remove_dir_all) } pub fn read_link(path: &Path) -> io::Result { @@ -77,6 +81,10 @@ pub fn read_link(path: &Path) -> io::Result { } pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { + // FIXME: use with_native_path on all platforms + #[cfg(windows)] + return imp::symlink(original, link); + #[cfg(not(windows))] with_native_path(original, &|original| { with_native_path(link, &|link| imp::symlink(original, link)) }) @@ -105,11 +113,17 @@ pub fn canonicalize(path: &Path) -> io::Result { } pub fn copy(from: &Path, to: &Path) -> io::Result { - // FIXME: use with_native_path - imp::copy(from, to) + // FIXME: use with_native_path on all platforms + #[cfg(not(windows))] + return imp::copy(from, to); + #[cfg(windows)] + with_native_path(from, &|from| with_native_path(to, &|to| imp::copy(from, to))) } pub fn exists(path: &Path) -> io::Result { - // FIXME: use with_native_path - imp::exists(path) + // FIXME: use with_native_path on all platforms + #[cfg(not(windows))] + return imp::exists(path); + #[cfg(windows)] + with_native_path(path, &imp::exists) } diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 15727c996837b..9215f93756710 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -12,7 +12,7 @@ use crate::sync::Arc; use crate::sys::handle::Handle; use crate::sys::pal::api::{self, WinError, set_file_information_by_handle}; use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul}; -use crate::sys::path::maybe_verbatim; +use crate::sys::path::{WCStr, maybe_verbatim}; use crate::sys::time::SystemTime; use crate::sys::{Align8, c, cvt}; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -298,10 +298,12 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { let path = maybe_verbatim(path)?; + // SAFETY: maybe_verbatim returns null-terminated strings + let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) }; Self::open_native(&path, opts) } - fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result { + fn open_native(path: &WCStr, opts: &OpenOptions) -> io::Result { let creation = opts.get_creation_mode()?; let handle = unsafe { c::CreateFileW( @@ -1212,9 +1214,8 @@ pub fn readdir(p: &Path) -> io::Result { } } -pub fn unlink(p: &Path) -> io::Result<()> { - let p_u16s = maybe_verbatim(p)?; - if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 { +pub fn unlink(path: &WCStr) -> io::Result<()> { + if unsafe { c::DeleteFileW(path.as_ptr()) } == 0 { let err = api::get_last_error(); // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove // the file while ignoring the readonly attribute. @@ -1223,7 +1224,7 @@ pub fn unlink(p: &Path) -> io::Result<()> { let mut opts = OpenOptions::new(); opts.access_mode(c::DELETE); opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT); - if let Ok(f) = File::open_native(&p_u16s, &opts) { + if let Ok(f) = File::open_native(&path, &opts) { if f.posix_delete().is_ok() { return Ok(()); } @@ -1236,10 +1237,7 @@ pub fn unlink(p: &Path) -> io::Result<()> { } } -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let old = maybe_verbatim(old)?; - let new = maybe_verbatim(new)?; - +pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> { if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 { let err = api::get_last_error(); // if `MoveFileExW` fails with ERROR_ACCESS_DENIED then try to move @@ -1253,7 +1251,8 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation` // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size. - let Ok(new_len_without_nul_in_bytes): Result = ((new.len() - 1) * 2).try_into() + let Ok(new_len_without_nul_in_bytes): Result = + ((new.count_bytes() - 1) * 2).try_into() else { return Err(err).io_result(); }; @@ -1282,7 +1281,7 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { new.as_ptr().copy_to_nonoverlapping( (&raw mut (*file_rename_info).FileName).cast::(), - new.len(), + new.count_bytes(), ); } @@ -1309,20 +1308,19 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { Ok(()) } -pub fn rmdir(p: &Path) -> io::Result<()> { - let p = maybe_verbatim(p)?; +pub fn rmdir(p: &WCStr) -> io::Result<()> { cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?; Ok(()) } -pub fn remove_dir_all(path: &Path) -> io::Result<()> { +pub fn remove_dir_all(path: &WCStr) -> io::Result<()> { // Open a file or directory without following symlinks. let mut opts = OpenOptions::new(); opts.access_mode(c::FILE_LIST_DIRECTORY); // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories. // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target. opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); - let file = File::open(path, &opts)?; + let file = File::open_native(path, &opts)?; // Test if the file is not a directory or a symlink to a directory. if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 { @@ -1333,14 +1331,14 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { remove_dir_all_iterative(file).io_result() } -pub fn readlink(path: &Path) -> io::Result { +pub fn readlink(path: &WCStr) -> io::Result { // Open the link with no access mode, instead of generic read. // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so // this is needed for a common case. let mut opts = OpenOptions::new(); opts.access_mode(0); opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); - let file = File::open(path, &opts)?; + let file = File::open_native(&path, &opts)?; file.readlink() } @@ -1378,19 +1376,17 @@ pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> } #[cfg(not(target_vendor = "uwp"))] -pub fn link(original: &Path, link: &Path) -> io::Result<()> { - let original = maybe_verbatim(original)?; - let link = maybe_verbatim(link)?; +pub fn link(original: &WCStr, link: &WCStr) -> io::Result<()> { cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?; Ok(()) } #[cfg(target_vendor = "uwp")] -pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { +pub fn link(_original: &WCStr, _link: &WCStr) -> io::Result<()> { return Err(io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP")); } -pub fn stat(path: &Path) -> io::Result { +pub fn stat(path: &WCStr) -> io::Result { match metadata(path, ReparsePoint::Follow) { Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { if let Ok(attrs) = lstat(path) { @@ -1404,7 +1400,7 @@ pub fn stat(path: &Path) -> io::Result { } } -pub fn lstat(path: &Path) -> io::Result { +pub fn lstat(path: &WCStr) -> io::Result { metadata(path, ReparsePoint::Open) } @@ -1420,7 +1416,7 @@ impl ReparsePoint { } } -fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { +fn metadata(path: &WCStr, reparse: ReparsePoint) -> io::Result { let mut opts = OpenOptions::new(); // No read or write permissions are necessary opts.access_mode(0); @@ -1429,7 +1425,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { // Attempt to open the file normally. // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`. // If the fallback fails for any reason we return the original error. - match File::open(path, &opts) { + match File::open_native(&path, &opts) { Ok(file) => file.file_attr(), Err(e) if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)] @@ -1442,8 +1438,6 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { // However, there are special system files, such as // `C:\hiberfil.sys`, that are locked in a way that denies even that. unsafe { - let path = maybe_verbatim(path)?; - // `FindFirstFileExW` accepts wildcard file names. // Fortunately wildcards are not valid file names and // `ERROR_SHARING_VIOLATION` means the file exists (but is locked) @@ -1482,8 +1476,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result { } } -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - let p = maybe_verbatim(p)?; +pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { unsafe { cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?; Ok(()) @@ -1499,17 +1492,17 @@ fn get_path(f: &File) -> io::Result { ) } -pub fn canonicalize(p: &Path) -> io::Result { +pub fn canonicalize(p: &WCStr) -> io::Result { let mut opts = OpenOptions::new(); // No read or write permissions are necessary opts.access_mode(0); // This flag is so we can open directories too opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); - let f = File::open(p, &opts)?; + let f = File::open_native(p, &opts)?; get_path(&f) } -pub fn copy(from: &Path, to: &Path) -> io::Result { +pub fn copy(from: &WCStr, to: &WCStr) -> io::Result { unsafe extern "system" fn callback( _TotalFileSize: i64, _TotalBytesTransferred: i64, @@ -1528,13 +1521,11 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { c::PROGRESS_CONTINUE } } - let pfrom = maybe_verbatim(from)?; - let pto = maybe_verbatim(to)?; let mut size = 0i64; cvt(unsafe { c::CopyFileExW( - pfrom.as_ptr(), - pto.as_ptr(), + from.as_ptr(), + to.as_ptr(), Some(callback), (&raw mut size) as *mut _, ptr::null_mut(), @@ -1624,14 +1615,14 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { } // Try to see if a file exists but, unlike `exists`, report I/O errors. -pub fn exists(path: &Path) -> io::Result { +pub fn exists(path: &WCStr) -> io::Result { // Open the file to ensure any symlinks are followed to their target. let mut opts = OpenOptions::new(); // No read, write, etc access rights are needed. opts.access_mode(0); // Backup semantics enables opening directories as well as files. opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); - match File::open(path, &opts) { + match File::open_native(path, &opts) { Err(e) => match e.kind() { // The file definitely does not exist io::ErrorKind::NotFound => Ok(false), diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index f8f220fafd1d1..bc4bf11cb7405 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -9,6 +9,7 @@ mod alloc; mod personality; pub mod anonymous_pipe; +pub mod args; pub mod backtrace; pub mod cmath; pub mod exit_guard; diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index bbe1e038dccf5..b35d5d2aa8418 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -1,5 +1,6 @@ use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; +#[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 26211bcb15202..821836824e2bc 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -18,7 +18,6 @@ use crate::os::raw::c_char; -pub mod args; pub mod env; pub mod futex; pub mod os; @@ -58,7 +57,7 @@ pub extern "C" fn __rust_abort() { // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { unsafe { - args::init(argc, argv); + crate::sys::args::init(argc, argv); } } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 52684e18ac276..8a87e7a7ae13d 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -9,7 +9,6 @@ use crate::io::ErrorKind; use crate::sync::atomic::{AtomicBool, Ordering}; pub mod abi; -pub mod args; pub mod env; mod libunwind_integration; pub mod os; @@ -24,7 +23,7 @@ pub mod waitqueue; // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { unsafe { - args::init(argc, argv); + crate::sys::args::init(argc, argv); } } diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 22052a168fd15..c41dc848a1b4a 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -16,8 +16,6 @@ pub mod itron { use super::unsupported; } -#[path = "../unsupported/args.rs"] -pub mod args; pub mod env; // `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as // `crate::sys::error` diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index c1921a2f40df5..b8095cec3e978 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -6,8 +6,6 @@ #![allow(unused_variables)] #![allow(dead_code)] -#[path = "../unsupported/args.rs"] -pub mod args; #[path = "../unsupported/env.rs"] pub mod env; //pub mod fd; diff --git a/library/std/src/sys/pal/trusty/mod.rs b/library/std/src/sys/pal/trusty/mod.rs index 5295d3fdc9145..04e6b4c818687 100644 --- a/library/std/src/sys/pal/trusty/mod.rs +++ b/library/std/src/sys/pal/trusty/mod.rs @@ -1,7 +1,5 @@ //! System bindings for the Trusty OS. -#[path = "../unsupported/args.rs"] -pub mod args; #[path = "../unsupported/common.rs"] #[deny(unsafe_op_in_unsafe_fn)] mod common; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 9760a23084aad..cd901f48b76f8 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -13,7 +13,6 @@ //! [`OsString`]: crate::ffi::OsString #![forbid(unsafe_op_in_unsafe_fn)] -pub mod args; pub mod env; pub mod helpers; pub mod os; diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index d7106c339747b..f8733eb611986 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -6,7 +6,6 @@ use crate::io::ErrorKind; #[macro_use] pub mod weak; -pub mod args; pub mod env; #[cfg(target_os = "fuchsia")] pub mod fuchsia; @@ -27,6 +26,7 @@ pub mod time; pub fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} #[cfg(not(target_os = "espidf"))] +#[cfg_attr(target_os = "vita", allow(unused_variables))] // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. // See `fn init()` in `library/std/src/rt.rs` for docs on `sigpipe`. @@ -47,7 +47,8 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { reset_sigpipe(sigpipe); stack_overflow::init(); - args::init(argc, argv); + #[cfg(not(target_os = "vita"))] + crate::sys::args::init(argc, argv); // Normally, `thread::spawn` will call `Thread::set_name` but since this thread // already exists, we have to call it ourselves. We only do this on Apple targets diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 38838b915b5c1..dea42a95dcc6f 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -1,6 +1,5 @@ #![deny(unsafe_op_in_unsafe_fn)] -pub mod args; pub mod env; pub mod os; pub mod pipe; diff --git a/library/std/src/sys/pal/wasi/args.rs b/library/std/src/sys/pal/wasi/args.rs deleted file mode 100644 index 52cfa202af825..0000000000000 --- a/library/std/src/sys/pal/wasi/args.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::ffi::{CStr, OsStr, OsString}; -use crate::os::wasi::ffi::OsStrExt; -use crate::{fmt, vec}; - -pub struct Args { - iter: vec::IntoIter, -} - -impl !Send for Args {} -impl !Sync for Args {} - -/// Returns the command line arguments -pub fn args() -> Args { - Args { iter: maybe_args().unwrap_or(Vec::new()).into_iter() } -} - -fn maybe_args() -> Option> { - unsafe { - let (argc, buf_size) = wasi::args_sizes_get().ok()?; - let mut argv = Vec::with_capacity(argc); - let mut buf = Vec::with_capacity(buf_size); - wasi::args_get(argv.as_mut_ptr(), buf.as_mut_ptr()).ok()?; - argv.set_len(argc); - let mut ret = Vec::with_capacity(argc); - for ptr in argv { - let s = CStr::from_ptr(ptr.cast()); - ret.push(OsStr::from_bytes(s.to_bytes()).to_owned()); - } - Some(ret) - } -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.iter.as_slice().fmt(f) - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 80853e7b5a26f..4ea42b1082b1d 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -13,7 +13,6 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! -pub mod args; pub mod env; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 504b947d09e2c..6445bf2cc0d2c 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -6,8 +6,6 @@ //! To begin with, this target mirrors the wasi target 1 to 1, but over //! time this will change significantly. -#[path = "../wasi/args.rs"] -pub mod args; #[path = "../wasi/env.rs"] pub mod env; #[allow(unused)] diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 8d39b70d0397a..af370020d96ab 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -16,8 +16,6 @@ #![deny(unsafe_op_in_unsafe_fn)] -#[path = "../unsupported/args.rs"] -pub mod args; pub mod env; #[path = "../unsupported/os.rs"] pub mod os; diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index bdf0cc2c59cf1..3c0a5c2de2636 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -14,7 +14,6 @@ pub mod compat; pub mod api; -pub mod args; pub mod c; pub mod env; #[cfg(not(target_vendor = "win7"))] diff --git a/library/std/src/sys/pal/xous/args.rs b/library/std/src/sys/pal/xous/args.rs deleted file mode 100644 index 00c44ca220a9e..0000000000000 --- a/library/std/src/sys/pal/xous/args.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::ffi::OsString; -use crate::sys::pal::xous::os::get_application_parameters; -use crate::sys::pal::xous::os::params::ArgumentList; -use crate::{fmt, vec}; - -pub struct Args { - parsed_args_list: vec::IntoIter, -} - -pub fn args() -> Args { - let Some(params) = get_application_parameters() else { - return Args { parsed_args_list: vec![].into_iter() }; - }; - - for param in params { - if let Ok(args) = ArgumentList::try_from(¶m) { - let mut parsed_args = vec![]; - for arg in args { - parsed_args.push(arg.into()); - } - return Args { parsed_args_list: parsed_args.into_iter() }; - } - } - Args { parsed_args_list: vec![].into_iter() } -} - -impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.parsed_args_list.as_slice().fmt(f) - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.parsed_args_list.next() - } - fn size_hint(&self) -> (usize, Option) { - self.parsed_args_list.size_hint() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.parsed_args_list.next_back() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.parsed_args_list.len() - } -} diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 58926e2beb1d0..4f652d3f130de 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,6 +1,5 @@ #![forbid(unsafe_op_in_unsafe_fn)] -pub mod args; #[path = "../unsupported/env.rs"] pub mod env; pub mod os; diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 4659dad16e85a..ebd7b03677988 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -8,11 +8,9 @@ //! will likely change over time. #![forbid(unsafe_op_in_unsafe_fn)] -const WORD_SIZE: usize = size_of::(); +pub const WORD_SIZE: usize = size_of::(); pub mod abi; -#[path = "../zkvm/args.rs"] -pub mod args; pub mod env; pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index 6547ed9aa5f32..e0e003f6a8192 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -10,6 +10,40 @@ mod tests; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; +/// A null terminated wide string. +#[repr(transparent)] +pub struct WCStr([u16]); + +impl WCStr { + /// Convert a slice to a WCStr without checks. + /// + /// Though it is memory safe, the slice should also not contain interior nulls + /// as this may lead to unwanted truncation. + /// + /// # Safety + /// + /// The slice must end in a null. + pub unsafe fn from_wchars_with_null_unchecked(s: &[u16]) -> &Self { + unsafe { &*(s as *const [u16] as *const Self) } + } + + pub fn as_ptr(&self) -> *const u16 { + self.0.as_ptr() + } + + pub fn count_bytes(&self) -> usize { + self.0.len() + } +} + +#[inline] +pub fn with_native_path(path: &Path, f: &dyn Fn(&WCStr) -> io::Result) -> io::Result { + let path = maybe_verbatim(path)?; + // SAFETY: maybe_verbatim returns null-terminated strings + let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) }; + f(path) +} + #[inline] pub fn is_sep_byte(b: u8) -> bool { b == b'/' || b == b'\\' diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 7ada3f269a002..acaf026c679bf 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -666,10 +666,11 @@ fn run_test_in_process( io::set_output_capture(None); - let test_result = match result { - Ok(()) => calc_result(&desc, Ok(()), time_opts.as_ref(), exec_time.as_ref()), - Err(e) => calc_result(&desc, Err(e.as_ref()), time_opts.as_ref(), exec_time.as_ref()), - }; + // Determine whether the test passed or failed, by comparing its panic + // payload (if any) with its `ShouldPanic` value, and by checking for + // fatal timeout. + let test_result = + calc_result(&desc, result.err().as_deref(), time_opts.as_ref(), exec_time.as_ref()); let stdout = data.lock().unwrap_or_else(|e| e.into_inner()).to_vec(); let message = CompletedTest::new(id, desc, test_result, exec_time, stdout); monitor_ch.send(message).unwrap(); @@ -741,10 +742,7 @@ fn spawn_test_subprocess( fn run_test_in_spawned_subprocess(desc: TestDesc, runnable_test: RunnableTest) -> ! { let builtin_panic_hook = panic::take_hook(); let record_result = Arc::new(move |panic_info: Option<&'_ PanicHookInfo<'_>>| { - let test_result = match panic_info { - Some(info) => calc_result(&desc, Err(info.payload()), None, None), - None => calc_result(&desc, Ok(()), None, None), - }; + let test_result = calc_result(&desc, panic_info.map(|info| info.payload()), None, None); // We don't support serializing TrFailedMsg, so just // print the message out to stderr. diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 73dcc2e2a0cca..959cd730fa432 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -39,15 +39,18 @@ pub enum TestResult { /// Creates a `TestResult` depending on the raw result of test execution /// and associated data. -pub(crate) fn calc_result<'a>( +pub(crate) fn calc_result( desc: &TestDesc, - task_result: Result<(), &'a (dyn Any + 'static + Send)>, + panic_payload: Option<&(dyn Any + Send)>, time_opts: Option<&time::TestTimeOptions>, exec_time: Option<&time::TestExecTime>, ) -> TestResult { - let result = match (&desc.should_panic, task_result) { - (&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TestResult::TrOk, - (&ShouldPanic::YesWithMessage(msg), Err(err)) => { + let result = match (desc.should_panic, panic_payload) { + // The test did or didn't panic, as expected. + (ShouldPanic::No, None) | (ShouldPanic::Yes, Some(_)) => TestResult::TrOk, + + // Check the actual panic message against the expected message. + (ShouldPanic::YesWithMessage(msg), Some(err)) => { let maybe_panic_str = err .downcast_ref::() .map(|e| &**e) @@ -71,10 +74,14 @@ pub(crate) fn calc_result<'a>( )) } } - (&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => { + + // The test should have panicked, but didn't panic. + (ShouldPanic::Yes, None) | (ShouldPanic::YesWithMessage(_), None) => { TestResult::TrFailedMsg("test did not panic as expected".to_string()) } - _ => TestResult::TrFailed, + + // The test should not have panicked, but did panic. + (ShouldPanic::No, Some(_)) => TestResult::TrFailed, }; // If test is already failed (or allowed to fail), do not change the result. diff --git a/src/ci/citool/Cargo.lock b/src/ci/citool/Cargo.lock index 800eaae076650..2fe219f368b9c 100644 --- a/src/ci/citool/Cargo.lock +++ b/src/ci/citool/Cargo.lock @@ -563,9 +563,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 4ec2909b8fa89..0aad8be982ff4 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -101,6 +101,7 @@ for more details. | `normalize-stdout` | Normalize actual stdout with a rule `"" -> ""` before comparing against snapshot | `ui`, `incremental` | `"" -> ""`, ``/`` is regex capture and replace syntax | | `dont-check-compiler-stderr` | Don't check actual compiler stderr vs stderr snapshot | `ui` | N/A | | `dont-check-compiler-stdout` | Don't check actual compiler stdout vs stdout snapshot | `ui` | N/A | +| `dont-require-annotations` | Don't require line annotations for the given diagnostic kind (`//~ KIND`) to be exhaustive | `ui`, `incremental` | `ERROR`, `WARN`, `NOTE`, `HELP`, `SUGGESTION` | | `run-rustfix` | Apply all suggestions via `rustfix`, snapshot fixed output, and check fixed output builds | `ui` | N/A | | `rustfix-only-machine-applicable` | `run-rustfix` but only machine-applicable suggestions | `ui` | N/A | | `exec-env` | Env var to set when executing a test | `ui`, `crashes` | `=` | diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index e862a07cae0a2..3243a3535ac81 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -303,8 +303,7 @@ It should be preferred to using `error-pattern`, which is imprecise and non-exha ### `error-pattern` The `error-pattern` [directive](directives.md) can be used for runtime messages, which don't -have a specific span, or for compile time messages if imprecise matching is required due to -multi-line platform specific diagnostics. +have a specific span, or in exceptional cases for compile time messages. Let's think about this test: @@ -318,7 +317,7 @@ fn main() { ``` We want to ensure this shows "index out of bounds" but we cannot use the `ERROR` -annotation since the error doesn't have any span. Then it's time to use the +annotation since the runtime error doesn't have any span. Then it's time to use the `error-pattern` directive: ```rust,ignore @@ -331,29 +330,51 @@ fn main() { } ``` -But for strict testing, try to use the `ERROR` annotation as much as possible, -including `//~?` annotations for diagnostics without span. -For compile time diagnostics `error-pattern` should very rarely be necessary. +Use of `error-pattern` is not recommended in general. -Per-line annotations (`//~`) are still checked in tests using `error-pattern`. -To opt out of these checks, use `//@ compile-flags: --error-format=human`. -Do that only in exceptional cases. +For strict testing of compile time output, try to use the line annotations `//~` as much as +possible, including `//~?` annotations for diagnostics without span. -### Error levels +If the compile time output is target dependent or too verbose, use directive +`//@ dont-require-annotations: ` to make the line annotation checking +non-exhaustive, some of the compiler messages can stay uncovered by annotations in this mode. -The error levels that you can have are: +For checking runtime output `//@ check-run-results` may be preferable. + +Only use `error-pattern` if none of the above works. + +Line annotations `//~` are still checked in tests using `error-pattern`. +In exceptional cases use `//@ compile-flags: --error-format=human` to opt out of these checks. + +### Diagnostic kinds (error levels) + +The diagnostic kinds that you can have are: - `ERROR` -- `WARN` or `WARNING` +- `WARN` (or `WARNING`) - `NOTE` -- `HELP` and `SUGGESTION` - -You are allowed to not include a level, but you should include it at least for -the primary message. +- `HELP` +- `SUGGESTION` -The `SUGGESTION` level is used for specifying what the expected replacement text +The `SUGGESTION` kind is used for specifying what the expected replacement text should be for a diagnostic suggestion. +`ERROR` and `WARN` kinds are required to be exhaustively covered by line annotations +`//~` by default. + +Other kinds only need to be line-annotated if at least one annotation of that kind appears +in the test file. For example, one `//~ NOTE` will also require all other `//~ NOTE`s in the file +to be written out explicitly. + +Use directive `//@ dont-require-annotations` to opt out of exhaustive annotations. +E.g. use `//@ dont-require-annotations: NOTE` to annotate notes selectively. +Avoid using this directive for `ERROR`s and `WARN`ings, unless there's a serious reason, like +target-dependent compiler output. + +Missing diagnostic kinds (`//~ message`) are currently accepted, but are being phased away. +They will match any compiler output kind, but will not force exhaustive annotations for that kind. +Prefer explicit kind and `//@ dont-require-annotations` to achieve the same effect. + UI tests use the `-A unused` flag by default to ignore all unused warnings, as unused warnings are usually not the focus of a test. However, simple code samples often have unused warnings. If the test is specifically testing an diff --git a/src/stage0 b/src/stage0 index b3841d253f347..6e86501a72ab4 100644 --- a/src/stage0 +++ b/src/stage0 @@ -476,4 +476,4 @@ dist/2025-04-02/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=e67a33440c3e021ff2 dist/2025-04-02/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=0ea7e17d7bb67d6a6c4b2f864aaffcd96512f15f17f0acc63751eb1df6c486a7 dist/2025-04-02/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=b73d37b704ab58921172cc561f5598db6a504dcd4d7980966f7c26caaf6d3594 dist/2025-04-02/rustc-nightly-x86_64-unknown-netbsd.tar.gz=986f6c594d37bcbd3833e053640ba8775f68d26a65c5618386654ef55d7b3542 -dist/2025-04-02/rustc-nightly-x86_64-unknown-netbsd.tar.xz=c0d9a88c30d2ab38ec3a11fabb5515ed9bc3ac1a8e35a438d68bf7ff82f6b843 \ No newline at end of file +dist/2025-04-02/rustc-nightly-x86_64-unknown-netbsd.tar.xz=c0d9a88c30d2ab38ec3a11fabb5515ed9bc3ac1a8e35a438d68bf7ff82f6b843 diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs index f51072718a349..d679084ae44bc 100644 --- a/src/tools/bump-stage0/src/main.rs +++ b/src/tools/bump-stage0/src/main.rs @@ -65,32 +65,33 @@ impl Tool { nightly_branch, } = &self.config; - file_content.push_str(&format!("dist_server={}", dist_server)); - file_content.push_str(&format!("\nartifacts_server={}", artifacts_server)); + file_content.push_str(&format!("dist_server={}\n", dist_server)); + file_content.push_str(&format!("artifacts_server={}\n", artifacts_server)); file_content.push_str(&format!( - "\nartifacts_with_llvm_assertions_server={}", + "artifacts_with_llvm_assertions_server={}\n", artifacts_with_llvm_assertions_server )); - file_content.push_str(&format!("\ngit_merge_commit_email={}", git_merge_commit_email)); - file_content.push_str(&format!("\ngit_repository={}", git_repository)); - file_content.push_str(&format!("\nnightly_branch={}", nightly_branch)); + file_content.push_str(&format!("git_merge_commit_email={}\n", git_merge_commit_email)); + file_content.push_str(&format!("git_repository={}\n", git_repository)); + file_content.push_str(&format!("nightly_branch={}\n", nightly_branch)); - file_content.push_str("\n\n"); + file_content.push_str("\n"); file_content.push_str(COMMENTS); + file_content.push_str("\n"); let compiler = self.detect_compiler()?; - file_content.push_str(&format!("\ncompiler_date={}", compiler.date)); - file_content.push_str(&format!("\ncompiler_version={}", compiler.version)); + file_content.push_str(&format!("compiler_date={}\n", compiler.date)); + file_content.push_str(&format!("compiler_version={}\n", compiler.version)); if let Some(rustfmt) = self.detect_rustfmt()? { - file_content.push_str(&format!("\nrustfmt_date={}", rustfmt.date)); - file_content.push_str(&format!("\nrustfmt_version={}", rustfmt.version)); + file_content.push_str(&format!("rustfmt_date={}\n", rustfmt.date)); + file_content.push_str(&format!("rustfmt_version={}\n", rustfmt.version)); } file_content.push_str("\n"); for (key, value) in self.checksums { - file_content.push_str(&format!("\n{}={}", key, value)); + file_content.push_str(&format!("{}={}\n", key, value)); } std::fs::write(PATH, file_content)?; diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 3db34ed24cc20..ba1b8f256586e 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -10,6 +10,7 @@ doctest = false # tidy-alphabetical-start anstyle-svg = "0.1.3" build_helper = { path = "../../build_helper" } +camino = "1" colored = "2" diff = "0.1.10" getopts = "0.2" diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 6750b5288f42a..604c5fcbddff0 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -1,18 +1,17 @@ use std::collections::{BTreeSet, HashMap, HashSet}; -use std::ffi::OsString; -use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; use std::sync::OnceLock; use std::{fmt, iter}; use build_helper::git::GitConfig; +use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; use serde::de::{Deserialize, Deserializer, Error as _}; pub use self::Mode::*; use crate::executor::{ColorConfig, OutputFormat}; -use crate::util::{PathBufExt, add_dylib_path}; +use crate::util::{Utf8PathBufExt, add_dylib_path}; macro_rules! string_enum { ($(#[$meta:meta])* $vis:vis enum $name:ident { $($variant:ident => $repr:expr,)* }) => { @@ -183,25 +182,25 @@ pub struct Config { pub fail_fast: bool, /// The library paths required for running the compiler. - pub compile_lib_path: PathBuf, + pub compile_lib_path: Utf8PathBuf, /// The library paths required for running compiled programs. - pub run_lib_path: PathBuf, + pub run_lib_path: Utf8PathBuf, /// The rustc executable. - pub rustc_path: PathBuf, + pub rustc_path: Utf8PathBuf, /// The cargo executable. - pub cargo_path: Option, + pub cargo_path: Option, /// Rustc executable used to compile run-make recipes. - pub stage0_rustc_path: Option, + pub stage0_rustc_path: Option, /// The rustdoc executable. - pub rustdoc_path: Option, + pub rustdoc_path: Option, /// The coverage-dump executable. - pub coverage_dump_path: Option, + pub coverage_dump_path: Option, /// The Python executable to use for LLDB and htmldocck. pub python: String, @@ -213,27 +212,27 @@ pub struct Config { pub jsondoclint_path: Option, /// The LLVM `FileCheck` binary path. - pub llvm_filecheck: Option, + pub llvm_filecheck: Option, /// Path to LLVM's bin directory. - pub llvm_bin_dir: Option, + pub llvm_bin_dir: Option, /// The path to the Clang executable to run Clang-based tests with. If /// `None` then these tests will be ignored. pub run_clang_based_tests_with: Option, /// The directory containing the sources. - pub src_root: PathBuf, + pub src_root: Utf8PathBuf, /// The directory containing the test suite sources. Must be a subdirectory of `src_root`. - pub src_test_suite_root: PathBuf, + pub src_test_suite_root: Utf8PathBuf, /// Root build directory (e.g. `build/`). - pub build_root: PathBuf, + pub build_root: Utf8PathBuf, /// Test suite specific build directory (e.g. `build/host/test/ui/`). - pub build_test_suite_root: PathBuf, + pub build_test_suite_root: Utf8PathBuf, /// The directory containing the compiler sysroot - pub sysroot_base: PathBuf, + pub sysroot_base: Utf8PathBuf, /// The number of the stage under test. pub stage: u32, @@ -301,7 +300,7 @@ pub struct Config { pub host: String, /// Path to / name of the Microsoft Console Debugger (CDB) executable - pub cdb: Option, + pub cdb: Option, /// Version of CDB pub cdb_version: Option<[u16; 4]>, @@ -322,7 +321,7 @@ pub struct Config { pub system_llvm: bool, /// Path to the android tools - pub android_cross_path: PathBuf, + pub android_cross_path: Utf8PathBuf, /// Extra parameter to run adb on arm-linux-androideabi pub adb_path: String, @@ -346,7 +345,7 @@ pub struct Config { pub color: ColorConfig, /// where to find the remote test client process, if we're using it - pub remote_test_client: Option, + pub remote_test_client: Option, /// mode describing what file the actual ui output will be compared to pub compare_mode: Option, @@ -414,7 +413,7 @@ pub struct Config { /// Path to minicore aux library, used for `no_core` tests that need `core` stubs in /// cross-compilation scenarios that do not otherwise want/need to `-Zbuild-std`. Used in e.g. /// ABI tests. - pub minicore_path: PathBuf, + pub minicore_path: Utf8PathBuf, } impl Config { @@ -804,8 +803,8 @@ fn serde_parse_u32<'de, D: Deserializer<'de>>(deserializer: D) -> Result, compare_mode: &Option, kind: &str, -) -> PathBuf { +) -> Utf8PathBuf { assert!(UI_EXTENSIONS.contains(&kind)); let mut parts = Vec::new(); @@ -865,7 +864,7 @@ pub const UI_COVERAGE_MAP: &str = "cov-map"; /// ``` /// /// This is created early when tests are collected to avoid race conditions. -pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf { +pub fn output_relative_path(config: &Config, relative_dir: &Utf8Path) -> Utf8PathBuf { config.build_test_suite_root.join(relative_dir) } @@ -874,10 +873,10 @@ pub fn output_testname_unique( config: &Config, testpaths: &TestPaths, revision: Option<&str>, -) -> PathBuf { +) -> Utf8PathBuf { let mode = config.compare_mode.as_ref().map_or("", |m| m.to_str()); let debugger = config.debugger.as_ref().map_or("", |m| m.to_str()); - PathBuf::from(&testpaths.file.file_stem().unwrap()) + Utf8PathBuf::from(&testpaths.file.file_stem().unwrap()) .with_extra_extension(config.mode.output_dir_disambiguator()) .with_extra_extension(revision.unwrap_or("")) .with_extra_extension(mode) @@ -887,7 +886,11 @@ pub fn output_testname_unique( /// Absolute path to the directory where all output for the given /// test/revision should reside. Example: /// /path/to/build/host-tuple/test/ui/relative/testname.revision.mode/ -pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { +pub fn output_base_dir( + config: &Config, + testpaths: &TestPaths, + revision: Option<&str>, +) -> Utf8PathBuf { output_relative_path(config, &testpaths.relative_dir) .join(output_testname_unique(config, testpaths, revision)) } @@ -895,12 +898,20 @@ pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option< /// Absolute path to the base filename used as output for the given /// test/revision. Example: /// /path/to/build/host-tuple/test/ui/relative/testname.revision.mode/testname -pub fn output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { +pub fn output_base_name( + config: &Config, + testpaths: &TestPaths, + revision: Option<&str>, +) -> Utf8PathBuf { output_base_dir(config, testpaths, revision).join(testpaths.file.file_stem().unwrap()) } /// Absolute path to the directory to use for incremental compilation. Example: /// /path/to/build/host-tuple/test/ui/relative/testname.mode/testname.inc -pub fn incremental_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { +pub fn incremental_dir( + config: &Config, + testpaths: &TestPaths, + revision: Option<&str>, +) -> Utf8PathBuf { output_base_name(config, testpaths, revision).with_extension("inc") } diff --git a/src/tools/compiletest/src/compute_diff.rs b/src/tools/compiletest/src/compute_diff.rs index 4c942c51bae13..509e7e117039c 100644 --- a/src/tools/compiletest/src/compute_diff.rs +++ b/src/tools/compiletest/src/compute_diff.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use std::fs::{File, FileType}; -use std::path::Path; + +use camino::Utf8Path; #[derive(Debug, PartialEq)] pub enum DiffLine { @@ -112,8 +113,8 @@ pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> S /// Returns whether any data was actually written. pub(crate) fn write_filtered_diff( diff_filename: &str, - out_dir: &Path, - compare_dir: &Path, + out_dir: &Utf8Path, + compare_dir: &Utf8Path, verbose: bool, filter: Filter, ) -> bool @@ -123,19 +124,21 @@ where use std::io::{Read, Write}; let mut diff_output = File::create(diff_filename).unwrap(); let mut wrote_data = false; - for entry in walkdir::WalkDir::new(out_dir) { + for entry in walkdir::WalkDir::new(out_dir.as_std_path()) { let entry = entry.expect("failed to read file"); let extension = entry.path().extension().and_then(|p| p.to_str()); if filter(entry.file_type(), extension) { - let expected_path = compare_dir.join(entry.path().strip_prefix(&out_dir).unwrap()); + let expected_path = compare_dir + .as_std_path() + .join(entry.path().strip_prefix(&out_dir.as_std_path()).unwrap()); let expected = if let Ok(s) = std::fs::read(&expected_path) { s } else { continue }; let actual_path = entry.path(); let actual = std::fs::read(&actual_path).unwrap(); let diff = unified_diff::diff( &expected, - &expected_path.to_string_lossy(), + &expected_path.to_str().unwrap(), &actual, - &actual_path.to_string_lossy(), + &actual_path.to_str().unwrap(), 3, ); wrote_data |= !diff.is_empty(); diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs index 5126e55aea123..c133d7fd4fbd0 100644 --- a/src/tools/compiletest/src/debuggers.rs +++ b/src/tools/compiletest/src/debuggers.rs @@ -1,9 +1,9 @@ use std::env; -use std::ffi::OsString; -use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::Arc; +use camino::{Utf8Path, Utf8PathBuf}; + use crate::common::{Config, Debugger}; pub(crate) fn configure_cdb(config: &Config) -> Option> { @@ -78,12 +78,15 @@ fn is_pc_windows_msvc_target(target: &str) -> bool { target.ends_with("-pc-windows-msvc") } -fn find_cdb(target: &str) -> Option { +fn find_cdb(target: &str) -> Option { if !(cfg!(windows) && is_pc_windows_msvc_target(target)) { return None; } - let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?; + let pf86 = Utf8PathBuf::from_path_buf( + env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?.into(), + ) + .unwrap(); let cdb_arch = if cfg!(target_arch = "x86") { "x86" } else if cfg!(target_arch = "x86_64") { @@ -96,8 +99,7 @@ fn find_cdb(target: &str) -> Option { return None; // No compatible CDB.exe in the Windows 10 SDK }; - let mut path = PathBuf::new(); - path.push(pf86); + let mut path = pf86; path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too? path.push(cdb_arch); path.push(r"cdb.exe"); @@ -106,15 +108,15 @@ fn find_cdb(target: &str) -> Option { return None; } - Some(path.into_os_string()) + Some(path) } /// Returns Path to CDB pub(crate) fn analyze_cdb( cdb: Option, target: &str, -) -> (Option, Option<[u16; 4]>) { - let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target)); +) -> (Option, Option<[u16; 4]>) { + let cdb = cdb.map(Utf8PathBuf::from).or_else(|| find_cdb(target)); let mut version = None; if let Some(cdb) = cdb.as_ref() { @@ -143,7 +145,7 @@ pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> { pub(crate) fn analyze_gdb( gdb: Option, target: &str, - android_cross_path: &Path, + android_cross_path: &Utf8Path, ) -> (Option, Option) { #[cfg(not(windows))] const GDB_FALLBACK: &str = "gdb"; @@ -152,10 +154,7 @@ pub(crate) fn analyze_gdb( let fallback_gdb = || { if is_android_gdb_target(target) { - let mut gdb_path = match android_cross_path.to_str() { - Some(x) => x.to_owned(), - None => panic!("cannot find android cross path"), - }; + let mut gdb_path = android_cross_path.to_string(); gdb_path.push_str("/bin/gdb"); gdb_path } else { diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs index 64d68eb7f23e5..3bb98276bf525 100644 --- a/src/tools/compiletest/src/errors.rs +++ b/src/tools/compiletest/src/errors.rs @@ -2,9 +2,9 @@ use std::fmt; use std::fs::File; use std::io::BufReader; use std::io::prelude::*; -use std::path::Path; use std::sync::OnceLock; +use camino::Utf8Path; use regex::Regex; use tracing::*; @@ -102,8 +102,8 @@ impl Error { /// /// If revision is not None, then we look /// for `//[X]~` instead, where `X` is the current revision. -pub fn load_errors(testfile: &Path, revision: Option<&str>) -> Vec { - let rdr = BufReader::new(File::open(testfile).unwrap()); +pub fn load_errors(testfile: &Utf8Path, revision: Option<&str>) -> Vec { + let rdr = BufReader::new(File::open(testfile.as_std_path()).unwrap()); // `last_nonfollow_error` tracks the most recently seen // line with an error template that did not use the diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 3406e8749a151..118307fe8b42e 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -1,11 +1,11 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use std::env; use std::fs::File; use std::io::BufReader; use std::io::prelude::*; -use std::path::{Path, PathBuf}; use std::process::Command; +use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; use tracing::*; @@ -45,12 +45,12 @@ pub struct EarlyProps { } impl EarlyProps { - pub fn from_file(config: &Config, testfile: &Path) -> Self { - let file = File::open(testfile).expect("open test file to parse earlyprops"); + pub fn from_file(config: &Config, testfile: &Utf8Path) -> Self { + let file = File::open(testfile.as_std_path()).expect("open test file to parse earlyprops"); Self::from_reader(config, testfile, file) } - pub fn from_reader(config: &Config, testfile: &Path, rdr: R) -> Self { + pub fn from_reader(config: &Config, testfile: &Utf8Path, rdr: R) -> Self { let mut props = EarlyProps::default(); let mut poisoned = false; iter_header( @@ -66,7 +66,7 @@ impl EarlyProps { ); if poisoned { - eprintln!("errors encountered during EarlyProps parsing: {}", testfile.display()); + eprintln!("errors encountered during EarlyProps parsing: {}", testfile); panic!("errors encountered during EarlyProps parsing"); } @@ -88,7 +88,7 @@ pub struct TestProps { pub doc_flags: Vec, // If present, the name of a file that this test should match when // pretty-printed - pub pp_exact: Option, + pub pp_exact: Option, /// Auxiliary crates that should be built and made available to this test. pub(crate) aux: AuxProps, // Environment settings to use for compiling @@ -134,7 +134,7 @@ pub struct TestProps { // not set by end-users; rather it is set by the incremental // testing harness and used when generating compilation // arguments. (In particular, it propagates to the aux-builds.) - pub incremental_dir: Option, + pub incremental_dir: Option, // If `true`, this test will use incremental compilation. // // This can be set manually with the `incremental` header, or implicitly @@ -198,7 +198,7 @@ pub struct TestProps { /// that don't otherwise want/need `-Z build-std`. pub add_core_stubs: bool, /// Whether line annotatins are required for the given error kind. - pub require_annotations: HashMap, + pub dont_require_annotations: HashSet, } mod directives { @@ -301,17 +301,16 @@ impl TestProps { no_auto_check_cfg: false, has_enzyme: false, add_core_stubs: false, - require_annotations: HashMap::from([ - (ErrorKind::Help, true), - (ErrorKind::Note, true), - (ErrorKind::Error, true), - (ErrorKind::Warning, true), - (ErrorKind::Suggestion, false), - ]), + dont_require_annotations: Default::default(), } } - pub fn from_aux_file(&self, testfile: &Path, revision: Option<&str>, config: &Config) -> Self { + pub fn from_aux_file( + &self, + testfile: &Utf8Path, + revision: Option<&str>, + config: &Config, + ) -> Self { let mut props = TestProps::new(); // copy over select properties to the aux build: @@ -322,10 +321,10 @@ impl TestProps { props } - pub fn from_file(testfile: &Path, revision: Option<&str>, config: &Config) -> Self { + pub fn from_file(testfile: &Utf8Path, revision: Option<&str>, config: &Config) -> Self { let mut props = TestProps::new(); props.load_from(testfile, revision, config); - props.exec_env.push(("RUSTC".to_string(), config.rustc_path.display().to_string())); + props.exec_env.push(("RUSTC".to_string(), config.rustc_path.to_string())); match (props.pass_mode, props.fail_mode) { (None, None) if config.mode == Mode::Ui => props.fail_mode = Some(FailMode::Check), @@ -340,10 +339,10 @@ impl TestProps { /// tied to a particular revision `foo` (indicated by writing /// `//@[foo]`), then the property is ignored unless `test_revision` is /// `Some("foo")`. - fn load_from(&mut self, testfile: &Path, test_revision: Option<&str>, config: &Config) { + fn load_from(&mut self, testfile: &Utf8Path, test_revision: Option<&str>, config: &Config) { let mut has_edition = false; if !testfile.is_dir() { - let file = File::open(testfile).unwrap(); + let file = File::open(testfile.as_std_path()).unwrap(); let mut poisoned = false; @@ -593,14 +592,14 @@ impl TestProps { if let Some(err_kind) = config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS) { - self.require_annotations - .insert(ErrorKind::expect_from_user_str(&err_kind), false); + self.dont_require_annotations + .insert(ErrorKind::expect_from_user_str(err_kind.trim())); } }, ); if poisoned { - eprintln!("errors encountered during TestProps parsing: {}", testfile.display()); + eprintln!("errors encountered during TestProps parsing: {}", testfile); panic!("errors encountered during TestProps parsing"); } } @@ -871,7 +870,7 @@ fn iter_header( mode: Mode, _suite: &str, poisoned: &mut bool, - testfile: &Path, + testfile: &Utf8Path, rdr: impl Read, it: &mut dyn FnMut(DirectiveLine<'_>), ) { @@ -923,9 +922,7 @@ fn iter_header( eprintln!( "error: detected unknown compiletest test directive `{}` in {}:{}", - directive_line.raw_directive, - testfile.display(), - line_number, + directive_line.raw_directive, testfile, line_number, ); return; @@ -937,10 +934,7 @@ fn iter_header( eprintln!( "error: detected trailing compiletest test directive `{}` in {}:{}\n \ help: put the trailing directive in it's own line: `//@ {}`", - trailing_directive, - testfile.display(), - line_number, - trailing_directive, + trailing_directive, testfile, line_number, trailing_directive, ); return; @@ -952,7 +946,12 @@ fn iter_header( } impl Config { - fn parse_and_update_revisions(&self, testfile: &Path, line: &str, existing: &mut Vec) { + fn parse_and_update_revisions( + &self, + testfile: &Utf8Path, + line: &str, + existing: &mut Vec, + ) { const FORBIDDEN_REVISION_NAMES: [&str; 2] = [ // `//@ revisions: true false` Implying `--cfg=true` and `--cfg=false` makes it very // weird for the test, since if the test writer wants a cfg of the same revision name @@ -965,26 +964,19 @@ impl Config { if let Some(raw) = self.parse_name_value_directive(line, "revisions") { if self.mode == Mode::RunMake { - panic!("`run-make` tests do not support revisions: {}", testfile.display()); + panic!("`run-make` tests do not support revisions: {}", testfile); } let mut duplicates: HashSet<_> = existing.iter().cloned().collect(); for revision in raw.split_whitespace() { if !duplicates.insert(revision.to_string()) { - panic!( - "duplicate revision: `{}` in line `{}`: {}", - revision, - raw, - testfile.display() - ); + panic!("duplicate revision: `{}` in line `{}`: {}", revision, raw, testfile); } if FORBIDDEN_REVISION_NAMES.contains(&revision) { panic!( "revision name `{revision}` is not permitted: `{}` in line `{}`: {}", - revision, - raw, - testfile.display() + revision, raw, testfile ); } @@ -995,8 +987,7 @@ impl Config { "revision name `{revision}` is not permitted in a test suite that uses \ `FileCheck` annotations as it is confusing when used as custom `FileCheck` \ prefix: `{revision}` in line `{}`: {}", - raw, - testfile.display() + raw, testfile ); } @@ -1016,11 +1007,11 @@ impl Config { (name.to_owned(), value.to_owned()) } - fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option { + fn parse_pp_exact(&self, line: &str, testfile: &Utf8Path) -> Option { if let Some(s) = self.parse_name_value_directive(line, "pp-exact") { - Some(PathBuf::from(&s)) + Some(Utf8PathBuf::from(&s)) } else if self.parse_name_directive(line, "pp-exact") { - testfile.file_name().map(PathBuf::from) + testfile.file_name().map(Utf8PathBuf::from) } else { None } @@ -1126,20 +1117,19 @@ fn expand_variables(mut value: String, config: &Config) -> String { if value.contains(CWD) { let cwd = env::current_dir().unwrap(); - value = value.replace(CWD, &cwd.to_string_lossy()); + value = value.replace(CWD, &cwd.to_str().unwrap()); } if value.contains(SRC_BASE) { - value = value.replace(SRC_BASE, &config.src_test_suite_root.to_str().unwrap()); + value = value.replace(SRC_BASE, &config.src_test_suite_root.as_str()); } if value.contains(TEST_SUITE_BUILD_BASE) { - value = - value.replace(TEST_SUITE_BUILD_BASE, &config.build_test_suite_root.to_str().unwrap()); + value = value.replace(TEST_SUITE_BUILD_BASE, &config.build_test_suite_root.as_str()); } if value.contains(SYSROOT_BASE) { - value = value.replace(SYSROOT_BASE, &config.sysroot_base.to_str().unwrap()); + value = value.replace(SYSROOT_BASE, &config.sysroot_base.as_str()); } if value.contains(TARGET_LINKER) { @@ -1152,9 +1142,9 @@ fn expand_variables(mut value: String, config: &Config) -> String { if value.contains(RUST_SRC_BASE) { let src_base = config.sysroot_base.join("lib/rustlib/src/rust"); - src_base.try_exists().expect(&*format!("{} should exists", src_base.display())); - let src_base = src_base.read_link().unwrap_or(src_base); - value = value.replace(RUST_SRC_BASE, &src_base.to_string_lossy()); + src_base.try_exists().expect(&*format!("{} should exists", src_base)); + let src_base = src_base.read_link_utf8().unwrap_or(src_base); + value = value.replace(RUST_SRC_BASE, &src_base.as_str()); } value @@ -1257,14 +1247,14 @@ pub fn llvm_has_libzstd(config: &Config) -> bool { // contains a path to that static lib, and that it exists. // // See compiler/rustc_llvm/build.rs for more details and similar expectations. - fn is_zstd_in_config(llvm_bin_dir: &Path) -> Option<()> { + fn is_zstd_in_config(llvm_bin_dir: &Utf8Path) -> Option<()> { let llvm_config_path = llvm_bin_dir.join("llvm-config"); let output = Command::new(llvm_config_path).arg("--system-libs").output().ok()?; assert!(output.status.success(), "running llvm-config --system-libs failed"); let libs = String::from_utf8(output.stdout).ok()?; for lib in libs.split_whitespace() { - if lib.ends_with("libzstd.a") && Path::new(lib).exists() { + if lib.ends_with("libzstd.a") && Utf8Path::new(lib).exists() { return Some(()); } } @@ -1282,7 +1272,7 @@ pub fn llvm_has_libzstd(config: &Config) -> bool { // `lld` supports it. If not, an error will be emitted: "LLVM was not built with // LLVM_ENABLE_ZSTD or did not find zstd at build time". #[cfg(unix)] - fn is_lld_built_with_zstd(llvm_bin_dir: &Path) -> Option<()> { + fn is_lld_built_with_zstd(llvm_bin_dir: &Utf8Path) -> Option<()> { let lld_path = llvm_bin_dir.join("lld"); if lld_path.exists() { // We can't call `lld` as-is, it expects to be invoked by a compiler driver using a @@ -1385,7 +1375,7 @@ pub(crate) fn make_test_description( config: &Config, cache: &HeadersCache, name: String, - path: &Path, + path: &Utf8Path, src: R, test_revision: Option<&str>, poisoned: &mut bool, @@ -1416,7 +1406,7 @@ pub(crate) fn make_test_description( ignore_message = Some(reason.into()); } IgnoreDecision::Error { message } => { - eprintln!("error: {}:{line_number}: {message}", path.display()); + eprintln!("error: {}:{line_number}: {message}", path); *poisoned = true; return; } @@ -1446,7 +1436,7 @@ pub(crate) fn make_test_description( ); if local_poisoned { - eprintln!("errors encountered when trying to make test description: {}", path.display()); + eprintln!("errors encountered when trying to make test description: {}", path); panic!("errors encountered when trying to make test description"); } @@ -1555,7 +1545,7 @@ fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision { IgnoreDecision::Continue } -fn ignore_llvm(config: &Config, path: &Path, line: &str) -> IgnoreDecision { +fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { if let Some(needed_components) = config.parse_name_value_directive(line, "needs-llvm-components") { @@ -1567,8 +1557,7 @@ fn ignore_llvm(config: &Config, path: &Path, line: &str) -> IgnoreDecision { if env::var_os("COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS").is_some() { panic!( "missing LLVM component {}, and COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS is set: {}", - missing_component, - path.display() + missing_component, path ); } return IgnoreDecision::Ignore { diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index f3461f3c244f3..3a8c3748de99f 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -1,6 +1,6 @@ use std::io::Read; -use std::path::Path; +use camino::Utf8Path; use semver::Version; use super::{ @@ -13,7 +13,7 @@ use crate::executor::{CollectedTestDesc, ShouldPanic}; fn make_test_description( config: &Config, name: String, - path: &Path, + path: &Utf8Path, src: R, revision: Option<&str>, ) -> CollectedTestDesc { @@ -230,12 +230,12 @@ fn cfg() -> ConfigBuilder { fn parse_rs(config: &Config, contents: &str) -> EarlyProps { let bytes = contents.as_bytes(); - EarlyProps::from_reader(config, Path::new("a.rs"), bytes) + EarlyProps::from_reader(config, Utf8Path::new("a.rs"), bytes) } fn check_ignore(config: &Config, contents: &str) -> bool { let tn = String::new(); - let p = Path::new("a.rs"); + let p = Utf8Path::new("a.rs"); let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None); d.ignore } @@ -244,7 +244,7 @@ fn check_ignore(config: &Config, contents: &str) -> bool { fn should_fail() { let config: Config = cfg().build(); let tn = String::new(); - let p = Path::new("a.rs"); + let p = Utf8Path::new("a.rs"); let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None); assert_eq!(d.should_panic, ShouldPanic::No); @@ -784,7 +784,7 @@ fn threads_support() { } } -fn run_path(poisoned: &mut bool, path: &Path, buf: &[u8]) { +fn run_path(poisoned: &mut bool, path: &Utf8Path, buf: &[u8]) { let rdr = std::io::Cursor::new(&buf); iter_header(Mode::Ui, "ui", poisoned, path, rdr, &mut |_| {}); } @@ -794,7 +794,7 @@ fn test_unknown_directive_check() { let mut poisoned = false; run_path( &mut poisoned, - Path::new("a.rs"), + Utf8Path::new("a.rs"), include_bytes!("./test-auxillary/unknown_directive.rs"), ); assert!(poisoned); @@ -805,7 +805,7 @@ fn test_known_directive_check_no_error() { let mut poisoned = false; run_path( &mut poisoned, - Path::new("a.rs"), + Utf8Path::new("a.rs"), include_bytes!("./test-auxillary/known_directive.rs"), ); assert!(!poisoned); @@ -816,7 +816,7 @@ fn test_error_annotation_no_error() { let mut poisoned = false; run_path( &mut poisoned, - Path::new("a.rs"), + Utf8Path::new("a.rs"), include_bytes!("./test-auxillary/error_annotation.rs"), ); assert!(!poisoned); @@ -827,7 +827,7 @@ fn test_non_rs_unknown_directive_not_checked() { let mut poisoned = false; run_path( &mut poisoned, - Path::new("a.Makefile"), + Utf8Path::new("a.Makefile"), include_bytes!("./test-auxillary/not_rs.Makefile"), ); assert!(!poisoned); @@ -836,21 +836,21 @@ fn test_non_rs_unknown_directive_not_checked() { #[test] fn test_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Path::new("a.rs"), b"//@ only-x86 only-arm"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm"); assert!(poisoned); } #[test] fn test_trailing_directive_with_comment() { let mut poisoned = false; - run_path(&mut poisoned, Path::new("a.rs"), b"//@ only-x86 only-arm with comment"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm with comment"); assert!(poisoned); } #[test] fn test_not_trailing_directive() { let mut poisoned = false; - run_path(&mut poisoned, Path::new("a.rs"), b"//@ revisions: incremental"); + run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ revisions: incremental"); assert!(!poisoned); } diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 720663b30ef42..b969b22750bc0 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -22,16 +22,15 @@ pub mod util; use core::panic; use std::collections::HashSet; -use std::ffi::OsString; use std::fmt::Write; use std::io::{self, ErrorKind}; -use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::sync::{Arc, OnceLock}; use std::time::SystemTime; use std::{env, fs, vec}; use build_helper::git::{get_git_modified_files, get_git_untracked_files}; +use camino::{Utf8Path, Utf8PathBuf}; use getopts::Options; use tracing::*; use walkdir::WalkDir; @@ -230,15 +229,19 @@ pub fn parse_config(args: Vec) -> Config { panic!() } - fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf { - match m.opt_str(nm) { - Some(s) => PathBuf::from(&s), - None => panic!("no option (=path) found for {}", nm), + fn make_absolute(path: Utf8PathBuf) -> Utf8PathBuf { + if path.is_relative() { + Utf8PathBuf::try_from(env::current_dir().unwrap()).unwrap().join(path) + } else { + path } } - fn make_absolute(path: PathBuf) -> PathBuf { - if path.is_relative() { env::current_dir().unwrap().join(path) } else { path } + fn opt_path(m: &getopts::Matches, nm: &str) -> Utf8PathBuf { + match m.opt_str(nm) { + Some(s) => Utf8PathBuf::from(&s), + None => panic!("no option (=path) found for {}", nm), + } } let target = opt_str2(matches.opt_str("target")); @@ -279,12 +282,12 @@ pub fn parse_config(args: Vec) -> Config { .free .iter() .map(|f| { - let path = Path::new(f); + let path = Utf8Path::new(f); let mut iter = path.iter().skip(1); // We skip the test folder and check if the user passed `rmake.rs`. if iter.next().is_some_and(|s| s == "rmake.rs") && iter.next().is_none() { - path.parent().unwrap().to_str().unwrap().to_string() + path.parent().unwrap().to_string() } else { f.to_string() } @@ -316,8 +319,8 @@ pub fn parse_config(args: Vec) -> Config { assert!( src_test_suite_root.starts_with(&src_root), "`src-root` must be a parent of `src-test-suite-root`: `src-root`=`{}`, `src-test-suite-root` = `{}`", - src_root.display(), - src_test_suite_root.display() + src_root, + src_test_suite_root ); let build_root = opt_path(matches, "build-root"); @@ -332,16 +335,16 @@ pub fn parse_config(args: Vec) -> Config { compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), rustc_path: opt_path(matches, "rustc-path"), - cargo_path: matches.opt_str("cargo-path").map(PathBuf::from), - stage0_rustc_path: matches.opt_str("stage0-rustc-path").map(PathBuf::from), - rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from), - coverage_dump_path: matches.opt_str("coverage-dump-path").map(PathBuf::from), + cargo_path: matches.opt_str("cargo-path").map(Utf8PathBuf::from), + stage0_rustc_path: matches.opt_str("stage0-rustc-path").map(Utf8PathBuf::from), + rustdoc_path: matches.opt_str("rustdoc-path").map(Utf8PathBuf::from), + coverage_dump_path: matches.opt_str("coverage-dump-path").map(Utf8PathBuf::from), python: matches.opt_str("python").unwrap(), jsondocck_path: matches.opt_str("jsondocck-path"), jsondoclint_path: matches.opt_str("jsondoclint-path"), run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"), - llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from), - llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from), + llvm_filecheck: matches.opt_str("llvm-filecheck").map(Utf8PathBuf::from), + llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(Utf8PathBuf::from), src_root, src_test_suite_root, @@ -407,7 +410,7 @@ pub fn parse_config(args: Vec) -> Config { }, only_modified: matches.opt_present("only-modified"), color, - remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from), + remote_test_client: matches.opt_str("remote-test-client").map(Utf8PathBuf::from), compare_mode, rustfix_coverage: matches.opt_present("rustfix-coverage"), has_html_tidy, @@ -450,19 +453,19 @@ pub fn parse_config(args: Vec) -> Config { pub fn log_config(config: &Config) { let c = config; logv(c, "configuration:".to_string()); - logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path)); - logv(c, format!("run_lib_path: {:?}", config.run_lib_path)); - logv(c, format!("rustc_path: {:?}", config.rustc_path.display())); + logv(c, format!("compile_lib_path: {}", config.compile_lib_path)); + logv(c, format!("run_lib_path: {}", config.run_lib_path)); + logv(c, format!("rustc_path: {}", config.rustc_path)); logv(c, format!("cargo_path: {:?}", config.cargo_path)); logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path)); - logv(c, format!("src_root: {}", config.src_root.display())); - logv(c, format!("src_test_suite_root: {}", config.src_test_suite_root.display())); + logv(c, format!("src_root: {}", config.src_root)); + logv(c, format!("src_test_suite_root: {}", config.src_test_suite_root)); - logv(c, format!("build_root: {}", config.build_root.display())); - logv(c, format!("build_test_suite_root: {}", config.build_test_suite_root.display())); + logv(c, format!("build_root: {}", config.build_root)); + logv(c, format!("build_test_suite_root: {}", config.build_test_suite_root)); - logv(c, format!("sysroot_base: {}", config.sysroot_base.display())); + logv(c, format!("sysroot_base: {}", config.sysroot_base)); logv(c, format!("stage: {}", config.stage)); logv(c, format!("stage_id: {}", config.stage_id)); @@ -480,16 +483,16 @@ pub fn log_config(config: &Config) { logv(c, format!("target-rustcflags: {:?}", config.target_rustcflags)); logv(c, format!("target: {}", config.target)); logv(c, format!("host: {}", config.host)); - logv(c, format!("android-cross-path: {:?}", config.android_cross_path.display())); - logv(c, format!("adb_path: {:?}", config.adb_path)); - logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir)); + logv(c, format!("android-cross-path: {}", config.android_cross_path)); + logv(c, format!("adb_path: {}", config.adb_path)); + logv(c, format!("adb_test_dir: {}", config.adb_test_dir)); logv(c, format!("adb_device_status: {}", config.adb_device_status)); logv(c, format!("ar: {}", config.ar)); logv(c, format!("target-linker: {:?}", config.target_linker)); logv(c, format!("host-linker: {:?}", config.host_linker)); logv(c, format!("verbose: {}", config.verbose)); logv(c, format!("format: {:?}", config.format)); - logv(c, format!("minicore_path: {:?}", config.minicore_path.display())); + logv(c, format!("minicore_path: {}", config.minicore_path)); logv(c, "\n".to_string()); } @@ -517,7 +520,7 @@ pub fn run_tests(config: Arc) { coverage_file_path.push("rustfix_missing_coverage.txt"); if coverage_file_path.exists() { if let Err(e) = fs::remove_file(&coverage_file_path) { - panic!("Could not delete {} due to {}", coverage_file_path.display(), e) + panic!("Could not delete {} due to {}", coverage_file_path, e) } } } @@ -619,13 +622,13 @@ struct TestCollectorCx { config: Arc, cache: HeadersCache, common_inputs_stamp: Stamp, - modified_tests: Vec, + modified_tests: Vec, } /// Mutable state used during test collection. struct TestCollector { tests: Vec, - found_path_stems: HashSet, + found_path_stems: HashSet, poisoned: bool, } @@ -635,14 +638,13 @@ struct TestCollector { /// regardless of whether any filters/tests were specified on the command-line, /// because filtering is handled later by libtest. pub(crate) fn collect_and_make_tests(config: Arc) -> Vec { - debug!("making tests from {}", config.src_test_suite_root.display()); + debug!("making tests from {}", config.src_test_suite_root); let common_inputs_stamp = common_inputs_stamp(&config); let modified_tests = modified_tests(&config, &config.src_test_suite_root).unwrap_or_else(|err| { panic!( "modified_tests got error from dir: {}, error: {}", - config.src_test_suite_root.display(), - err + config.src_test_suite_root, err ) }); let cache = HeadersCache::load(&config); @@ -651,12 +653,9 @@ pub(crate) fn collect_and_make_tests(config: Arc) -> Vec let mut collector = TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false }; - collect_tests_from_dir(&cx, &mut collector, &cx.config.src_test_suite_root, Path::new("")) + collect_tests_from_dir(&cx, &mut collector, &cx.config.src_test_suite_root, Utf8Path::new("")) .unwrap_or_else(|reason| { - panic!( - "Could not read tests from {}: {reason}", - cx.config.src_test_suite_root.display() - ) + panic!("Could not read tests from {}: {reason}", cx.config.src_test_suite_root) }); let TestCollector { tests, found_path_stems, poisoned } = collector; @@ -725,24 +724,29 @@ fn common_inputs_stamp(config: &Config) -> Stamp { /// the `--only-modified` flag is in use. /// /// (Might be inaccurate in some cases.) -fn modified_tests(config: &Config, dir: &Path) -> Result, String> { +fn modified_tests(config: &Config, dir: &Utf8Path) -> Result, String> { // If `--only-modified` wasn't passed, the list of modified tests won't be // used for anything, so avoid some work and just return an empty list. if !config.only_modified { return Ok(vec![]); } - let files = - get_git_modified_files(&config.git_config(), Some(dir), &vec!["rs", "stderr", "fixed"])?; + let files = get_git_modified_files( + &config.git_config(), + Some(dir.as_std_path()), + &vec!["rs", "stderr", "fixed"], + )?; // Add new test cases to the list, it will be convenient in daily development. let untracked_files = get_git_untracked_files(&config.git_config(), None)?.unwrap_or(vec![]); let all_paths = [&files[..], &untracked_files[..]].concat(); let full_paths = { - let mut full_paths: Vec = all_paths + let mut full_paths: Vec = all_paths .into_iter() - .map(|f| PathBuf::from(f).with_extension("").with_extension("rs")) - .filter_map(|f| if Path::new(&f).exists() { f.canonicalize().ok() } else { None }) + .map(|f| Utf8PathBuf::from(f).with_extension("").with_extension("rs")) + .filter_map( + |f| if Utf8Path::new(&f).exists() { f.canonicalize_utf8().ok() } else { None }, + ) .collect(); full_paths.dedup(); full_paths.sort_unstable(); @@ -756,8 +760,8 @@ fn modified_tests(config: &Config, dir: &Path) -> Result, String> { fn collect_tests_from_dir( cx: &TestCollectorCx, collector: &mut TestCollector, - dir: &Path, - relative_dir_path: &Path, + dir: &Utf8Path, + relative_dir_path: &Utf8Path, ) -> io::Result<()> { // Ignore directories that contain a file named `compiletest-ignore-dir`. if dir.join("compiletest-ignore-dir").exists() { @@ -790,16 +794,16 @@ fn collect_tests_from_dir( // subdirectories we find, except for `auxiliary` directories. // FIXME: this walks full tests tree, even if we have something to ignore // use walkdir/ignore like in tidy? - for file in fs::read_dir(dir)? { + for file in fs::read_dir(dir.as_std_path())? { let file = file?; - let file_path = file.path(); - let file_name = file.file_name(); + let file_path = Utf8PathBuf::try_from(file.path()).unwrap(); + let file_name = file_path.file_name().unwrap(); - if is_test(&file_name) + if is_test(file_name) && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) { // We found a test file, so create the corresponding libtest structures. - debug!("found test file: {:?}", file_path.display()); + debug!(%file_path, "found test file"); // Record the stem of the test file, to check for overlaps later. let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); @@ -810,22 +814,20 @@ fn collect_tests_from_dir( make_test(cx, collector, &paths); } else if file_path.is_dir() { // Recurse to find more tests in a subdirectory. - let relative_file_path = relative_dir_path.join(file.file_name()); - if &file_name != "auxiliary" { - debug!("found directory: {:?}", file_path.display()); + let relative_file_path = relative_dir_path.join(file_name); + if file_name != "auxiliary" { + debug!(%file_path, "found directory"); collect_tests_from_dir(cx, collector, &file_path, &relative_file_path)?; } } else { - debug!("found other file/directory: {:?}", file_path.display()); + debug!(%file_path, "found other file/directory"); } } Ok(()) } /// Returns true if `file_name` looks like a proper test file name. -pub fn is_test(file_name: &OsString) -> bool { - let file_name = file_name.to_str().unwrap(); - +pub fn is_test(file_name: &str) -> bool { if !file_name.ends_with(".rs") { return false; } @@ -844,7 +846,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te let test_path = if cx.config.mode == Mode::RunMake { testpaths.file.join("rmake.rs") } else { - PathBuf::from(&testpaths.file) + testpaths.file.clone() }; // Scan the test file to discover its revisions, if any. @@ -899,7 +901,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te /// The path of the `stamp` file that gets created or updated whenever a /// particular test completes successfully. -fn stamp_file_path(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { +fn stamp_file_path(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> Utf8PathBuf { output_base_dir(config, testpaths, revision).join("stamp") } @@ -912,7 +914,7 @@ fn files_related_to_test( testpaths: &TestPaths, props: &EarlyProps, revision: Option<&str>, -) -> Vec { +) -> Vec { let mut related = vec![]; if testpaths.file.is_dir() { @@ -920,7 +922,7 @@ fn files_related_to_test( for entry in WalkDir::new(&testpaths.file) { let path = entry.unwrap().into_path(); if path.is_file() { - related.push(path); + related.push(Utf8PathBuf::try_from(path).unwrap()); } } } else { @@ -991,7 +993,7 @@ struct Stamp { impl Stamp { /// Creates a timestamp holding the last-modified time of the specified file. - fn from_path(path: &Path) -> Self { + fn from_path(path: &Utf8Path) -> Self { let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH }; stamp.add_path(path); stamp @@ -999,8 +1001,8 @@ impl Stamp { /// Updates this timestamp to the last-modified time of the specified file, /// if it is later than the currently-stored timestamp. - fn add_path(&mut self, path: &Path) { - let modified = fs::metadata(path) + fn add_path(&mut self, path: &Utf8Path) { + let modified = fs::metadata(path.as_std_path()) .and_then(|metadata| metadata.modified()) .unwrap_or(SystemTime::UNIX_EPOCH); self.time = self.time.max(modified); @@ -1009,7 +1011,8 @@ impl Stamp { /// Updates this timestamp to the most recent last-modified time of all files /// recursively contained in the given directory, if it is later than the /// currently-stored timestamp. - fn add_dir(&mut self, path: &Path) { + fn add_dir(&mut self, path: &Utf8Path) { + let path = path.as_std_path(); for entry in WalkDir::new(path) { let entry = entry.unwrap(); if entry.file_type().is_file() { @@ -1042,7 +1045,7 @@ fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str> config.mode, debugger, mode_suffix, - path.display(), + path, revision.map_or("".to_string(), |rev| format!("#{}", rev)) ) } @@ -1064,7 +1067,7 @@ fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str> /// To avoid problems, we forbid test names from overlapping in this way. /// /// See for more context. -fn check_for_overlapping_test_paths(found_path_stems: &HashSet) { +fn check_for_overlapping_test_paths(found_path_stems: &HashSet) { let mut collisions = Vec::new(); for path in found_path_stems { for ancestor in path.ancestors().skip(1) { @@ -1077,7 +1080,7 @@ fn check_for_overlapping_test_paths(found_path_stems: &HashSet) { collisions.sort(); let collisions: String = collisions .into_iter() - .map(|(path, check_parent)| format!("test {path:?} clashes with {check_parent:?}\n")) + .map(|(path, check_parent)| format!("test {path} clashes with {check_parent}\n")) .collect(); panic!( "{collisions}\n\ diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 208d32833c9cf..24fc2ddb74104 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1,15 +1,15 @@ use std::borrow::Cow; use std::collections::{HashMap, HashSet}; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsString; use std::fs::{self, File, create_dir_all}; use std::hash::{DefaultHasher, Hash, Hasher}; use std::io::prelude::*; use std::io::{self, BufReader}; -use std::path::{Path, PathBuf}; use std::process::{Child, Command, ExitStatus, Output, Stdio}; use std::sync::Arc; use std::{env, iter, str}; +use camino::{Utf8Path, Utf8PathBuf}; use colored::Colorize; use regex::{Captures, Regex}; use tracing::*; @@ -22,10 +22,10 @@ use crate::common::{ output_base_dir, output_base_name, output_testname_unique, }; use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff}; -use crate::errors::{self, Error, ErrorKind}; +use crate::errors::{Error, ErrorKind}; use crate::header::TestProps; use crate::read2::{Truncated, read2_abbreviated}; -use crate::util::{PathBufExt, add_dylib_path, logv, static_regex}; +use crate::util::{Utf8PathBufExt, add_dylib_path, logv, static_regex}; use crate::{ColorConfig, json, stamp_file_path}; mod debugger; @@ -131,7 +131,7 @@ pub fn run(config: Arc, testpaths: &TestPaths, revision: Option<&str>) { // We're going to be dumping a lot of info. Start on a new line. print!("\n\n"); } - debug!("running {:?}", testpaths.file.display()); + debug!("running {}", testpaths.file); let mut props = TestProps::from_file(&testpaths.file, revision, &config); // For non-incremental (i.e. regular UI) tests, the incremental directory @@ -144,7 +144,7 @@ pub fn run(config: Arc, testpaths: &TestPaths, revision: Option<&str>) { let cx = TestCx { config: &config, props: &props, testpaths, revision }; if let Err(e) = create_dir_all(&cx.output_base_dir()) { - panic!("failed to create output base directory {}: {e}", cx.output_base_dir().display()); + panic!("failed to create output base directory {}: {e}", cx.output_base_dir()); } if props.incremental { @@ -207,7 +207,8 @@ pub fn compute_stamp_hash(config: &Config) -> String { format!("{:x}", hash.finish()) } -fn remove_and_create_dir_all(path: &Path) { +fn remove_and_create_dir_all(path: &Utf8Path) { + let path = path.as_std_path(); let _ = fs::remove_dir_all(path); fs::create_dir_all(path).unwrap(); } @@ -423,7 +424,7 @@ impl<'test> TestCx<'test> { let aux_dir = self.aux_output_dir_name(); let input: &str = match read_from { ReadFrom::Stdin(_) => "-", - ReadFrom::Path => self.testpaths.file.to_str().unwrap(), + ReadFrom::Path => self.testpaths.file.as_str(), }; let mut rustc = Command::new(&self.config.rustc_path); @@ -590,10 +591,7 @@ impl<'test> TestCx<'test> { // FIXME(#65865) return; } else { - self.fatal(&format!( - "no error pattern specified in {:?}", - self.testpaths.file.display() - )); + self.fatal(&format!("no error pattern specified in {}", self.testpaths.file)); } } @@ -675,7 +673,7 @@ impl<'test> TestCx<'test> { } } - fn check_expected_errors(&self, expected_errors: Vec, proc_res: &ProcRes) { + fn check_expected_errors(&self, expected_errors: Vec, proc_res: &ProcRes) { debug!( "check_expected_errors: expected_errors={:?} proc_res.status={:?}", expected_errors, proc_res.status @@ -697,21 +695,25 @@ impl<'test> TestCx<'test> { } // On Windows, translate all '\' path separators to '/' - let file_name = format!("{}", self.testpaths.file.display()).replace(r"\", "/"); + let file_name = self.testpaths.file.to_string().replace(r"\", "/"); // On Windows, keep all '\' path separators to match the paths reported in the JSON output // from the compiler let diagnostic_file_name = if self.props.remap_src_base { - let mut p = PathBuf::from(FAKE_SRC_BASE); + let mut p = Utf8PathBuf::from(FAKE_SRC_BASE); p.push(&self.testpaths.relative_dir); p.push(self.testpaths.file.file_name().unwrap()); - p.display().to_string() + p.to_string() } else { - self.testpaths.file.display().to_string() + self.testpaths.file.to_string() }; - let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help)); - let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note)); + // Errors and warnings are always expected, other diagnostics are only expected + // if one of them actually occurs in the test. + let expected_kinds: HashSet<_> = [ErrorKind::Error, ErrorKind::Warning] + .into_iter() + .chain(expected_errors.iter().filter_map(|e| e.kind)) + .collect(); // Parse the JSON output from the compiler and extract out the messages. let actual_errors = json::parse_output(&diagnostic_file_name, &proc_res.stderr, proc_res); @@ -737,8 +739,11 @@ impl<'test> TestCx<'test> { } None => { - // If the test is a known bug, don't require that the error is annotated - if self.is_unexpected_compiler_message(&actual_error, expect_help, expect_note) + if actual_error.require_annotation + && actual_error.kind.map_or(false, |kind| { + expected_kinds.contains(&kind) + && !self.props.dont_require_annotations.contains(&kind) + }) { self.error(&format!( "{}:{}: unexpected {}: '{}'", @@ -796,27 +801,6 @@ impl<'test> TestCx<'test> { } } - /// Returns `true` if we should report an error about `actual_error`, - /// which did not match any of the expected error. - fn is_unexpected_compiler_message( - &self, - actual_error: &Error, - expect_help: bool, - expect_note: bool, - ) -> bool { - actual_error.require_annotation - && actual_error.kind.map_or(false, |err_kind| { - // If the test being checked doesn't contain any "help" or "note" annotations, then - // we don't require annotating "help" or "note" (respecively) diagnostics at all. - let default_require_annotations = self.props.require_annotations[&err_kind]; - match err_kind { - ErrorKind::Help => expect_help && default_require_annotations, - ErrorKind::Note => expect_note && default_require_annotations, - _ => default_require_annotations, - } - }) - } - fn should_emit_metadata(&self, pm: Option) -> Emit { match (pm, self.props.fail_mode, self.config.mode) { (Some(PassMode::Check), ..) | (_, Some(FailMode::Check), Ui) => Emit::Metadata, @@ -887,7 +871,7 @@ impl<'test> TestCx<'test> { /// `root_out_dir` and `root_testpaths` refer to the parameters of the actual test being run. /// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths. - fn document(&self, root_out_dir: &Path, root_testpaths: &TestPaths) -> ProcRes { + fn document(&self, root_out_dir: &Utf8Path, root_testpaths: &TestPaths) -> ProcRes { if self.props.build_aux_docs { for rel_ab in &self.props.aux.builds { let aux_testpaths = self.compute_aux_test_paths(root_testpaths, rel_ab); @@ -916,13 +900,13 @@ impl<'test> TestCx<'test> { // actual --out-dir given to the auxiliary or test, as opposed to the root out dir for the entire // test - let out_dir: Cow<'_, Path> = if self.props.unique_doc_out_dir { + let out_dir: Cow<'_, Utf8Path> = if self.props.unique_doc_out_dir { let file_name = self.testpaths.file.file_stem().expect("file name should not be empty"); - let out_dir = PathBuf::from_iter([ + let out_dir = Utf8PathBuf::from_iter([ root_out_dir, - Path::new("docs"), - Path::new(file_name), - Path::new("doc"), + Utf8Path::new("docs"), + Utf8Path::new(file_name), + Utf8Path::new("doc"), ]); create_dir_all(&out_dir).unwrap(); Cow::Owned(out_dir) @@ -935,7 +919,7 @@ impl<'test> TestCx<'test> { rustdoc.current_dir(current_dir); rustdoc .arg("-L") - .arg(self.config.run_lib_path.to_str().unwrap()) + .arg(self.config.run_lib_path.as_path()) .arg("-L") .arg(aux_dir) .arg("-o") @@ -1073,7 +1057,7 @@ impl<'test> TestCx<'test> { let test_ab = of.file.parent().expect("test file path has no parent").join("auxiliary").join(rel_ab); if !test_ab.exists() { - self.fatal(&format!("aux-build `{}` source not found", test_ab.display())) + self.fatal(&format!("aux-build `{}` source not found", test_ab)) } TestPaths { @@ -1110,7 +1094,7 @@ impl<'test> TestCx<'test> { || !self.props.aux.proc_macros.is_empty() } - fn aux_output_dir(&self) -> PathBuf { + fn aux_output_dir(&self) -> Utf8PathBuf { let aux_dir = self.aux_output_dir_name(); if !self.props.aux.builds.is_empty() { @@ -1126,7 +1110,7 @@ impl<'test> TestCx<'test> { aux_dir } - fn build_all_auxiliary(&self, of: &TestPaths, aux_dir: &Path, rustc: &mut Command) { + fn build_all_auxiliary(&self, of: &TestPaths, aux_dir: &Utf8Path, rustc: &mut Command) { for rel_ab in &self.props.aux.builds { self.build_auxiliary(of, rel_ab, &aux_dir, None); } @@ -1146,12 +1130,7 @@ impl<'test> TestCx<'test> { |rustc: &mut Command, aux_name: &str, aux_path: &str, aux_type: AuxType| { let lib_name = get_lib_name(&path_to_crate_name(aux_path), aux_type); if let Some(lib_name) = lib_name { - rustc.arg("--extern").arg(format!( - "{}={}/{}", - aux_name, - aux_dir.display(), - lib_name - )); + rustc.arg("--extern").arg(format!("{}={}/{}", aux_name, aux_dir, lib_name)); } }; @@ -1172,7 +1151,7 @@ impl<'test> TestCx<'test> { let aux_type = self.build_auxiliary(of, aux_file, aux_dir, None); if let Some(lib_name) = get_lib_name(aux_file.trim_end_matches(".rs"), aux_type) { let lib_path = aux_dir.join(&lib_name); - rustc.arg(format!("-Zcodegen-backend={}", lib_path.display())); + rustc.arg(format!("-Zcodegen-backend={}", lib_path)); } } } @@ -1188,7 +1167,7 @@ impl<'test> TestCx<'test> { if self.props.add_core_stubs { let minicore_path = self.build_minicore(); rustc.arg("--extern"); - rustc.arg(&format!("minicore={}", minicore_path.to_str().unwrap())); + rustc.arg(&format!("minicore={}", minicore_path)); } let aux_dir = self.aux_output_dir(); @@ -1206,7 +1185,7 @@ impl<'test> TestCx<'test> { /// Builds `minicore`. Returns the path to the minicore rlib within the base test output /// directory. - fn build_minicore(&self) -> PathBuf { + fn build_minicore(&self) -> Utf8PathBuf { let output_file_path = self.output_base_dir().join("libminicore.rlib"); let mut rustc = self.make_compile_args( &self.config.minicore_path, @@ -1223,10 +1202,7 @@ impl<'test> TestCx<'test> { let res = self.compose_and_run(rustc, self.config.compile_lib_path.as_path(), None, None); if !res.status.success() { self.fatal_proc_rec( - &format!( - "auxiliary build of {:?} failed to compile: ", - self.config.minicore_path.display() - ), + &format!("auxiliary build of {} failed to compile: ", self.config.minicore_path), &res, ); } @@ -1241,7 +1217,7 @@ impl<'test> TestCx<'test> { &self, of: &TestPaths, source_path: &str, - aux_dir: &Path, + aux_dir: &Utf8Path, aux_type: Option, ) -> AuxType { let aux_testpaths = self.compute_aux_test_paths(of, source_path); @@ -1338,10 +1314,7 @@ impl<'test> TestCx<'test> { ); if !auxres.status.success() { self.fatal_proc_rec( - &format!( - "auxiliary build of {:?} failed to compile: ", - aux_testpaths.file.display() - ), + &format!("auxiliary build of {} failed to compile: ", aux_testpaths.file), &auxres, ); } @@ -1350,8 +1323,8 @@ impl<'test> TestCx<'test> { fn read2_abbreviated(&self, child: Child) -> (Output, Truncated) { let mut filter_paths_from_len = Vec::new(); - let mut add_path = |path: &Path| { - let path = path.display().to_string(); + let mut add_path = |path: &Utf8Path| { + let path = path.to_string(); let windows = path.replace("\\", "\\\\"); if windows != path { filter_paths_from_len.push(windows); @@ -1373,8 +1346,8 @@ impl<'test> TestCx<'test> { fn compose_and_run( &self, mut command: Command, - lib_path: &Path, - aux_path: Option<&Path>, + lib_path: &Utf8Path, + aux_path: Option<&Utf8Path>, input: Option, ) -> ProcRes { let cmdline = { @@ -1419,9 +1392,9 @@ impl<'test> TestCx<'test> { matches!(self.config.suite.as_str(), "rustdoc-ui" | "rustdoc-js" | "rustdoc-json") } - fn get_mir_dump_dir(&self) -> PathBuf { + fn get_mir_dump_dir(&self) -> Utf8PathBuf { let mut mir_dump_dir = self.config.build_test_suite_root.clone(); - debug!("input_file: {:?}", self.testpaths.file); + debug!("input_file: {}", self.testpaths.file); mir_dump_dir.push(&self.testpaths.relative_dir); mir_dump_dir.push(self.testpaths.file.file_stem().unwrap()); mir_dump_dir @@ -1429,7 +1402,7 @@ impl<'test> TestCx<'test> { fn make_compile_args( &self, - input_file: &Path, + input_file: &Utf8Path, output_file: TargetLocation, emit: Emit, allow_unused: AllowUnused, @@ -1470,7 +1443,7 @@ impl<'test> TestCx<'test> { // Similarly, vendored sources shouldn't be shown when running from a dist tarball. rustc.arg("-Z").arg(format!( "ignore-directory-in-diagnostics-source-blocks={}", - self.config.src_root.join("vendor").to_str().unwrap(), + self.config.src_root.join("vendor"), )); // Optionally prevent default --sysroot if specified in test compile-flags. @@ -1494,7 +1467,7 @@ impl<'test> TestCx<'test> { if !is_rustdoc { if let Some(ref incremental_dir) = self.props.incremental_dir { - rustc.args(&["-C", &format!("incremental={}", incremental_dir.display())]); + rustc.args(&["-C", &format!("incremental={}", incremental_dir)]); rustc.args(&["-Z", "incremental-verify-ich"]); } @@ -1538,7 +1511,7 @@ impl<'test> TestCx<'test> { let mir_dump_dir = self.get_mir_dump_dir(); remove_and_create_dir_all(&mir_dump_dir); let mut dir_opt = "-Zdump-mir-dir=".to_string(); - dir_opt.push_str(mir_dump_dir.to_str().unwrap()); + dir_opt.push_str(mir_dump_dir.as_str()); debug!("dir_opt: {:?}", dir_opt); rustc.arg(dir_opt); }; @@ -1631,8 +1604,7 @@ impl<'test> TestCx<'test> { if self.props.remap_src_base { rustc.arg(format!( "--remap-path-prefix={}={}", - self.config.src_test_suite_root.to_str().unwrap(), - FAKE_SRC_BASE, + self.config.src_test_suite_root, FAKE_SRC_BASE, )); } @@ -1755,7 +1727,7 @@ impl<'test> TestCx<'test> { rustc } - fn make_exe_name(&self) -> PathBuf { + fn make_exe_name(&self) -> Utf8PathBuf { // Using a single letter here to keep the path length down for // Windows. Some test names get very long. rustc creates `rcgu` // files with the module name appended to it which can more than @@ -1806,7 +1778,7 @@ impl<'test> TestCx<'test> { } } - fn make_cmdline(&self, command: &Command, libpath: &Path) -> String { + fn make_cmdline(&self, command: &Command, libpath: &Utf8Path) -> String { use crate::util; // Linux and mac don't require adjusting the library search path @@ -1819,7 +1791,7 @@ impl<'test> TestCx<'test> { format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path)) } - format!("{} {:?}", lib_path_cmd_prefix(libpath.to_str().unwrap()), command) + format!("{} {:?}", lib_path_cmd_prefix(libpath.as_str()), command) } } @@ -1833,20 +1805,19 @@ impl<'test> TestCx<'test> { return; } - let path = Path::new(proc_name); + let path = Utf8Path::new(proc_name); let proc_name = if path.file_stem().is_some_and(|p| p == "rmake") { - OsString::from_iter( + String::from_iter( path.parent() .unwrap() .file_name() .into_iter() - .chain(Some(OsStr::new("/"))) + .chain(Some("/")) .chain(path.file_name()), ) } else { path.file_name().unwrap().into() }; - let proc_name = proc_name.to_string_lossy(); println!("------{proc_name} stdout------------------------------"); println!("{}", out); println!("------{proc_name} stderr------------------------------"); @@ -1856,18 +1827,18 @@ impl<'test> TestCx<'test> { fn dump_output_file(&self, out: &str, extension: &str) { let outfile = self.make_out_name(extension); - fs::write(&outfile, out).unwrap(); + fs::write(outfile.as_std_path(), out).unwrap(); } /// Creates a filename for output with the given extension. /// E.g., `/.../testname.revision.mode/testname.extension`. - fn make_out_name(&self, extension: &str) -> PathBuf { + fn make_out_name(&self, extension: &str) -> Utf8PathBuf { self.output_base_name().with_extension(extension) } /// Gets the directory where auxiliary files are written. /// E.g., `/.../testname.revision.mode/auxiliary/`. - fn aux_output_dir_name(&self) -> PathBuf { + fn aux_output_dir_name(&self) -> Utf8PathBuf { self.output_base_dir() .join("auxiliary") .with_extra_extension(self.config.mode.aux_dir_disambiguator()) @@ -1875,12 +1846,12 @@ impl<'test> TestCx<'test> { /// Gets the directory where auxiliary binaries are written. /// E.g., `/.../testname.revision.mode/auxiliary/bin`. - fn aux_bin_output_dir_name(&self) -> PathBuf { + fn aux_bin_output_dir_name(&self) -> Utf8PathBuf { self.aux_output_dir_name().join("bin") } /// Generates a unique name for the test, such as `testname.revision.mode`. - fn output_testname_unique(&self) -> PathBuf { + fn output_testname_unique(&self) -> Utf8PathBuf { output_testname_unique(self.config, self.testpaths, self.safe_revision()) } @@ -1893,14 +1864,14 @@ impl<'test> TestCx<'test> { /// Gets the absolute path to the directory where all output for the given /// test/revision should reside. /// E.g., `/path/to/build/host-tuple/test/ui/relative/testname.revision.mode/`. - fn output_base_dir(&self) -> PathBuf { + fn output_base_dir(&self) -> Utf8PathBuf { output_base_dir(self.config, self.testpaths, self.safe_revision()) } /// Gets the absolute path to the base filename used as output for the given /// test/revision. /// E.g., `/.../relative/testname.revision.mode/testname`. - fn output_base_name(&self) -> PathBuf { + fn output_base_name(&self) -> Utf8PathBuf { output_base_name(self.config, self.testpaths, self.safe_revision()) } @@ -1935,7 +1906,7 @@ impl<'test> TestCx<'test> { // codegen tests (using FileCheck) - fn compile_test_and_save_ir(&self) -> (ProcRes, PathBuf) { + fn compile_test_and_save_ir(&self) -> (ProcRes, Utf8PathBuf) { let output_path = self.output_base_name().with_extension("ll"); let input_file = &self.testpaths.file; let rustc = self.make_compile_args( @@ -1951,7 +1922,7 @@ impl<'test> TestCx<'test> { (proc_res, output_path) } - fn verify_with_filecheck(&self, output: &Path) -> ProcRes { + fn verify_with_filecheck(&self, output: &Utf8Path) -> ProcRes { let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap()); filecheck.arg("--input-file").arg(output).arg(&self.testpaths.file); @@ -1981,7 +1952,7 @@ impl<'test> TestCx<'test> { filecheck.args(&self.props.filecheck_flags); // FIXME(jieyouxu): don't pass an empty Path - self.compose_and_run(filecheck, Path::new(""), None, None) + self.compose_and_run(filecheck, Utf8Path::new(""), None, None) } fn charset() -> &'static str { @@ -1989,7 +1960,7 @@ impl<'test> TestCx<'test> { if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" } } - fn compare_to_default_rustdoc(&mut self, out_dir: &Path) { + fn compare_to_default_rustdoc(&mut self, out_dir: &Utf8Path) { if !self.config.has_html_tidy { return; } @@ -2141,12 +2112,8 @@ impl<'test> TestCx<'test> { }; } - fn get_lines>( - &self, - path: &P, - mut other_files: Option<&mut Vec>, - ) -> Vec { - let content = fs::read_to_string(&path).unwrap(); + fn get_lines(&self, path: &Utf8Path, mut other_files: Option<&mut Vec>) -> Vec { + let content = fs::read_to_string(path.as_std_path()).unwrap(); let mut ignore = false; content .lines() @@ -2192,8 +2159,8 @@ impl<'test> TestCx<'test> { for other_file in other_files { let mut path = self.testpaths.file.clone(); path.set_file_name(&format!("{}.rs", other_file)); - let path = fs::canonicalize(path).expect("failed to canonicalize"); - let normalized = path.to_str().unwrap().replace('\\', "/"); + let path = path.canonicalize_utf8().expect("failed to canonicalize"); + let normalized = path.as_str().replace('\\', "/"); files.insert(normalized, self.get_lines(&path, None)); } @@ -2377,26 +2344,24 @@ impl<'test> TestCx<'test> { let mut normalized = output.to_string(); - let mut normalize_path = |from: &Path, to: &str| { - let mut from = from.display().to_string(); - if json { - from = from.replace("\\", "\\\\"); - } - normalized = normalized.replace(&from, to); + let mut normalize_path = |from: &Utf8Path, to: &str| { + let from = if json { &from.as_str().replace("\\", "\\\\") } else { from.as_str() }; + + normalized = normalized.replace(from, to); }; let parent_dir = self.testpaths.file.parent().unwrap(); normalize_path(parent_dir, "$DIR"); if self.props.remap_src_base { - let mut remapped_parent_dir = PathBuf::from(FAKE_SRC_BASE); - if self.testpaths.relative_dir != Path::new("") { + let mut remapped_parent_dir = Utf8PathBuf::from(FAKE_SRC_BASE); + if self.testpaths.relative_dir != Utf8Path::new("") { remapped_parent_dir.push(&self.testpaths.relative_dir); } normalize_path(&remapped_parent_dir, "$DIR"); } - let base_dir = Path::new("/rustc/FAKE_PREFIX"); + let base_dir = Utf8Path::new("/rustc/FAKE_PREFIX"); // Fake paths into the libstd/libcore normalize_path(&base_dir.join("library"), "$SRC_DIR"); // `ui-fulldeps` tests can show paths to the compiler source when testing macros from @@ -2406,8 +2371,8 @@ impl<'test> TestCx<'test> { // Real paths into the libstd/libcore let rust_src_dir = &self.config.sysroot_base.join("lib/rustlib/src/rust"); - rust_src_dir.try_exists().expect(&*format!("{} should exists", rust_src_dir.display())); - let rust_src_dir = rust_src_dir.read_link().unwrap_or(rust_src_dir.to_path_buf()); + rust_src_dir.try_exists().expect(&*format!("{} should exists", rust_src_dir)); + let rust_src_dir = rust_src_dir.read_link_utf8().unwrap_or(rust_src_dir.to_path_buf()); normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL"); // eg. @@ -2547,7 +2512,7 @@ impl<'test> TestCx<'test> { .replace("\r\n", "\n") } - fn expected_output_path(&self, kind: &str) -> PathBuf { + fn expected_output_path(&self, kind: &str) -> Utf8PathBuf { let mut path = expected_output_path(&self.testpaths, self.revision, &self.config.compare_mode, kind); @@ -2576,19 +2541,18 @@ impl<'test> TestCx<'test> { } } - fn load_expected_output_from_path(&self, path: &Path) -> Result { - fs::read_to_string(path).map_err(|err| { - format!("failed to load expected output from `{}`: {}", path.display(), err) - }) + fn load_expected_output_from_path(&self, path: &Utf8Path) -> Result { + fs::read_to_string(path) + .map_err(|err| format!("failed to load expected output from `{}`: {}", path, err)) } - fn delete_file(&self, file: &Path) { + fn delete_file(&self, file: &Utf8Path) { if !file.exists() { // Deleting a nonexistent file would error. return; } - if let Err(e) = fs::remove_file(file) { - self.fatal(&format!("failed to delete `{}`: {}", file.display(), e,)); + if let Err(e) = fs::remove_file(file.as_std_path()) { + self.fatal(&format!("failed to delete `{}`: {}", file, e,)); } } @@ -2694,8 +2658,8 @@ impl<'test> TestCx<'test> { fn show_diff( &self, stream: &str, - expected_path: &Path, - actual_path: &Path, + expected_path: &Utf8Path, + actual_path: &Utf8Path, expected: &str, actual: &str, actual_unnormalized: &str, @@ -2834,7 +2798,7 @@ impl<'test> TestCx<'test> { fs::create_dir_all(&incremental_dir).unwrap(); if self.config.verbose { - println!("init_incremental_test: incremental_dir={}", incremental_dir.display()); + println!("init_incremental_test: incremental_dir={incremental_dir}"); } } } @@ -2892,8 +2856,8 @@ impl ProcRes { #[derive(Debug)] enum TargetLocation { - ThisFile(PathBuf), - ThisDirectory(PathBuf), + ThisFile(Utf8PathBuf), + ThisDirectory(Utf8PathBuf), } enum AllowUnused { diff --git a/src/tools/compiletest/src/runtest/assembly.rs b/src/tools/compiletest/src/runtest/assembly.rs index 89d7de58c203c..91d4f620f7194 100644 --- a/src/tools/compiletest/src/runtest/assembly.rs +++ b/src/tools/compiletest/src/runtest/assembly.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use camino::Utf8PathBuf; use super::{AllowUnused, Emit, LinkToAux, ProcRes, TargetLocation, TestCx}; @@ -19,7 +19,7 @@ impl TestCx<'_> { } } - fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) { + fn compile_test_and_save_assembly(&self) -> (ProcRes, Utf8PathBuf) { // This works with both `--emit asm` (as default output name for the assembly) // and `ptx-linker` because the latter can write output at requested location. let output_path = self.output_base_name().with_extension("s"); diff --git a/src/tools/compiletest/src/runtest/codegen_units.rs b/src/tools/compiletest/src/runtest/codegen_units.rs index 6c866cbef21ab..8dfa8d18d1a0b 100644 --- a/src/tools/compiletest/src/runtest/codegen_units.rs +++ b/src/tools/compiletest/src/runtest/codegen_units.rs @@ -26,9 +26,7 @@ impl TestCx<'_> { .stdout .lines() .filter(|line| line.starts_with(PREFIX)) - .map(|line| { - line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string() - }) + .map(|line| line.replace(&self.testpaths.file.as_str(), "TEST_PATH").to_string()) .map(|line| str_to_mono_item(&line, true)) .collect(); diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs index 56fc5baf5f248..41cfeaee35ffb 100644 --- a/src/tools/compiletest/src/runtest/coverage.rs +++ b/src/tools/compiletest/src/runtest/coverage.rs @@ -1,9 +1,9 @@ //! Code specific to the coverage test suites. use std::ffi::OsStr; -use std::path::{Path, PathBuf}; use std::process::Command; +use camino::{Utf8Path, Utf8PathBuf}; use glob::glob; use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP}; @@ -11,7 +11,7 @@ use crate::runtest::{Emit, ProcRes, TestCx, WillExecute}; use crate::util::static_regex; impl<'test> TestCx<'test> { - fn coverage_dump_path(&self) -> &Path { + fn coverage_dump_path(&self) -> &Utf8Path { self.config .coverage_dump_path .as_deref() @@ -79,10 +79,8 @@ impl<'test> TestCx<'test> { std::fs::remove_file(&profdata_path).unwrap(); } - let proc_res = self.exec_compiled_test_general( - &[("LLVM_PROFILE_FILE", &profraw_path.to_str().unwrap())], - false, - ); + let proc_res = + self.exec_compiled_test_general(&[("LLVM_PROFILE_FILE", profraw_path.as_str())], false); if self.props.failure_status.is_some() { self.check_correct_failure_status(&proc_res); } else if !proc_res.status.success() { @@ -158,8 +156,8 @@ impl<'test> TestCx<'test> { /// `.profraw` files and doctest executables to the given vectors. fn run_doctests_for_coverage( &self, - profraw_paths: &mut Vec, - bin_paths: &mut Vec, + profraw_paths: &mut Vec, + bin_paths: &mut Vec, ) { // Put .profraw files and doctest executables in dedicated directories, // to make it easier to glob them all later. @@ -204,10 +202,9 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec("rustdoc --test failed!", &proc_res) } - fn glob_iter(path: impl AsRef) -> impl Iterator { - let path_str = path.as_ref().to_str().unwrap(); - let iter = glob(path_str).unwrap(); - iter.map(Result::unwrap) + fn glob_iter(path: impl AsRef) -> impl Iterator { + let iter = glob(path.as_ref().as_str()).unwrap(); + iter.map(Result::unwrap).map(Utf8PathBuf::try_from).map(Result::unwrap) } // Find all profraw files in the profraw directory. diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs index d9e5c3fa0d8fa..a4103c5b4a9a4 100644 --- a/src/tools/compiletest/src/runtest/debugger.rs +++ b/src/tools/compiletest/src/runtest/debugger.rs @@ -1,7 +1,8 @@ use std::fmt::Write; use std::fs::File; use std::io::{BufRead, BufReader}; -use std::path::{Path, PathBuf}; + +use camino::{Utf8Path, Utf8PathBuf}; use crate::common::Config; use crate::runtest::ProcRes; @@ -15,11 +16,15 @@ pub(super) struct DebuggerCommands { /// Contains the source line number to check and the line itself check_lines: Vec<(usize, String)>, /// Source file name - file: PathBuf, + file: Utf8PathBuf, } impl DebuggerCommands { - pub fn parse_from(file: &Path, config: &Config, debugger_prefix: &str) -> Result { + pub fn parse_from( + file: &Utf8Path, + config: &Config, + debugger_prefix: &str, + ) -> Result { let command_directive = format!("{debugger_prefix}-command"); let check_directive = format!("{debugger_prefix}-check"); @@ -27,7 +32,7 @@ impl DebuggerCommands { let mut commands = vec![]; let mut check_lines = vec![]; let mut counter = 0; - let reader = BufReader::new(File::open(file).unwrap()); + let reader = BufReader::new(File::open(file.as_std_path()).unwrap()); for (line_no, line) in reader.lines().enumerate() { counter += 1; let line = line.map_err(|e| format!("Error while parsing debugger commands: {}", e))?; @@ -50,7 +55,7 @@ impl DebuggerCommands { } } - Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_owned() }) + Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_path_buf() }) } /// Given debugger output and lines to check, ensure that every line is @@ -81,10 +86,10 @@ impl DebuggerCommands { if missing.is_empty() { Ok(()) } else { - let fname = self.file.file_name().unwrap().to_string_lossy(); + let fname = self.file.file_name().unwrap(); let mut msg = format!( "check directive(s) from `{}` not found in debugger output. errors:", - self.file.display() + self.file ); for (src_lineno, err_line) in missing { diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs index 50e733cd31b6b..31240dff9a196 100644 --- a/src/tools/compiletest/src/runtest/debuginfo.rs +++ b/src/tools/compiletest/src/runtest/debuginfo.rs @@ -1,9 +1,9 @@ use std::ffi::{OsStr, OsString}; use std::fs::File; use std::io::{BufRead, BufReader, Read}; -use std::path::Path; use std::process::{Command, Output, Stdio}; +use camino::Utf8Path; use tracing::debug; use super::debugger::DebuggerCommands; @@ -73,11 +73,11 @@ impl TestCx<'_> { let mut js_extension = self.testpaths.file.clone(); js_extension.set_extension("cdb.js"); if js_extension.exists() { - script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension.to_string_lossy())); + script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension)); } // Set breakpoints on every line that contains the string "#break" - let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); + let source_file_name = self.testpaths.file.file_name().unwrap(); for line in &dbg_cmds.breakpoint_lines { script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line)); } @@ -151,16 +151,11 @@ impl TestCx<'_> { if is_android_gdb_target(&self.config.target) { cmds = cmds.replace("run", "continue"); - let tool_path = match self.config.android_cross_path.to_str() { - Some(x) => x.to_owned(), - None => self.fatal("cannot find android cross path"), - }; - // write debugger script let mut script_str = String::with_capacity(2048); script_str.push_str(&format!("set charset {}\n", Self::charset())); - script_str.push_str(&format!("set sysroot {}\n", tool_path)); - script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap())); + script_str.push_str(&format!("set sysroot {}\n", &self.config.android_cross_path)); + script_str.push_str(&format!("file {}\n", exe_file)); script_str.push_str("target remote :5039\n"); script_str.push_str(&format!( "set solib-search-path \ @@ -169,12 +164,8 @@ impl TestCx<'_> { )); for line in &dbg_cmds.breakpoint_lines { script_str.push_str( - format!( - "break {:?}:{}\n", - self.testpaths.file.file_name().unwrap().to_string_lossy(), - *line - ) - .as_str(), + format!("break {}:{}\n", self.testpaths.file.file_name().unwrap(), *line) + .as_str(), ); } script_str.push_str(&cmds); @@ -203,7 +194,7 @@ impl TestCx<'_> { self.config.adb_test_dir.clone(), if self.config.target.contains("aarch64") { "64" } else { "" }, self.config.adb_test_dir.clone(), - exe_file.file_name().unwrap().to_str().unwrap() + exe_file.file_name().unwrap() ); debug!("adb arg: {}", adb_arg); @@ -242,7 +233,7 @@ impl TestCx<'_> { let mut gdb = Command::new(&format!("{}-gdb", self.config.target)); gdb.args(debugger_opts); // FIXME(jieyouxu): don't pass an empty Path - let cmdline = self.make_cmdline(&gdb, Path::new("")); + let cmdline = self.make_cmdline(&gdb, Utf8Path::new("")); logv(self.config, format!("executing {}", cmdline)); cmdline }; @@ -259,7 +250,6 @@ impl TestCx<'_> { } } else { let rust_pp_module_abs_path = self.config.src_root.join("src").join("etc"); - let rust_pp_module_abs_path = rust_pp_module_abs_path.to_str().unwrap(); // write debugger script let mut script_str = String::with_capacity(2048); script_str.push_str(&format!("set charset {}\n", Self::charset())); @@ -274,17 +264,15 @@ impl TestCx<'_> { // GDB's script auto loading safe path script_str.push_str(&format!( "add-auto-load-safe-path {}\n", - rust_pp_module_abs_path.replace(r"\", r"\\") + rust_pp_module_abs_path.as_str().replace(r"\", r"\\") )); - let output_base_dir = self.output_base_dir().to_str().unwrap().to_owned(); - // Add the directory containing the output binary to // include embedded pretty printers to GDB's script // auto loading safe path script_str.push_str(&format!( "add-auto-load-safe-path {}\n", - output_base_dir.replace(r"\", r"\\") + self.output_base_dir().as_str().replace(r"\", r"\\") )); } } @@ -301,12 +289,13 @@ impl TestCx<'_> { script_str.push_str("set print pretty off\n"); // Add the pretty printer directory to GDB's source-file search path - script_str - .push_str(&format!("directory {}\n", rust_pp_module_abs_path.replace(r"\", r"\\"))); + script_str.push_str(&format!( + "directory {}\n", + rust_pp_module_abs_path.as_str().replace(r"\", r"\\") + )); // Load the target executable - script_str - .push_str(&format!("file {}\n", exe_file.to_str().unwrap().replace(r"\", r"\\"))); + script_str.push_str(&format!("file {}\n", exe_file.as_str().replace(r"\", r"\\"))); // Force GDB to print values in the Rust format. script_str.push_str("set language rust\n"); @@ -315,7 +304,7 @@ impl TestCx<'_> { for line in &dbg_cmds.breakpoint_lines { script_str.push_str(&format!( "break '{}':{}\n", - self.testpaths.file.file_name().unwrap().to_string_lossy(), + self.testpaths.file.file_name().unwrap(), *line )); } @@ -410,14 +399,14 @@ impl TestCx<'_> { script_str.push_str(&format!( "command script import {}/lldb_lookup.py\n", - rust_pp_module_abs_path.to_str().unwrap() + rust_pp_module_abs_path )); File::open(rust_pp_module_abs_path.join("lldb_commands")) .and_then(|mut file| file.read_to_string(&mut script_str)) .expect("Failed to read lldb_commands"); // Set breakpoints on every line that contains the string "#break" - let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); + let source_file_name = self.testpaths.file.file_name().unwrap(); for line in &dbg_cmds.breakpoint_lines { script_str.push_str(&format!( "breakpoint set --file '{}' --line {}\n", @@ -451,7 +440,7 @@ impl TestCx<'_> { } } - fn run_lldb(&self, test_executable: &Path, debugger_script: &Path) -> ProcRes { + fn run_lldb(&self, test_executable: &Utf8Path, debugger_script: &Utf8Path) -> ProcRes { // Prepare the lldb_batchmode which executes the debugger script let lldb_script_path = self.config.src_root.join("src/etc/lldb_batchmode.py"); let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") { diff --git a/src/tools/compiletest/src/runtest/js_doc.rs b/src/tools/compiletest/src/runtest/js_doc.rs index d630affbec104..fd53f01ca1746 100644 --- a/src/tools/compiletest/src/runtest/js_doc.rs +++ b/src/tools/compiletest/src/runtest/js_doc.rs @@ -9,8 +9,7 @@ impl TestCx<'_> { self.document(&out_dir, &self.testpaths); - let file_stem = - self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem"); + let file_stem = self.testpaths.file.file_stem().expect("no file stem"); let res = self.run_command_to_procres( Command::new(&nodejs) .arg(self.config.src_root.join("src/tools/rustdoc-js/tester.js")) diff --git a/src/tools/compiletest/src/runtest/mir_opt.rs b/src/tools/compiletest/src/runtest/mir_opt.rs index d1ec00357449d..ded6a68fe5876 100644 --- a/src/tools/compiletest/src/runtest/mir_opt.rs +++ b/src/tools/compiletest/src/runtest/mir_opt.rs @@ -1,6 +1,6 @@ use std::fs; -use std::path::{Path, PathBuf}; +use camino::{Utf8Path, Utf8PathBuf}; use glob::glob; use miropt_test_tools::{MiroptTest, MiroptTestFile, files_for_miropt_test}; use tracing::debug; @@ -14,7 +14,7 @@ impl TestCx<'_> { let should_run = self.should_run(pm); let mut test_info = files_for_miropt_test( - &self.testpaths.file, + &self.testpaths.file.as_std_path(), self.config.get_pointer_width(), self.config.target_cfg().panic.for_miropt_test_tools(), ); @@ -38,20 +38,15 @@ impl TestCx<'_> { fn check_mir_dump(&self, test_info: MiroptTest) { let test_dir = self.testpaths.file.parent().unwrap(); - let test_crate = - self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_"); + let test_crate = self.testpaths.file.file_stem().unwrap().replace('-', "_"); let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info; if self.config.bless { - for e in - glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, suffix)).unwrap() - { + for e in glob(&format!("{}/{}.*{}.mir", test_dir, test_crate, suffix)).unwrap() { fs::remove_file(e.unwrap()).unwrap(); } - for e in - glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, suffix)).unwrap() - { + for e in glob(&format!("{}/{}.*{}.diff", test_dir, test_crate, suffix)).unwrap() { fs::remove_file(e.unwrap()).unwrap(); } } @@ -60,19 +55,15 @@ impl TestCx<'_> { let dumped_string = if let Some(after) = to_file { self.diff_mir_files(from_file.into(), after.into()) } else { - let mut output_file = PathBuf::new(); + let mut output_file = Utf8PathBuf::new(); output_file.push(self.get_mir_dump_dir()); output_file.push(&from_file); - debug!( - "comparing the contents of: {} with {}", - output_file.display(), - expected_file.display() - ); + debug!("comparing the contents of: {} with {:?}", output_file, expected_file); if !output_file.exists() { panic!( "Output file `{}` from test does not exist, available files are in `{}`", - output_file.display(), - output_file.parent().unwrap().display() + output_file, + output_file.parent().unwrap() ); } self.check_mir_test_timestamp(&from_file, &output_file); @@ -107,21 +98,20 @@ impl TestCx<'_> { } } - fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String { - let to_full_path = |path: PathBuf| { + fn diff_mir_files(&self, before: Utf8PathBuf, after: Utf8PathBuf) -> String { + let to_full_path = |path: Utf8PathBuf| { let full = self.get_mir_dump_dir().join(&path); if !full.exists() { panic!( "the mir dump file for {} does not exist (requested in {})", - path.display(), - self.testpaths.file.display(), + path, self.testpaths.file, ); } full }; let before = to_full_path(before); let after = to_full_path(after); - debug!("comparing the contents of: {} with {}", before.display(), after.display()); + debug!("comparing the contents of: {} with {}", before, after); let before = fs::read_to_string(before).unwrap(); let after = fs::read_to_string(after).unwrap(); let before = self.normalize_output(&before, &[]); @@ -138,8 +128,8 @@ impl TestCx<'_> { dumped_string } - fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) { - let t = |file| fs::metadata(file).unwrap().modified().unwrap(); + fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Utf8Path) { + let t = |file: &Utf8Path| fs::metadata(file.as_std_path()).unwrap().modified().unwrap(); let source_file = &self.testpaths.file; let output_time = t(output_file); let source_time = t(source_file); @@ -147,8 +137,7 @@ impl TestCx<'_> { debug!("source file time: {:?} output file time: {:?}", source_time, output_time); panic!( "test source file `{}` is newer than potentially stale output file `{}`.", - source_file.display(), - test_name + source_file, test_name ); } } diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 073116933bdb6..a5ce929f9b8e4 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -1,8 +1,8 @@ -use std::path::Path; use std::process::{Command, Output, Stdio}; use std::{env, fs}; use build_helper::fs::{ignore_not_found, recursive_remove}; +use camino::{Utf8Path, Utf8PathBuf}; use super::{ProcRes, TestCx, disable_error_reporting}; use crate::util::{copy_dir_all, dylib_env_var}; @@ -39,14 +39,16 @@ impl TestCx<'_> { // Copy all input files (apart from rmake.rs) to the temporary directory, // so that the input directory structure from `tests/run-make/` is mirrored // to the `rmake_out` directory. - for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) { - let path = path.unwrap().path().to_path_buf(); + for entry in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) { + let entry = entry.unwrap(); + let path = entry.path(); + let path = <&Utf8Path>::try_from(path).unwrap(); if path.file_name().is_some_and(|s| s != "rmake.rs") { let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap()); if path.is_dir() { - copy_dir_all(&path, target).unwrap(); + copy_dir_all(&path, &target).unwrap(); } else { - fs::copy(&path, target).unwrap(); + fs::copy(path.as_std_path(), target).unwrap(); } } } @@ -83,8 +85,10 @@ impl TestCx<'_> { // on some linux distros. // 2. Specific library paths in `self.config.compile_lib_path` needed for running rustc. - let base_dylib_search_paths = - Vec::from_iter(env::split_paths(&env::var(dylib_env_var()).unwrap())); + let base_dylib_search_paths = Vec::from_iter( + env::split_paths(&env::var(dylib_env_var()).unwrap()) + .map(|p| Utf8PathBuf::try_from(p).expect("dylib env var contains non-UTF8 paths")), + ); // Calculate the paths of the recipe binary. As previously discussed, this is placed at // `/` with `bin_name` being `rmake` or `rmake.exe` depending on @@ -113,13 +117,13 @@ impl TestCx<'_> { .arg("-o") .arg(&recipe_bin) // Specify library search paths for `run_make_support`. - .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap().to_string_lossy())) - .arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy())) - .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy())) + .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap())) + .arg(format!("-Ldependency={}", &support_lib_deps)) + .arg(format!("-Ldependency={}", &support_lib_deps_deps)) // Provide `run_make_support` as extern prelude, so test writers don't need to write // `extern run_make_support;`. .arg("--extern") - .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy())) + .arg(format!("run_make_support={}", &support_lib_path)) .arg("--edition=2021") .arg(&self.testpaths.file.join("rmake.rs")) .arg("-Cprefer-dynamic"); @@ -240,7 +244,7 @@ impl TestCx<'_> { if self.config.target.contains("msvc") && !self.config.cc.is_empty() { // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` // and that `lib.exe` lives next to it. - let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); + let lib = Utf8Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); // MSYS doesn't like passing flags of the form `/foo` as it thinks it's // a path and instead passes `C:\msys64\foo`, so convert all @@ -262,8 +266,8 @@ impl TestCx<'_> { cmd.env("IS_MSVC", "1") .env("IS_WINDOWS", "1") - .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) - .env("MSVC_LIB_PATH", format!("{}", lib.display())) + .env("MSVC_LIB", format!("'{}' -nologo", lib)) + .env("MSVC_LIB_PATH", &lib) // Note: we diverge from legacy run_make and don't lump `CC` the compiler and // default flags together. .env("CC_DEFAULT_FLAGS", &cflags) diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index 974e5170465ec..e87b037cd289d 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -68,7 +68,7 @@ impl TestCx<'_> { { let mut coverage_file_path = self.config.build_test_suite_root.clone(); coverage_file_path.push("rustfix_missing_coverage.txt"); - debug!("coverage_file_path: {}", coverage_file_path.display()); + debug!("coverage_file_path: {}", coverage_file_path); let mut file = OpenOptions::new() .create(true) @@ -76,8 +76,8 @@ impl TestCx<'_> { .open(coverage_file_path.as_path()) .expect("could not create or open file"); - if let Err(e) = writeln!(file, "{}", self.testpaths.file.display()) { - panic!("couldn't write to {}: {e:?}", coverage_file_path.display()); + if let Err(e) = writeln!(file, "{}", self.testpaths.file) { + panic!("couldn't write to {}: {e:?}", coverage_file_path); } } } else if self.props.run_rustfix { @@ -119,7 +119,7 @@ impl TestCx<'_> { self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap()); println!( "To only update this specific test, also pass `--test-args {}`", - relative_path_to_file.display(), + relative_path_to_file, ); self.fatal_proc_rec( &format!("{} errors occurred comparing output.", errors), @@ -211,8 +211,6 @@ impl TestCx<'_> { let crate_name = self.testpaths.file.file_stem().expect("test must have a file stem"); // crate name must be alphanumeric or `_`. - let crate_name = - crate_name.to_str().expect("crate name implies file name must be valid UTF-8"); // replace `a.foo` -> `a__foo` for crate name purposes. // replace `revision-name-with-dashes` -> `revision_name_with_underscore` let crate_name = crate_name.replace('.', "__"); diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index 43c6dc0a67e89..e3e4a81755d09 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -1,5 +1,3 @@ -use std::ffi::OsString; - use crate::debuggers::{extract_gdb_version, extract_lldb_version}; use crate::is_test; @@ -60,11 +58,11 @@ fn test_extract_lldb_version() { #[test] fn is_test_test() { - assert!(is_test(&OsString::from("a_test.rs"))); - assert!(!is_test(&OsString::from(".a_test.rs"))); - assert!(!is_test(&OsString::from("a_cat.gif"))); - assert!(!is_test(&OsString::from("#a_dog_gif"))); - assert!(!is_test(&OsString::from("~a_temp_file"))); + assert!(is_test("a_test.rs")); + assert!(!is_test(".a_test.rs")); + assert!(!is_test("a_cat.gif")); + assert!(!is_test("#a_dog_gif")); + assert!(!is_test("~a_temp_file")); } #[test] diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index bff02f1db9f02..81f5679aead77 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -1,8 +1,7 @@ use std::env; -use std::ffi::OsStr; -use std::path::{Path, PathBuf}; use std::process::Command; +use camino::{Utf8Path, Utf8PathBuf}; use tracing::*; use crate::common::Config; @@ -34,21 +33,21 @@ pub fn logv(config: &Config, s: String) { } } -pub trait PathBufExt { +pub trait Utf8PathBufExt { /// Append an extension to the path, even if it already has one. - fn with_extra_extension>(&self, extension: S) -> PathBuf; + fn with_extra_extension(&self, extension: &str) -> Utf8PathBuf; } -impl PathBufExt for PathBuf { - fn with_extra_extension>(&self, extension: S) -> PathBuf { - if extension.as_ref().is_empty() { +impl Utf8PathBufExt for Utf8PathBuf { + fn with_extra_extension(&self, extension: &str) -> Utf8PathBuf { + if extension.is_empty() { self.clone() } else { - let mut fname = self.file_name().unwrap().to_os_string(); - if !extension.as_ref().to_str().unwrap().starts_with('.') { - fname.push("."); + let mut fname = self.file_name().unwrap().to_string(); + if !extension.starts_with('.') { + fname.push_str("."); } - fname.push(extension); + fname.push_str(extension); self.with_file_name(fname) } } @@ -71,22 +70,27 @@ pub fn dylib_env_var() -> &'static str { /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path. /// If the dylib_path_var is already set for this cmd, the old value will be overwritten! -pub fn add_dylib_path(cmd: &mut Command, paths: impl Iterator>) { +pub fn add_dylib_path( + cmd: &mut Command, + paths: impl Iterator>, +) { let path_env = env::var_os(dylib_env_var()); let old_paths = path_env.as_ref().map(env::split_paths); let new_paths = paths.map(Into::into).chain(old_paths.into_iter().flatten()); cmd.env(dylib_env_var(), env::join_paths(new_paths).unwrap()); } -pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> std::io::Result<()> { - std::fs::create_dir_all(&dst)?; - for entry in std::fs::read_dir(src)? { +pub fn copy_dir_all(src: &Utf8Path, dst: &Utf8Path) -> std::io::Result<()> { + std::fs::create_dir_all(dst.as_std_path())?; + for entry in std::fs::read_dir(src.as_std_path())? { let entry = entry?; + let path = Utf8PathBuf::try_from(entry.path()).unwrap(); + let file_name = path.file_name().unwrap(); let ty = entry.file_type()?; if ty.is_dir() { - copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + copy_dir_all(&path, &dst.join(file_name))?; } else { - std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + std::fs::copy(path.as_std_path(), dst.join(file_name).as_std_path())?; } } Ok(()) diff --git a/src/tools/compiletest/src/util/tests.rs b/src/tools/compiletest/src/util/tests.rs index b09a183b14e6a..5bcae0dcee146 100644 --- a/src/tools/compiletest/src/util/tests.rs +++ b/src/tools/compiletest/src/util/tests.rs @@ -3,12 +3,12 @@ use super::*; #[test] fn path_buf_with_extra_extension_test() { assert_eq!( - PathBuf::from("foo.rs.stderr"), - PathBuf::from("foo.rs").with_extra_extension("stderr") + Utf8PathBuf::from("foo.rs.stderr"), + Utf8PathBuf::from("foo.rs").with_extra_extension("stderr") ); assert_eq!( - PathBuf::from("foo.rs.stderr"), - PathBuf::from("foo.rs").with_extra_extension(".stderr") + Utf8PathBuf::from("foo.rs.stderr"), + Utf8PathBuf::from("foo.rs").with_extra_extension(".stderr") ); - assert_eq!(PathBuf::from("foo.rs"), PathBuf::from("foo.rs").with_extra_extension("")); + assert_eq!(Utf8PathBuf::from("foo.rs"), Utf8PathBuf::from("foo.rs").with_extra_extension("")); } diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock index 24a8efc873d6d..276c518e74f34 100644 --- a/src/tools/miri/test_dependencies/Cargo.lock +++ b/src/tools/miri/test_dependencies/Cargo.lock @@ -156,9 +156,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index 09bbbd61de509..e0939afc09bb7 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -981,9 +981,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] diff --git a/src/tools/rustdoc-gui-test/Cargo.toml b/src/tools/rustdoc-gui-test/Cargo.toml index f7384a98f8565..8d958ac94f30e 100644 --- a/src/tools/rustdoc-gui-test/Cargo.toml +++ b/src/tools/rustdoc-gui-test/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] build_helper = { path = "../../build_helper" } +camino = "1" compiletest = { path = "../compiletest" } getopts = "0.2" walkdir = "2" diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs index f1c6e13d3ae89..addb0af4a541c 100644 --- a/src/tools/rustdoc-gui-test/src/main.rs +++ b/src/tools/rustdoc-gui-test/src/main.rs @@ -118,7 +118,11 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse ..Default::default() }; - let test_props = TestProps::from_file(&librs, None, &compiletest_c); + let test_props = TestProps::from_file( + &camino::Utf8PathBuf::try_from(librs).unwrap(), + None, + &compiletest_c, + ); if !test_props.compile_flags.is_empty() { cargo.env("RUSTDOCFLAGS", test_props.compile_flags.join(" ")); diff --git a/tests/codegen/dont-shuffle-bswaps.rs b/tests/codegen/dont-shuffle-bswaps.rs index e100474f606f9..c1dab2bc29536 100644 --- a/tests/codegen/dont-shuffle-bswaps.rs +++ b/tests/codegen/dont-shuffle-bswaps.rs @@ -1,8 +1,11 @@ -//@ revisions: OPT2 OPT3 +//@ revisions: OPT2 OPT3 OPT3_S390X //@[OPT2] compile-flags: -Copt-level=2 //@[OPT3] compile-flags: -C opt-level=3 // some targets don't do the opt we are looking for //@[OPT3] only-64bit +//@[OPT3] ignore-s390x +//@[OPT3_S390X] compile-flags: -C opt-level=3 -C target-cpu=z13 +//@[OPT3_S390X] only-s390x #![crate_type = "lib"] #![no_std] @@ -17,6 +20,10 @@ // OPT3-NEXT: call <8 x i16> @llvm.bswap // OPT3-NEXT: store <8 x i16> // OPT3-NEXT: ret void +// OPT3_S390X: load <8 x i16> +// OPT3_S390X-NEXT: call <8 x i16> @llvm.bswap +// OPT3_S390X-NEXT: store <8 x i16> +// OPT3_S390X-NEXT: ret void #[no_mangle] pub fn convert(value: [u16; 8]) -> [u8; 16] { #[cfg(target_endian = "little")] diff --git a/tests/codegen/intrinsics/select_unpredictable.rs b/tests/codegen/intrinsics/select_unpredictable.rs index 68a02c8342d06..2db4ae174b333 100644 --- a/tests/codegen/intrinsics/select_unpredictable.rs +++ b/tests/codegen/intrinsics/select_unpredictable.rs @@ -46,21 +46,21 @@ pub fn test_zst(p: bool, a: (), b: ()) -> () { pub fn test_int2(p: bool, a: u64, b: u64) -> u64 { // CHECK-LABEL: define{{.*}} @test_int2 // CHECK: select i1 %p, i64 %a, i64 %b, !unpredictable - p.select_unpredictable(a, b) + core::hint::select_unpredictable(p, a, b) } #[no_mangle] pub fn test_pair2(p: bool, a: (u64, u64), b: (u64, u64)) -> (u64, u64) { // CHECK-LABEL: define{{.*}} @test_pair2 // CHECK: select i1 %p, {{.*}}, !unpredictable - p.select_unpredictable(a, b) + core::hint::select_unpredictable(p, a, b) } #[no_mangle] pub fn test_struct2(p: bool, a: Large, b: Large) -> Large { // CHECK-LABEL: define{{.*}} @test_struct2 // CHECK: select i1 %p, {{.*}}, !unpredictable - p.select_unpredictable(a, b) + core::hint::select_unpredictable(p, a, b) } #[no_mangle] @@ -68,5 +68,5 @@ pub fn test_zst2(p: bool, a: (), b: ()) -> () { // CHECK-LABEL: define{{.*}} @test_zst2 // CHECK-NEXT: start: // CHECK-NEXT: ret void - p.select_unpredictable(a, b) + core::hint::select_unpredictable(p, a, b) } diff --git a/tests/ui/async-await/suggest-missing-await.rs b/tests/ui/async-await/suggest-missing-await.rs index 989792825cf85..de0b2ce52b691 100644 --- a/tests/ui/async-await/suggest-missing-await.rs +++ b/tests/ui/async-await/suggest-missing-await.rs @@ -1,4 +1,5 @@ //@ edition:2018 +//@ dont-require-annotations: SUGGESTION fn take_u32(_x: u32) {} diff --git a/tests/ui/async-await/suggest-missing-await.stderr b/tests/ui/async-await/suggest-missing-await.stderr index f9db86ea40a93..9db7eb980effe 100644 --- a/tests/ui/async-await/suggest-missing-await.stderr +++ b/tests/ui/async-await/suggest-missing-await.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:12:14 + --> $DIR/suggest-missing-await.rs:13:14 | LL | take_u32(x) | -------- ^ expected `u32`, found future @@ -7,12 +7,12 @@ LL | take_u32(x) | arguments to this function are incorrect | note: calling an async function returns a future - --> $DIR/suggest-missing-await.rs:12:14 + --> $DIR/suggest-missing-await.rs:13:14 | LL | take_u32(x) | ^ note: function defined here - --> $DIR/suggest-missing-await.rs:3:4 + --> $DIR/suggest-missing-await.rs:4:4 | LL | fn take_u32(_x: u32) {} | ^^^^^^^^ ------- @@ -22,13 +22,13 @@ LL | take_u32(x.await) | ++++++ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:22:5 + --> $DIR/suggest-missing-await.rs:23:5 | LL | dummy() | ^^^^^^^ expected `()`, found future | note: calling an async function returns a future - --> $DIR/suggest-missing-await.rs:22:5 + --> $DIR/suggest-missing-await.rs:23:5 | LL | dummy() | ^^^^^^^ @@ -42,7 +42,7 @@ LL | dummy(); | + error[E0308]: `if` and `else` have incompatible types - --> $DIR/suggest-missing-await.rs:35:9 + --> $DIR/suggest-missing-await.rs:36:9 | LL | let _x = if true { | ______________- @@ -64,7 +64,7 @@ LL | dummy().await | ++++++ error[E0308]: `match` arms have incompatible types - --> $DIR/suggest-missing-await.rs:45:14 + --> $DIR/suggest-missing-await.rs:46:14 | LL | let _x = match 0usize { | ______________- @@ -87,7 +87,7 @@ LL ~ 1 => dummy().await, | error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:53:9 + --> $DIR/suggest-missing-await.rs:54:9 | LL | let _x = match dummy() { | ------- this expression has type `impl Future` @@ -102,7 +102,7 @@ LL | let _x = match dummy().await { | ++++++ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:67:9 + --> $DIR/suggest-missing-await.rs:68:9 | LL | match dummy_result() { | -------------- this expression has type `impl Future>` @@ -118,7 +118,7 @@ LL | match dummy_result().await { | ++++++ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:69:9 + --> $DIR/suggest-missing-await.rs:70:9 | LL | match dummy_result() { | -------------- this expression has type `impl Future>` @@ -134,7 +134,7 @@ LL | match dummy_result().await { | ++++++ error[E0308]: mismatched types - --> $DIR/suggest-missing-await.rs:77:27 + --> $DIR/suggest-missing-await.rs:78:27 | LL | Some(do_async()).map(|()| {}); | ^^ diff --git a/tests/ui/cast/cast-as-bool.rs b/tests/ui/cast/cast-as-bool.rs index 511a02718fe21..fa9664e572c03 100644 --- a/tests/ui/cast/cast-as-bool.rs +++ b/tests/ui/cast/cast-as-bool.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: SUGGESTION + fn main() { let u = 5 as bool; //~ ERROR cannot cast `i32` as `bool` //~| HELP compare with zero instead diff --git a/tests/ui/cast/cast-as-bool.stderr b/tests/ui/cast/cast-as-bool.stderr index b2c9ae5c6ad61..25377ebebe2b2 100644 --- a/tests/ui/cast/cast-as-bool.stderr +++ b/tests/ui/cast/cast-as-bool.stderr @@ -1,5 +1,5 @@ error[E0054]: cannot cast `i32` as `bool` - --> $DIR/cast-as-bool.rs:2:13 + --> $DIR/cast-as-bool.rs:4:13 | LL | let u = 5 as bool; | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL + let u = 5 != 0; | error[E0054]: cannot cast `i32` as `bool` - --> $DIR/cast-as-bool.rs:6:13 + --> $DIR/cast-as-bool.rs:8:13 | LL | let t = (1 + 2) as bool; | ^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL + let t = (1 + 2) != 0; | error[E0054]: cannot cast `u32` as `bool` - --> $DIR/cast-as-bool.rs:10:13 + --> $DIR/cast-as-bool.rs:12:13 | LL | let _ = 5_u32 as bool; | ^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + let _ = 5_u32 != 0; | error[E0054]: cannot cast `f64` as `bool` - --> $DIR/cast-as-bool.rs:13:13 + --> $DIR/cast-as-bool.rs:15:13 | LL | let _ = 64.0_f64 as bool; | ^^^^^^^^^^^^^^^^ @@ -47,43 +47,43 @@ LL + let _ = 64.0_f64 != 0; | error[E0054]: cannot cast `IntEnum` as `bool` - --> $DIR/cast-as-bool.rs:24:13 + --> $DIR/cast-as-bool.rs:26:13 | LL | let _ = IntEnum::One as bool; | ^^^^^^^^^^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `fn(u8) -> String {uwu}` as `bool` - --> $DIR/cast-as-bool.rs:33:13 + --> $DIR/cast-as-bool.rs:35:13 | LL | let _ = uwu as bool; | ^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `unsafe fn() {owo}` as `bool` - --> $DIR/cast-as-bool.rs:35:13 + --> $DIR/cast-as-bool.rs:37:13 | LL | let _ = owo as bool; | ^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `fn(u8) -> String` as `bool` - --> $DIR/cast-as-bool.rs:38:13 + --> $DIR/cast-as-bool.rs:40:13 | LL | let _ = uwu as fn(u8) -> String as bool; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `char` as `bool` - --> $DIR/cast-as-bool.rs:40:13 + --> $DIR/cast-as-bool.rs:42:13 | LL | let _ = 'x' as bool; | ^^^^^^^^^^^ unsupported cast error[E0054]: cannot cast `*const ()` as `bool` - --> $DIR/cast-as-bool.rs:44:13 + --> $DIR/cast-as-bool.rs:46:13 | LL | let _ = ptr as bool; | ^^^^^^^^^^^ unsupported cast error[E0606]: casting `&'static str` as `bool` is invalid - --> $DIR/cast-as-bool.rs:46:13 + --> $DIR/cast-as-bool.rs:48:13 | LL | let v = "hello" as bool; | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/cfg/cfg_false_no_std-2.rs b/tests/ui/cfg/cfg_false_no_std-2.rs index 349c49412ffa0..18b2c699fd7a5 100644 --- a/tests/ui/cfg/cfg_false_no_std-2.rs +++ b/tests/ui/cfg/cfg_false_no_std-2.rs @@ -1,6 +1,6 @@ // Error, the linked empty library is `no_std` and doesn't provide a panic handler. -//@ dont-require-annotations:ERROR +//@ dont-require-annotations: ERROR //@ dont-check-compiler-stderr //@ aux-build: cfg_false_lib_no_std_before.rs diff --git a/tests/ui/fn/suggest-return-closure.rs b/tests/ui/fn/suggest-return-closure.rs index 30e25ca8edccd..67be8de92431b 100644 --- a/tests/ui/fn/suggest-return-closure.rs +++ b/tests/ui/fn/suggest-return-closure.rs @@ -19,6 +19,7 @@ fn fn_mut() -> _ { let x = String::new(); //~^ HELP: consider changing this to be mutable //~| NOTE binding `x` declared here + //~| SUGGESTION mut |c| { //~ NOTE: value captured here x.push(c); //~^ ERROR: does not live long enough diff --git a/tests/ui/fn/suggest-return-closure.stderr b/tests/ui/fn/suggest-return-closure.stderr index 45c12b548e678..1860d1ca5d927 100644 --- a/tests/ui/fn/suggest-return-closure.stderr +++ b/tests/ui/fn/suggest-return-closure.stderr @@ -21,7 +21,7 @@ LL | fn fn_mut() -> _ { = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/suggest-return-closure.rs:32:13 + --> $DIR/suggest-return-closure.rs:33:13 | LL | fn fun() -> _ { | ^ @@ -32,7 +32,7 @@ LL | fn fun() -> _ { = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable - --> $DIR/suggest-return-closure.rs:23:9 + --> $DIR/suggest-return-closure.rs:24:9 | LL | x.push(c); | ^ cannot borrow as mutable @@ -43,7 +43,7 @@ LL | let mut x = String::new(); | +++ error[E0597]: `x` does not live long enough - --> $DIR/suggest-return-closure.rs:23:9 + --> $DIR/suggest-return-closure.rs:24:9 | LL | let x = String::new(); | - binding `x` declared here diff --git a/tests/ui/panic-runtime/two-panic-runtimes.rs b/tests/ui/panic-runtime/two-panic-runtimes.rs index 7add07ef60045..80591edd10771 100644 --- a/tests/ui/panic-runtime/two-panic-runtimes.rs +++ b/tests/ui/panic-runtime/two-panic-runtimes.rs @@ -1,6 +1,6 @@ // ignore-tidy-linelength //@ build-fail -//@ dont-require-annotations:ERROR +//@ dont-require-annotations: ERROR //@ dont-check-compiler-stderr //@ aux-build:panic-runtime-unwind.rs //@ aux-build:panic-runtime-unwind2.rs diff --git a/tests/ui/panic-runtime/want-abort-got-unwind.rs b/tests/ui/panic-runtime/want-abort-got-unwind.rs index 1ae2e623f1030..42cdf8bc6626b 100644 --- a/tests/ui/panic-runtime/want-abort-got-unwind.rs +++ b/tests/ui/panic-runtime/want-abort-got-unwind.rs @@ -1,6 +1,6 @@ // ignore-tidy-linelength //@ build-fail -//@ dont-require-annotations:ERROR +//@ dont-require-annotations: ERROR //@ dont-check-compiler-stderr //@ aux-build:panic-runtime-unwind.rs //@ compile-flags:-C panic=abort diff --git a/tests/ui/panic-runtime/want-abort-got-unwind2.rs b/tests/ui/panic-runtime/want-abort-got-unwind2.rs index dc4d3ea86d865..ddf12cd2a9a52 100644 --- a/tests/ui/panic-runtime/want-abort-got-unwind2.rs +++ b/tests/ui/panic-runtime/want-abort-got-unwind2.rs @@ -1,6 +1,6 @@ // ignore-tidy-linelength //@ build-fail -//@ dont-require-annotations:ERROR +//@ dont-require-annotations: ERROR //@ dont-check-compiler-stderr //@ aux-build:panic-runtime-unwind.rs //@ aux-build:wants-panic-runtime-unwind.rs diff --git a/tests/ui/parser/inverted-parameters.rs b/tests/ui/parser/inverted-parameters.rs index 5c4272504e061..bc2f53f0be1bb 100644 --- a/tests/ui/parser/inverted-parameters.rs +++ b/tests/ui/parser/inverted-parameters.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: SUGGESTION + struct S; impl S { diff --git a/tests/ui/parser/inverted-parameters.stderr b/tests/ui/parser/inverted-parameters.stderr index 8662277820390..7b969032d0f9c 100644 --- a/tests/ui/parser/inverted-parameters.stderr +++ b/tests/ui/parser/inverted-parameters.stderr @@ -1,5 +1,5 @@ error: expected one of `:`, `@`, or `|`, found `bar` - --> $DIR/inverted-parameters.rs:4:24 + --> $DIR/inverted-parameters.rs:6:24 | LL | fn foo(&self, &str bar) {} | -----^^^ @@ -8,7 +8,7 @@ LL | fn foo(&self, &str bar) {} | help: declare the type after the parameter binding: `: ` error: expected one of `:`, `@`, or `|`, found `quux` - --> $DIR/inverted-parameters.rs:10:10 + --> $DIR/inverted-parameters.rs:12:10 | LL | fn baz(S quux, xyzzy: i32) {} | --^^^^ @@ -17,19 +17,19 @@ LL | fn baz(S quux, xyzzy: i32) {} | help: declare the type after the parameter binding: `: ` error: expected one of `:`, `@`, or `|`, found `a` - --> $DIR/inverted-parameters.rs:15:12 + --> $DIR/inverted-parameters.rs:17:12 | LL | fn one(i32 a b) {} | ^ expected one of `:`, `@`, or `|` error: expected one of `:` or `|`, found `(` - --> $DIR/inverted-parameters.rs:18:23 + --> $DIR/inverted-parameters.rs:20:23 | LL | fn pattern((i32, i32) (a, b)) {} | ^ expected one of `:` or `|` error: expected one of `:`, `@`, or `|`, found `)` - --> $DIR/inverted-parameters.rs:21:12 + --> $DIR/inverted-parameters.rs:23:12 | LL | fn fizz(i32) {} | ^ expected one of `:`, `@`, or `|` @@ -49,7 +49,7 @@ LL | fn fizz(_: i32) {} | ++ error: expected one of `:`, `@`, or `|`, found `S` - --> $DIR/inverted-parameters.rs:27:23 + --> $DIR/inverted-parameters.rs:29:23 | LL | fn missing_colon(quux S) {} | -----^ diff --git a/tests/ui/suggestions/suggest-ref-mut.rs b/tests/ui/suggestions/suggest-ref-mut.rs index 9f5df9303c330..2a933f6305f63 100644 --- a/tests/ui/suggestions/suggest-ref-mut.rs +++ b/tests/ui/suggestions/suggest-ref-mut.rs @@ -1,3 +1,5 @@ +//@ dont-require-annotations: SUGGESTION + struct X(usize); impl X { diff --git a/tests/ui/suggestions/suggest-ref-mut.stderr b/tests/ui/suggestions/suggest-ref-mut.stderr index 935a04c052ac9..7c0899f0fbc16 100644 --- a/tests/ui/suggestions/suggest-ref-mut.stderr +++ b/tests/ui/suggestions/suggest-ref-mut.stderr @@ -1,5 +1,5 @@ error[E0594]: cannot assign to `self.0`, which is behind a `&` reference - --> $DIR/suggest-ref-mut.rs:7:9 + --> $DIR/suggest-ref-mut.rs:9:9 | LL | self.0 = 32; | ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written @@ -10,7 +10,7 @@ LL | fn zap(&mut self) { | +++ error[E0594]: cannot assign to `*foo`, which is behind a `&` reference - --> $DIR/suggest-ref-mut.rs:15:5 + --> $DIR/suggest-ref-mut.rs:17:5 | LL | *foo = 32; | ^^^^^^^^^ `foo` is a `&` reference, so the data it refers to cannot be written @@ -21,7 +21,7 @@ LL | let ref mut foo = 16; | +++ error[E0594]: cannot assign to `*bar`, which is behind a `&` reference - --> $DIR/suggest-ref-mut.rs:19:9 + --> $DIR/suggest-ref-mut.rs:21:9 | LL | *bar = 32; | ^^^^^^^^^ `bar` is a `&` reference, so the data it refers to cannot be written @@ -32,7 +32,7 @@ LL | if let Some(ref mut bar) = Some(16) { | +++ error[E0594]: cannot assign to `*quo`, which is behind a `&` reference - --> $DIR/suggest-ref-mut.rs:23:22 + --> $DIR/suggest-ref-mut.rs:25:22 | LL | ref quo => { *quo = 32; }, | ^^^^^^^^^ `quo` is a `&` reference, so the data it refers to cannot be written diff --git a/tests/ui/typeck/issue-90027-async-fn-return-suggestion.rs b/tests/ui/typeck/issue-90027-async-fn-return-suggestion.rs index 0be1237749fcc..dd833957a70ae 100644 --- a/tests/ui/typeck/issue-90027-async-fn-return-suggestion.rs +++ b/tests/ui/typeck/issue-90027-async-fn-return-suggestion.rs @@ -1,4 +1,5 @@ //@ edition:2018 +//@ dont-require-annotations: SUGGESTION async fn hello() { //~ HELP try adding a return type 0 diff --git a/tests/ui/typeck/issue-90027-async-fn-return-suggestion.stderr b/tests/ui/typeck/issue-90027-async-fn-return-suggestion.stderr index c46f4ec1ec301..3680df25f0ba5 100644 --- a/tests/ui/typeck/issue-90027-async-fn-return-suggestion.stderr +++ b/tests/ui/typeck/issue-90027-async-fn-return-suggestion.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-90027-async-fn-return-suggestion.rs:4:5 + --> $DIR/issue-90027-async-fn-return-suggestion.rs:5:5 | LL | async fn hello() { | - help: try adding a return type: `-> i32` @@ -7,7 +7,7 @@ LL | 0 | ^ expected `()`, found integer error[E0308]: mismatched types - --> $DIR/issue-90027-async-fn-return-suggestion.rs:9:5 + --> $DIR/issue-90027-async-fn-return-suggestion.rs:10:5 | LL | async fn world() -> () { | -- expected `()` because of return type @@ -15,13 +15,13 @@ LL | 0 | ^ expected `()`, found integer error[E0308]: mismatched types - --> $DIR/issue-90027-async-fn-return-suggestion.rs:14:5 + --> $DIR/issue-90027-async-fn-return-suggestion.rs:15:5 | LL | hello() | ^^^^^^^ expected `()`, found future | note: calling an async function returns a future - --> $DIR/issue-90027-async-fn-return-suggestion.rs:14:5 + --> $DIR/issue-90027-async-fn-return-suggestion.rs:15:5 | LL | hello() | ^^^^^^^