diff --git a/Cargo.lock b/Cargo.lock index 2345f07b10df0..2a66cf6bd8cdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,7 +199,7 @@ dependencies = [ "rustc-hash 2.1.1", "serde", "serde_derive", - "syn 2.0.110", + "syn", ] [[package]] @@ -396,7 +396,7 @@ checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -573,7 +573,6 @@ checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ "iana-time-zone", "num-traits", - "serde", "windows-link 0.2.1", ] @@ -635,10 +634,10 @@ version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -768,7 +767,6 @@ dependencies = [ "serde", "serde_json", "similar", - "spdx-rs", ] [[package]] @@ -804,7 +802,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1047,7 +1045,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.110", + "syn", ] [[package]] @@ -1061,7 +1059,7 @@ dependencies = [ "indexmap", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1079,7 +1077,7 @@ dependencies = [ "indexmap", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1103,7 +1101,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.110", + "syn", ] [[package]] @@ -1114,7 +1112,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1146,7 +1144,7 @@ checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1167,7 +1165,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1177,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.110", + "syn", ] [[package]] @@ -1189,7 +1187,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1264,7 +1262,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1686,12 +1684,6 @@ dependencies = [ "foldhash 0.2.0", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -2092,7 +2084,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -2389,7 +2381,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -2534,7 +2526,7 @@ checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -2915,7 +2907,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -3125,7 +3117,7 @@ checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -3316,7 +3308,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -3409,7 +3401,7 @@ checksum = "8100bb34c0a1d0f907143db3149e6b4eea3c33b9ee8b189720168e818303986f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -4125,7 +4117,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -4271,7 +4263,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.110", + "syn", "synstructure", ] @@ -4853,7 +4845,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", "synstructure", ] @@ -4953,7 +4945,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.110", + "syn", ] [[package]] @@ -5070,7 +5062,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.110", + "syn", ] [[package]] @@ -5156,7 +5148,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5167,7 +5159,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5300,35 +5292,6 @@ dependencies = [ "color-eyre", ] -[[package]] -name = "spdx-expression" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d7ac03c67c572d85049d6db815e20a4a19b41b3d5cca732ac582342021ad77" -dependencies = [ - "nom", - "serde", - "thiserror 1.0.69", - "tracing", -] - -[[package]] -name = "spdx-rs" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990870190ec8d8c64ba66e4a6746243d6e57d99353991e0e6092334833f429b1" -dependencies = [ - "chrono", - "log", - "nom", - "serde", - "spdx-expression", - "strum", - "strum_macros", - "thiserror 1.0.69", - "uuid", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -5394,36 +5357,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.110" @@ -5443,7 +5376,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5580,7 +5513,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5591,7 +5524,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5833,7 +5766,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -6029,7 +5962,7 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.110", + "syn", "unic-langid-impl", ] @@ -6267,7 +6200,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.110", + "syn", "wasm-bindgen-shared", ] @@ -6524,7 +6457,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -6535,7 +6468,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -6905,7 +6838,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", "synstructure", ] @@ -6926,7 +6859,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -6946,7 +6879,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", "synstructure", ] @@ -6981,7 +6914,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index adcf4086fb1e4..db5aecde3472c 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -370,6 +370,8 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> Box { let ecx = &cx.ext_cx; let mut tests = cx.test_cases.clone(); + // Note that this sort is load-bearing: the libtest harness uses binary search to find tests by + // name. tests.sort_by(|a, b| a.name.as_str().cmp(b.name.as_str())); ecx.expr_array_ref( diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 3231c4193064c..2416406576204 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -93,31 +93,31 @@ const MAX_THREE_B: u32 = 0x10000; /// The highest valid code point a `char` can have, `'\u{10FFFF}'`. Use [`char::MAX`] instead. #[stable(feature = "rust1", since = "1.0.0")] +#[deprecated(since = "TBD", note = "replaced by the `MAX` associated constant on `char`")] pub const MAX: char = char::MAX; -/// The maximum number of bytes required to [encode](char::encode_utf8) a `char` to -/// UTF-8 encoding. -#[unstable(feature = "char_max_len", issue = "121714")] -pub const MAX_LEN_UTF8: usize = char::MAX_LEN_UTF8; - -/// The maximum number of two-byte units required to [encode](char::encode_utf16) a `char` -/// to UTF-16 encoding. -#[unstable(feature = "char_max_len", issue = "121714")] -pub const MAX_LEN_UTF16: usize = char::MAX_LEN_UTF16; - /// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a /// decoding error. Use [`char::REPLACEMENT_CHARACTER`] instead. #[stable(feature = "decode_utf16", since = "1.9.0")] +#[deprecated( + since = "TBD", + note = "replaced by the `REPLACEMENT_CHARACTER` associated constant on `char`" +)] pub const REPLACEMENT_CHARACTER: char = char::REPLACEMENT_CHARACTER; /// The version of [Unicode](https://www.unicode.org/) that the Unicode parts of /// `char` and `str` methods are based on. Use [`char::UNICODE_VERSION`] instead. #[stable(feature = "unicode_version", since = "1.45.0")] +#[deprecated( + since = "TBD", + note = "replaced by the `UNICODE_VERSION` associated constant on `char`" +)] pub const UNICODE_VERSION: (u8, u8, u8) = char::UNICODE_VERSION; /// Creates an iterator over the UTF-16 encoded code points in `iter`, returning /// unpaired surrogates as `Err`s. Use [`char::decode_utf16`] instead. #[stable(feature = "decode_utf16", since = "1.9.0")] +#[deprecated(since = "TBD", note = "replaced by the `decode_utf16` method on `char`")] #[inline] pub fn decode_utf16>(iter: I) -> DecodeUtf16 { self::decode::decode_utf16(iter) @@ -126,6 +126,7 @@ pub fn decode_utf16>(iter: I) -> DecodeUtf16 Option { @@ -136,6 +137,7 @@ pub const fn from_u32(i: u32) -> Option { /// instead. #[stable(feature = "char_from_unchecked", since = "1.5.0")] #[rustc_const_stable(feature = "const_char_from_u32_unchecked", since = "1.81.0")] +#[deprecated(since = "TBD", note = "replaced by the `from_u32_unchecked` method on `char`")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { @@ -146,6 +148,7 @@ pub const unsafe fn from_u32_unchecked(i: u32) -> char { /// Converts a digit in the given radix to a `char`. Use [`char::from_digit`] instead. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_char_convert", since = "1.67.0")] +#[deprecated(since = "TBD", note = "replaced by the `from_digit` method on `char`")] #[must_use] #[inline] pub const fn from_digit(num: u32, radix: u32) -> Option { diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs new file mode 100644 index 0000000000000..fe12de2952f0a --- /dev/null +++ b/library/core/src/io/error.rs @@ -0,0 +1,372 @@ +#![unstable(feature = "core_io", issue = "154046")] + +use crate::fmt; + +/// A list specifying general categories of I/O error. +/// +/// This list is intended to grow over time and it is not recommended to +/// exhaustively match against it. +/// +/// # Handling errors and matching on `ErrorKind` +/// +/// In application code, use `match` for the `ErrorKind` values you are +/// expecting; use `_` to match "all other errors". +/// +/// In comprehensive and thorough tests that want to verify that a test doesn't +/// return any known incorrect error kind, you may want to cut-and-paste the +/// current full list of errors from here into your test code, and then match +/// `_` as the correct case. This seems counterintuitive, but it will make your +/// tests more robust. In particular, if you want to verify that your code does +/// produce an unrecognized error kind, the robust solution is to check for all +/// the recognized error kinds and fail in those cases. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "io_errorkind")] +#[allow(deprecated)] +#[non_exhaustive] +pub enum ErrorKind { + /// An entity was not found, often a file. + #[stable(feature = "rust1", since = "1.0.0")] + NotFound, + /// The operation lacked the necessary privileges to complete. + #[stable(feature = "rust1", since = "1.0.0")] + PermissionDenied, + /// The connection was refused by the remote server. + #[stable(feature = "rust1", since = "1.0.0")] + ConnectionRefused, + /// The connection was reset by the remote server. + #[stable(feature = "rust1", since = "1.0.0")] + ConnectionReset, + /// The remote host is not reachable. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + HostUnreachable, + /// The network containing the remote host is not reachable. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + NetworkUnreachable, + /// The connection was aborted (terminated) by the remote server. + #[stable(feature = "rust1", since = "1.0.0")] + ConnectionAborted, + /// The network operation failed because it was not connected yet. + #[stable(feature = "rust1", since = "1.0.0")] + NotConnected, + /// A socket address could not be bound because the address is already in + /// use elsewhere. + #[stable(feature = "rust1", since = "1.0.0")] + AddrInUse, + /// A nonexistent interface was requested or the requested address was not + /// local. + #[stable(feature = "rust1", since = "1.0.0")] + AddrNotAvailable, + /// The system's networking is down. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + NetworkDown, + /// The operation failed because a pipe was closed. + #[stable(feature = "rust1", since = "1.0.0")] + BrokenPipe, + /// An entity already exists, often a file. + #[stable(feature = "rust1", since = "1.0.0")] + AlreadyExists, + /// The operation needs to block to complete, but the blocking operation was + /// requested to not occur. + #[stable(feature = "rust1", since = "1.0.0")] + WouldBlock, + /// A filesystem object is, unexpectedly, not a directory. + /// + /// For example, a filesystem path was specified where one of the intermediate directory + /// components was, in fact, a plain file. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + NotADirectory, + /// The filesystem object is, unexpectedly, a directory. + /// + /// A directory was specified when a non-directory was expected. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + IsADirectory, + /// A non-empty directory was specified where an empty directory was expected. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + DirectoryNotEmpty, + /// The filesystem or storage medium is read-only, but a write operation was attempted. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + ReadOnlyFilesystem, + /// Loop in the filesystem or IO subsystem; often, too many levels of symbolic links. + /// + /// There was a loop (or excessively long chain) resolving a filesystem object + /// or file IO object. + /// + /// On Unix this is usually the result of a symbolic link loop; or, of exceeding the + /// system-specific limit on the depth of symlink traversal. + #[unstable(feature = "io_error_more", issue = "86442")] + FilesystemLoop, + /// Stale network file handle. + /// + /// With some network filesystems, notably NFS, an open file (or directory) can be invalidated + /// by problems with the network or server. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + StaleNetworkFileHandle, + /// A parameter was incorrect. + #[stable(feature = "rust1", since = "1.0.0")] + InvalidInput, + /// Data not valid for the operation were encountered. + /// + /// Unlike [`InvalidInput`], this typically means that the operation + /// parameters were valid, however the error was caused by malformed + /// input data. + /// + /// For example, a function that reads a file into a string will error with + /// `InvalidData` if the file's contents are not valid UTF-8. + /// + /// [`InvalidInput`]: ErrorKind::InvalidInput + #[stable(feature = "io_invalid_data", since = "1.2.0")] + InvalidData, + /// The I/O operation's timeout expired, causing it to be canceled. + #[stable(feature = "rust1", since = "1.0.0")] + TimedOut, + /// An error returned when an operation could not be completed because a + /// call to an underlying writer returned [`Ok(0)`]. + /// + /// This typically means that an operation could only succeed if it wrote a + /// particular number of bytes but only a smaller number of bytes could be + /// written. + /// + /// [`Ok(0)`]: Ok + #[stable(feature = "rust1", since = "1.0.0")] + WriteZero, + /// The underlying storage (typically, a filesystem) is full. + /// + /// This does not include out of quota errors. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + StorageFull, + /// Seek on unseekable file. + /// + /// Seeking was attempted on an open file handle which is not suitable for seeking - for + /// example, on Unix, a named pipe opened with `File::open`. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + NotSeekable, + /// Filesystem quota or some other kind of quota was exceeded. + #[stable(feature = "io_error_quota_exceeded", since = "1.85.0")] + QuotaExceeded, + /// File larger than allowed or supported. + /// + /// This might arise from a hard limit of the underlying filesystem or file access API, or from + /// an administratively imposed resource limitation. Simple disk full, and out of quota, have + /// their own errors. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + FileTooLarge, + /// Resource is busy. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + ResourceBusy, + /// Executable file is busy. + /// + /// An attempt was made to write to a file which is also in use as a running program. (Not all + /// operating systems detect this situation.) + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + ExecutableFileBusy, + /// Deadlock (avoided). + /// + /// A file locking operation would result in deadlock. This situation is typically detected, if + /// at all, on a best-effort basis. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + Deadlock, + /// Cross-device or cross-filesystem (hard) link or rename. + #[stable(feature = "io_error_crosses_devices", since = "1.85.0")] + CrossesDevices, + /// Too many (hard) links to the same filesystem object. + /// + /// The filesystem does not support making so many hardlinks to the same file. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + TooManyLinks, + /// A filename was invalid. + /// + /// This error can also occur if a length limit for a name was exceeded. + #[stable(feature = "io_error_invalid_filename", since = "1.87.0")] + InvalidFilename, + /// Program argument list too long. + /// + /// When trying to run an external program, a system or process limit on the size of the + /// arguments would have been exceeded. + #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] + ArgumentListTooLong, + /// This operation was interrupted. + /// + /// Interrupted operations can typically be retried. + #[stable(feature = "rust1", since = "1.0.0")] + Interrupted, + + /// This operation is unsupported on this platform. + /// + /// This means that the operation can never succeed. + #[stable(feature = "unsupported_error", since = "1.53.0")] + Unsupported, + + // ErrorKinds which are primarily categorisations for OS error + // codes should be added above. + // + /// An error returned when an operation could not be completed because an + /// "end of file" was reached prematurely. + /// + /// This typically means that an operation could only succeed if it read a + /// particular number of bytes but only a smaller number of bytes could be + /// read. + #[stable(feature = "read_exact", since = "1.6.0")] + UnexpectedEof, + + /// An operation could not be completed, because it failed + /// to allocate enough memory. + #[stable(feature = "out_of_memory_error", since = "1.54.0")] + OutOfMemory, + + /// The operation was partially successful and needs to be checked + /// later on due to not blocking. + #[unstable(feature = "io_error_inprogress", issue = "130840")] + InProgress, + + // "Unusual" error kinds which do not correspond simply to (sets + // of) OS error codes, should be added just above this comment. + // `Other` and `Uncategorized` should remain at the end: + // + /// A custom error that does not fall under any other I/O error kind. + /// + /// This can be used to construct your own errors that do not match any + /// [`ErrorKind`]. + /// + /// This [`ErrorKind`] is not used by the standard library. + /// + /// Errors from the standard library that do not fall under any of the I/O + /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern. + /// New [`ErrorKind`]s might be added in the future for some of those. + #[stable(feature = "rust1", since = "1.0.0")] + Other, + + /// Any I/O error from the standard library that's not part of this list. + /// + /// Errors that are `Uncategorized` now may move to a different or a new + /// [`ErrorKind`] variant in the future. It is not recommended to match + /// an error against `Uncategorized`; use a wildcard match (`_`) instead. + #[unstable(feature = "io_error_uncategorized", issue = "none")] + #[doc(hidden)] + Uncategorized, +} + +impl ErrorKind { + pub(crate) const fn as_str(&self) -> &'static str { + use ErrorKind::*; + match *self { + // tidy-alphabetical-start + AddrInUse => "address in use", + AddrNotAvailable => "address not available", + AlreadyExists => "entity already exists", + ArgumentListTooLong => "argument list too long", + BrokenPipe => "broken pipe", + ConnectionAborted => "connection aborted", + ConnectionRefused => "connection refused", + ConnectionReset => "connection reset", + CrossesDevices => "cross-device link or rename", + Deadlock => "deadlock", + DirectoryNotEmpty => "directory not empty", + ExecutableFileBusy => "executable file busy", + FileTooLarge => "file too large", + FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", + HostUnreachable => "host unreachable", + InProgress => "in progress", + Interrupted => "operation interrupted", + InvalidData => "invalid data", + InvalidFilename => "invalid filename", + InvalidInput => "invalid input parameter", + IsADirectory => "is a directory", + NetworkDown => "network down", + NetworkUnreachable => "network unreachable", + NotADirectory => "not a directory", + NotConnected => "not connected", + NotFound => "entity not found", + NotSeekable => "seek on unseekable file", + Other => "other error", + OutOfMemory => "out of memory", + PermissionDenied => "permission denied", + QuotaExceeded => "quota exceeded", + ReadOnlyFilesystem => "read-only filesystem or storage medium", + ResourceBusy => "resource busy", + StaleNetworkFileHandle => "stale network file handle", + StorageFull => "no storage space", + TimedOut => "timed out", + TooManyLinks => "too many links", + Uncategorized => "uncategorized error", + UnexpectedEof => "unexpected end of file", + Unsupported => "unsupported", + WouldBlock => "operation would block", + WriteZero => "write zero", + // tidy-alphabetical-end + } + } + + // This compiles to the same code as the check+transmute, but doesn't require + // unsafe, or to hard-code max ErrorKind or its size in a way the compiler + // couldn't verify. + #[inline] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[doc(hidden)] + pub const fn from_prim(ek: u32) -> Option { + macro_rules! from_prim { + ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ + // Force a compile error if the list gets out of date. + const _: fn(e: $Enum) = |e: $Enum| match e { + $($Enum::$Variant => (),)* + }; + match $prim { + $(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)* + _ => None, + } + }} + } + from_prim!(ek => ErrorKind { + NotFound, + PermissionDenied, + ConnectionRefused, + ConnectionReset, + HostUnreachable, + NetworkUnreachable, + ConnectionAborted, + NotConnected, + AddrInUse, + AddrNotAvailable, + NetworkDown, + BrokenPipe, + AlreadyExists, + WouldBlock, + NotADirectory, + IsADirectory, + DirectoryNotEmpty, + ReadOnlyFilesystem, + FilesystemLoop, + StaleNetworkFileHandle, + InvalidInput, + InvalidData, + TimedOut, + WriteZero, + StorageFull, + NotSeekable, + QuotaExceeded, + FileTooLarge, + ResourceBusy, + ExecutableFileBusy, + Deadlock, + CrossesDevices, + TooManyLinks, + InvalidFilename, + ArgumentListTooLong, + Interrupted, + Other, + UnexpectedEof, + Unsupported, + OutOfMemory, + InProgress, + Uncategorized, + }) + } +} + +#[stable(feature = "io_errorkind_display", since = "1.60.0")] +impl fmt::Display for ErrorKind { + /// Shows a human-readable description of the `ErrorKind`. + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.as_str()) + } +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 2f20180cdc9a2..c34421523b643 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -1,6 +1,9 @@ //! Traits, helpers, and type definitions for core I/O functionality. mod borrowed_buf; +mod error; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; +#[unstable(feature = "core_io", issue = "154046")] +pub use self::error::ErrorKind; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 3b72752cee9a1..bdc1c48f70dfe 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -305,7 +305,7 @@ pub mod bstr; pub mod cell; pub mod char; pub mod ffi; -#[unstable(feature = "core_io_borrowed_buf", issue = "117693")] +#[unstable(feature = "core_io", issue = "154046")] pub mod io; pub mod iter; pub mod net; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 33397e56b86c5..08a12b6447e61 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -168,7 +168,7 @@ macro_rules! assert_ne { #[allow_internal_unstable(panic_internals)] #[rustc_macro_transparency = "semiopaque"] pub macro assert_matches { - ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => { + ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => {{ match $left { $( $pattern )|+ $( if $guard )? => {} ref left_val => { @@ -179,8 +179,8 @@ pub macro assert_matches { ); } } - }, - ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+) => { + }}, + ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+) => {{ match $left { $( $pattern )|+ $( if $guard )? => {} ref left_val => { @@ -191,7 +191,7 @@ pub macro assert_matches { ); } } - }, + }}, } /// Selects code at compile-time based on `cfg` predicates. diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 2c561b5b0529e..c4292c2a421b1 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -12,7 +12,6 @@ #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] -#![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(cmp_minmax)] #![feature(const_array)] @@ -37,6 +36,7 @@ #![feature(const_unsigned_bigint_helpers)] #![feature(core_intrinsics)] #![feature(core_intrinsics_fallbacks)] +#![feature(core_io)] #![feature(core_io_borrowed_buf)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] diff --git a/library/coretests/tests/macros.rs b/library/coretests/tests/macros.rs index 50b5eb63e43a7..9f73ebd253c3b 100644 --- a/library/coretests/tests/macros.rs +++ b/library/coretests/tests/macros.rs @@ -1,5 +1,7 @@ #![allow(unused_must_use)] +use std::{assert_matches, debug_assert_matches}; + #[allow(dead_code)] trait Trait { fn blah(&self); @@ -219,3 +221,27 @@ fn _matches_does_not_trigger_non_exhaustive_omitted_patterns_lint(o: core::sync: // Ordering is a #[non_exhaustive] enum from a separate crate let _m = matches!(o, core::sync::atomic::Ordering::Relaxed); } + +struct MutRefWithDrop<'a>(&'a mut u32); + +// MutRefWithDrop needs to have a non-trivial drop to encounter potential lifetime issues if the +// macros don't introduce a temporary scope. +impl Drop for MutRefWithDrop<'_> { + fn drop(&mut self) { + *self.0 = u32::MAX; + } +} + +#[test] +fn temporary_scope_introduction() { + // Fails to compile if the macros don't introduce a temporary scope, since `&mut val` would + // create a second mutable borrow while `MutRefWithDrop` still holds a unique ref. + // See https://github.com/rust-lang/rust/issues/154406 for reference. + let mut val = 0; + + (assert_matches!(*MutRefWithDrop(&mut val).0, 0), std::mem::take(&mut val)); + (assert_matches!(*MutRefWithDrop(&mut val).0, 0, "msg"), std::mem::take(&mut val)); + + (debug_assert_matches!(*MutRefWithDrop(&mut val).0, 0), std::mem::take(&mut val)); + (debug_assert_matches!(*MutRefWithDrop(&mut val).0, 0, "msg"), std::mem::take(&mut val)); +} diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index e6c6f7d766c02..6f565bb37c53b 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,6 +1,9 @@ #[cfg(test)] mod tests; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::io::ErrorKind; + // On 64-bit platforms, `io::Error` may use a bit-packed representation to // reduce size. However, this representation assumes that error codes are // always 32-bit wide. @@ -206,323 +209,6 @@ struct Custom { error: Box, } -/// A list specifying general categories of I/O error. -/// -/// This list is intended to grow over time and it is not recommended to -/// exhaustively match against it. -/// -/// It is used with the [`io::Error`] type. -/// -/// [`io::Error`]: Error -/// -/// # Handling errors and matching on `ErrorKind` -/// -/// In application code, use `match` for the `ErrorKind` values you are -/// expecting; use `_` to match "all other errors". -/// -/// In comprehensive and thorough tests that want to verify that a test doesn't -/// return any known incorrect error kind, you may want to cut-and-paste the -/// current full list of errors from here into your test code, and then match -/// `_` as the correct case. This seems counterintuitive, but it will make your -/// tests more robust. In particular, if you want to verify that your code does -/// produce an unrecognized error kind, the robust solution is to check for all -/// the recognized error kinds and fail in those cases. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "io_errorkind")] -#[allow(deprecated)] -#[non_exhaustive] -pub enum ErrorKind { - /// An entity was not found, often a file. - #[stable(feature = "rust1", since = "1.0.0")] - NotFound, - /// The operation lacked the necessary privileges to complete. - #[stable(feature = "rust1", since = "1.0.0")] - PermissionDenied, - /// The connection was refused by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionRefused, - /// The connection was reset by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionReset, - /// The remote host is not reachable. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - HostUnreachable, - /// The network containing the remote host is not reachable. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - NetworkUnreachable, - /// The connection was aborted (terminated) by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionAborted, - /// The network operation failed because it was not connected yet. - #[stable(feature = "rust1", since = "1.0.0")] - NotConnected, - /// A socket address could not be bound because the address is already in - /// use elsewhere. - #[stable(feature = "rust1", since = "1.0.0")] - AddrInUse, - /// A nonexistent interface was requested or the requested address was not - /// local. - #[stable(feature = "rust1", since = "1.0.0")] - AddrNotAvailable, - /// The system's networking is down. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - NetworkDown, - /// The operation failed because a pipe was closed. - #[stable(feature = "rust1", since = "1.0.0")] - BrokenPipe, - /// An entity already exists, often a file. - #[stable(feature = "rust1", since = "1.0.0")] - AlreadyExists, - /// The operation needs to block to complete, but the blocking operation was - /// requested to not occur. - #[stable(feature = "rust1", since = "1.0.0")] - WouldBlock, - /// A filesystem object is, unexpectedly, not a directory. - /// - /// For example, a filesystem path was specified where one of the intermediate directory - /// components was, in fact, a plain file. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - NotADirectory, - /// The filesystem object is, unexpectedly, a directory. - /// - /// A directory was specified when a non-directory was expected. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - IsADirectory, - /// A non-empty directory was specified where an empty directory was expected. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - DirectoryNotEmpty, - /// The filesystem or storage medium is read-only, but a write operation was attempted. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - ReadOnlyFilesystem, - /// Loop in the filesystem or IO subsystem; often, too many levels of symbolic links. - /// - /// There was a loop (or excessively long chain) resolving a filesystem object - /// or file IO object. - /// - /// On Unix this is usually the result of a symbolic link loop; or, of exceeding the - /// system-specific limit on the depth of symlink traversal. - #[unstable(feature = "io_error_more", issue = "86442")] - FilesystemLoop, - /// Stale network file handle. - /// - /// With some network filesystems, notably NFS, an open file (or directory) can be invalidated - /// by problems with the network or server. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - StaleNetworkFileHandle, - /// A parameter was incorrect. - #[stable(feature = "rust1", since = "1.0.0")] - InvalidInput, - /// Data not valid for the operation were encountered. - /// - /// Unlike [`InvalidInput`], this typically means that the operation - /// parameters were valid, however the error was caused by malformed - /// input data. - /// - /// For example, a function that reads a file into a string will error with - /// `InvalidData` if the file's contents are not valid UTF-8. - /// - /// [`InvalidInput`]: ErrorKind::InvalidInput - #[stable(feature = "io_invalid_data", since = "1.2.0")] - InvalidData, - /// The I/O operation's timeout expired, causing it to be canceled. - #[stable(feature = "rust1", since = "1.0.0")] - TimedOut, - /// An error returned when an operation could not be completed because a - /// call to [`write`] returned [`Ok(0)`]. - /// - /// This typically means that an operation could only succeed if it wrote a - /// particular number of bytes but only a smaller number of bytes could be - /// written. - /// - /// [`write`]: crate::io::Write::write - /// [`Ok(0)`]: Ok - #[stable(feature = "rust1", since = "1.0.0")] - WriteZero, - /// The underlying storage (typically, a filesystem) is full. - /// - /// This does not include out of quota errors. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - StorageFull, - /// Seek on unseekable file. - /// - /// Seeking was attempted on an open file handle which is not suitable for seeking - for - /// example, on Unix, a named pipe opened with `File::open`. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - NotSeekable, - /// Filesystem quota or some other kind of quota was exceeded. - #[stable(feature = "io_error_quota_exceeded", since = "1.85.0")] - QuotaExceeded, - /// File larger than allowed or supported. - /// - /// This might arise from a hard limit of the underlying filesystem or file access API, or from - /// an administratively imposed resource limitation. Simple disk full, and out of quota, have - /// their own errors. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - FileTooLarge, - /// Resource is busy. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - ResourceBusy, - /// Executable file is busy. - /// - /// An attempt was made to write to a file which is also in use as a running program. (Not all - /// operating systems detect this situation.) - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - ExecutableFileBusy, - /// Deadlock (avoided). - /// - /// A file locking operation would result in deadlock. This situation is typically detected, if - /// at all, on a best-effort basis. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - Deadlock, - /// Cross-device or cross-filesystem (hard) link or rename. - #[stable(feature = "io_error_crosses_devices", since = "1.85.0")] - CrossesDevices, - /// Too many (hard) links to the same filesystem object. - /// - /// The filesystem does not support making so many hardlinks to the same file. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - TooManyLinks, - /// A filename was invalid. - /// - /// This error can also occur if a length limit for a name was exceeded. - #[stable(feature = "io_error_invalid_filename", since = "1.87.0")] - InvalidFilename, - /// Program argument list too long. - /// - /// When trying to run an external program, a system or process limit on the size of the - /// arguments would have been exceeded. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - ArgumentListTooLong, - /// This operation was interrupted. - /// - /// Interrupted operations can typically be retried. - #[stable(feature = "rust1", since = "1.0.0")] - Interrupted, - - /// This operation is unsupported on this platform. - /// - /// This means that the operation can never succeed. - #[stable(feature = "unsupported_error", since = "1.53.0")] - Unsupported, - - // ErrorKinds which are primarily categorisations for OS error - // codes should be added above. - // - /// An error returned when an operation could not be completed because an - /// "end of file" was reached prematurely. - /// - /// This typically means that an operation could only succeed if it read a - /// particular number of bytes but only a smaller number of bytes could be - /// read. - #[stable(feature = "read_exact", since = "1.6.0")] - UnexpectedEof, - - /// An operation could not be completed, because it failed - /// to allocate enough memory. - #[stable(feature = "out_of_memory_error", since = "1.54.0")] - OutOfMemory, - - /// The operation was partially successful and needs to be checked - /// later on due to not blocking. - #[unstable(feature = "io_error_inprogress", issue = "130840")] - InProgress, - - // "Unusual" error kinds which do not correspond simply to (sets - // of) OS error codes, should be added just above this comment. - // `Other` and `Uncategorized` should remain at the end: - // - /// A custom error that does not fall under any other I/O error kind. - /// - /// This can be used to construct your own [`Error`]s that do not match any - /// [`ErrorKind`]. - /// - /// This [`ErrorKind`] is not used by the standard library. - /// - /// Errors from the standard library that do not fall under any of the I/O - /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern. - /// New [`ErrorKind`]s might be added in the future for some of those. - #[stable(feature = "rust1", since = "1.0.0")] - Other, - - /// Any I/O error from the standard library that's not part of this list. - /// - /// Errors that are `Uncategorized` now may move to a different or a new - /// [`ErrorKind`] variant in the future. It is not recommended to match - /// an error against `Uncategorized`; use a wildcard match (`_`) instead. - #[unstable(feature = "io_error_uncategorized", issue = "none")] - #[doc(hidden)] - Uncategorized, -} - -impl ErrorKind { - pub(crate) fn as_str(&self) -> &'static str { - use ErrorKind::*; - match *self { - // tidy-alphabetical-start - AddrInUse => "address in use", - AddrNotAvailable => "address not available", - AlreadyExists => "entity already exists", - ArgumentListTooLong => "argument list too long", - BrokenPipe => "broken pipe", - ConnectionAborted => "connection aborted", - ConnectionRefused => "connection refused", - ConnectionReset => "connection reset", - CrossesDevices => "cross-device link or rename", - Deadlock => "deadlock", - DirectoryNotEmpty => "directory not empty", - ExecutableFileBusy => "executable file busy", - FileTooLarge => "file too large", - FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", - HostUnreachable => "host unreachable", - InProgress => "in progress", - Interrupted => "operation interrupted", - InvalidData => "invalid data", - InvalidFilename => "invalid filename", - InvalidInput => "invalid input parameter", - IsADirectory => "is a directory", - NetworkDown => "network down", - NetworkUnreachable => "network unreachable", - NotADirectory => "not a directory", - NotConnected => "not connected", - NotFound => "entity not found", - NotSeekable => "seek on unseekable file", - Other => "other error", - OutOfMemory => "out of memory", - PermissionDenied => "permission denied", - QuotaExceeded => "quota exceeded", - ReadOnlyFilesystem => "read-only filesystem or storage medium", - ResourceBusy => "resource busy", - StaleNetworkFileHandle => "stale network file handle", - StorageFull => "no storage space", - TimedOut => "timed out", - TooManyLinks => "too many links", - Uncategorized => "uncategorized error", - UnexpectedEof => "unexpected end of file", - Unsupported => "unsupported", - WouldBlock => "operation would block", - WriteZero => "write zero", - // tidy-alphabetical-end - } - } -} - -#[stable(feature = "io_errorkind_display", since = "1.60.0")] -impl fmt::Display for ErrorKind { - /// Shows a human-readable description of the `ErrorKind`. - /// - /// This is similar to `impl Display for Error`, but doesn't require first converting to Error. - /// - /// # Examples - /// ``` - /// use std::io::ErrorKind; - /// assert_eq!("entity not found", ErrorKind::NotFound.to_string()); - /// ``` - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(self.as_str()) - } -} - /// Intended for use for errors not exposed to the user, where allocating onto /// the heap (for normal construction via Error::new) is too costly. #[stable(feature = "io_error_from_errorkind", since = "1.14.0")] @@ -1051,7 +737,7 @@ impl fmt::Display for Error { write!(fmt, "{detail} (os error {code})") } ErrorData::Custom(ref c) => c.error.fmt(fmt), - ErrorData::Simple(kind) => write!(fmt, "{}", kind.as_str()), + ErrorData::Simple(kind) => kind.fmt(fmt), ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), } } diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 7353816a8171b..7c237825459af 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -253,7 +253,7 @@ where } TAG_SIMPLE => { let kind_bits = (bits >> 32) as u32; - let kind = kind_from_prim(kind_bits).unwrap_or_else(|| { + let kind = ErrorKind::from_prim(kind_bits).unwrap_or_else(|| { debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits); // This means the `ptr` passed in was not valid, which violates // the unsafe contract of `decode_repr`. @@ -283,69 +283,6 @@ where } } -// This compiles to the same code as the check+transmute, but doesn't require -// unsafe, or to hard-code max ErrorKind or its size in a way the compiler -// couldn't verify. -#[inline] -fn kind_from_prim(ek: u32) -> Option { - macro_rules! from_prim { - ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ - // Force a compile error if the list gets out of date. - const _: fn(e: $Enum) = |e: $Enum| match e { - $($Enum::$Variant => ()),* - }; - match $prim { - $(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)* - _ => None, - } - }} - } - from_prim!(ek => ErrorKind { - NotFound, - PermissionDenied, - ConnectionRefused, - ConnectionReset, - HostUnreachable, - NetworkUnreachable, - ConnectionAborted, - NotConnected, - AddrInUse, - AddrNotAvailable, - NetworkDown, - BrokenPipe, - AlreadyExists, - WouldBlock, - NotADirectory, - IsADirectory, - DirectoryNotEmpty, - ReadOnlyFilesystem, - FilesystemLoop, - StaleNetworkFileHandle, - InvalidInput, - InvalidData, - TimedOut, - WriteZero, - StorageFull, - NotSeekable, - QuotaExceeded, - FileTooLarge, - ResourceBusy, - ExecutableFileBusy, - Deadlock, - CrossesDevices, - TooManyLinks, - InvalidFilename, - ArgumentListTooLong, - Interrupted, - Other, - UnexpectedEof, - Unsupported, - OutOfMemory, - InProgress, - Uncategorized, - }) -} - // Some static checking to alert us if a change breaks any of the assumptions // that our encoding relies on for correctness and soundness. (Some of these are // a bit overly thorough/cautious, admittedly) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index faac8d9e51fd2..807befec1ad11 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -321,7 +321,9 @@ #![feature(const_default)] #![feature(core_float_math)] #![feature(core_intrinsics)] +#![feature(core_io)] #![feature(core_io_borrowed_buf)] +#![feature(core_io_internals)] #![feature(cstr_display)] #![feature(drop_guard)] #![feature(duration_constants)] @@ -344,6 +346,9 @@ #![feature(hashmap_internals)] #![feature(hint_must_use)] #![feature(int_from_ascii)] +#![feature(io_error_inprogress)] +#![feature(io_error_more)] +#![feature(io_error_uncategorized)] #![feature(ip)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 7fd46b31f7d83..cc9d3329edf52 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -4,11 +4,11 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::fs::{self, Metadata, OpenOptions}; +use crate::fs::{self, Metadata, OpenOptions, Permissions}; use crate::io::BorrowedCursor; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys::{AsInner, AsInnerMut, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; use crate::{io, sys}; @@ -61,6 +61,7 @@ pub trait FileExt { /// # Examples /// /// ```no_run + /// #![feature(core_io)] /// #![feature(core_io_borrowed_buf)] /// #![feature(read_buf_at)] /// @@ -368,6 +369,67 @@ impl OpenOptionsExt2 for OpenOptions { } } +/// Windows-specific extensions to [`fs::Permissions`]. This extension trait +/// provides extra utilities to shows what Windows file attributes are enabled +/// in [`Permissions`] and to manually set file attributes on [`Permissions`]. +/// +/// See Microsoft's [`File Attribute Constants`] page to know what file +/// attribute metadata are defined and stored on Windows files. +/// +/// [`Permissions`]: fs::Permissions +/// [`File Attribute Constants`]: +/// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants +/// +/// # Example +/// +/// ```no_run +/// use std::fs::Permissions; +/// use std::os::windows::fs::PermissionsExt; +/// +/// const FILE_ATTRIBUTE_SYSTEM: u32 = 0x4; +/// const FILE_ATTRIBUTE_ARCHIVE: u32 = 0x20; +/// let my_file_attr = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE; +/// let mut permissions = Permissions::from_file_attributes(my_file_attr); +/// assert_eq!(permissions.file_attributes(), my_file_attr); +/// +/// const FILE_ATTRIBUTE_HIDDEN: u32 = 0x2; +/// let new_file_attr = permissions.file_attributes() | FILE_ATTRIBUTE_HIDDEN; +/// permissions.set_file_attributes(new_file_attr); +/// assert_eq!(permissions.file_attributes(), new_file_attr); +/// ``` +#[unstable(feature = "windows_permissions_ext", issue = "152956")] +pub trait PermissionsExt: Sealed { + /// Returns the file attribute bits. + #[unstable(feature = "windows_permissions_ext", issue = "152956")] + fn file_attributes(&self) -> u32; + + /// Sets the file attribute bits. + #[unstable(feature = "windows_permissions_ext", issue = "152956")] + fn set_file_attributes(&mut self, mask: u32); + + /// Creates a new instance from the given file attribute bits. + #[unstable(feature = "windows_permissions_ext", issue = "152956")] + fn from_file_attributes(mask: u32) -> Self; +} + +#[unstable(feature = "windows_permissions_ext", issue = "152956")] +impl Sealed for fs::Permissions {} + +#[unstable(feature = "windows_permissions_ext", issue = "152956")] +impl PermissionsExt for fs::Permissions { + fn file_attributes(&self) -> u32 { + self.as_inner().file_attributes() + } + + fn set_file_attributes(&mut self, mask: u32) { + *self = Permissions::from_inner(FromInner::from_inner(mask)); + } + + fn from_file_attributes(mask: u32) -> Self { + Permissions::from_inner(FromInner::from_inner(mask)) + } +} + /// Windows-specific extensions to [`fs::Metadata`]. /// /// The data members that this trait exposes correspond to the members diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 74854cdeb498d..e0b02670264d9 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1167,6 +1167,16 @@ impl FilePermissions { self.attrs &= !c::FILE_ATTRIBUTE_READONLY; } } + + pub fn file_attributes(&self) -> u32 { + self.attrs as u32 + } +} + +impl FromInner for FilePermissions { + fn from_inner(attrs: u32) -> FilePermissions { + FilePermissions { attrs } + } } impl FileTimes { diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 13b2b3d502c81..b1c5404a7160c 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -16,7 +16,7 @@ use super::helpers::metrics::MetricMap; use super::options::{Options, OutputFormat}; use super::test_result::TestResult; use super::time::{TestExecTime, TestSuiteExecTime}; -use super::types::{NamePadding, TestDesc, TestDescAndFn}; +use super::types::{NamePadding, TestDesc, TestDescAndFn, TestList}; use super::{filter_tests, run_tests, term}; /// Generic wrapper over stdout. @@ -170,7 +170,7 @@ impl ConsoleTestState { } // List the tests to console, and optionally to logfile. Filters are honored. -pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { +pub(crate) fn list_tests_console(opts: &TestOpts, tests: TestList) -> io::Result<()> { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout().lock()), Some(t) => OutputLocation::Pretty(t), @@ -186,7 +186,7 @@ pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec) -> let mut st = ConsoleTestDiscoveryState::new(opts)?; out.write_discovery_start()?; - for test in filter_tests(opts, tests).into_iter() { + for test in filter_tests(opts, tests) { use crate::TestFn::*; let TestDescAndFn { desc, testfn } = test; @@ -307,8 +307,9 @@ pub(crate) fn get_formatter(opts: &TestOpts, max_name_len: usize) -> Box) -> io::Result { +pub fn run_tests_console(opts: &TestOpts, tests: TestList) -> io::Result { let max_name_len = tests + .tests .iter() .max_by_key(|t| len_if_padded(t)) .map(|t| t.desc.name.as_slice().len()) diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index f3dbd3d0556ab..791af5f8ee2a9 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -49,7 +49,7 @@ pub mod test { pub use crate::time::{TestExecTime, TestTimeOptions}; pub use crate::types::{ DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, - TestDescAndFn, TestId, TestName, TestType, + TestDescAndFn, TestId, TestList, TestListOrder, TestName, TestType, }; pub use crate::{assert_test_result, filter_tests, run_test, test_main, test_main_static}; } @@ -106,6 +106,16 @@ pub fn test_main_with_exit_callback( tests: Vec, options: Option, exit_callback: F, +) { + let tests = TestList::new(tests, TestListOrder::Unsorted); + test_main_inner(args, tests, options, exit_callback) +} + +fn test_main_inner( + args: &[String], + tests: TestList, + options: Option, + exit_callback: F, ) { let mut opts = match cli::parse_opts(args) { Some(Ok(o)) => o, @@ -180,7 +190,9 @@ pub fn test_main_with_exit_callback( pub fn test_main_static(tests: &[&TestDescAndFn]) { let args = env::args().collect::>(); let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); - test_main(&args, owned_tests, None) + // Tests are sorted by name at compile time by mk_tests_slice. + let tests = TestList::new(owned_tests, TestListOrder::Sorted); + test_main_inner(&args, tests, None, || {}) } /// A variant optimized for invocation with a static test vector. @@ -229,7 +241,9 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { let args = env::args().collect::>(); let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); - test_main(&args, owned_tests, Some(Options::new().panic_abort(true))) + // Tests are sorted by name at compile time by mk_tests_slice. + let tests = TestList::new(owned_tests, TestListOrder::Sorted); + test_main_inner(&args, tests, Some(Options::new().panic_abort(true)), || {}) } /// Clones static values for putting into a dynamic vector, which test_main() @@ -298,7 +312,7 @@ impl FilteredTests { pub fn run_tests( opts: &TestOpts, - tests: Vec, + tests: TestList, mut notify_about_test_event: F, ) -> io::Result<()> where @@ -334,7 +348,7 @@ where timeout: Instant, } - let tests_len = tests.len(); + let tests_len = tests.tests.len(); let mut filtered = FilteredTests { tests: Vec::new(), benches: Vec::new(), next_id: 0 }; @@ -512,25 +526,48 @@ where Ok(()) } -pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec { +pub fn filter_tests(opts: &TestOpts, tests: TestList) -> Vec { + let TestList { tests, order } = tests; let mut filtered = tests; - let matches_filter = |test: &TestDescAndFn, filter: &str| { - let test_name = test.desc.name.as_slice(); - - match opts.filter_exact { - true => test_name == filter, - false => test_name.contains(filter), - } - }; - // Remove tests that don't match the test filter + // Remove tests that don't match the test filter. if !opts.filters.is_empty() { - filtered.retain(|test| opts.filters.iter().any(|filter| matches_filter(test, filter))); + if opts.filter_exact && order == TestListOrder::Sorted { + // Let's say that `f` is the number of filters and `n` is the number + // of tests. + // + // The test array is sorted by name (guaranteed by the caller via + // TestListOrder::Sorted), so use binary search for O(f log n) + // exact-match lookups instead of an O(n) linear scan. + // + // This is important for Miri, where the interpreted execution makes + // the linear scan very expensive. + filtered = filter_exact_match(filtered, &opts.filters); + } else { + filtered.retain(|test| { + let test_name = test.desc.name.as_slice(); + opts.filters.iter().any(|filter| { + if opts.filter_exact { + test_name == filter.as_str() + } else { + test_name.contains(filter.as_str()) + } + }) + }); + } } // Skip tests that match any of the skip filters + // + // After exact positive filtering above, the filtered set is small, so a + // linear scan is acceptable even under Miri. if !opts.skip.is_empty() { - filtered.retain(|test| !opts.skip.iter().any(|sf| matches_filter(test, sf))); + filtered.retain(|test| { + let name = test.desc.name.as_slice(); + !opts.skip.iter().any(|sf| { + if opts.filter_exact { name == sf.as_str() } else { name.contains(sf.as_str()) } + }) + }); } // Excludes #[should_panic] tests @@ -553,6 +590,30 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec, filters: &[String]) -> Vec { + // Binary search for each filter in the sorted test list. + let mut indexes: Vec = filters + .iter() + .filter_map(|f| tests.binary_search_by(|t| t.desc.name.as_slice().cmp(f.as_str())).ok()) + .collect(); + indexes.sort_unstable(); + indexes.dedup(); + + // Extract matching tests. Process indexes in descending order so that + // swap_remove (which replaces the removed element with the last) does not + // invalidate indexes we haven't visited yet. + let mut result = Vec::with_capacity(indexes.len()); + for &idx in indexes.iter().rev() { + result.push(tests.swap_remove(idx)); + } + // Reverse to restore the original sorted order, since we extracted the + // matching tests in descending index order. + result.reverse(); + result +} + pub fn convert_benchmarks_to_tests(tests: Vec) -> Vec { // convert benchmarks to tests, if we're not benchmarking them tests diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index d986bd74f772b..b25462cce1f99 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -477,7 +477,7 @@ fn filter_for_ignored_option() { opts.run_tests = true; opts.run_ignored = RunIgnored::Only; - let tests = one_ignored_one_unignored_test(); + let tests = TestList::new(one_ignored_one_unignored_test(), TestListOrder::Unsorted); let filtered = filter_tests(&opts, tests); assert_eq!(filtered.len(), 1); @@ -494,7 +494,7 @@ fn run_include_ignored_option() { opts.run_tests = true; opts.run_ignored = RunIgnored::Yes; - let tests = one_ignored_one_unignored_test(); + let tests = TestList::new(one_ignored_one_unignored_test(), TestListOrder::Unsorted); let filtered = filter_tests(&opts, tests); assert_eq!(filtered.len(), 2); @@ -527,7 +527,7 @@ fn exclude_should_panic_option() { testfn: DynTestFn(Box::new(move || Ok(()))), }); - let filtered = filter_tests(&opts, tests); + let filtered = filter_tests(&opts, TestList::new(tests, TestListOrder::Unsorted)); assert_eq!(filtered.len(), 2); assert!(filtered.iter().all(|test| test.desc.should_panic == ShouldPanic::No)); @@ -535,8 +535,8 @@ fn exclude_should_panic_option() { #[test] fn exact_filter_match() { - fn tests() -> Vec { - ["base", "base::test", "base::test1", "base::test2"] + fn tests() -> TestList { + let tests = ["base", "base::test", "base::test1", "base::test2"] .into_iter() .map(|name| TestDescAndFn { desc: TestDesc { @@ -555,7 +555,8 @@ fn exact_filter_match() { }, testfn: DynTestFn(Box::new(move || Ok(()))), }) - .collect() + .collect(); + TestList::new(tests, TestListOrder::Sorted) } let substr = @@ -908,7 +909,8 @@ fn test_dyn_bench_returning_err_fails_when_run_as_test() { } Ok(()) }; - run_tests(&TestOpts { run_tests: true, ..TestOpts::new() }, vec![desc], notify).unwrap(); + let tests = TestList::new(vec![desc], TestListOrder::Unsorted); + run_tests(&TestOpts { run_tests: true, ..TestOpts::new() }, tests, notify).unwrap(); let result = rx.recv().unwrap().result; assert_eq!(result, TrFailed); } diff --git a/library/test/src/types.rs b/library/test/src/types.rs index 802cab989c6a9..14c81bc2d1cf1 100644 --- a/library/test/src/types.rs +++ b/library/test/src/types.rs @@ -284,3 +284,30 @@ impl TestDescAndFn { } } } + +/// Whether a [`TestList`]'s tests are known to be sorted by name. +/// +/// When tests are sorted, `filter_tests` can use binary search for `--exact` +/// matches instead of a linear scan. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TestListOrder { + /// Tests are sorted by name. This is guaranteed for tests generated by + /// `rustc --test` (see `mk_tests_slice` in + /// `compiler/rustc_builtin_macros/src/test_harness.rs`). + Sorted, + /// Test order is unknown; binary search must not be used. + Unsorted, +} + +/// A list of tests, tagged with whether they are sorted by name. +#[derive(Debug)] +pub struct TestList { + pub tests: Vec, + pub order: TestListOrder, +} + +impl TestList { + pub fn new(tests: Vec, order: TestListOrder) -> Self { + Self { tests, order } + } +} diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml index f84da24428155..ae41d2fbb009b 100644 --- a/src/tools/collect-license-metadata/Cargo.toml +++ b/src/tools/collect-license-metadata/Cargo.toml @@ -10,4 +10,3 @@ anyhow = "1.0.65" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" similar = "2.7.0" -spdx-rs = "0.5.1" diff --git a/src/tools/collect-license-metadata/src/main.rs b/src/tools/collect-license-metadata/src/main.rs index 4e218ea59fda6..156871b1b3a80 100644 --- a/src/tools/collect-license-metadata/src/main.rs +++ b/src/tools/collect-license-metadata/src/main.rs @@ -1,6 +1,7 @@ mod licenses; mod path_tree; mod reuse; +mod spdx; use std::path::PathBuf; diff --git a/src/tools/collect-license-metadata/src/reuse.rs b/src/tools/collect-license-metadata/src/reuse.rs index dbe46781b7c5b..6bc41453a53ff 100644 --- a/src/tools/collect-license-metadata/src/reuse.rs +++ b/src/tools/collect-license-metadata/src/reuse.rs @@ -15,18 +15,15 @@ pub(crate) fn collect( let raw = &obtain_spdx_document(reuse_exe)?; println!("finished gathering the license information from REUSE in {:.2?}", start.elapsed()); - let document = spdx_rs::parsers::spdx_from_tag_value(&raw)?; + let files = crate::spdx::parse_tag_value(raw)?; let mut result = Vec::new(); - for file in document.file_information { - let concluded_license = file.concluded_license.expect("File should have licence info"); - let copyright_text = file.copyright_text.expect("File should have copyright text"); + for file in files { let license = interner.intern(License { - spdx: concluded_license.to_string(), - copyright: copyright_text.split('\n').map(|s| s.into()).collect(), + spdx: file.concluded_license, + copyright: file.copyright_text.split('\n').map(|s| s.into()).collect(), }); - - result.push((file.file_name.into(), license)); + result.push((file.name.into(), license)); } Ok(result) diff --git a/src/tools/collect-license-metadata/src/spdx/mod.rs b/src/tools/collect-license-metadata/src/spdx/mod.rs new file mode 100644 index 0000000000000..a94f2bcf51eca --- /dev/null +++ b/src/tools/collect-license-metadata/src/spdx/mod.rs @@ -0,0 +1,102 @@ +use anyhow::Error; + +/// A single file entry extracted from an SPDX tag-value document. +pub(crate) struct SpdxFileEntry { + pub(crate) name: String, + pub(crate) concluded_license: String, + pub(crate) copyright_text: String, +} + +/// Parses an SPDX tag-value document and extracts file information. +/// +/// This is a minimal parser that only extracts the fields we need +/// (FileName, LicenseConcluded, FileCopyrightText) rather than the full model. +/// The format is specified by the SPDX specification: +/// each line is a `Tag: Value` pair, +/// and multi-line values are wrapped in ``. +pub(crate) fn parse_tag_value(input: &str) -> Result, Error> { + let mut files = Vec::new(); + let mut current_name: Option = None; + let mut current_license: Option = None; + let mut current_copyright: Option = None; + + let mut lines = input.lines(); + while let Some(line) = lines.next() { + let Some((tag, value)) = line.split_once(": ") else { + continue; + }; + + let value = resolve_multiline_value(value, &mut lines)?; + + match tag { + "FileName" => { + // A new file section begins. Flush the previous one if present. + if let Some(name) = current_name.take() { + files.push(build_file_entry( + name, + current_license.take(), + current_copyright.take(), + )?); + } + current_name = Some(value); + current_license = None; + current_copyright = None; + } + "LicenseConcluded" => current_license = Some(value), + "FileCopyrightText" => current_copyright = Some(value), + _ => {} + } + } + + // Flush the last file section. + if let Some(name) = current_name { + files.push(build_file_entry(name, current_license, current_copyright)?); + } + + Ok(files) +} + +/// Resolves a tag value that might span multiple lines using ``. +fn resolve_multiline_value<'a>( + value: &str, + further_lines: &mut impl Iterator, +) -> Result { + let Some(start) = value.strip_prefix("") else { + return Ok(value.to_string()); + }; + + // The closing tag might be on the same line. + if let Some(content) = start.strip_suffix("") { + return Ok(content.to_string()); + } + + let mut text = start.to_string(); + for line in further_lines.by_ref() { + if let Some(rest) = line.strip_suffix("") { + text.push('\n'); + text.push_str(rest); + return Ok(text); + } + text.push('\n'); + text.push_str(line); + } + + anyhow::bail!("unexpected end of input inside block") +} + +fn build_file_entry( + name: String, + concluded_license: Option, + copyright_text: Option, +) -> Result { + Ok(SpdxFileEntry { + name, + concluded_license: concluded_license + .ok_or_else(|| anyhow::anyhow!("file missing LicenseConcluded"))?, + copyright_text: copyright_text + .ok_or_else(|| anyhow::anyhow!("file missing FileCopyrightText"))?, + }) +} + +#[cfg(test)] +mod tests; diff --git a/src/tools/collect-license-metadata/src/spdx/tests.rs b/src/tools/collect-license-metadata/src/spdx/tests.rs new file mode 100644 index 0000000000000..5b7cb411931dc --- /dev/null +++ b/src/tools/collect-license-metadata/src/spdx/tests.rs @@ -0,0 +1,134 @@ +use super::*; + +// Clause 8.1 ("File name field") specifies that each file section begins with +// a `FileName` tag whose value is a relative path prefixed with "./". +// Clause 8.5 ("Concluded license") and 8.8 ("Copyright text") give the +// corresponding per-file fields. +// +#[test] +fn single_file_entry() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: LGPL-2.0-only +FileCopyrightText: Copyright 2008-2010 John Smith"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 1); + assert_eq!(files[0].name, "./package/foo.c"); + assert_eq!(files[0].concluded_license, "LGPL-2.0-only"); + assert_eq!(files[0].copyright_text, "Copyright 2008-2010 John Smith"); +} + +// Clause 8.5 shows compound SPDX licence expressions as valid values for +// `LicenseConcluded`, e.g. "(LGPL-2.0-only OR LicenseRef-2)". +// +#[test] +fn compound_license_expression() { + let input = "\ +FileName: ./src/lib.rs +LicenseConcluded: (LGPL-2.0-only OR LicenseRef-2) +FileCopyrightText: Copyright Example Company"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 1); + assert_eq!(files[0].concluded_license, "(LGPL-2.0-only OR LicenseRef-2)"); +} + +// Clause 8.8 shows the copyright text wrapped in a single-line +// ... block: e.g. +// `FileCopyrightText: Copyright 2008-2010 John Smith` +// +#[test] +fn single_line_text_block() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: LGPL-2.0-only +FileCopyrightText: Copyright 2008-2010 John Smith"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 1); + assert_eq!(files[0].copyright_text, "Copyright 2008-2010 John Smith"); +} + +// Clause 6.10 ("Creator comment") demonstrates a multi-line ... block. +// +#[test] +fn multi_line_text_block() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: MIT +FileCopyrightText: Copyright 2008-2010 John Smith +Copyright 2019 Jane Doe"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 1); + assert_eq!(files[0].copyright_text, "Copyright 2008-2010 John Smith\nCopyright 2019 Jane Doe"); +} + +// Clause 5 ("Composition of an SPDX document") states that a document may +// contain zero or many File Information sections. Each `FileName` tag starts +// a new section, so consecutive file blocks must be parsed independently. +// +#[test] +fn multiple_file_entries() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: LGPL-2.0-only +FileCopyrightText: Copyright 2008-2010 John Smith +FileName: ./package/bar.c +LicenseConcluded: MIT +FileCopyrightText: Copyright Example Company"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 2); + + assert_eq!(files[0].name, "./package/foo.c"); + assert_eq!(files[0].concluded_license, "LGPL-2.0-only"); + assert_eq!(files[0].copyright_text, "Copyright 2008-2010 John Smith"); + + assert_eq!(files[1].name, "./package/bar.c"); + assert_eq!(files[1].concluded_license, "MIT"); + assert_eq!(files[1].copyright_text, "Copyright Example Company"); +} + +// A file section without a `LicenseConcluded` tag is malformed. +#[test] +fn missing_license_is_an_error() { + let input = "\ +FileName: ./package/foo.c +FileCopyrightText: Copyright 2008-2010 John Smith"; + + assert!(parse_tag_value(input).is_err()); +} + +// A file section without a `FileCopyrightText` tag is malformed. +#[test] +fn missing_copyright_is_an_error() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: MIT"; + + assert!(parse_tag_value(input).is_err()); +} + +// A section with an unterminated block (no closing ) is malformed. +#[test] +fn unterminated_text_block_is_an_error() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: MIT +FileCopyrightText: Copyright 2008-2010 John Smith"; + + assert!(parse_tag_value(input).is_err()); +} + +// A document with no `FileName` tags at all should produce an empty result. +#[test] +fn empty_document_returns_no_entries() { + let input = "\ +SPDXVersion: SPDX-2.3 +DataLicense: CC0-1.0"; + + let files = parse_tag_value(input).unwrap(); + assert!(files.is_empty()); +} diff --git a/tests/codegen-llvm/issues/issue-114532.rs b/tests/codegen-llvm/issues/issue-114532.rs new file mode 100644 index 0000000000000..41d9effbe4c94 --- /dev/null +++ b/tests/codegen-llvm/issues/issue-114532.rs @@ -0,0 +1,34 @@ +//@ compile-flags: -Copt-level=3 +#![crate_type = "lib"] + +// Regression test for #114532. +// Dead code elimination used to fail when a Drop impl contained a panic +// and a potentially-panicking function was called after the value was created. + +struct Foo(bool); + +impl Drop for Foo { + fn drop(&mut self) { + if self.0 { + return; + } + panic!("dead"); + } +} + +// CHECK-LABEL: @foo( +// CHECK-NOT: panic +// CHECK-NOT: call void @{{.*}}panicking +// CHECK: call {{.*}} @unknown( +// CHECK-NEXT: ret void +#[no_mangle] +pub fn foo() { + let _a = Foo(true); + unsafe { + unknown(9); + } +} + +extern "Rust" { + fn unknown(x: i32) -> bool; +} diff --git a/tests/rustdoc-html/intra-doc/deprecated.rs b/tests/rustdoc-html/intra-doc/deprecated.rs index 6f8639593a2d4..1705fe8fc7abe 100644 --- a/tests/rustdoc-html/intra-doc/deprecated.rs +++ b/tests/rustdoc-html/intra-doc/deprecated.rs @@ -1,6 +1,6 @@ //@ has deprecated/struct.A.html '//a[@href="{{channel}}/core/ops/range/struct.Range.html#structfield.start"]' 'start' -//@ has deprecated/struct.B1.html '//a[@href="{{channel}}/std/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' -//@ has deprecated/struct.B2.html '//a[@href="{{channel}}/std/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' +//@ has deprecated/struct.B1.html '//a[@href="{{channel}}/core/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' +//@ has deprecated/struct.B2.html '//a[@href="{{channel}}/core/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' #[deprecated = "[start][std::ops::Range::start]"] pub struct A; diff --git a/tests/rustdoc-html/intra-doc/field.rs b/tests/rustdoc-html/intra-doc/field.rs index e98419618e23f..610eb1ec1bf65 100644 --- a/tests/rustdoc-html/intra-doc/field.rs +++ b/tests/rustdoc-html/intra-doc/field.rs @@ -1,9 +1,9 @@ //@ has field/index.html '//a[@href="{{channel}}/core/ops/range/struct.Range.html#structfield.start"]' 'start' -//@ has field/index.html '//a[@href="{{channel}}/std/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' +//@ has field/index.html '//a[@href="{{channel}}/core/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' //@ has field/index.html '//a[@href="struct.FieldAndMethod.html#structfield.x"]' 'x' //@ has field/index.html '//a[@href="enum.VariantAndMethod.html#variant.X"]' 'X' //! [start][std::ops::Range::start] -//! [not_found][std::io::ErrorKind::NotFound] +//! [not_found][core::io::ErrorKind::NotFound] //! [x][field@crate::FieldAndMethod::x] //! [X][variant@crate::VariantAndMethod::X] diff --git a/tests/ui/feature-gates/feature-gate-io_error_kind_in_core.rs b/tests/ui/feature-gates/feature-gate-io_error_kind_in_core.rs new file mode 100644 index 0000000000000..514e4e1277afa --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-io_error_kind_in_core.rs @@ -0,0 +1,12 @@ +// Ensure `ErrorKind` from `core` is gated behind `core_io` +//@ edition:2024 + +use std::io::ErrorKind as ErrorKindFromStd; + +use core::io::ErrorKind as ErrorKindFromCore; +//~^ ERROR use of unstable library feature `core_io` + +// Asserting both ErrorKinds are the same. +const _: [ErrorKindFromCore; 1] = [ErrorKindFromStd::Other]; + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-io_error_kind_in_core.stderr b/tests/ui/feature-gates/feature-gate-io_error_kind_in_core.stderr new file mode 100644 index 0000000000000..499b603761dce --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-io_error_kind_in_core.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature `core_io` + --> $DIR/feature-gate-io_error_kind_in_core.rs:6:5 + | +LL | use core::io::ErrorKind as ErrorKindFromCore; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #154046 for more information + = help: add `#![feature(core_io)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/triagebot.toml b/triagebot.toml index 953ef08a9cc8a..a4155914fb02b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1633,6 +1633,18 @@ dep-bumps = [ "/src/llvm-project" = ["@cuviper"] "/src/rustdoc-json-types" = ["rustdoc"] "/src/stage0" = ["bootstrap"] +"/tests/assembly-llvm" = ["compiler"] +"/tests/auxiliary" = ["compiler"] +"/tests/codegen-llvm" = ["compiler"] +"/tests/codegen-units" = ["compiler"] +"/tests/COMPILER_TESTS.md" = ["compiler"] +"/tests/coverage-run-rustdoc" = ["compiler"] +"/tests/coverage" = ["compiler"] +"/tests/crashes" = ["compiler"] +"/tests/debuginfo" = ["compiler"] +"/tests/incremental" = ["compiler"] +"/tests/mir-opt" = ["compiler"] +"/tests/pretty" = ["compiler"] "/tests/run-make" = ["compiler"] "/tests/run-make-cargo" = ["compiler"] "/tests/rustdoc-html" = ["rustdoc"] @@ -1642,6 +1654,7 @@ dep-bumps = [ "/tests/rustdoc-json" = ["@aDotInTheVoid"] "/tests/rustdoc-ui" = ["rustdoc"] "/tests/ui" = ["compiler"] +"/tests/ui-fulldeps" = ["compiler"] "/src/tools/build-manifest" = ["bootstrap"] "/src/tools/cargo" = ["@ehuss"] "/src/tools/compiletest" = ["bootstrap", "@wesleywiser", "@oli-obk", "@jieyouxu"]