diff options
Diffstat (limited to 'library/std')
24 files changed, 323 insertions, 117 deletions
diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 30ac0512348..5bd20ebe208 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -78,7 +78,7 @@ pub fn current_dir() -> io::Result<PathBuf> { /// assert!(env::set_current_dir(&root).is_ok()); /// println!("Successfully changed working directory to {}!", root.display()); /// ``` -#[doc(alias = "chdir")] +#[doc(alias = "chdir", alias = "SetCurrentDirectory", alias = "SetCurrentDirectoryW")] #[stable(feature = "env", since = "1.0.0")] pub fn set_current_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { os_imp::chdir(path.as_ref()) @@ -655,6 +655,7 @@ pub fn home_dir() -> Option<PathBuf> { /// } /// ``` #[must_use] +#[doc(alias = "GetTempPath", alias = "GetTempPath2")] #[stable(feature = "env", since = "1.0.0")] pub fn temp_dir() -> PathBuf { os_imp::temp_dir() diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index 97e78d17786..818571ddaaa 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -127,6 +127,11 @@ //! trait, which provides a [`from_wide`] method to convert a native Windows //! string (without the terminating nul character) to an [`OsString`]. //! +//! ## Other platforms +//! +//! Many other platforms provide their own extension traits in a +//! `std::os::*::ffi` module. +//! //! ## On all platforms //! //! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of @@ -135,6 +140,8 @@ //! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_encoded_bytes`] and //! [`OsStr::from_encoded_bytes_unchecked`]. //! +//! For basic string processing, see [`OsStr::slice_encoded_bytes`]. +//! //! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value //! [Unicode code point]: https://www.unicode.org/glossary/#code_point //! [`env::set_var()`]: crate::env::set_var "env::set_var" diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 81973182148..28747ad8f34 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -9,7 +9,7 @@ use crate::hash::{Hash, Hasher}; use crate::ops::{self, Range}; use crate::rc::Rc; use crate::slice; -use crate::str::{from_utf8 as str_from_utf8, FromStr}; +use crate::str::FromStr; use crate::sync::Arc; use crate::sys::os_str::{Buf, Slice}; @@ -997,42 +997,15 @@ impl OsStr { /// ``` #[unstable(feature = "os_str_slice", issue = "118485")] pub fn slice_encoded_bytes<R: ops::RangeBounds<usize>>(&self, range: R) -> &Self { - #[track_caller] - fn check_valid_boundary(bytes: &[u8], index: usize) { - if index == 0 || index == bytes.len() { - return; - } - - // Fast path - if bytes[index - 1].is_ascii() || bytes[index].is_ascii() { - return; - } - - let (before, after) = bytes.split_at(index); - - // UTF-8 takes at most 4 bytes per codepoint, so we don't - // need to check more than that. - let after = after.get(..4).unwrap_or(after); - match str_from_utf8(after) { - Ok(_) => return, - Err(err) if err.valid_up_to() != 0 => return, - Err(_) => (), - } - - for len in 2..=4.min(index) { - let before = &before[index - len..]; - if str_from_utf8(before).is_ok() { - return; - } - } - - panic!("byte index {index} is not an OsStr boundary"); - } - let encoded_bytes = self.as_encoded_bytes(); let Range { start, end } = slice::range(range, ..encoded_bytes.len()); - check_valid_boundary(encoded_bytes, start); - check_valid_boundary(encoded_bytes, end); + + // `check_public_boundary` should panic if the index does not lie on an + // `OsStr` boundary as described above. It's possible to do this in an + // encoding-agnostic way, but details of the internal encoding might + // permit a more efficient implementation. + self.inner.check_public_boundary(start); + self.inner.check_public_boundary(end); // SAFETY: `slice::range` ensures that `start` and `end` are valid let slice = unsafe { encoded_bytes.get_unchecked(start..end) }; diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index 60cde376d32..b020e05eaab 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -194,15 +194,65 @@ fn slice_encoded_bytes() { } #[test] -#[should_panic(expected = "byte index 2 is not an OsStr boundary")] +#[should_panic] +fn slice_out_of_bounds() { + let crab = OsStr::new("🦀"); + let _ = crab.slice_encoded_bytes(..5); +} + +#[test] +#[should_panic] fn slice_mid_char() { let crab = OsStr::new("🦀"); let _ = crab.slice_encoded_bytes(..2); } +#[cfg(unix)] +#[test] +#[should_panic(expected = "byte index 1 is not an OsStr boundary")] +fn slice_invalid_data() { + use crate::os::unix::ffi::OsStrExt; + + let os_string = OsStr::from_bytes(b"\xFF\xFF"); + let _ = os_string.slice_encoded_bytes(1..); +} + +#[cfg(unix)] +#[test] +#[should_panic(expected = "byte index 1 is not an OsStr boundary")] +fn slice_partial_utf8() { + use crate::os::unix::ffi::{OsStrExt, OsStringExt}; + + let part_crab = OsStr::from_bytes(&"🦀".as_bytes()[..3]); + let mut os_string = OsString::from_vec(vec![0xFF]); + os_string.push(part_crab); + let _ = os_string.slice_encoded_bytes(1..); +} + +#[cfg(unix)] +#[test] +fn slice_invalid_edge() { + use crate::os::unix::ffi::{OsStrExt, OsStringExt}; + + let os_string = OsStr::from_bytes(b"a\xFFa"); + assert_eq!(os_string.slice_encoded_bytes(..1), "a"); + assert_eq!(os_string.slice_encoded_bytes(1..), OsStr::from_bytes(b"\xFFa")); + assert_eq!(os_string.slice_encoded_bytes(..2), OsStr::from_bytes(b"a\xFF")); + assert_eq!(os_string.slice_encoded_bytes(2..), "a"); + + let os_string = OsStr::from_bytes(&"abc🦀".as_bytes()[..6]); + assert_eq!(os_string.slice_encoded_bytes(..3), "abc"); + assert_eq!(os_string.slice_encoded_bytes(3..), OsStr::from_bytes(b"\xF0\x9F\xA6")); + + let mut os_string = OsString::from_vec(vec![0xFF]); + os_string.push("🦀"); + assert_eq!(os_string.slice_encoded_bytes(..1), OsStr::from_bytes(b"\xFF")); + assert_eq!(os_string.slice_encoded_bytes(1..), "🦀"); +} + #[cfg(windows)] #[test] -#[should_panic(expected = "byte index 3 is not an OsStr boundary")] +#[should_panic(expected = "byte index 3 lies between surrogate codepoints")] fn slice_between_surrogates() { use crate::os::windows::ffi::OsStringExt; @@ -216,10 +266,14 @@ fn slice_between_surrogates() { fn slice_surrogate_edge() { use crate::os::windows::ffi::OsStringExt; - let os_string = OsString::from_wide(&[0xD800]); - let mut with_crab = os_string.clone(); - with_crab.push("🦀"); + let surrogate = OsString::from_wide(&[0xD800]); + let mut pre_crab = surrogate.clone(); + pre_crab.push("🦀"); + assert_eq!(pre_crab.slice_encoded_bytes(..3), surrogate); + assert_eq!(pre_crab.slice_encoded_bytes(3..), "🦀"); - assert_eq!(with_crab.slice_encoded_bytes(..3), os_string); - assert_eq!(with_crab.slice_encoded_bytes(3..), "🦀"); + let mut post_crab = OsString::from("🦀"); + post_crab.push(&surrogate); + assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀"); + assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate); } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index db8de1b1e3d..6b1dd1b5af4 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -656,6 +656,7 @@ impl File { /// /// Note that this method alters the permissions of the underlying file, /// even though it takes `&self` rather than `&mut self`. + #[doc(alias = "fchmod", alias = "SetFileInformationByHandle")] #[stable(feature = "set_permissions_atomic", since = "1.16.0")] pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> { self.inner.set_permissions(perm.0) @@ -1314,6 +1315,7 @@ impl Metadata { /// Ok(()) /// } /// ``` + #[doc(alias = "mtime", alias = "ftLastWriteTime")] #[stable(feature = "fs_time", since = "1.10.0")] pub fn modified(&self) -> io::Result<SystemTime> { self.0.modified().map(FromInner::from_inner) @@ -1349,6 +1351,7 @@ impl Metadata { /// Ok(()) /// } /// ``` + #[doc(alias = "atime", alias = "ftLastAccessTime")] #[stable(feature = "fs_time", since = "1.10.0")] pub fn accessed(&self) -> io::Result<SystemTime> { self.0.accessed().map(FromInner::from_inner) @@ -1381,6 +1384,7 @@ impl Metadata { /// Ok(()) /// } /// ``` + #[doc(alias = "btime", alias = "birthtime", alias = "ftCreationTime")] #[stable(feature = "fs_time", since = "1.10.0")] pub fn created(&self) -> io::Result<SystemTime> { self.0.created().map(FromInner::from_inner) @@ -1879,6 +1883,7 @@ impl AsInner<fs_imp::DirEntry> for DirEntry { /// Ok(()) /// } /// ``` +#[doc(alias = "rm", alias = "unlink", alias = "DeleteFile")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> { fs_imp::unlink(path.as_ref()) @@ -1917,6 +1922,7 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> { /// Ok(()) /// } /// ``` +#[doc(alias = "stat")] #[stable(feature = "rust1", since = "1.0.0")] pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { fs_imp::stat(path.as_ref()).map(Metadata) @@ -1951,6 +1957,7 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { /// Ok(()) /// } /// ``` +#[doc(alias = "lstat")] #[stable(feature = "symlink_metadata", since = "1.1.0")] pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { fs_imp::lstat(path.as_ref()).map(Metadata) @@ -1994,6 +2001,7 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { /// Ok(()) /// } /// ``` +#[doc(alias = "mv", alias = "MoveFile", alias = "MoveFileEx")] #[stable(feature = "rust1", since = "1.0.0")] pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { fs_imp::rename(from.as_ref(), to.as_ref()) @@ -2052,6 +2060,9 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> /// Ok(()) /// } /// ``` +#[doc(alias = "cp")] +#[doc(alias = "CopyFile", alias = "CopyFileEx")] +#[doc(alias = "fclonefileat", alias = "fcopyfile")] #[stable(feature = "rust1", since = "1.0.0")] pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { fs_imp::copy(from.as_ref(), to.as_ref()) @@ -2096,6 +2107,7 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { /// Ok(()) /// } /// ``` +#[doc(alias = "CreateHardLink", alias = "linkat")] #[stable(feature = "rust1", since = "1.0.0")] pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> { fs_imp::link(original.as_ref(), link.as_ref()) @@ -2245,7 +2257,7 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { /// Ok(()) /// } /// ``` -#[doc(alias = "mkdir")] +#[doc(alias = "mkdir", alias = "CreateDirectory")] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "fs_create_dir")] pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { @@ -2326,7 +2338,7 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// Ok(()) /// } /// ``` -#[doc(alias = "rmdir")] +#[doc(alias = "rmdir", alias = "RemoveDirectory")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { fs_imp::rmdir(path.as_ref()) @@ -2449,6 +2461,7 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// Ok(()) /// } /// ``` +#[doc(alias = "ls", alias = "opendir", alias = "FindFirstFile", alias = "FindNextFile")] #[stable(feature = "rust1", since = "1.0.0")] pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { fs_imp::readdir(path.as_ref()).map(ReadDir) @@ -2484,6 +2497,7 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { /// Ok(()) /// } /// ``` +#[doc(alias = "chmod", alias = "SetFileAttributes")] #[stable(feature = "set_permissions", since = "1.1.0")] pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> { fs_imp::set_perm(path.as_ref(), perm.0) diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index e995d5133f8..058e9b90cc7 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -397,7 +397,6 @@ pub trait OpenOptionsExt { /// /// ```no_run /// # #![feature(rustc_private)] - /// use libc; /// use std::fs::OpenOptions; /// use std::os::unix::fs::OpenOptionsExt; /// diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 3da8c835511..4525c3aa914 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -173,51 +173,61 @@ pub trait FileExt { /// /// This corresponds to the `fd_tell` syscall and is similar to /// `seek` where you offset 0 bytes from the current position. + #[doc(alias = "fd_tell")] fn tell(&self) -> io::Result<u64>; /// Adjust the flags associated with this file. /// /// This corresponds to the `fd_fdstat_set_flags` syscall. + #[doc(alias = "fd_fdstat_set_flags")] fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>; /// Adjust the rights associated with this file. /// /// This corresponds to the `fd_fdstat_set_rights` syscall. + #[doc(alias = "fd_fdstat_set_rights")] fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>; /// Provide file advisory information on a file descriptor. /// /// This corresponds to the `fd_advise` syscall. + #[doc(alias = "fd_advise")] fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>; /// Force the allocation of space in a file. /// /// This corresponds to the `fd_allocate` syscall. + #[doc(alias = "fd_allocate")] fn allocate(&self, offset: u64, len: u64) -> io::Result<()>; /// Create a directory. /// /// This corresponds to the `path_create_directory` syscall. + #[doc(alias = "path_create_directory")] fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()>; /// Read the contents of a symbolic link. /// /// This corresponds to the `path_readlink` syscall. + #[doc(alias = "path_readlink")] fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf>; /// Return the attributes of a file or directory. /// /// This corresponds to the `path_filestat_get` syscall. + #[doc(alias = "path_filestat_get")] fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata>; /// Unlink a file. /// /// This corresponds to the `path_unlink_file` syscall. + #[doc(alias = "path_unlink_file")] fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()>; /// Remove a directory. /// /// This corresponds to the `path_remove_directory` syscall. + #[doc(alias = "path_remove_directory")] fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()>; } @@ -359,6 +369,7 @@ pub trait OpenOptionsExt { /// Open a file or directory. /// /// This corresponds to the `path_open` syscall. + #[doc(alias = "path_open")] fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File>; } @@ -500,6 +511,7 @@ impl DirEntryExt for fs::DirEntry { /// Create a hard link. /// /// This corresponds to the `path_link` syscall. +#[doc(alias = "path_link")] pub fn link<P: AsRef<Path>, U: AsRef<Path>>( old_fd: &File, old_flags: u32, @@ -518,6 +530,7 @@ pub fn link<P: AsRef<Path>, U: AsRef<Path>>( /// Rename a file or directory. /// /// This corresponds to the `path_rename` syscall. +#[doc(alias = "path_rename")] pub fn rename<P: AsRef<Path>, U: AsRef<Path>>( old_fd: &File, old_path: P, @@ -534,6 +547,7 @@ pub fn rename<P: AsRef<Path>, U: AsRef<Path>>( /// Create a symbolic link. /// /// This corresponds to the `path_symlink` syscall. +#[doc(alias = "path_symlink")] pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>( old_path: P, fd: &File, diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8294160b72c..c8306c1b597 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -337,8 +337,9 @@ pub mod panic_count { #[doc(hidden)] #[cfg(not(feature = "panic_immediate_abort"))] #[unstable(feature = "update_panic_count", issue = "none")] -// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint -#[allow(static_mut_ref)] +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint +#[cfg_attr(bootstrap, allow(static_mut_ref))] +#[cfg_attr(not(bootstrap), allow(static_mut_refs))] pub mod panic_count { use crate::cell::Cell; use crate::sync::atomic::{AtomicUsize, Ordering}; diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 3a75ce9ebb7..4ca3f1cd185 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -211,6 +211,49 @@ impl Slice { unsafe { mem::transmute(s) } } + #[track_caller] + #[inline] + pub fn check_public_boundary(&self, index: usize) { + if index == 0 || index == self.inner.len() { + return; + } + if index < self.inner.len() + && (self.inner[index - 1].is_ascii() || self.inner[index].is_ascii()) + { + return; + } + + slow_path(&self.inner, index); + + /// We're betting that typical splits will involve an ASCII character. + /// + /// Putting the expensive checks in a separate function generates notably + /// better assembly. + #[track_caller] + #[inline(never)] + fn slow_path(bytes: &[u8], index: usize) { + let (before, after) = bytes.split_at(index); + + // UTF-8 takes at most 4 bytes per codepoint, so we don't + // need to check more than that. + let after = after.get(..4).unwrap_or(after); + match str::from_utf8(after) { + Ok(_) => return, + Err(err) if err.valid_up_to() != 0 => return, + Err(_) => (), + } + + for len in 2..=4.min(index) { + let before = &before[index - len..]; + if str::from_utf8(before).is_ok() { + return; + } + } + + panic!("byte index {index} is not an OsStr boundary"); + } + } + #[inline] pub fn from_str(s: &str) -> &Slice { unsafe { Slice::from_encoded_bytes_unchecked(s.as_bytes()) } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 237854fac4e..352bd735903 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -6,7 +6,7 @@ use crate::fmt; use crate::mem; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::wtf8::{Wtf8, Wtf8Buf}; +use crate::sys_common::wtf8::{check_utf8_boundary, Wtf8, Wtf8Buf}; use crate::sys_common::{AsInner, FromInner, IntoInner}; #[derive(Clone, Hash)] @@ -171,6 +171,11 @@ impl Slice { mem::transmute(Wtf8::from_bytes_unchecked(s)) } + #[track_caller] + pub fn check_public_boundary(&self, index: usize) { + check_utf8_boundary(&self.inner, index); + } + #[inline] pub fn from_str(s: &str) -> &Slice { unsafe { mem::transmute(Wtf8::from_str(s)) } diff --git a/library/std/src/sys/pal/common/small_c_string.rs b/library/std/src/sys/pal/common/small_c_string.rs index af9b18e372d..37812fc0659 100644 --- a/library/std/src/sys/pal/common/small_c_string.rs +++ b/library/std/src/sys/pal/common/small_c_string.rs @@ -15,22 +15,28 @@ const NUL_ERR: io::Error = io::const_io_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte"); #[inline] -pub fn run_path_with_cstr<T, F>(path: &Path, f: F) -> io::Result<T> -where - F: FnOnce(&CStr) -> io::Result<T>, -{ +pub fn run_path_with_cstr<T>(path: &Path, f: &dyn Fn(&CStr) -> io::Result<T>) -> io::Result<T> { run_with_cstr(path.as_os_str().as_encoded_bytes(), f) } #[inline] -pub fn run_with_cstr<T, F>(bytes: &[u8], f: F) -> io::Result<T> -where - F: FnOnce(&CStr) -> io::Result<T>, -{ +pub fn run_with_cstr<T>(bytes: &[u8], f: &dyn Fn(&CStr) -> io::Result<T>) -> io::Result<T> { + // Dispatch and dyn erase the closure type to prevent mono bloat. + // See https://github.com/rust-lang/rust/pull/121101. if bytes.len() >= MAX_STACK_ALLOCATION { - return run_with_cstr_allocating(bytes, f); + run_with_cstr_allocating(bytes, f) + } else { + unsafe { run_with_cstr_stack(bytes, f) } } +} +/// # Safety +/// +/// `bytes` must have a length less than `MAX_STACK_ALLOCATION`. +unsafe fn run_with_cstr_stack<T>( + bytes: &[u8], + f: &dyn Fn(&CStr) -> io::Result<T>, +) -> io::Result<T> { let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); let buf_ptr = buf.as_mut_ptr() as *mut u8; @@ -47,10 +53,7 @@ where #[cold] #[inline(never)] -fn run_with_cstr_allocating<T, F>(bytes: &[u8], f: F) -> io::Result<T> -where - F: FnOnce(&CStr) -> io::Result<T>, -{ +fn run_with_cstr_allocating<T>(bytes: &[u8], f: &dyn Fn(&CStr) -> io::Result<T>) -> io::Result<T> { match CString::new(bytes) { Ok(s) => f(&s), Err(_) => Err(NUL_ERR), diff --git a/library/std/src/sys/pal/common/tests.rs b/library/std/src/sys/pal/common/tests.rs index 32dc18ee1cf..e72d02203da 100644 --- a/library/std/src/sys/pal/common/tests.rs +++ b/library/std/src/sys/pal/common/tests.rs @@ -7,7 +7,7 @@ use core::iter::repeat; #[test] fn stack_allocation_works() { let path = Path::new("abc"); - let result = run_path_with_cstr(path, |p| { + let result = run_path_with_cstr(path, &|p| { assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap()); Ok(42) }); @@ -17,14 +17,14 @@ fn stack_allocation_works() { #[test] fn stack_allocation_fails() { let path = Path::new("ab\0"); - assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); + assert!(run_path_with_cstr::<()>(path, &|_| unreachable!()).is_err()); } #[test] fn heap_allocation_works() { let path = repeat("a").take(384).collect::<String>(); let path = Path::new(&path); - let result = run_path_with_cstr(path, |p| { + let result = run_path_with_cstr(path, &|p| { assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap()); Ok(42) }); @@ -36,7 +36,7 @@ fn heap_allocation_fails() { let mut path = repeat("a").take(384).collect::<String>(); path.push('\0'); let path = Path::new(&path); - assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); + assert!(run_path_with_cstr::<()>(path, &|_| unreachable!()).is_err()); } #[bench] @@ -44,7 +44,7 @@ fn bench_stack_path_alloc(b: &mut test::Bencher) { let path = repeat("a").take(383).collect::<String>(); let p = Path::new(&path); b.iter(|| { - run_path_with_cstr(p, |cstr| { + run_path_with_cstr(p, &|cstr| { black_box(cstr); Ok(()) }) @@ -57,7 +57,7 @@ fn bench_heap_path_alloc(b: &mut test::Bencher) { let path = repeat("a").take(384).collect::<String>(); let p = Path::new(&path); b.iter(|| { - run_path_with_cstr(p, |cstr| { + run_path_with_cstr(p, &|cstr| { black_box(cstr); Ok(()) }) diff --git a/library/std/src/sys/pal/common/thread_local/fast_local.rs b/library/std/src/sys/pal/common/thread_local/fast_local.rs index 04c0dd6f750..646dcd7f3a3 100644 --- a/library/std/src/sys/pal/common/thread_local/fast_local.rs +++ b/library/std/src/sys/pal/common/thread_local/fast_local.rs @@ -13,8 +13,9 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ #[inline] #[deny(unsafe_op_in_unsafe_fn)] - // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint - #[allow(static_mut_ref)] + // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint + #[cfg_attr(bootstrap, allow(static_mut_ref))] + #[cfg_attr(not(bootstrap), allow(static_mut_refs))] unsafe fn __getit( _init: $crate::option::Option<&mut $crate::option::Option<$t>>, ) -> $crate::option::Option<&'static $t> { diff --git a/library/std/src/sys/pal/common/thread_local/static_local.rs b/library/std/src/sys/pal/common/thread_local/static_local.rs index 0dde78b14db..4f2b6868962 100644 --- a/library/std/src/sys/pal/common/thread_local/static_local.rs +++ b/library/std/src/sys/pal/common/thread_local/static_local.rs @@ -11,8 +11,9 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ #[inline] // see comments below #[deny(unsafe_op_in_unsafe_fn)] - // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint - #[allow(static_mut_ref)] + // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint + #[cfg_attr(bootstrap, allow(static_mut_ref))] + #[cfg_attr(not(bootstrap), allow(static_mut_refs))] unsafe fn __getit( _init: $crate::option::Option<&mut $crate::option::Option<$t>>, ) -> $crate::option::Option<&'static $t> { diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index 694482a8a30..d4da53fd3df 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -269,7 +269,7 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { - run_path_with_cstr(path, |path| File::open_c(&path, opts)) + run_path_with_cstr(path, &|path| File::open_c(&path, opts)) } pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> { @@ -421,7 +421,7 @@ pub fn readdir(_p: &Path) -> io::Result<ReadDir> { } pub fn unlink(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, |path| cvt(unsafe { abi::unlink(path.as_ptr()) }).map(|_| ())) + run_path_with_cstr(path, &|path| cvt(unsafe { abi::unlink(path.as_ptr()) }).map(|_| ())) } pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index ff81544ba91..5ceab3b27e0 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -172,7 +172,7 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option<OsString> { // environment variables with a nul byte can't be set, so their value is // always None as well - run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(k.as_bytes(), &|k| { let _guard = env_read_lock(); let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; @@ -190,8 +190,8 @@ pub fn getenv(k: &OsStr) -> Option<OsString> { } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), |k| { - run_with_cstr(v.as_bytes(), |v| { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| { let _guard = ENV_LOCK.write(); cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) }) @@ -199,7 +199,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { } pub fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), |nbuf| { + run_with_cstr(n.as_bytes(), &|nbuf| { let _guard = ENV_LOCK.write(); cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) }) diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 6d0b892ea2f..c75323ef775 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -1118,7 +1118,7 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { - run_path_with_cstr(path, |path| File::open_c(path, opts)) + run_path_with_cstr(path, &|path| File::open_c(path, opts)) } pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> { @@ -1394,7 +1394,7 @@ impl DirBuilder { } pub fn mkdir(&self, p: &Path) -> io::Result<()> { - run_path_with_cstr(p, |p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())) + run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())) } pub fn set_mode(&mut self, mode: u32) { @@ -1575,7 +1575,7 @@ impl fmt::Debug for File { } pub fn readdir(path: &Path) -> io::Result<ReadDir> { - let ptr = run_path_with_cstr(path, |p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; + let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; if ptr.is_null() { Err(Error::last_os_error()) } else { @@ -1586,27 +1586,27 @@ pub fn readdir(path: &Path) -> io::Result<ReadDir> { } pub fn unlink(p: &Path) -> io::Result<()> { - run_path_with_cstr(p, |p| cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ())) + run_path_with_cstr(p, &|p| cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ())) } pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - run_path_with_cstr(old, |old| { - run_path_with_cstr(new, |new| { + run_path_with_cstr(old, &|old| { + run_path_with_cstr(new, &|new| { cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ()) }) }) } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - run_path_with_cstr(p, |p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())) + run_path_with_cstr(p, &|p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())) } pub fn rmdir(p: &Path) -> io::Result<()> { - run_path_with_cstr(p, |p| cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ())) + run_path_with_cstr(p, &|p| cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ())) } pub fn readlink(p: &Path) -> io::Result<PathBuf> { - run_path_with_cstr(p, |c_path| { + run_path_with_cstr(p, &|c_path| { let p = c_path.as_ptr(); let mut buf = Vec::with_capacity(256); @@ -1635,16 +1635,16 @@ pub fn readlink(p: &Path) -> io::Result<PathBuf> { } pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { - run_path_with_cstr(original, |original| { - run_path_with_cstr(link, |link| { + run_path_with_cstr(original, &|original| { + run_path_with_cstr(link, &|link| { cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ()) }) }) } pub fn link(original: &Path, link: &Path) -> io::Result<()> { - run_path_with_cstr(original, |original| { - run_path_with_cstr(link, |link| { + run_path_with_cstr(original, &|original| { + run_path_with_cstr(link, &|link| { cfg_if::cfg_if! { if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves @@ -1678,7 +1678,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { } pub fn stat(p: &Path) -> io::Result<FileAttr> { - run_path_with_cstr(p, |p| { + run_path_with_cstr(p, &|p| { cfg_has_statx! { if let Some(ret) = unsafe { try_statx( libc::AT_FDCWD, @@ -1697,7 +1697,7 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> { } pub fn lstat(p: &Path) -> io::Result<FileAttr> { - run_path_with_cstr(p, |p| { + run_path_with_cstr(p, &|p| { cfg_has_statx! { if let Some(ret) = unsafe { try_statx( libc::AT_FDCWD, @@ -1716,7 +1716,7 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> { } pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { - let r = run_path_with_cstr(p, |path| unsafe { + let r = run_path_with_cstr(p, &|path| unsafe { Ok(libc::realpath(path.as_ptr(), ptr::null_mut())) })?; if r.is_null() { @@ -1879,7 +1879,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { // Opportunistically attempt to create a copy-on-write clone of `from` // using `fclonefileat`. if HAS_FCLONEFILEAT.load(Ordering::Relaxed) { - let clonefile_result = run_path_with_cstr(to, |to| { + let clonefile_result = run_path_with_cstr(to, &|to| { cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }) }); match clonefile_result { @@ -1925,7 +1925,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { } pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { - run_path_with_cstr(path, |path| { + run_path_with_cstr(path, &|path| { cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) .map(|_| ()) }) @@ -1937,7 +1937,7 @@ pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { } pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { - run_path_with_cstr(path, |path| { + run_path_with_cstr(path, &|path| { cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) .map(|_| ()) }) @@ -1945,7 +1945,7 @@ pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] pub fn chroot(dir: &Path) -> io::Result<()> { - run_path_with_cstr(dir, |dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) + run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) } pub use remove_dir_impl::remove_dir_all; @@ -2140,7 +2140,7 @@ mod remove_dir_impl { if attr.file_type().is_symlink() { crate::fs::remove_file(p) } else { - run_path_with_cstr(p, |p| remove_dir_all_recursive(None, &p)) + run_path_with_cstr(p, &|p| remove_dir_all_recursive(None, &p)) } } diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 881b3a25c51..af2b9db4685 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -186,7 +186,7 @@ pub fn chdir(_p: &path::Path) -> io::Result<()> { #[cfg(not(target_os = "espidf"))] pub fn chdir(p: &path::Path) -> io::Result<()> { - let result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; + let result = run_path_with_cstr(p, &|p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) } } @@ -643,7 +643,7 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option<OsString> { // environment variables with a nul byte can't be set, so their value is // always None as well - run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(k.as_bytes(), &|k| { let _guard = env_read_lock(); let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; @@ -661,8 +661,8 @@ pub fn getenv(k: &OsStr) -> Option<OsString> { } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), |k| { - run_with_cstr(v.as_bytes(), |v| { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| { let _guard = ENV_LOCK.write(); cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) }) @@ -670,7 +670,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { } pub fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), |nbuf| { + run_with_cstr(n.as_bytes(), &|nbuf| { let _guard = ENV_LOCK.write(); cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) }) diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index e8238665452..529b82e0198 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -698,7 +698,7 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> { /// Note that this can fail if `p` doesn't look like it can be opened relative /// to any pre-opened file descriptor. fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> { - run_path_with_cstr(p, |p| { + run_path_with_cstr(p, &|p| { let mut buf = Vec::<u8>::with_capacity(512); loop { unsafe { diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index 530d3602172..d62ff8a2f18 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -95,7 +95,7 @@ pub fn getcwd() -> io::Result<PathBuf> { } pub fn chdir(p: &path::Path) -> io::Result<()> { - let result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; + let result = run_path_with_cstr(p, &|p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; match result == (0 as libc::c_int) { true => Ok(()), false => Err(io::Error::last_os_error()), @@ -227,7 +227,7 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option<OsString> { // environment variables with a nul byte can't be set, so their value is // always None as well - run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(k.as_bytes(), &|k| { let _guard = env_read_lock(); let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; @@ -245,8 +245,8 @@ pub fn getenv(k: &OsStr) -> Option<OsString> { } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), |k| { - run_with_cstr(v.as_bytes(), |v| unsafe { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| unsafe { let _guard = env_write_lock(); cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) }) @@ -254,7 +254,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { } pub fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), |nbuf| unsafe { + run_with_cstr(n.as_bytes(), &|nbuf| unsafe { let _guard = env_write_lock(); cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) }) diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 8712bd2eca7..de7d31baaaf 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -199,7 +199,7 @@ impl<'a> TryFrom<(&'a str, u16)> for LookupHost { fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> { init(); - run_with_cstr(host.as_bytes(), |c_host| { + run_with_cstr(host.as_bytes(), &|c_host| { let mut hints: c::addrinfo = unsafe { mem::zeroed() }; hints.ai_socktype = c::SOCK_STREAM; let mut res = ptr::null_mut(); diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 67db5ebd89c..2dbd19d7171 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -885,15 +885,43 @@ fn decode_surrogate_pair(lead: u16, trail: u16) -> char { unsafe { char::from_u32_unchecked(code_point) } } -/// Copied from core::str::StrPrelude::is_char_boundary +/// Copied from str::is_char_boundary #[inline] pub fn is_code_point_boundary(slice: &Wtf8, index: usize) -> bool { - if index == slice.len() { + if index == 0 { return true; } match slice.bytes.get(index) { - None => false, - Some(&b) => b < 128 || b >= 192, + None => index == slice.len(), + Some(&b) => (b as i8) >= -0x40, + } +} + +/// Verify that `index` is at the edge of either a valid UTF-8 codepoint +/// (i.e. a codepoint that's not a surrogate) or of the whole string. +/// +/// These are the cases currently permitted by `OsStr::slice_encoded_bytes`. +/// Splitting between surrogates is valid as far as WTF-8 is concerned, but +/// we do not permit it in the public API because WTF-8 is considered an +/// implementation detail. +#[track_caller] +#[inline] +pub fn check_utf8_boundary(slice: &Wtf8, index: usize) { + if index == 0 { + return; + } + match slice.bytes.get(index) { + Some(0xED) => (), // Might be a surrogate + Some(&b) if (b as i8) >= -0x40 => return, + Some(_) => panic!("byte index {index} is not a codepoint boundary"), + None if index == slice.len() => return, + None => panic!("byte index {index} is out of bounds"), + } + if slice.bytes[index + 1] >= 0xA0 { + // There's a surrogate after index. Now check before index. + if index >= 3 && slice.bytes[index - 3] == 0xED && slice.bytes[index - 2] >= 0xA0 { + panic!("byte index {index} lies between surrogate codepoints"); + } } } diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index 28a426648e5..6a1cc41a8fb 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -663,3 +663,65 @@ fn wtf8_to_owned() { assert_eq!(string.bytes, b"\xED\xA0\x80"); assert!(!string.is_known_utf8); } + +#[test] +fn wtf8_valid_utf8_boundaries() { + let mut string = Wtf8Buf::from_str("aé 💩"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + string.push(CodePoint::from_u32(0xD800).unwrap()); + check_utf8_boundary(&string, 0); + check_utf8_boundary(&string, 1); + check_utf8_boundary(&string, 3); + check_utf8_boundary(&string, 4); + check_utf8_boundary(&string, 8); + check_utf8_boundary(&string, 14); + assert_eq!(string.len(), 14); + + string.push_char('a'); + check_utf8_boundary(&string, 14); + check_utf8_boundary(&string, 15); + + let mut string = Wtf8Buf::from_str("a"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + check_utf8_boundary(&string, 1); + + let mut string = Wtf8Buf::from_str("\u{D7FF}"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + check_utf8_boundary(&string, 3); + + let mut string = Wtf8Buf::new(); + string.push(CodePoint::from_u32(0xD800).unwrap()); + string.push_char('\u{D7FF}'); + check_utf8_boundary(&string, 3); +} + +#[test] +#[should_panic(expected = "byte index 4 is out of bounds")] +fn wtf8_utf8_boundary_out_of_bounds() { + let string = Wtf8::from_str("aé"); + check_utf8_boundary(&string, 4); +} + +#[test] +#[should_panic(expected = "byte index 1 is not a codepoint boundary")] +fn wtf8_utf8_boundary_inside_codepoint() { + let string = Wtf8::from_str("é"); + check_utf8_boundary(&string, 1); +} + +#[test] +#[should_panic(expected = "byte index 1 is not a codepoint boundary")] +fn wtf8_utf8_boundary_inside_surrogate() { + let mut string = Wtf8Buf::new(); + string.push(CodePoint::from_u32(0xD800).unwrap()); + check_utf8_boundary(&string, 1); +} + +#[test] +#[should_panic(expected = "byte index 3 lies between surrogate codepoints")] +fn wtf8_utf8_boundary_between_surrogates() { + let mut string = Wtf8Buf::new(); + string.push(CodePoint::from_u32(0xD800).unwrap()); + string.push(CodePoint::from_u32(0xD800).unwrap()); + check_utf8_boundary(&string, 3); +} diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 83d5d63556f..d1213e2f166 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -180,8 +180,8 @@ impl<T: 'static> fmt::Debug for LocalKey<T> { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")] #[allow_internal_unstable(thread_local_internals)] -// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint -#[allow(static_mut_ref)] +// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint +#[cfg_attr(not(bootstrap), allow(static_mut_refs))] macro_rules! thread_local { // empty (base case for the recursion) () => {}; |
