From 51a7396ad3d78d9326ee1537b9ff29ab3919556f Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sun, 3 Dec 2023 12:25:11 +0100 Subject: [PATCH 1/8] Move `OsStr::slice_encoded_bytes` validation to platform modules On Windows and UEFI this improves performance and error messaging. On other platforms we optimize the fast path a bit more. This also prepares for later relaxing the checks on certain platforms. --- library/std/src/ffi/mod.rs | 7 +++ library/std/src/ffi/os_str.rs | 43 +++------------ library/std/src/ffi/os_str/tests.rs | 68 +++++++++++++++++++++--- library/std/src/sys/os_str/bytes.rs | 43 +++++++++++++++ library/std/src/sys/os_str/wtf8.rs | 7 ++- library/std/src/sys_common/wtf8.rs | 36 +++++++++++-- library/std/src/sys_common/wtf8/tests.rs | 62 +++++++++++++++++++++ 7 files changed, 219 insertions(+), 47 deletions(-) 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>(&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/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_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); +} From 3a917cdfcb59b952f130443ececdb6cf98a2ba3b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 14 Feb 2024 05:25:57 +0200 Subject: [PATCH 2/8] make "invalid fragment specifier" translatable --- compiler/rustc_expand/messages.ftl | 5 +++++ compiler/rustc_expand/src/errors.rs | 10 ++++++++++ compiler/rustc_expand/src/mbe/quoted.rs | 18 +++++++++--------- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 3e3b4814300..5a3303327db 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -61,6 +61,11 @@ expand_invalid_cfg_multiple_predicates = multiple `cfg` predicates are specified expand_invalid_cfg_no_parens = `cfg` is not followed by parentheses expand_invalid_cfg_no_predicate = `cfg` predicate is not specified expand_invalid_cfg_predicate_literal = `cfg` predicate key cannot be a literal + +expand_invalid_fragment_specifier = + invalid fragment specifier `{$fragment}` + .help = {$help} + expand_macro_body_stability = macros cannot have body stability attributes .label = invalid body stability attribute diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 2584ff62e98..929f3479466 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -408,3 +408,13 @@ pub struct DuplicateMatcherBinding { #[label(expand_label2)] pub prev: Span, } + +#[derive(Diagnostic)] +#[diag(expand_invalid_fragment_specifier)] +#[help] +pub struct InvalidFragmentSpecifier { + #[primary_span] + pub span: Span, + pub fragment: Ident, + pub help: String, +} diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 4824b67d277..0fdfa563138 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -1,3 +1,4 @@ +use crate::errors; use crate::mbe::macro_parser::count_metavar_decls; use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree}; @@ -60,11 +61,11 @@ pub(super) fn parse( Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => { match trees.next() { Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { - Some((frag, _)) => { + Some((fragment, _)) => { let span = token.span.with_lo(start_sp.lo()); let kind = - token::NonterminalKind::from_symbol(frag.name, || { + token::NonterminalKind::from_symbol(fragment.name, || { // FIXME(#85708) - once we properly decode a foreign // crate's `SyntaxContext::root`, then we can replace // this with just `span.edition()`. A @@ -81,14 +82,13 @@ pub(super) fn parse( }) .unwrap_or_else( || { - let msg = format!( - "invalid fragment specifier `{}`", - frag.name + sess.dcx().emit_err( + errors::InvalidFragmentSpecifier { + span, + fragment, + help: VALID_FRAGMENT_NAMES_MSG.into(), + }, ); - sess.dcx() - .struct_span_err(span, msg) - .with_help(VALID_FRAGMENT_NAMES_MSG) - .emit(); token::NonterminalKind::Ident }, ); From 6f1383cf5b036236dc7c72ff7bb68401f5e4169a Mon Sep 17 00:00:00 2001 From: hi-rustin Date: Sat, 17 Feb 2024 16:11:32 +0800 Subject: [PATCH 3/8] Remove unnecessary unit binding Signed-off-by: hi-rustin --- library/alloc/src/raw_vec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 49f5f53f9b3..dd8d6f6c7e6 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -261,7 +261,7 @@ impl RawVec { // and could hypothetically handle differences between stride and size, but this memory // has already been allocated so we know it can't overflow and currently rust does not // support such types. So we can do better by skipping some checks and avoid an unwrap. - let _: () = const { assert!(mem::size_of::() % mem::align_of::() == 0) }; + const { assert!(mem::size_of::() % mem::align_of::() == 0) }; unsafe { let align = mem::align_of::(); let size = mem::size_of::().unchecked_mul(self.cap.0); @@ -465,7 +465,7 @@ impl RawVec { let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) }; // See current_memory() why this assert is here - let _: () = const { assert!(mem::size_of::() % mem::align_of::() == 0) }; + const { assert!(mem::size_of::() % mem::align_of::() == 0) }; // If shrinking to 0, deallocate the buffer. We don't reach this point // for the T::IS_ZST case since current_memory() will have returned From e3859d206ce49998f0f33e0c7fceca261aeaf7e8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 17 Feb 2024 14:15:22 +0200 Subject: [PATCH 4/8] add test to guard against inaccurate diagnostic Also replaces an incomplete test --- tests/ui/macros/invalid-fragment-specifier.rs | 10 ++++++++++ .../macros/invalid-fragment-specifier.stderr | 18 ++++++++++++++++++ tests/ui/macros/macro-invalid-fragment-spec.rs | 8 -------- .../macros/macro-invalid-fragment-spec.stderr | 10 ---------- 4 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 tests/ui/macros/invalid-fragment-specifier.rs create mode 100644 tests/ui/macros/invalid-fragment-specifier.stderr delete mode 100644 tests/ui/macros/macro-invalid-fragment-spec.rs delete mode 100644 tests/ui/macros/macro-invalid-fragment-spec.stderr diff --git a/tests/ui/macros/invalid-fragment-specifier.rs b/tests/ui/macros/invalid-fragment-specifier.rs new file mode 100644 index 00000000000..1daf0a95434 --- /dev/null +++ b/tests/ui/macros/invalid-fragment-specifier.rs @@ -0,0 +1,10 @@ +macro_rules! test { + ($wrong:id) => {}; +} //~^ ERROR: invalid fragment specifier `id` + +// guard against breaking raw identifier diagnostic +macro_rules! test_raw_identifer { + ($wrong:r#if) => {}; +} //~^ ERROR: invalid fragment specifier `r#if` + +fn main() {} diff --git a/tests/ui/macros/invalid-fragment-specifier.stderr b/tests/ui/macros/invalid-fragment-specifier.stderr new file mode 100644 index 00000000000..7516dbc3a08 --- /dev/null +++ b/tests/ui/macros/invalid-fragment-specifier.stderr @@ -0,0 +1,18 @@ +error: invalid fragment specifier `id` + --> $DIR/invalid-fragment-specifier.rs:2:6 + | +LL | ($wrong:id) => {}; + | ^^^^^^^^^ + | + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + +error: invalid fragment specifier `r#if` + --> $DIR/invalid-fragment-specifier.rs:7:6 + | +LL | ($wrong:r#if) => {}; + | ^^^^^^^^^^^ + | + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/macro-invalid-fragment-spec.rs b/tests/ui/macros/macro-invalid-fragment-spec.rs deleted file mode 100644 index dc4d75096af..00000000000 --- a/tests/ui/macros/macro-invalid-fragment-spec.rs +++ /dev/null @@ -1,8 +0,0 @@ -macro_rules! foo( - ($x:foo) => () - //~^ ERROR invalid fragment specifier -); - -fn main() { - foo!(foo); -} diff --git a/tests/ui/macros/macro-invalid-fragment-spec.stderr b/tests/ui/macros/macro-invalid-fragment-spec.stderr deleted file mode 100644 index 919111ede51..00000000000 --- a/tests/ui/macros/macro-invalid-fragment-spec.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: invalid fragment specifier `foo` - --> $DIR/macro-invalid-fragment-spec.rs:2:6 - | -LL | ($x:foo) => () - | ^^^^^^ - | - = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` - -error: aborting due to 1 previous error - From 5793f8203046da13726db57a5418c3e49ce66902 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 17 Feb 2024 15:26:45 -0800 Subject: [PATCH 5/8] Add help to `hir_analysis_unrecognized_intrinsic_function` To help remind forgetful people like me what step they forgot. --- compiler/rustc_hir_analysis/messages.ftl | 1 + compiler/rustc_hir_analysis/src/errors.rs | 1 + tests/ui/error-codes/E0093.stderr | 2 ++ tests/ui/feature-gates/feature-gate-abi.stderr | 4 ++++ tests/ui/feature-gates/feature-gate-intrinsics.stderr | 4 ++++ tests/ui/intrinsics-always-extern.stderr | 2 ++ 6 files changed, 14 insertions(+) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index a61cfd0e4ce..7468b38ce1a 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -462,6 +462,7 @@ hir_analysis_unrecognized_atomic_operation = hir_analysis_unrecognized_intrinsic_function = unrecognized intrinsic function: `{$name}` .label = unrecognized intrinsic + .help = if you're adding an intrinsic, be sure to update `check_intrinsic_type` hir_analysis_unused_associated_type_bounds = unnecessary associated type bound for not object safe associated type diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6a505b96197..cd4096a9d47 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -143,6 +143,7 @@ pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> { #[derive(Diagnostic)] #[diag(hir_analysis_unrecognized_intrinsic_function, code = E0093)] +#[help] pub struct UnrecognizedIntrinsicFunction { #[primary_span] #[label] diff --git a/tests/ui/error-codes/E0093.stderr b/tests/ui/error-codes/E0093.stderr index 387e0c55d4d..51c367b343a 100644 --- a/tests/ui/error-codes/E0093.stderr +++ b/tests/ui/error-codes/E0093.stderr @@ -3,6 +3,8 @@ error[E0093]: unrecognized intrinsic function: `foo` | LL | fn foo(); | ^^^^^^^^^ unrecognized intrinsic + | + = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` error: aborting due to 1 previous error diff --git a/tests/ui/feature-gates/feature-gate-abi.stderr b/tests/ui/feature-gates/feature-gate-abi.stderr index aa60434d9fe..3fd1e1189ab 100644 --- a/tests/ui/feature-gates/feature-gate-abi.stderr +++ b/tests/ui/feature-gates/feature-gate-abi.stderr @@ -187,12 +187,16 @@ error[E0093]: unrecognized intrinsic function: `f1` | LL | extern "rust-intrinsic" fn f1() {} | ^^ unrecognized intrinsic + | + = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` error[E0093]: unrecognized intrinsic function: `f2` --> $DIR/feature-gate-abi.rs:18:32 | LL | extern "platform-intrinsic" fn f2() {} | ^^ unrecognized intrinsic + | + = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` error: intrinsic must be in `extern "rust-intrinsic" { ... }` block --> $DIR/feature-gate-abi.rs:25:32 diff --git a/tests/ui/feature-gates/feature-gate-intrinsics.stderr b/tests/ui/feature-gates/feature-gate-intrinsics.stderr index 78c21843adb..583a4a1a84e 100644 --- a/tests/ui/feature-gates/feature-gate-intrinsics.stderr +++ b/tests/ui/feature-gates/feature-gate-intrinsics.stderr @@ -21,12 +21,16 @@ error[E0093]: unrecognized intrinsic function: `bar` | LL | fn bar(); | ^^^^^^^^^ unrecognized intrinsic + | + = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` error[E0093]: unrecognized intrinsic function: `baz` --> $DIR/feature-gate-intrinsics.rs:5:28 | LL | extern "rust-intrinsic" fn baz() {} | ^^^ unrecognized intrinsic + | + = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` error: intrinsic must be in `extern "rust-intrinsic" { ... }` block --> $DIR/feature-gate-intrinsics.rs:5:34 diff --git a/tests/ui/intrinsics-always-extern.stderr b/tests/ui/intrinsics-always-extern.stderr index 32468f99197..1f7bb5a3b0d 100644 --- a/tests/ui/intrinsics-always-extern.stderr +++ b/tests/ui/intrinsics-always-extern.stderr @@ -9,6 +9,8 @@ error[E0093]: unrecognized intrinsic function: `hello` | LL | extern "rust-intrinsic" fn hello() { | ^^^^^ unrecognized intrinsic + | + = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` error: intrinsic must be in `extern "rust-intrinsic" { ... }` block --> $DIR/intrinsics-always-extern.rs:8:43 From 61479730826b83c92285d60b8571b8f5ce428129 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sun, 18 Feb 2024 10:41:31 +0200 Subject: [PATCH 6/8] remove extraneous text from example config --- config.example.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.example.toml b/config.example.toml index 098811195d7..975fcd71fc9 100644 --- a/config.example.toml +++ b/config.example.toml @@ -829,7 +829,7 @@ # target triples containing `-none`, `nvptx`, `switch`, or `-uefi`. #no-std = (bool) -# This is an array of the codegen backends that will be compiled a rustc +# This is an array of the codegen backends that will be # compiled for this target, overriding the global rust.codegen-backends option. # See that option for more info. #codegen-backends = rust.codegen-backends (array) From 50e316bdc0f5ebcd35831c983b0f1856ec48b519 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Sun, 18 Feb 2024 15:28:58 +0530 Subject: [PATCH 7/8] Remove const_prop.rs and move its content to const_prop_lint.rs and dataflow_const_prop.rs where it is used --- .../rustc_mir_transform/src/const_prop.rs | 161 ------------------ .../src/const_prop_lint.rs | 132 +++++++++++++- .../src/dataflow_const_prop.rs | 27 ++- compiler/rustc_mir_transform/src/lib.rs | 1 - 4 files changed, 153 insertions(+), 168 deletions(-) delete mode 100644 compiler/rustc_mir_transform/src/const_prop.rs diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs deleted file mode 100644 index eba62aae60f..00000000000 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! Propagates constants for early reporting of statically known -//! assertion failures - -use rustc_index::bit_set::BitSet; -use rustc_index::IndexVec; -use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::*; -use rustc_middle::ty::{ParamEnv, TyCtxt}; -use rustc_target::abi::Size; - -/// The maximum number of bytes that we'll allocate space for a local or the return value. -/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just -/// Severely regress performance. -const MAX_ALLOC_LIMIT: u64 = 1024; - -/// Macro for machine-specific `InterpError` without allocation. -/// (These will never be shown to the user, but they help diagnose ICEs.) -pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{ - // We make a new local type for it. The type itself does not carry any information, - // but its vtable (for the `MachineStopType` trait) does. - #[derive(Debug)] - struct Zst; - // Printing this type shows the desired string. - impl std::fmt::Display for Zst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, $($tt)*) - } - } - - impl rustc_middle::mir::interpret::MachineStopType for Zst { - fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage { - self.to_string().into() - } - - fn add_args( - self: Box, - _: &mut dyn FnMut(rustc_errors::DiagnosticArgName, rustc_errors::DiagnosticArgValue), - ) {} - } - throw_machine_stop!(Zst) -}} - -/// The mode that `ConstProp` is allowed to run in for a given `Local`. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ConstPropMode { - /// The `Local` can be propagated into and reads of this `Local` can also be propagated. - FullConstProp, - /// The `Local` can only be propagated into and from its own block. - OnlyInsideOwnBlock, - /// The `Local` cannot be part of propagation at all. Any statement - /// referencing it either for reading or writing will not get propagated. - NoPropagation, -} - -pub struct CanConstProp { - can_const_prop: IndexVec, - // False at the beginning. Once set, no more assignments are allowed to that local. - found_assignment: BitSet, -} - -impl CanConstProp { - /// Returns true if `local` can be propagated - pub fn check<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - body: &Body<'tcx>, - ) -> IndexVec { - let mut cpv = CanConstProp { - can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), - found_assignment: BitSet::new_empty(body.local_decls.len()), - }; - for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { - let ty = body.local_decls[local].ty; - match tcx.layout_of(param_env.and(ty)) { - Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {} - // Either the layout fails to compute, then we can't use this local anyway - // or the local is too large, then we don't want to. - _ => { - *val = ConstPropMode::NoPropagation; - continue; - } - } - } - // Consider that arguments are assigned on entry. - for arg in body.args_iter() { - cpv.found_assignment.insert(arg); - } - cpv.visit_body(body); - cpv.can_const_prop - } -} - -impl<'tcx> Visitor<'tcx> for CanConstProp { - fn visit_place(&mut self, place: &Place<'tcx>, mut context: PlaceContext, loc: Location) { - use rustc_middle::mir::visit::PlaceContext::*; - - // Dereferencing just read the addess of `place.local`. - if place.projection.first() == Some(&PlaceElem::Deref) { - context = NonMutatingUse(NonMutatingUseContext::Copy); - } - - self.visit_local(place.local, context, loc); - self.visit_projection(place.as_ref(), context, loc); - } - - fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { - use rustc_middle::mir::visit::PlaceContext::*; - match context { - // These are just stores, where the storing is not propagatable, but there may be later - // mutations of the same local via `Store` - | MutatingUse(MutatingUseContext::Call) - | MutatingUse(MutatingUseContext::AsmOutput) - | MutatingUse(MutatingUseContext::Deinit) - // Actual store that can possibly even propagate a value - | MutatingUse(MutatingUseContext::Store) - | MutatingUse(MutatingUseContext::SetDiscriminant) => { - if !self.found_assignment.insert(local) { - match &mut self.can_const_prop[local] { - // If the local can only get propagated in its own block, then we don't have - // to worry about multiple assignments, as we'll nuke the const state at the - // end of the block anyway, and inside the block we overwrite previous - // states as applicable. - ConstPropMode::OnlyInsideOwnBlock => {} - ConstPropMode::NoPropagation => {} - other @ ConstPropMode::FullConstProp => { - trace!( - "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}", - local, other, - ); - *other = ConstPropMode::OnlyInsideOwnBlock; - } - } - } - } - // Reading constants is allowed an arbitrary number of times - NonMutatingUse(NonMutatingUseContext::Copy) - | NonMutatingUse(NonMutatingUseContext::Move) - | NonMutatingUse(NonMutatingUseContext::Inspect) - | NonMutatingUse(NonMutatingUseContext::PlaceMention) - | NonUse(_) => {} - - // These could be propagated with a smarter analysis or just some careful thinking about - // whether they'd be fine right now. - MutatingUse(MutatingUseContext::Yield) - | MutatingUse(MutatingUseContext::Drop) - | MutatingUse(MutatingUseContext::Retag) - // These can't ever be propagated under any scheme, as we can't reason about indirect - // mutation. - | NonMutatingUse(NonMutatingUseContext::SharedBorrow) - | NonMutatingUse(NonMutatingUseContext::FakeBorrow) - | NonMutatingUse(NonMutatingUseContext::AddressOf) - | MutatingUse(MutatingUseContext::Borrow) - | MutatingUse(MutatingUseContext::AddressOf) => { - trace!("local {:?} can't be propagated because it's used: {:?}", local, context); - self.can_const_prop[local] = ConstPropMode::NoPropagation; - } - MutatingUse(MutatingUseContext::Projection) - | NonMutatingUse(NonMutatingUseContext::Projection) => bug!("visit_place should not pass {context:?} for {local:?}"), - } - } -} diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 6212155a8fe..6f2ef8f9a4f 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -9,17 +9,14 @@ use rustc_const_eval::interpret::{ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::HirId; -use rustc_index::bit_set::BitSet; -use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::visit::Visitor; +use rustc_index::{bit_set::BitSet, Idx, IndexVec}; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::{self, ConstInt, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; use rustc_target::abi::{Abi, FieldIdx, HasDataLayout, Size, TargetDataLayout, VariantIdx}; -use crate::const_prop::CanConstProp; -use crate::const_prop::ConstPropMode; use crate::dataflow_const_prop::DummyMachine; use crate::errors::{AssertLint, AssertLintKind}; use crate::MirLint; @@ -849,3 +846,128 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { } } } + +/// The maximum number of bytes that we'll allocate space for a local or the return value. +/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just +/// Severely regress performance. +const MAX_ALLOC_LIMIT: u64 = 1024; + +/// The mode that `ConstProp` is allowed to run in for a given `Local`. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum ConstPropMode { + /// The `Local` can be propagated into and reads of this `Local` can also be propagated. + FullConstProp, + /// The `Local` can only be propagated into and from its own block. + OnlyInsideOwnBlock, + /// The `Local` cannot be part of propagation at all. Any statement + /// referencing it either for reading or writing will not get propagated. + NoPropagation, +} + +pub struct CanConstProp { + can_const_prop: IndexVec, + // False at the beginning. Once set, no more assignments are allowed to that local. + found_assignment: BitSet, +} + +impl CanConstProp { + /// Returns true if `local` can be propagated + pub fn check<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + body: &Body<'tcx>, + ) -> IndexVec { + let mut cpv = CanConstProp { + can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), + found_assignment: BitSet::new_empty(body.local_decls.len()), + }; + for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { + let ty = body.local_decls[local].ty; + match tcx.layout_of(param_env.and(ty)) { + Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {} + // Either the layout fails to compute, then we can't use this local anyway + // or the local is too large, then we don't want to. + _ => { + *val = ConstPropMode::NoPropagation; + continue; + } + } + } + // Consider that arguments are assigned on entry. + for arg in body.args_iter() { + cpv.found_assignment.insert(arg); + } + cpv.visit_body(body); + cpv.can_const_prop + } +} + +impl<'tcx> Visitor<'tcx> for CanConstProp { + fn visit_place(&mut self, place: &Place<'tcx>, mut context: PlaceContext, loc: Location) { + use rustc_middle::mir::visit::PlaceContext::*; + + // Dereferencing just read the addess of `place.local`. + if place.projection.first() == Some(&PlaceElem::Deref) { + context = NonMutatingUse(NonMutatingUseContext::Copy); + } + + self.visit_local(place.local, context, loc); + self.visit_projection(place.as_ref(), context, loc); + } + + fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { + use rustc_middle::mir::visit::PlaceContext::*; + match context { + // These are just stores, where the storing is not propagatable, but there may be later + // mutations of the same local via `Store` + | MutatingUse(MutatingUseContext::Call) + | MutatingUse(MutatingUseContext::AsmOutput) + | MutatingUse(MutatingUseContext::Deinit) + // Actual store that can possibly even propagate a value + | MutatingUse(MutatingUseContext::Store) + | MutatingUse(MutatingUseContext::SetDiscriminant) => { + if !self.found_assignment.insert(local) { + match &mut self.can_const_prop[local] { + // If the local can only get propagated in its own block, then we don't have + // to worry about multiple assignments, as we'll nuke the const state at the + // end of the block anyway, and inside the block we overwrite previous + // states as applicable. + ConstPropMode::OnlyInsideOwnBlock => {} + ConstPropMode::NoPropagation => {} + other @ ConstPropMode::FullConstProp => { + trace!( + "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}", + local, other, + ); + *other = ConstPropMode::OnlyInsideOwnBlock; + } + } + } + } + // Reading constants is allowed an arbitrary number of times + NonMutatingUse(NonMutatingUseContext::Copy) + | NonMutatingUse(NonMutatingUseContext::Move) + | NonMutatingUse(NonMutatingUseContext::Inspect) + | NonMutatingUse(NonMutatingUseContext::PlaceMention) + | NonUse(_) => {} + + // These could be propagated with a smarter analysis or just some careful thinking about + // whether they'd be fine right now. + MutatingUse(MutatingUseContext::Yield) + | MutatingUse(MutatingUseContext::Drop) + | MutatingUse(MutatingUseContext::Retag) + // These can't ever be propagated under any scheme, as we can't reason about indirect + // mutation. + | NonMutatingUse(NonMutatingUseContext::SharedBorrow) + | NonMutatingUse(NonMutatingUseContext::FakeBorrow) + | NonMutatingUse(NonMutatingUseContext::AddressOf) + | MutatingUse(MutatingUseContext::Borrow) + | MutatingUse(MutatingUseContext::AddressOf) => { + trace!("local {:?} can't be propagated because it's used: {:?}", local, context); + self.can_const_prop[local] = ConstPropMode::NoPropagation; + } + MutatingUse(MutatingUseContext::Projection) + | NonMutatingUse(NonMutatingUseContext::Projection) => bug!("visit_place should not pass {context:?} for {local:?}"), + } + } +} diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 86e99a8a5b5..9dc7a50eca9 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -21,7 +21,32 @@ use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; use rustc_target::abi::{Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT}; -use crate::const_prop::throw_machine_stop_str; +/// Macro for machine-specific `InterpError` without allocation. +/// (These will never be shown to the user, but they help diagnose ICEs.) +pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{ + // We make a new local type for it. The type itself does not carry any information, + // but its vtable (for the `MachineStopType` trait) does. + #[derive(Debug)] + struct Zst; + // Printing this type shows the desired string. + impl std::fmt::Display for Zst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $($tt)*) + } + } + + impl rustc_middle::mir::interpret::MachineStopType for Zst { + fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage { + self.to_string().into() + } + + fn add_args( + self: Box, + _: &mut dyn FnMut(rustc_errors::DiagnosticArgName, rustc_errors::DiagnosticArgValue), + ) {} + } + throw_machine_stop!(Zst) +}} // These constants are somewhat random guesses and have not been optimized. // If `tcx.sess.mir_opt_level() >= 4`, we ignore the limits (this can become very expensive). diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 7f0e6f90dbb..74b36eb5ee8 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -59,7 +59,6 @@ mod remove_place_mention; mod add_subtyping_projections; pub mod cleanup_post_borrowck; mod const_debuginfo; -mod const_prop; mod const_prop_lint; mod copy_prop; mod coroutine; From 6be93ccbee1caf4aaf2d9f5d386b9dccdb385ca8 Mon Sep 17 00:00:00 2001 From: SabrinaJewson Date: Sun, 18 Feb 2024 14:04:27 +0000 Subject: [PATCH 8/8] Add uncontroversial syscall doc aliases to std docs --- library/std/src/env.rs | 3 ++- library/std/src/fs.rs | 18 ++++++++++++++++-- library/std/src/os/wasi/fs.rs | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 3 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 { /// 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>(path: P) -> io::Result<()> { os_imp::chdir(path.as_ref()) @@ -655,6 +655,7 @@ pub fn home_dir() -> Option { /// } /// ``` #[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/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 { 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 { 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 { self.0.created().map(FromInner::from_inner) @@ -1879,6 +1883,7 @@ impl AsInner for DirEntry { /// Ok(()) /// } /// ``` +#[doc(alias = "rm", alias = "unlink", alias = "DeleteFile")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove_file>(path: P) -> io::Result<()> { fs_imp::unlink(path.as_ref()) @@ -1917,6 +1922,7 @@ pub fn remove_file>(path: P) -> io::Result<()> { /// Ok(()) /// } /// ``` +#[doc(alias = "stat")] #[stable(feature = "rust1", since = "1.0.0")] pub fn metadata>(path: P) -> io::Result { fs_imp::stat(path.as_ref()).map(Metadata) @@ -1951,6 +1957,7 @@ pub fn metadata>(path: P) -> io::Result { /// Ok(()) /// } /// ``` +#[doc(alias = "lstat")] #[stable(feature = "symlink_metadata", since = "1.1.0")] pub fn symlink_metadata>(path: P) -> io::Result { fs_imp::lstat(path.as_ref()).map(Metadata) @@ -1994,6 +2001,7 @@ pub fn symlink_metadata>(path: P) -> io::Result { /// Ok(()) /// } /// ``` +#[doc(alias = "mv", alias = "MoveFile", alias = "MoveFileEx")] #[stable(feature = "rust1", since = "1.0.0")] pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { fs_imp::rename(from.as_ref(), to.as_ref()) @@ -2052,6 +2060,9 @@ pub fn rename, Q: AsRef>(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, Q: AsRef>(from: P, to: Q) -> io::Result { fs_imp::copy(from.as_ref(), to.as_ref()) @@ -2096,6 +2107,7 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// Ok(()) /// } /// ``` +#[doc(alias = "CreateHardLink", alias = "linkat")] #[stable(feature = "rust1", since = "1.0.0")] pub fn hard_link, Q: AsRef>(original: P, link: Q) -> io::Result<()> { fs_imp::link(original.as_ref(), link.as_ref()) @@ -2245,7 +2257,7 @@ pub fn canonicalize>(path: P) -> io::Result { /// 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>(path: P) -> io::Result<()> { @@ -2326,7 +2338,7 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { /// Ok(()) /// } /// ``` -#[doc(alias = "rmdir")] +#[doc(alias = "rmdir", alias = "RemoveDirectory")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove_dir>(path: P) -> io::Result<()> { fs_imp::rmdir(path.as_ref()) @@ -2449,6 +2461,7 @@ pub fn remove_dir_all>(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>(path: P) -> io::Result { fs_imp::readdir(path.as_ref()).map(ReadDir) @@ -2484,6 +2497,7 @@ pub fn read_dir>(path: P) -> io::Result { /// Ok(()) /// } /// ``` +#[doc(alias = "chmod", alias = "SetFileAttributes")] #[stable(feature = "set_permissions", since = "1.1.0")] pub fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { fs_imp::set_perm(path.as_ref(), perm.0) 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; /// 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>(&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>(&self, path: P) -> io::Result; /// Return the attributes of a file or directory. /// /// This corresponds to the `path_filestat_get` syscall. + #[doc(alias = "path_filestat_get")] fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result; /// Unlink a file. /// /// This corresponds to the `path_unlink_file` syscall. + #[doc(alias = "path_unlink_file")] fn remove_file>(&self, path: P) -> io::Result<()>; /// Remove a directory. /// /// This corresponds to the `path_remove_directory` syscall. + #[doc(alias = "path_remove_directory")] fn remove_directory>(&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>(&self, file: &File, path: P) -> io::Result; } @@ -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, U: AsRef>( old_fd: &File, old_flags: u32, @@ -518,6 +530,7 @@ pub fn link, U: AsRef>( /// Rename a file or directory. /// /// This corresponds to the `path_rename` syscall. +#[doc(alias = "path_rename")] pub fn rename, U: AsRef>( old_fd: &File, old_path: P, @@ -534,6 +547,7 @@ pub fn rename, U: AsRef>( /// Create a symbolic link. /// /// This corresponds to the `path_symlink` syscall. +#[doc(alias = "path_symlink")] pub fn symlink, U: AsRef>( old_path: P, fd: &File,