From de6193450bd36ada3ea995ca455399c8bfb8a298 Mon Sep 17 00:00:00 2001 From: Vladimir Petrzhikovskii Date: Tue, 5 Aug 2025 16:34:11 +0200 Subject: [PATCH 1/6] std: clarify `OpenOptions` error for create without write access Previously, attempting to create/truncate a file without write/append access would result in platform-specific error messages: - Unix: "Invalid argument" - Windows: raw OS error code 87 These error codes look like system errors, which could waste hours of debugging for what is actually an API misuse issue. --- library/std/src/fs.rs | 7 +++++- library/std/src/fs/tests.rs | 38 ++++++++++++++++++++++++++----- library/std/src/sys/fs/unix.rs | 26 ++++++++++++++++++--- library/std/src/sys/fs/windows.rs | 24 ++++++++++++++++--- 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 72ad7c244eeba..0015d1659aeb5 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1609,6 +1609,10 @@ impl OpenOptions { /// See also [`std::fs::write()`][self::write] for a simple function to /// create a file with some given data. /// + /// # Errors + /// + /// If `.create(true)` is set without `.write(true)` or `.append(true)`, + /// calling [`open`](Self::open) will fail with [`InvalidInput`](io::ErrorKind::InvalidInput) error. /// # Examples /// /// ```no_run @@ -1680,7 +1684,8 @@ impl OpenOptions { /// * [`AlreadyExists`]: `create_new` was specified and the file already /// exists. /// * [`InvalidInput`]: Invalid combinations of open options (truncate - /// without write access, no access mode set, etc.). + /// without write access, create without write or append access, + /// no access mode set, etc.). /// /// The following errors don't match any existing [`io::ErrorKind`] at the moment: /// * One of the directory components of the specified file path diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index c81e3af2f0d4c..f39dd638cd557 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1265,12 +1265,7 @@ fn open_flavors() { let mut ra = OO::new(); ra.read(true).append(true); - #[cfg(windows)] - let invalid_options = 87; // ERROR_INVALID_PARAMETER - #[cfg(all(unix, not(target_os = "vxworks")))] - let invalid_options = "Invalid argument"; - #[cfg(target_os = "vxworks")] - let invalid_options = "invalid argument"; + let invalid_options = "creating or truncating a file requires write or append access"; // Test various combinations of creation modes and access modes. // @@ -2084,3 +2079,34 @@ fn test_rename_junction() { // Junction links are always absolute so we just check the file name is correct. assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str())); } + +#[test] +fn test_open_options_invalid_combinations() { + use crate::fs::OpenOptions as OO; + + let test_cases: &[(fn() -> OO, &str)] = &[ + (|| OO::new().create(true).read(true).clone(), "create without write"), + (|| OO::new().create_new(true).read(true).clone(), "create_new without write"), + (|| OO::new().truncate(true).read(true).clone(), "truncate without write"), + (|| OO::new().truncate(true).append(true).clone(), "truncate with append"), + ]; + + for (make_opts, desc) in test_cases { + let opts = make_opts(); + let result = opts.open("nonexistent.txt"); + assert!(result.is_err(), "{desc} should fail"); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput, "{desc} - wrong error kind"); + assert_eq!( + err.to_string(), + "creating or truncating a file requires write or append access", + "{desc} - wrong error message" + ); + } + + let result = OO::new().open("nonexistent.txt"); + assert!(result.is_err(), "no access mode should fail"); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + assert_eq!(err.to_string(), "must specify at least one of read, write, or append access"); +} diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index b310db2dac485..14524abc4ab09 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1122,7 +1122,21 @@ impl OpenOptions { (true, true, false) => Ok(libc::O_RDWR), (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), - (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), + (false, false, false) => { + // If no access mode is set, check if any creation flags are set + // to provide a more descriptive error message + if self.create || self.create_new || self.truncate { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "must specify at least one of read, write, or append access", + )) + } + } } } @@ -1131,12 +1145,18 @@ impl OpenOptions { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err(Error::from_raw_os_error(libc::EINVAL)); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); } } (_, true) => { if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(libc::EINVAL)); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); } } } diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 9b674a251652b..943195ac7d0f7 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -258,7 +258,19 @@ impl OpenOptions { Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)) } (false, false, false, None) => { - Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)) + // If no access mode is set, check if any creation flags are set + // to provide a more descriptive error message + if self.create || self.create_new || self.truncate { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "must specify at least one of read, write, or append access", + )) + } } } } @@ -268,12 +280,18 @@ impl OpenOptions { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); } } (_, true) => { if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); } } } From b951b5dca116803b89380aae55ce9053a4674f31 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Fri, 15 Aug 2025 16:56:11 +0000 Subject: [PATCH 2/6] stabilize strict provenance atomic ptr --- library/core/src/sync/atomic.rs | 21 +++++++-------------- library/coretests/tests/lib.rs | 1 - library/std/src/lib.rs | 1 - src/tools/miri/tests/pass/atomic.rs | 1 - tests/codegen-llvm/atomicptr.rs | 1 - 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 44a6895f90ac6..7bd68bcd0bc5d 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -2199,7 +2199,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let atom = AtomicPtr::::new(core::ptr::null_mut()); @@ -2209,7 +2208,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_ptr_add(&self, val: usize, order: Ordering) -> *mut T { self.fetch_byte_add(val.wrapping_mul(size_of::()), order) @@ -2240,7 +2239,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let array = [1i32, 2i32]; @@ -2254,7 +2252,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_ptr_sub(&self, val: usize, order: Ordering) -> *mut T { self.fetch_byte_sub(val.wrapping_mul(size_of::()), order) @@ -2279,7 +2277,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let atom = AtomicPtr::::new(core::ptr::null_mut()); @@ -2289,7 +2286,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_byte_add(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2315,7 +2312,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let mut arr = [0i64, 1]; @@ -2325,7 +2321,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_byte_sub(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2361,7 +2357,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let pointer = &mut 3i64 as *mut i64; @@ -2376,7 +2371,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_or(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2412,7 +2407,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let pointer = &mut 3i64 as *mut i64; @@ -2426,7 +2420,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_and(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. @@ -2462,7 +2456,6 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(strict_provenance_atomic_ptr)] /// use core::sync::atomic::{AtomicPtr, Ordering}; /// /// let pointer = &mut 3i64 as *mut i64; @@ -2474,7 +2467,7 @@ impl AtomicPtr { /// ``` #[inline] #[cfg(target_has_atomic = "ptr")] - #[unstable(feature = "strict_provenance_atomic_ptr", issue = "99108")] + #[stable(feature = "strict_provenance_atomic_ptr", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_xor(&self, val: usize, order: Ordering) -> *mut T { // SAFETY: data races are prevented by atomic intrinsics. diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0c4d49f3c994b..92774e1268108 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -95,7 +95,6 @@ #![feature(std_internals)] #![feature(step_trait)] #![feature(str_internals)] -#![feature(strict_provenance_atomic_ptr)] #![feature(strict_provenance_lints)] #![feature(test)] #![feature(trusted_len)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f111fcb4a4712..77c7878ce0896 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -370,7 +370,6 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] -#![feature(strict_provenance_atomic_ptr)] #![feature(sync_unsafe_cell)] #![feature(temporary_niche_types)] #![feature(ub_checks)] diff --git a/src/tools/miri/tests/pass/atomic.rs b/src/tools/miri/tests/pass/atomic.rs index 3de34e570c7ee..d8ac5114f27fa 100644 --- a/src/tools/miri/tests/pass/atomic.rs +++ b/src/tools/miri/tests/pass/atomic.rs @@ -2,7 +2,6 @@ //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance -#![feature(strict_provenance_atomic_ptr)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/tests/codegen-llvm/atomicptr.rs b/tests/codegen-llvm/atomicptr.rs index ce6c4aa0d2b8e..9d5e618fe76f2 100644 --- a/tests/codegen-llvm/atomicptr.rs +++ b/tests/codegen-llvm/atomicptr.rs @@ -6,7 +6,6 @@ //@ compile-flags: -Copt-level=3 -Cno-prepopulate-passes #![crate_type = "lib"] -#![feature(strict_provenance_atomic_ptr)] use std::ptr::without_provenance_mut; use std::sync::atomic::AtomicPtr; From 6fd0e50ecf25e3d1ef6185a3b85dfec24e9e8cb2 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 29 Aug 2025 10:42:44 +0200 Subject: [PATCH 3/6] autoderef final ty is already resolved --- compiler/rustc_hir_analysis/src/autoderef.rs | 12 ++++-------- compiler/rustc_hir_typeck/src/autoderef.rs | 2 +- compiler/rustc_hir_typeck/src/callee.rs | 5 ++--- compiler/rustc_hir_typeck/src/expr.rs | 2 +- compiler/rustc_hir_typeck/src/method/probe.rs | 2 +- compiler/rustc_hir_typeck/src/place_op.rs | 3 +-- 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index c88c534e13548..e27e68d36624e 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -202,14 +202,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { Some((normalized_ty, ocx.into_pending_obligations())) } - /// Returns the final type we ended up with, which may be an inference - /// variable (we will resolve it first, if we want). - pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> { - if resolve { - self.infcx.resolve_vars_if_possible(self.state.cur_ty) - } else { - self.state.cur_ty - } + /// Returns the final type we ended up with, which may be an unresolved + /// inference variable. + pub fn final_ty(&self) -> Ty<'tcx> { + self.state.cur_ty } pub fn step_count(&self) -> usize { diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 7af26623ce764..4fe77278706ec 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -42,7 +42,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut obligations = PredicateObligations::new(); let targets = - steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false))); + steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty())); let steps: Vec<_> = steps .iter() .map(|&(source, kind)| { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 4200afb74e634..c6a4d78dcc830 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef); } - match autoderef.final_ty(false).kind() { + match autoderef.final_ty().kind() { ty::FnDef(def_id, _) => { let abi = self.tcx.fn_sig(def_id).skip_binder().skip_binder().abi; self.check_call_abi(abi, call_expr.span); @@ -200,8 +200,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], autoderef: &Autoderef<'a, 'tcx>, ) -> Option> { - let adjusted_ty = - self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false)); + let adjusted_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty()); // If the callee is a function pointer or a closure, then we're all set. match *adjusted_ty.kind() { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 940f0e3708d0a..a652e08905a55 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2918,7 +2918,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Emits an error if we deref an infer variable, like calling `.field` on a base type // of `&_`. We can also use this to suppress unnecessary "missing field" errors that // will follow ambiguity errors. - let final_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false)); + let final_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty()); if let ty::Error(_) = final_ty.kind() { return final_ty; } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index bb4748b05651b..ab584eb7c909e 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -629,7 +629,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( .collect(); (steps, autoderef_via_deref.reached_recursion_limit()) }; - let final_ty = autoderef_via_deref.final_ty(true); + let final_ty = autoderef_via_deref.final_ty(); let opt_bad_ty = match final_ty.kind() { ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index fedc75abe4927..1125e98408045 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -109,8 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { index_ty: Ty<'tcx>, index_expr: &hir::Expr<'_>, ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { - let adjusted_ty = - self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false)); + let adjusted_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty()); debug!( "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ index_ty={:?})", From e3f1e94be775db497c15e2119d09b62bb834c91f Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Fri, 29 Aug 2025 12:13:30 +0300 Subject: [PATCH 4/6] std: haiku: fix `B_FIND_PATH_IMAGE_PATH` --- library/std/src/sys/pal/unix/os.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 81275afa70777..d70f1aab77ea1 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -469,7 +469,7 @@ pub fn current_exe() -> io::Result { unsafe { let result = libc::find_path( crate::ptr::null_mut(), - libc::path_base_directory::B_FIND_PATH_IMAGE_PATH, + libc::B_FIND_PATH_IMAGE_PATH, crate::ptr::null_mut(), name.as_mut_ptr(), name.len(), From 85cefabfcd8caa1ab95926537252164a6d63351d Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 11 Aug 2025 12:06:38 +0200 Subject: [PATCH 5/6] std: use a TAIT to define `SplitPaths` on UNIX --- library/std/src/sys/pal/unix/os.rs | 36 ++++++------------------------ 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 81275afa70777..0b1abe88275dc 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -16,10 +16,7 @@ use crate::{fmt, io, iter, mem, ptr, slice, str}; const TMPBUF_SZ: usize = 128; -const PATH_SEPARATOR: u8 = cfg_select! { - target_os = "redox" => b';', - _ => b':', -}; +const PATH_SEPARATOR: u8 = if cfg!(target_os = "redox") { b';' } else { b':' }; unsafe extern "C" { #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] @@ -189,33 +186,14 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) } } -pub struct SplitPaths<'a> { - iter: iter::Map bool>, fn(&'a [u8]) -> PathBuf>, -} +pub type SplitPaths<'a> = impl Iterator; +#[define_opaque(SplitPaths)] pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { - fn bytes_to_path(b: &[u8]) -> PathBuf { - PathBuf::from(::from_bytes(b)) - } - fn is_separator(b: &u8) -> bool { - *b == PATH_SEPARATOR - } - let unparsed = unparsed.as_bytes(); - SplitPaths { - iter: unparsed - .split(is_separator as fn(&u8) -> bool) - .map(bytes_to_path as fn(&[u8]) -> PathBuf), - } -} - -impl<'a> Iterator for SplitPaths<'a> { - type Item = PathBuf; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } + unparsed + .as_bytes() + .split(|&b| b == PATH_SEPARATOR) + .map(|part| PathBuf::from(OsStr::from_bytes(part))) } #[derive(Debug)] From 638a52c789e92009cf26ed73a4a5bc597814fe7b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 29 Aug 2025 16:26:23 +0200 Subject: [PATCH 6/6] Improve librustdoc error when a file creation/modification failed --- src/librustdoc/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index e2682045ab432..058d9cc7c90cb 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -760,7 +760,9 @@ fn run_renderer< tcx.dcx().struct_fatal(format!("couldn't generate documentation: {}", e.error)); let file = e.file.display().to_string(); if !file.is_empty() { - msg.note(format!("failed to create or modify \"{file}\"")); + msg.note(format!("failed to create or modify {e}")); + } else { + msg.note(format!("failed to create or modify file: {e}")); } msg.emit(); }