This commit is contained in:
Chris Denton 2025-04-13 11:48:23 +00:00 committed by GitHub
commit aaf546e8e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
71 changed files with 494 additions and 565 deletions

View File

@ -1212,7 +1212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
"crc32fast",
"miniz_oxide 0.8.7",
"miniz_oxide 0.8.8",
]
[[package]]
@ -2282,9 +2282,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]

View File

@ -176,9 +176,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
"compiler_builtins",

View File

@ -61,52 +61,4 @@ impl bool {
pub fn then<T, F: FnOnce() -> T>(self, f: F) -> Option<T> {
if self { Some(f()) } else { None }
}
/// Returns either `true_val` or `false_val` depending on the value of
/// `self`, with a hint to the compiler that `self` is unlikely
/// to be correctly predicted by a CPUs branch predictor.
///
/// This method is functionally equivalent to
/// ```ignore (this is just for illustrative purposes)
/// fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
/// if b { true_val } else { false_val }
/// }
/// ```
/// but might generate different assembly. In particular, on platforms with
/// a conditional move or select instruction (like `cmov` on x86 or `csel`
/// on ARM) the optimizer might use these instructions to avoid branches,
/// which can benefit performance if the branch predictor is struggling
/// with predicting `condition`, such as in an implementation of binary
/// search.
///
/// Note however that this lowering is not guaranteed (on any platform) and
/// should not be relied upon when trying to write constant-time code. Also
/// be aware that this lowering might *decrease* performance if `condition`
/// is well-predictable. It is advisable to perform benchmarks to tell if
/// this function is useful.
///
/// # Examples
///
/// Distribute values evenly between two buckets:
/// ```
/// #![feature(select_unpredictable)]
///
/// use std::hash::BuildHasher;
///
/// fn append<H: BuildHasher>(hasher: &H, v: i32, bucket_one: &mut Vec<i32>, bucket_two: &mut Vec<i32>) {
/// let hash = hasher.hash_one(&v);
/// let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two);
/// bucket.push(v);
/// }
/// # let hasher = std::collections::hash_map::RandomState::new();
/// # let mut bucket_one = Vec::new();
/// # let mut bucket_two = Vec::new();
/// # append(&hasher, 42, &mut bucket_one, &mut bucket_two);
/// # assert_eq!(bucket_one.len() + bucket_two.len(), 1);
/// ```
#[inline(always)]
#[unstable(feature = "select_unpredictable", issue = "133962")]
pub fn select_unpredictable<T>(self, true_val: T, false_val: T) -> T {
crate::intrinsics::select_unpredictable(self, true_val, false_val)
}
}

View File

@ -734,3 +734,52 @@ pub const fn unlikely(b: bool) -> bool {
pub const fn cold_path() {
crate::intrinsics::cold_path()
}
/// Returns either `true_val` or `false_val` depending on the value of `b`,
/// with a hint to the compiler that `b` is unlikely to be correctly
/// predicted by a CPUs branch predictor.
///
/// This method is functionally equivalent to
/// ```ignore (this is just for illustrative purposes)
/// fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
/// if b { true_val } else { false_val }
/// }
/// ```
/// but might generate different assembly. In particular, on platforms with
/// a conditional move or select instruction (like `cmov` on x86 or `csel`
/// on ARM) the optimizer might use these instructions to avoid branches,
/// which can benefit performance if the branch predictor is struggling
/// with predicting `condition`, such as in an implementation of binary
/// search.
///
/// Note however that this lowering is not guaranteed (on any platform) and
/// should not be relied upon when trying to write constant-time code. Also
/// be aware that this lowering might *decrease* performance if `condition`
/// is well-predictable. It is advisable to perform benchmarks to tell if
/// this function is useful.
///
/// # Examples
///
/// Distribute values evenly between two buckets:
/// ```
/// #![feature(select_unpredictable)]
///
/// use std::hash::BuildHasher;
/// use std::hint;
///
/// fn append<H: BuildHasher>(hasher: &H, v: i32, bucket_one: &mut Vec<i32>, bucket_two: &mut Vec<i32>) {
/// let hash = hasher.hash_one(&v);
/// let bucket = hint::select_unpredictable(hash % 2 == 0, bucket_one, bucket_two);
/// bucket.push(v);
/// }
/// # let hasher = std::collections::hash_map::RandomState::new();
/// # let mut bucket_one = Vec::new();
/// # let mut bucket_two = Vec::new();
/// # append(&hasher, 42, &mut bucket_one, &mut bucket_two);
/// # assert_eq!(bucket_one.len() + bucket_two.len(), 1);
/// ```
#[inline(always)]
#[unstable(feature = "select_unpredictable", issue = "133962")]
pub fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
crate::intrinsics::select_unpredictable(b, true_val, false_val)
}

View File

@ -1326,7 +1326,7 @@ pub const fn unlikely(b: bool) -> bool {
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The public form of this instrinsic is [`bool::select_unpredictable`].
/// The public form of this instrinsic is [`core::hint::select_unpredictable`].
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_intrinsic]
#[rustc_nounwind]

View File

@ -2822,7 +2822,7 @@ impl<T> [T] {
// Binary search interacts poorly with branch prediction, so force
// the compiler to use conditional moves if supported by the target
// architecture.
base = (cmp == Greater).select_unpredictable(base, mid);
base = hint::select_unpredictable(cmp == Greater, base, mid);
// This is imprecise in the case where `size` is odd and the
// comparison returns Greater: the mid element still gets included

View File

@ -2,7 +2,7 @@
use crate::mem::{self, ManuallyDrop, MaybeUninit};
use crate::slice::sort::shared::FreezeMarker;
use crate::{intrinsics, ptr, slice};
use crate::{hint, intrinsics, ptr, slice};
// It's important to differentiate between SMALL_SORT_THRESHOLD performance for
// small slices and small-sort performance sorting small sub-slices as part of
@ -408,8 +408,8 @@ where
// }
// The goal is to generate cmov instructions here.
let v_a_swap = should_swap.select_unpredictable(v_b, v_a);
let v_b_swap = should_swap.select_unpredictable(v_a, v_b);
let v_a_swap = hint::select_unpredictable(should_swap, v_b, v_a);
let v_b_swap = hint::select_unpredictable(should_swap, v_a, v_b);
let v_b_swap_tmp = ManuallyDrop::new(ptr::read(v_b_swap));
ptr::copy(v_a_swap, v_a, 1);
@ -640,15 +640,15 @@ pub unsafe fn sort4_stable<T, F: FnMut(&T, &T) -> bool>(
// 1, 1 | c b a d
let c3 = is_less(&*c, &*a);
let c4 = is_less(&*d, &*b);
let min = c3.select_unpredictable(c, a);
let max = c4.select_unpredictable(b, d);
let unknown_left = c3.select_unpredictable(a, c4.select_unpredictable(c, b));
let unknown_right = c4.select_unpredictable(d, c3.select_unpredictable(b, c));
let min = hint::select_unpredictable(c3, c, a);
let max = hint::select_unpredictable(c4, b, d);
let unknown_left = hint::select_unpredictable(c3, a, hint::select_unpredictable(c4, c, b));
let unknown_right = hint::select_unpredictable(c4, d, hint::select_unpredictable(c3, b, c));
// Sort the last two unknown elements.
let c5 = is_less(&*unknown_right, &*unknown_left);
let lo = c5.select_unpredictable(unknown_right, unknown_left);
let hi = c5.select_unpredictable(unknown_left, unknown_right);
let lo = hint::select_unpredictable(c5, unknown_right, unknown_left);
let hi = hint::select_unpredictable(c5, unknown_left, unknown_right);
ptr::copy_nonoverlapping(min, dst, 1);
ptr::copy_nonoverlapping(lo, dst.add(1), 1);

View File

@ -9,8 +9,14 @@ use std::path::PathBuf;
fn main() {
if let Ok(rt) = tracked_env_var("LLVM_PROFILER_RT_LIB") {
println!("cargo::rustc-link-lib=static:+verbatim={rt}");
return;
let rt = PathBuf::from(rt);
if let Some(lib) = rt.file_name() {
if let Some(dir) = rt.parent() {
println!("cargo::rustc-link-search=native={}", dir.display());
}
println!("cargo::rustc-link-lib=static:+verbatim={}", lib.to_str().unwrap());
return;
}
}
let target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set");

View File

@ -0,0 +1,43 @@
use crate::ffi::OsString;
use crate::{fmt, vec};
pub struct Args {
iter: vec::IntoIter<OsString>,
}
impl !Send for Args {}
impl !Sync for Args {}
impl Args {
pub(super) fn new(args: Vec<OsString>) -> Self {
Args { iter: args.into_iter() }
}
}
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.iter.as_slice().fmt(f)
}
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize {
self.iter.len()
}
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.iter.next_back()
}
}

View File

@ -1,8 +1,12 @@
use crate::ffi::{CStr, OsString, c_char};
use crate::os::hermit::ffi::OsStringExt;
use crate::ptr;
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
use crate::sync::atomic::{AtomicIsize, AtomicPtr};
use crate::{fmt, ptr, vec};
#[path = "common.rs"]
mod common;
pub use common::Args;
static ARGC: AtomicIsize = AtomicIsize::new(0);
static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
@ -27,40 +31,5 @@ pub fn args() -> Args {
})
.collect();
Args { iter: args.into_iter() }
}
pub struct Args {
iter: vec::IntoIter<OsString>,
}
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.iter.as_slice().fmt(f)
}
}
impl !Send for Args {}
impl !Sync for Args {}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize {
self.iter.len()
}
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.iter.next_back()
}
Args::new(args)
}

View File

@ -0,0 +1,34 @@
//! Platform-dependent command line arguments abstraction.
#![forbid(unsafe_op_in_unsafe_fn)]
cfg_if::cfg_if! {
if #[cfg(all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))))] {
mod unix;
pub use unix::*;
} else if #[cfg(target_family = "windows")] {
mod windows;
pub use windows::*;
} else if #[cfg(target_os = "hermit")] {
mod hermit;
pub use hermit::*;
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
mod sgx;
pub use sgx::*;
} else if #[cfg(target_os = "uefi")] {
mod uefi;
pub use uefi::*;
} else if #[cfg(target_os = "wasi")] {
mod wasi;
pub use wasi::*;
} else if #[cfg(target_os = "xous")] {
mod xous;
pub use xous::*;
} else if #[cfg(target_os = "zkvm")] {
mod zkvm;
pub use zkvm::*;
} else {
mod unsupported;
pub use unsupported::*;
}
}

View File

@ -1,8 +1,10 @@
use super::abi::usercalls::alloc;
use super::abi::usercalls::raw::ByteBuffer;
#![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers
use crate::ffi::OsString;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::os_str::Buf;
use crate::sys::pal::abi::usercalls::alloc;
use crate::sys::pal::abi::usercalls::raw::ByteBuffer;
use crate::sys_common::FromInner;
use crate::{fmt, slice};

View File

@ -1,14 +1,13 @@
use r_efi::protocols::loaded_image;
use super::helpers;
use crate::env::current_exe;
use crate::ffi::OsString;
use crate::iter::Iterator;
use crate::{fmt, vec};
use crate::sys::pal::helpers;
pub struct Args {
parsed_args_list: vec::IntoIter<OsString>,
}
#[path = "common.rs"]
mod common;
pub use common::Args;
pub fn args() -> Args {
let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]);
@ -22,51 +21,17 @@ pub fn args() -> Args {
let lp_size = unsafe { (*protocol.as_ptr()).load_options_size } as usize;
// Break if we are sure that it cannot be UTF-16
if lp_size < size_of::<u16>() || lp_size % size_of::<u16>() != 0 {
return Args { parsed_args_list: lazy_current_exe().into_iter() };
return Args::new(lazy_current_exe());
}
let lp_size = lp_size / size_of::<u16>();
let lp_cmd_line = unsafe { (*protocol.as_ptr()).load_options as *const u16 };
if !lp_cmd_line.is_aligned() {
return Args { parsed_args_list: lazy_current_exe().into_iter() };
return Args::new(lazy_current_exe());
}
let lp_cmd_line = unsafe { crate::slice::from_raw_parts(lp_cmd_line, lp_size) };
Args {
parsed_args_list: parse_lp_cmd_line(lp_cmd_line)
.unwrap_or_else(lazy_current_exe)
.into_iter(),
}
}
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.parsed_args_list.as_slice().fmt(f)
}
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.parsed_args_list.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.parsed_args_list.size_hint()
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize {
self.parsed_args_list.len()
}
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.parsed_args_list.next_back()
}
Args::new(parse_lp_cmd_line(lp_cmd_line).unwrap_or_else(lazy_current_exe))
}
/// Implements the UEFI command-line argument parsing algorithm.

View File

@ -5,13 +5,16 @@
#![allow(dead_code)] // runtime init functions not used during testing
use crate::ffi::{CStr, OsString};
use crate::ffi::CStr;
use crate::os::unix::ffi::OsStringExt;
use crate::{fmt, vec};
#[path = "common.rs"]
mod common;
pub use common::Args;
/// One-time global initialization.
pub unsafe fn init(argc: isize, argv: *const *const u8) {
imp::init(argc, argv)
unsafe { imp::init(argc, argv) }
}
/// Returns the command line arguments
@ -55,42 +58,7 @@ pub fn args() -> Args {
vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec()));
}
Args { iter: vec.into_iter() }
}
pub struct Args {
iter: vec::IntoIter<OsString>,
}
impl !Send for Args {}
impl !Sync for Args {}
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.iter.as_slice().fmt(f)
}
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize {
self.iter.len()
}
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.iter.next_back()
}
Args::new(vec)
}
#[cfg(any(
@ -141,7 +109,7 @@ mod imp {
pub unsafe fn init(argc: isize, argv: *const *const u8) {
// on GNU/Linux if we are main then we will init argv and argc twice, it "duplicates work"
// BUT edge-cases are real: only using .init_array can break most emulators, dlopen, etc.
really_init(argc, argv);
unsafe { really_init(argc, argv) };
}
/// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension.
@ -159,9 +127,7 @@ mod imp {
argv: *const *const u8,
_envp: *const *const u8,
) {
unsafe {
really_init(argc as isize, argv);
}
unsafe { really_init(argc as isize, argv) };
}
init_wrapper
};
@ -228,16 +194,3 @@ mod imp {
(argc as isize, argv.cast())
}
}
#[cfg(any(target_os = "espidf", target_os = "vita"))]
mod imp {
use crate::ffi::c_char;
use crate::ptr;
#[inline(always)]
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
pub fn argc_argv() -> (isize, *const *const c_char) {
(0, ptr::null())
}
}

View File

@ -0,0 +1,29 @@
#![forbid(unsafe_op_in_unsafe_fn)]
use crate::ffi::{CStr, OsStr, OsString};
use crate::os::wasi::ffi::OsStrExt;
#[path = "common.rs"]
mod common;
pub use common::Args;
/// Returns the command line arguments
pub fn args() -> Args {
Args::new(maybe_args().unwrap_or(Vec::new()))
}
fn maybe_args() -> Option<Vec<OsString>> {
unsafe {
let (argc, buf_size) = wasi::args_sizes_get().ok()?;
let mut argv = Vec::with_capacity(argc);
let mut buf = Vec::with_capacity(buf_size);
wasi::args_get(argv.as_mut_ptr(), buf.as_mut_ptr()).ok()?;
argv.set_len(argc);
let mut ret = Vec::with_capacity(argc);
for ptr in argv {
let s = CStr::from_ptr(ptr.cast());
ret.push(OsStr::from_bytes(s.to_bytes()).to_owned());
}
Some(ret)
}
}

View File

@ -6,17 +6,21 @@
#[cfg(test)]
mod tests;
use super::ensure_no_nuls;
use super::os::current_exe;
use crate::ffi::{OsStr, OsString};
use crate::num::NonZero;
use crate::os::windows::prelude::*;
use crate::path::{Path, PathBuf};
use crate::sys::pal::os::current_exe;
use crate::sys::pal::{ensure_no_nuls, fill_utf16_buf};
use crate::sys::path::get_long_path;
use crate::sys::{c, to_u16s};
use crate::sys_common::AsInner;
use crate::sys_common::wstr::WStrUnits;
use crate::{fmt, io, iter, vec};
use crate::{io, iter, ptr};
#[path = "common.rs"]
mod common;
pub use common::Args;
pub fn args() -> Args {
// SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16
@ -27,7 +31,7 @@ pub fn args() -> Args {
current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new())
});
Args { parsed_args_list: parsed_args_list.into_iter() }
Args::new(parsed_args_list)
}
}
@ -153,38 +157,6 @@ fn parse_lp_cmd_line<'a, F: Fn() -> OsString>(
ret_val
}
pub struct Args {
parsed_args_list: vec::IntoIter<OsString>,
}
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.parsed_args_list.as_slice().fmt(f)
}
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.parsed_args_list.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.parsed_args_list.size_hint()
}
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.parsed_args_list.next_back()
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize {
self.parsed_args_list.len()
}
}
#[derive(Debug)]
pub(crate) enum Arg {
/// Add quotes (if needed)
@ -384,9 +356,6 @@ pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
from_wide_to_user_path(to_u16s(path)?)
}
pub(crate) fn from_wide_to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
use super::fill_utf16_buf;
use crate::ptr;
// UTF-16 encoded code points, used in parsing and building UTF-16 paths.
// All of these are in the ASCII range so they can be cast directly to `u16`.
const SEP: u16 = b'\\' as _;

View File

@ -0,0 +1,23 @@
use crate::sys::pal::os::get_application_parameters;
use crate::sys::pal::os::params::ArgumentList;
#[path = "common.rs"]
mod common;
pub use common::Args;
pub fn args() -> Args {
let Some(params) = get_application_parameters() else {
return Args::new(vec![]);
};
for param in params {
if let Ok(args) = ArgumentList::try_from(&param) {
let mut parsed_args = vec![];
for arg in args {
parsed_args.push(arg.into());
}
return Args::new(parsed_args);
}
}
Args::new(vec![])
}

View File

@ -1,7 +1,7 @@
use super::{WORD_SIZE, abi};
use crate::ffi::OsString;
use crate::fmt;
use crate::sys::os_str;
use crate::sys::pal::{WORD_SIZE, abi};
use crate::sys_common::FromInner;
pub struct Args {

View File

@ -71,9 +71,11 @@ const fn max_iov() -> usize {
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "espidf",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "nuttx",
target_os = "nto",
target_os = "openbsd",
target_os = "horizon",

View File

@ -20,6 +20,7 @@ cfg_if::cfg_if! {
mod windows;
use windows as imp;
pub use windows::{symlink_inner, junction_point};
use crate::sys::path::with_native_path;
} else if #[cfg(target_os = "hermit")] {
mod hermit;
use hermit as imp;
@ -39,7 +40,7 @@ cfg_if::cfg_if! {
}
// FIXME: Replace this with platform-specific path conversion functions.
#[cfg(not(target_family = "unix"))]
#[cfg(not(any(target_family = "unix", target_os = "windows")))]
#[inline]
pub fn with_native_path<T>(path: &Path, f: &dyn Fn(&Path) -> io::Result<T>) -> io::Result<T> {
f(path)
@ -51,7 +52,7 @@ pub use imp::{
};
pub fn read_dir(path: &Path) -> io::Result<ReadDir> {
// FIXME: use with_native_path
// FIXME: use with_native_path on all platforms
imp::readdir(path)
}
@ -68,8 +69,11 @@ pub fn remove_dir(path: &Path) -> io::Result<()> {
}
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
// FIXME: use with_native_path
imp::remove_dir_all(path)
// FIXME: use with_native_path on all platforms
#[cfg(not(windows))]
return imp::remove_dir_all(path);
#[cfg(windows)]
with_native_path(path, &imp::remove_dir_all)
}
pub fn read_link(path: &Path) -> io::Result<PathBuf> {
@ -77,6 +81,10 @@ pub fn read_link(path: &Path) -> io::Result<PathBuf> {
}
pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
// FIXME: use with_native_path on all platforms
#[cfg(windows)]
return imp::symlink(original, link);
#[cfg(not(windows))]
with_native_path(original, &|original| {
with_native_path(link, &|link| imp::symlink(original, link))
})
@ -105,11 +113,17 @@ pub fn canonicalize(path: &Path) -> io::Result<PathBuf> {
}
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
// FIXME: use with_native_path
imp::copy(from, to)
// FIXME: use with_native_path on all platforms
#[cfg(not(windows))]
return imp::copy(from, to);
#[cfg(windows)]
with_native_path(from, &|from| with_native_path(to, &|to| imp::copy(from, to)))
}
pub fn exists(path: &Path) -> io::Result<bool> {
// FIXME: use with_native_path
imp::exists(path)
// FIXME: use with_native_path on all platforms
#[cfg(not(windows))]
return imp::exists(path);
#[cfg(windows)]
with_native_path(path, &imp::exists)
}

View File

@ -12,7 +12,7 @@ use crate::sync::Arc;
use crate::sys::handle::Handle;
use crate::sys::pal::api::{self, WinError, set_file_information_by_handle};
use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul};
use crate::sys::path::maybe_verbatim;
use crate::sys::path::{WCStr, maybe_verbatim};
use crate::sys::time::SystemTime;
use crate::sys::{Align8, c, cvt};
use crate::sys_common::{AsInner, FromInner, IntoInner};
@ -298,10 +298,12 @@ impl OpenOptions {
impl File {
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
let path = maybe_verbatim(path)?;
// SAFETY: maybe_verbatim returns null-terminated strings
let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) };
Self::open_native(&path, opts)
}
fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result<File> {
fn open_native(path: &WCStr, opts: &OpenOptions) -> io::Result<File> {
let creation = opts.get_creation_mode()?;
let handle = unsafe {
c::CreateFileW(
@ -1212,9 +1214,8 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
}
}
pub fn unlink(p: &Path) -> io::Result<()> {
let p_u16s = maybe_verbatim(p)?;
if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 {
pub fn unlink(path: &WCStr) -> io::Result<()> {
if unsafe { c::DeleteFileW(path.as_ptr()) } == 0 {
let err = api::get_last_error();
// if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove
// the file while ignoring the readonly attribute.
@ -1223,7 +1224,7 @@ pub fn unlink(p: &Path) -> io::Result<()> {
let mut opts = OpenOptions::new();
opts.access_mode(c::DELETE);
opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT);
if let Ok(f) = File::open_native(&p_u16s, &opts) {
if let Ok(f) = File::open_native(&path, &opts) {
if f.posix_delete().is_ok() {
return Ok(());
}
@ -1236,10 +1237,7 @@ pub fn unlink(p: &Path) -> io::Result<()> {
}
}
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
let old = maybe_verbatim(old)?;
let new = maybe_verbatim(new)?;
pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> {
if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 {
let err = api::get_last_error();
// if `MoveFileExW` fails with ERROR_ACCESS_DENIED then try to move
@ -1253,7 +1251,8 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
// Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation`
// This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
let Ok(new_len_without_nul_in_bytes): Result<u32, _> = ((new.len() - 1) * 2).try_into()
let Ok(new_len_without_nul_in_bytes): Result<u32, _> =
((new.count_bytes() - 1) * 2).try_into()
else {
return Err(err).io_result();
};
@ -1282,7 +1281,7 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
new.as_ptr().copy_to_nonoverlapping(
(&raw mut (*file_rename_info).FileName).cast::<u16>(),
new.len(),
new.count_bytes(),
);
}
@ -1309,20 +1308,19 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
Ok(())
}
pub fn rmdir(p: &Path) -> io::Result<()> {
let p = maybe_verbatim(p)?;
pub fn rmdir(p: &WCStr) -> io::Result<()> {
cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
Ok(())
}
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
pub fn remove_dir_all(path: &WCStr) -> io::Result<()> {
// Open a file or directory without following symlinks.
let mut opts = OpenOptions::new();
opts.access_mode(c::FILE_LIST_DIRECTORY);
// `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
// `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
let file = File::open(path, &opts)?;
let file = File::open_native(path, &opts)?;
// Test if the file is not a directory or a symlink to a directory.
if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 {
@ -1333,14 +1331,14 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> {
remove_dir_all_iterative(file).io_result()
}
pub fn readlink(path: &Path) -> io::Result<PathBuf> {
pub fn readlink(path: &WCStr) -> io::Result<PathBuf> {
// Open the link with no access mode, instead of generic read.
// By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
// this is needed for a common case.
let mut opts = OpenOptions::new();
opts.access_mode(0);
opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
let file = File::open(path, &opts)?;
let file = File::open_native(&path, &opts)?;
file.readlink()
}
@ -1378,19 +1376,17 @@ pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()>
}
#[cfg(not(target_vendor = "uwp"))]
pub fn link(original: &Path, link: &Path) -> io::Result<()> {
let original = maybe_verbatim(original)?;
let link = maybe_verbatim(link)?;
pub fn link(original: &WCStr, link: &WCStr) -> io::Result<()> {
cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
Ok(())
}
#[cfg(target_vendor = "uwp")]
pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
pub fn link(_original: &WCStr, _link: &WCStr) -> io::Result<()> {
return Err(io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP"));
}
pub fn stat(path: &Path) -> io::Result<FileAttr> {
pub fn stat(path: &WCStr) -> io::Result<FileAttr> {
match metadata(path, ReparsePoint::Follow) {
Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => {
if let Ok(attrs) = lstat(path) {
@ -1404,7 +1400,7 @@ pub fn stat(path: &Path) -> io::Result<FileAttr> {
}
}
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
pub fn lstat(path: &WCStr) -> io::Result<FileAttr> {
metadata(path, ReparsePoint::Open)
}
@ -1420,7 +1416,7 @@ impl ReparsePoint {
}
}
fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
fn metadata(path: &WCStr, reparse: ReparsePoint) -> io::Result<FileAttr> {
let mut opts = OpenOptions::new();
// No read or write permissions are necessary
opts.access_mode(0);
@ -1429,7 +1425,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
// Attempt to open the file normally.
// If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`.
// If the fallback fails for any reason we return the original error.
match File::open(path, &opts) {
match File::open_native(&path, &opts) {
Ok(file) => file.file_attr(),
Err(e)
if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)]
@ -1442,8 +1438,6 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
// However, there are special system files, such as
// `C:\hiberfil.sys`, that are locked in a way that denies even that.
unsafe {
let path = maybe_verbatim(path)?;
// `FindFirstFileExW` accepts wildcard file names.
// Fortunately wildcards are not valid file names and
// `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
@ -1482,8 +1476,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
}
}
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
let p = maybe_verbatim(p)?;
pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> {
unsafe {
cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
Ok(())
@ -1499,17 +1492,17 @@ fn get_path(f: &File) -> io::Result<PathBuf> {
)
}
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
let mut opts = OpenOptions::new();
// No read or write permissions are necessary
opts.access_mode(0);
// This flag is so we can open directories too
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
let f = File::open(p, &opts)?;
let f = File::open_native(p, &opts)?;
get_path(&f)
}
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
pub fn copy(from: &WCStr, to: &WCStr) -> io::Result<u64> {
unsafe extern "system" fn callback(
_TotalFileSize: i64,
_TotalBytesTransferred: i64,
@ -1528,13 +1521,11 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
c::PROGRESS_CONTINUE
}
}
let pfrom = maybe_verbatim(from)?;
let pto = maybe_verbatim(to)?;
let mut size = 0i64;
cvt(unsafe {
c::CopyFileExW(
pfrom.as_ptr(),
pto.as_ptr(),
from.as_ptr(),
to.as_ptr(),
Some(callback),
(&raw mut size) as *mut _,
ptr::null_mut(),
@ -1624,14 +1615,14 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> {
}
// Try to see if a file exists but, unlike `exists`, report I/O errors.
pub fn exists(path: &Path) -> io::Result<bool> {
pub fn exists(path: &WCStr) -> io::Result<bool> {
// Open the file to ensure any symlinks are followed to their target.
let mut opts = OpenOptions::new();
// No read, write, etc access rights are needed.
opts.access_mode(0);
// Backup semantics enables opening directories as well as files.
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
match File::open(path, &opts) {
match File::open_native(path, &opts) {
Err(e) => match e.kind() {
// The file definitely does not exist
io::ErrorKind::NotFound => Ok(false),

View File

@ -9,6 +9,7 @@ mod alloc;
mod personality;
pub mod anonymous_pipe;
pub mod args;
pub mod backtrace;
pub mod cmath;
pub mod exit_guard;

View File

@ -1,5 +1,6 @@
use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t};
#[cfg(not(any(target_os = "espidf", target_os = "nuttx")))]
use crate::ffi::CStr;
use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Shutdown, SocketAddr};

View File

@ -18,7 +18,6 @@
use crate::os::raw::c_char;
pub mod args;
pub mod env;
pub mod futex;
pub mod os;
@ -58,7 +57,7 @@ pub extern "C" fn __rust_abort() {
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
unsafe {
args::init(argc, argv);
crate::sys::args::init(argc, argv);
}
}

View File

@ -9,7 +9,6 @@ use crate::io::ErrorKind;
use crate::sync::atomic::{AtomicBool, Ordering};
pub mod abi;
pub mod args;
pub mod env;
mod libunwind_integration;
pub mod os;
@ -24,7 +23,7 @@ pub mod waitqueue;
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
unsafe {
args::init(argc, argv);
crate::sys::args::init(argc, argv);
}
}

View File

@ -16,8 +16,6 @@ pub mod itron {
use super::unsupported;
}
#[path = "../unsupported/args.rs"]
pub mod args;
pub mod env;
// `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as
// `crate::sys::error`

View File

@ -6,8 +6,6 @@
#![allow(unused_variables)]
#![allow(dead_code)]
#[path = "../unsupported/args.rs"]
pub mod args;
#[path = "../unsupported/env.rs"]
pub mod env;
//pub mod fd;

View File

@ -1,7 +1,5 @@
//! System bindings for the Trusty OS.
#[path = "../unsupported/args.rs"]
pub mod args;
#[path = "../unsupported/common.rs"]
#[deny(unsafe_op_in_unsafe_fn)]
mod common;

View File

@ -13,7 +13,6 @@
//! [`OsString`]: crate::ffi::OsString
#![forbid(unsafe_op_in_unsafe_fn)]
pub mod args;
pub mod env;
pub mod helpers;
pub mod os;

View File

@ -6,7 +6,6 @@ use crate::io::ErrorKind;
#[macro_use]
pub mod weak;
pub mod args;
pub mod env;
#[cfg(target_os = "fuchsia")]
pub mod fuchsia;
@ -27,6 +26,7 @@ pub mod time;
pub fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
#[cfg(not(target_os = "espidf"))]
#[cfg_attr(target_os = "vita", allow(unused_variables))]
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
// See `fn init()` in `library/std/src/rt.rs` for docs on `sigpipe`.
@ -47,7 +47,8 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
reset_sigpipe(sigpipe);
stack_overflow::init();
args::init(argc, argv);
#[cfg(not(target_os = "vita"))]
crate::sys::args::init(argc, argv);
// Normally, `thread::spawn` will call `Thread::set_name` but since this thread
// already exists, we have to call it ourselves. We only do this on Apple targets

View File

@ -1,6 +1,5 @@
#![deny(unsafe_op_in_unsafe_fn)]
pub mod args;
pub mod env;
pub mod os;
pub mod pipe;

View File

@ -1,61 +0,0 @@
#![forbid(unsafe_op_in_unsafe_fn)]
use crate::ffi::{CStr, OsStr, OsString};
use crate::os::wasi::ffi::OsStrExt;
use crate::{fmt, vec};
pub struct Args {
iter: vec::IntoIter<OsString>,
}
impl !Send for Args {}
impl !Sync for Args {}
/// Returns the command line arguments
pub fn args() -> Args {
Args { iter: maybe_args().unwrap_or(Vec::new()).into_iter() }
}
fn maybe_args() -> Option<Vec<OsString>> {
unsafe {
let (argc, buf_size) = wasi::args_sizes_get().ok()?;
let mut argv = Vec::with_capacity(argc);
let mut buf = Vec::with_capacity(buf_size);
wasi::args_get(argv.as_mut_ptr(), buf.as_mut_ptr()).ok()?;
argv.set_len(argc);
let mut ret = Vec::with_capacity(argc);
for ptr in argv {
let s = CStr::from_ptr(ptr.cast());
ret.push(OsStr::from_bytes(s.to_bytes()).to_owned());
}
Some(ret)
}
}
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.iter.as_slice().fmt(f)
}
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize {
self.iter.len()
}
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.iter.next_back()
}
}

View File

@ -13,7 +13,6 @@
//! compiling for wasm. That way it's a compile time error for something that's
//! guaranteed to be a runtime error!
pub mod args;
pub mod env;
#[allow(unused)]
#[path = "../wasm/atomics/futex.rs"]

View File

@ -6,8 +6,6 @@
//! To begin with, this target mirrors the wasi target 1 to 1, but over
//! time this will change significantly.
#[path = "../wasi/args.rs"]
pub mod args;
#[path = "../wasi/env.rs"]
pub mod env;
#[allow(unused)]

View File

@ -16,8 +16,6 @@
#![deny(unsafe_op_in_unsafe_fn)]
#[path = "../unsupported/args.rs"]
pub mod args;
pub mod env;
#[path = "../unsupported/os.rs"]
pub mod os;

View File

@ -14,7 +14,6 @@ pub mod compat;
pub mod api;
pub mod args;
pub mod c;
pub mod env;
#[cfg(not(target_vendor = "win7"))]

View File

@ -1,53 +0,0 @@
use crate::ffi::OsString;
use crate::sys::pal::xous::os::get_application_parameters;
use crate::sys::pal::xous::os::params::ArgumentList;
use crate::{fmt, vec};
pub struct Args {
parsed_args_list: vec::IntoIter<OsString>,
}
pub fn args() -> Args {
let Some(params) = get_application_parameters() else {
return Args { parsed_args_list: vec![].into_iter() };
};
for param in params {
if let Ok(args) = ArgumentList::try_from(&param) {
let mut parsed_args = vec![];
for arg in args {
parsed_args.push(arg.into());
}
return Args { parsed_args_list: parsed_args.into_iter() };
}
}
Args { parsed_args_list: vec![].into_iter() }
}
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.parsed_args_list.as_slice().fmt(f)
}
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.parsed_args_list.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.parsed_args_list.size_hint()
}
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.parsed_args_list.next_back()
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize {
self.parsed_args_list.len()
}
}

View File

@ -1,6 +1,5 @@
#![forbid(unsafe_op_in_unsafe_fn)]
pub mod args;
#[path = "../unsupported/env.rs"]
pub mod env;
pub mod os;

View File

@ -8,11 +8,9 @@
//! will likely change over time.
#![forbid(unsafe_op_in_unsafe_fn)]
const WORD_SIZE: usize = size_of::<u32>();
pub const WORD_SIZE: usize = size_of::<u32>();
pub mod abi;
#[path = "../zkvm/args.rs"]
pub mod args;
pub mod env;
pub mod os;
#[path = "../unsupported/pipe.rs"]

View File

@ -10,6 +10,40 @@ mod tests;
pub const MAIN_SEP_STR: &str = "\\";
pub const MAIN_SEP: char = '\\';
/// A null terminated wide string.
#[repr(transparent)]
pub struct WCStr([u16]);
impl WCStr {
/// Convert a slice to a WCStr without checks.
///
/// Though it is memory safe, the slice should also not contain interior nulls
/// as this may lead to unwanted truncation.
///
/// # Safety
///
/// The slice must end in a null.
pub unsafe fn from_wchars_with_null_unchecked(s: &[u16]) -> &Self {
unsafe { &*(s as *const [u16] as *const Self) }
}
pub fn as_ptr(&self) -> *const u16 {
self.0.as_ptr()
}
pub fn count_bytes(&self) -> usize {
self.0.len()
}
}
#[inline]
pub fn with_native_path<T>(path: &Path, f: &dyn Fn(&WCStr) -> io::Result<T>) -> io::Result<T> {
let path = maybe_verbatim(path)?;
// SAFETY: maybe_verbatim returns null-terminated strings
let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) };
f(path)
}
#[inline]
pub fn is_sep_byte(b: u8) -> bool {
b == b'/' || b == b'\\'

View File

@ -666,10 +666,11 @@ fn run_test_in_process(
io::set_output_capture(None);
let test_result = match result {
Ok(()) => calc_result(&desc, Ok(()), time_opts.as_ref(), exec_time.as_ref()),
Err(e) => calc_result(&desc, Err(e.as_ref()), time_opts.as_ref(), exec_time.as_ref()),
};
// Determine whether the test passed or failed, by comparing its panic
// payload (if any) with its `ShouldPanic` value, and by checking for
// fatal timeout.
let test_result =
calc_result(&desc, result.err().as_deref(), time_opts.as_ref(), exec_time.as_ref());
let stdout = data.lock().unwrap_or_else(|e| e.into_inner()).to_vec();
let message = CompletedTest::new(id, desc, test_result, exec_time, stdout);
monitor_ch.send(message).unwrap();
@ -741,10 +742,7 @@ fn spawn_test_subprocess(
fn run_test_in_spawned_subprocess(desc: TestDesc, runnable_test: RunnableTest) -> ! {
let builtin_panic_hook = panic::take_hook();
let record_result = Arc::new(move |panic_info: Option<&'_ PanicHookInfo<'_>>| {
let test_result = match panic_info {
Some(info) => calc_result(&desc, Err(info.payload()), None, None),
None => calc_result(&desc, Ok(()), None, None),
};
let test_result = calc_result(&desc, panic_info.map(|info| info.payload()), None, None);
// We don't support serializing TrFailedMsg, so just
// print the message out to stderr.

View File

@ -39,15 +39,18 @@ pub enum TestResult {
/// Creates a `TestResult` depending on the raw result of test execution
/// and associated data.
pub(crate) fn calc_result<'a>(
pub(crate) fn calc_result(
desc: &TestDesc,
task_result: Result<(), &'a (dyn Any + 'static + Send)>,
panic_payload: Option<&(dyn Any + Send)>,
time_opts: Option<&time::TestTimeOptions>,
exec_time: Option<&time::TestExecTime>,
) -> TestResult {
let result = match (&desc.should_panic, task_result) {
(&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TestResult::TrOk,
(&ShouldPanic::YesWithMessage(msg), Err(err)) => {
let result = match (desc.should_panic, panic_payload) {
// The test did or didn't panic, as expected.
(ShouldPanic::No, None) | (ShouldPanic::Yes, Some(_)) => TestResult::TrOk,
// Check the actual panic message against the expected message.
(ShouldPanic::YesWithMessage(msg), Some(err)) => {
let maybe_panic_str = err
.downcast_ref::<String>()
.map(|e| &**e)
@ -71,10 +74,14 @@ pub(crate) fn calc_result<'a>(
))
}
}
(&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => {
// The test should have panicked, but didn't panic.
(ShouldPanic::Yes, None) | (ShouldPanic::YesWithMessage(_), None) => {
TestResult::TrFailedMsg("test did not panic as expected".to_string())
}
_ => TestResult::TrFailed,
// The test should not have panicked, but did panic.
(ShouldPanic::No, Some(_)) => TestResult::TrFailed,
};
// If test is already failed (or allowed to fail), do not change the result.

View File

@ -563,9 +563,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.5"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]

View File

@ -101,6 +101,7 @@ for more details.
| `normalize-stdout` | Normalize actual stdout with a rule `"<raw>" -> "<normalized>"` before comparing against snapshot | `ui`, `incremental` | `"<RAW>" -> "<NORMALIZED>"`, `<RAW>`/`<NORMALIZED>` is regex capture and replace syntax |
| `dont-check-compiler-stderr` | Don't check actual compiler stderr vs stderr snapshot | `ui` | N/A |
| `dont-check-compiler-stdout` | Don't check actual compiler stdout vs stdout snapshot | `ui` | N/A |
| `dont-require-annotations` | Don't require line annotations for the given diagnostic kind (`//~ KIND`) to be exhaustive | `ui`, `incremental` | `ERROR`, `WARN`, `NOTE`, `HELP`, `SUGGESTION` |
| `run-rustfix` | Apply all suggestions via `rustfix`, snapshot fixed output, and check fixed output builds | `ui` | N/A |
| `rustfix-only-machine-applicable` | `run-rustfix` but only machine-applicable suggestions | `ui` | N/A |
| `exec-env` | Env var to set when executing a test | `ui`, `crashes` | `<KEY>=<VALUE>` |

View File

@ -303,8 +303,7 @@ It should be preferred to using `error-pattern`, which is imprecise and non-exha
### `error-pattern`
The `error-pattern` [directive](directives.md) can be used for runtime messages, which don't
have a specific span, or for compile time messages if imprecise matching is required due to
multi-line platform specific diagnostics.
have a specific span, or in exceptional cases for compile time messages.
Let's think about this test:
@ -318,7 +317,7 @@ fn main() {
```
We want to ensure this shows "index out of bounds" but we cannot use the `ERROR`
annotation since the error doesn't have any span. Then it's time to use the
annotation since the runtime error doesn't have any span. Then it's time to use the
`error-pattern` directive:
```rust,ignore
@ -331,29 +330,51 @@ fn main() {
}
```
But for strict testing, try to use the `ERROR` annotation as much as possible,
including `//~?` annotations for diagnostics without span.
For compile time diagnostics `error-pattern` should very rarely be necessary.
Use of `error-pattern` is not recommended in general.
Per-line annotations (`//~`) are still checked in tests using `error-pattern`.
To opt out of these checks, use `//@ compile-flags: --error-format=human`.
Do that only in exceptional cases.
For strict testing of compile time output, try to use the line annotations `//~` as much as
possible, including `//~?` annotations for diagnostics without span.
### Error levels
If the compile time output is target dependent or too verbose, use directive
`//@ dont-require-annotations: <diagnostic-kind>` to make the line annotation checking
non-exhaustive, some of the compiler messages can stay uncovered by annotations in this mode.
The error levels that you can have are:
For checking runtime output `//@ check-run-results` may be preferable.
Only use `error-pattern` if none of the above works.
Line annotations `//~` are still checked in tests using `error-pattern`.
In exceptional cases use `//@ compile-flags: --error-format=human` to opt out of these checks.
### Diagnostic kinds (error levels)
The diagnostic kinds that you can have are:
- `ERROR`
- `WARN` or `WARNING`
- `WARN` (or `WARNING`)
- `NOTE`
- `HELP` and `SUGGESTION`
- `HELP`
- `SUGGESTION`
You are allowed to not include a level, but you should include it at least for
the primary message.
The `SUGGESTION` level is used for specifying what the expected replacement text
The `SUGGESTION` kind is used for specifying what the expected replacement text
should be for a diagnostic suggestion.
`ERROR` and `WARN` kinds are required to be exhaustively covered by line annotations
`//~` by default.
Other kinds only need to be line-annotated if at least one annotation of that kind appears
in the test file. For example, one `//~ NOTE` will also require all other `//~ NOTE`s in the file
to be written out explicitly.
Use directive `//@ dont-require-annotations` to opt out of exhaustive annotations.
E.g. use `//@ dont-require-annotations: NOTE` to annotate notes selectively.
Avoid using this directive for `ERROR`s and `WARN`ings, unless there's a serious reason, like
target-dependent compiler output.
Missing diagnostic kinds (`//~ message`) are currently accepted, but are being phased away.
They will match any compiler output kind, but will not force exhaustive annotations for that kind.
Prefer explicit kind and `//@ dont-require-annotations` to achieve the same effect.
UI tests use the `-A unused` flag by default to ignore all unused warnings, as
unused warnings are usually not the focus of a test. However, simple code
samples often have unused warnings. If the test is specifically testing an

View File

@ -476,4 +476,4 @@ dist/2025-04-02/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=e67a33440c3e021ff2
dist/2025-04-02/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=0ea7e17d7bb67d6a6c4b2f864aaffcd96512f15f17f0acc63751eb1df6c486a7
dist/2025-04-02/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=b73d37b704ab58921172cc561f5598db6a504dcd4d7980966f7c26caaf6d3594
dist/2025-04-02/rustc-nightly-x86_64-unknown-netbsd.tar.gz=986f6c594d37bcbd3833e053640ba8775f68d26a65c5618386654ef55d7b3542
dist/2025-04-02/rustc-nightly-x86_64-unknown-netbsd.tar.xz=c0d9a88c30d2ab38ec3a11fabb5515ed9bc3ac1a8e35a438d68bf7ff82f6b843
dist/2025-04-02/rustc-nightly-x86_64-unknown-netbsd.tar.xz=c0d9a88c30d2ab38ec3a11fabb5515ed9bc3ac1a8e35a438d68bf7ff82f6b843

View File

@ -65,32 +65,33 @@ impl Tool {
nightly_branch,
} = &self.config;
file_content.push_str(&format!("dist_server={}", dist_server));
file_content.push_str(&format!("\nartifacts_server={}", artifacts_server));
file_content.push_str(&format!("dist_server={}\n", dist_server));
file_content.push_str(&format!("artifacts_server={}\n", artifacts_server));
file_content.push_str(&format!(
"\nartifacts_with_llvm_assertions_server={}",
"artifacts_with_llvm_assertions_server={}\n",
artifacts_with_llvm_assertions_server
));
file_content.push_str(&format!("\ngit_merge_commit_email={}", git_merge_commit_email));
file_content.push_str(&format!("\ngit_repository={}", git_repository));
file_content.push_str(&format!("\nnightly_branch={}", nightly_branch));
file_content.push_str(&format!("git_merge_commit_email={}\n", git_merge_commit_email));
file_content.push_str(&format!("git_repository={}\n", git_repository));
file_content.push_str(&format!("nightly_branch={}\n", nightly_branch));
file_content.push_str("\n\n");
file_content.push_str("\n");
file_content.push_str(COMMENTS);
file_content.push_str("\n");
let compiler = self.detect_compiler()?;
file_content.push_str(&format!("\ncompiler_date={}", compiler.date));
file_content.push_str(&format!("\ncompiler_version={}", compiler.version));
file_content.push_str(&format!("compiler_date={}\n", compiler.date));
file_content.push_str(&format!("compiler_version={}\n", compiler.version));
if let Some(rustfmt) = self.detect_rustfmt()? {
file_content.push_str(&format!("\nrustfmt_date={}", rustfmt.date));
file_content.push_str(&format!("\nrustfmt_version={}", rustfmt.version));
file_content.push_str(&format!("rustfmt_date={}\n", rustfmt.date));
file_content.push_str(&format!("rustfmt_version={}\n", rustfmt.version));
}
file_content.push_str("\n");
for (key, value) in self.checksums {
file_content.push_str(&format!("\n{}={}", key, value));
file_content.push_str(&format!("{}={}\n", key, value));
}
std::fs::write(PATH, file_content)?;

View File

@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::BufReader;
@ -198,7 +198,7 @@ pub struct TestProps {
/// that don't otherwise want/need `-Z build-std`.
pub add_core_stubs: bool,
/// Whether line annotatins are required for the given error kind.
pub require_annotations: HashMap<ErrorKind, bool>,
pub dont_require_annotations: HashSet<ErrorKind>,
}
mod directives {
@ -301,13 +301,7 @@ impl TestProps {
no_auto_check_cfg: false,
has_enzyme: false,
add_core_stubs: false,
require_annotations: HashMap::from([
(ErrorKind::Help, true),
(ErrorKind::Note, true),
(ErrorKind::Error, true),
(ErrorKind::Warning, true),
(ErrorKind::Suggestion, false),
]),
dont_require_annotations: Default::default(),
}
}
@ -593,8 +587,8 @@ impl TestProps {
if let Some(err_kind) =
config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS)
{
self.require_annotations
.insert(ErrorKind::expect_from_user_str(&err_kind), false);
self.dont_require_annotations
.insert(ErrorKind::expect_from_user_str(err_kind.trim()));
}
},
);

View File

@ -22,7 +22,7 @@ use crate::common::{
output_base_dir, output_base_name, output_testname_unique,
};
use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff};
use crate::errors::{self, Error, ErrorKind};
use crate::errors::{Error, ErrorKind};
use crate::header::TestProps;
use crate::read2::{Truncated, read2_abbreviated};
use crate::util::{PathBufExt, add_dylib_path, logv, static_regex};
@ -675,7 +675,7 @@ impl<'test> TestCx<'test> {
}
}
fn check_expected_errors(&self, expected_errors: Vec<errors::Error>, proc_res: &ProcRes) {
fn check_expected_errors(&self, expected_errors: Vec<Error>, proc_res: &ProcRes) {
debug!(
"check_expected_errors: expected_errors={:?} proc_res.status={:?}",
expected_errors, proc_res.status
@ -710,8 +710,12 @@ impl<'test> TestCx<'test> {
self.testpaths.file.display().to_string()
};
let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help));
let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note));
// Errors and warnings are always expected, other diagnostics are only expected
// if one of them actually occurs in the test.
let expected_kinds: HashSet<_> = [ErrorKind::Error, ErrorKind::Warning]
.into_iter()
.chain(expected_errors.iter().filter_map(|e| e.kind))
.collect();
// Parse the JSON output from the compiler and extract out the messages.
let actual_errors = json::parse_output(&diagnostic_file_name, &proc_res.stderr, proc_res);
@ -737,8 +741,11 @@ impl<'test> TestCx<'test> {
}
None => {
// If the test is a known bug, don't require that the error is annotated
if self.is_unexpected_compiler_message(&actual_error, expect_help, expect_note)
if actual_error.require_annotation
&& actual_error.kind.map_or(false, |kind| {
expected_kinds.contains(&kind)
&& !self.props.dont_require_annotations.contains(&kind)
})
{
self.error(&format!(
"{}:{}: unexpected {}: '{}'",
@ -796,27 +803,6 @@ impl<'test> TestCx<'test> {
}
}
/// Returns `true` if we should report an error about `actual_error`,
/// which did not match any of the expected error.
fn is_unexpected_compiler_message(
&self,
actual_error: &Error,
expect_help: bool,
expect_note: bool,
) -> bool {
actual_error.require_annotation
&& actual_error.kind.map_or(false, |err_kind| {
// If the test being checked doesn't contain any "help" or "note" annotations, then
// we don't require annotating "help" or "note" (respecively) diagnostics at all.
let default_require_annotations = self.props.require_annotations[&err_kind];
match err_kind {
ErrorKind::Help => expect_help && default_require_annotations,
ErrorKind::Note => expect_note && default_require_annotations,
_ => default_require_annotations,
}
})
}
fn should_emit_metadata(&self, pm: Option<PassMode>) -> Emit {
match (pm, self.props.fail_mode, self.config.mode) {
(Some(PassMode::Check), ..) | (_, Some(FailMode::Check), Ui) => Emit::Metadata,

View File

@ -156,9 +156,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]

View File

@ -981,9 +981,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]

View File

@ -1,8 +1,11 @@
//@ revisions: OPT2 OPT3
//@ revisions: OPT2 OPT3 OPT3_S390X
//@[OPT2] compile-flags: -Copt-level=2
//@[OPT3] compile-flags: -C opt-level=3
// some targets don't do the opt we are looking for
//@[OPT3] only-64bit
//@[OPT3] ignore-s390x
//@[OPT3_S390X] compile-flags: -C opt-level=3 -C target-cpu=z13
//@[OPT3_S390X] only-s390x
#![crate_type = "lib"]
#![no_std]
@ -17,6 +20,10 @@
// OPT3-NEXT: call <8 x i16> @llvm.bswap
// OPT3-NEXT: store <8 x i16>
// OPT3-NEXT: ret void
// OPT3_S390X: load <8 x i16>
// OPT3_S390X-NEXT: call <8 x i16> @llvm.bswap
// OPT3_S390X-NEXT: store <8 x i16>
// OPT3_S390X-NEXT: ret void
#[no_mangle]
pub fn convert(value: [u16; 8]) -> [u8; 16] {
#[cfg(target_endian = "little")]

View File

@ -46,21 +46,21 @@ pub fn test_zst(p: bool, a: (), b: ()) -> () {
pub fn test_int2(p: bool, a: u64, b: u64) -> u64 {
// CHECK-LABEL: define{{.*}} @test_int2
// CHECK: select i1 %p, i64 %a, i64 %b, !unpredictable
p.select_unpredictable(a, b)
core::hint::select_unpredictable(p, a, b)
}
#[no_mangle]
pub fn test_pair2(p: bool, a: (u64, u64), b: (u64, u64)) -> (u64, u64) {
// CHECK-LABEL: define{{.*}} @test_pair2
// CHECK: select i1 %p, {{.*}}, !unpredictable
p.select_unpredictable(a, b)
core::hint::select_unpredictable(p, a, b)
}
#[no_mangle]
pub fn test_struct2(p: bool, a: Large, b: Large) -> Large {
// CHECK-LABEL: define{{.*}} @test_struct2
// CHECK: select i1 %p, {{.*}}, !unpredictable
p.select_unpredictable(a, b)
core::hint::select_unpredictable(p, a, b)
}
#[no_mangle]
@ -68,5 +68,5 @@ pub fn test_zst2(p: bool, a: (), b: ()) -> () {
// CHECK-LABEL: define{{.*}} @test_zst2
// CHECK-NEXT: start:
// CHECK-NEXT: ret void
p.select_unpredictable(a, b)
core::hint::select_unpredictable(p, a, b)
}

View File

@ -1,4 +1,5 @@
//@ edition:2018
//@ dont-require-annotations: SUGGESTION
fn take_u32(_x: u32) {}

View File

@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:12:14
--> $DIR/suggest-missing-await.rs:13:14
|
LL | take_u32(x)
| -------- ^ expected `u32`, found future
@ -7,12 +7,12 @@ LL | take_u32(x)
| arguments to this function are incorrect
|
note: calling an async function returns a future
--> $DIR/suggest-missing-await.rs:12:14
--> $DIR/suggest-missing-await.rs:13:14
|
LL | take_u32(x)
| ^
note: function defined here
--> $DIR/suggest-missing-await.rs:3:4
--> $DIR/suggest-missing-await.rs:4:4
|
LL | fn take_u32(_x: u32) {}
| ^^^^^^^^ -------
@ -22,13 +22,13 @@ LL | take_u32(x.await)
| ++++++
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:22:5
--> $DIR/suggest-missing-await.rs:23:5
|
LL | dummy()
| ^^^^^^^ expected `()`, found future
|
note: calling an async function returns a future
--> $DIR/suggest-missing-await.rs:22:5
--> $DIR/suggest-missing-await.rs:23:5
|
LL | dummy()
| ^^^^^^^
@ -42,7 +42,7 @@ LL | dummy();
| +
error[E0308]: `if` and `else` have incompatible types
--> $DIR/suggest-missing-await.rs:35:9
--> $DIR/suggest-missing-await.rs:36:9
|
LL | let _x = if true {
| ______________-
@ -64,7 +64,7 @@ LL | dummy().await
| ++++++
error[E0308]: `match` arms have incompatible types
--> $DIR/suggest-missing-await.rs:45:14
--> $DIR/suggest-missing-await.rs:46:14
|
LL | let _x = match 0usize {
| ______________-
@ -87,7 +87,7 @@ LL ~ 1 => dummy().await,
|
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:53:9
--> $DIR/suggest-missing-await.rs:54:9
|
LL | let _x = match dummy() {
| ------- this expression has type `impl Future<Output = ()>`
@ -102,7 +102,7 @@ LL | let _x = match dummy().await {
| ++++++
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:67:9
--> $DIR/suggest-missing-await.rs:68:9
|
LL | match dummy_result() {
| -------------- this expression has type `impl Future<Output = Result<(), ()>>`
@ -118,7 +118,7 @@ LL | match dummy_result().await {
| ++++++
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:69:9
--> $DIR/suggest-missing-await.rs:70:9
|
LL | match dummy_result() {
| -------------- this expression has type `impl Future<Output = Result<(), ()>>`
@ -134,7 +134,7 @@ LL | match dummy_result().await {
| ++++++
error[E0308]: mismatched types
--> $DIR/suggest-missing-await.rs:77:27
--> $DIR/suggest-missing-await.rs:78:27
|
LL | Some(do_async()).map(|()| {});
| ^^

View File

@ -1,3 +1,5 @@
//@ dont-require-annotations: SUGGESTION
fn main() {
let u = 5 as bool; //~ ERROR cannot cast `i32` as `bool`
//~| HELP compare with zero instead

View File

@ -1,5 +1,5 @@
error[E0054]: cannot cast `i32` as `bool`
--> $DIR/cast-as-bool.rs:2:13
--> $DIR/cast-as-bool.rs:4:13
|
LL | let u = 5 as bool;
| ^^^^^^^^^
@ -11,7 +11,7 @@ LL + let u = 5 != 0;
|
error[E0054]: cannot cast `i32` as `bool`
--> $DIR/cast-as-bool.rs:6:13
--> $DIR/cast-as-bool.rs:8:13
|
LL | let t = (1 + 2) as bool;
| ^^^^^^^^^^^^^^^
@ -23,7 +23,7 @@ LL + let t = (1 + 2) != 0;
|
error[E0054]: cannot cast `u32` as `bool`
--> $DIR/cast-as-bool.rs:10:13
--> $DIR/cast-as-bool.rs:12:13
|
LL | let _ = 5_u32 as bool;
| ^^^^^^^^^^^^^
@ -35,7 +35,7 @@ LL + let _ = 5_u32 != 0;
|
error[E0054]: cannot cast `f64` as `bool`
--> $DIR/cast-as-bool.rs:13:13
--> $DIR/cast-as-bool.rs:15:13
|
LL | let _ = 64.0_f64 as bool;
| ^^^^^^^^^^^^^^^^
@ -47,43 +47,43 @@ LL + let _ = 64.0_f64 != 0;
|
error[E0054]: cannot cast `IntEnum` as `bool`
--> $DIR/cast-as-bool.rs:24:13
--> $DIR/cast-as-bool.rs:26:13
|
LL | let _ = IntEnum::One as bool;
| ^^^^^^^^^^^^^^^^^^^^ unsupported cast
error[E0054]: cannot cast `fn(u8) -> String {uwu}` as `bool`
--> $DIR/cast-as-bool.rs:33:13
--> $DIR/cast-as-bool.rs:35:13
|
LL | let _ = uwu as bool;
| ^^^^^^^^^^^ unsupported cast
error[E0054]: cannot cast `unsafe fn() {owo}` as `bool`
--> $DIR/cast-as-bool.rs:35:13
--> $DIR/cast-as-bool.rs:37:13
|
LL | let _ = owo as bool;
| ^^^^^^^^^^^ unsupported cast
error[E0054]: cannot cast `fn(u8) -> String` as `bool`
--> $DIR/cast-as-bool.rs:38:13
--> $DIR/cast-as-bool.rs:40:13
|
LL | let _ = uwu as fn(u8) -> String as bool;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported cast
error[E0054]: cannot cast `char` as `bool`
--> $DIR/cast-as-bool.rs:40:13
--> $DIR/cast-as-bool.rs:42:13
|
LL | let _ = 'x' as bool;
| ^^^^^^^^^^^ unsupported cast
error[E0054]: cannot cast `*const ()` as `bool`
--> $DIR/cast-as-bool.rs:44:13
--> $DIR/cast-as-bool.rs:46:13
|
LL | let _ = ptr as bool;
| ^^^^^^^^^^^ unsupported cast
error[E0606]: casting `&'static str` as `bool` is invalid
--> $DIR/cast-as-bool.rs:46:13
--> $DIR/cast-as-bool.rs:48:13
|
LL | let v = "hello" as bool;
| ^^^^^^^^^^^^^^^

View File

@ -1,6 +1,6 @@
// Error, the linked empty library is `no_std` and doesn't provide a panic handler.
//@ dont-require-annotations:ERROR
//@ dont-require-annotations: ERROR
//@ dont-check-compiler-stderr
//@ aux-build: cfg_false_lib_no_std_before.rs

View File

@ -19,6 +19,7 @@ fn fn_mut() -> _ {
let x = String::new();
//~^ HELP: consider changing this to be mutable
//~| NOTE binding `x` declared here
//~| SUGGESTION mut
|c| { //~ NOTE: value captured here
x.push(c);
//~^ ERROR: does not live long enough

View File

@ -21,7 +21,7 @@ LL | fn fn_mut() -> _ {
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/suggest-return-closure.rs:32:13
--> $DIR/suggest-return-closure.rs:33:13
|
LL | fn fun() -> _ {
| ^
@ -32,7 +32,7 @@ LL | fn fun() -> _ {
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
--> $DIR/suggest-return-closure.rs:23:9
--> $DIR/suggest-return-closure.rs:24:9
|
LL | x.push(c);
| ^ cannot borrow as mutable
@ -43,7 +43,7 @@ LL | let mut x = String::new();
| +++
error[E0597]: `x` does not live long enough
--> $DIR/suggest-return-closure.rs:23:9
--> $DIR/suggest-return-closure.rs:24:9
|
LL | let x = String::new();
| - binding `x` declared here

View File

@ -1,6 +1,6 @@
// ignore-tidy-linelength
//@ build-fail
//@ dont-require-annotations:ERROR
//@ dont-require-annotations: ERROR
//@ dont-check-compiler-stderr
//@ aux-build:panic-runtime-unwind.rs
//@ aux-build:panic-runtime-unwind2.rs

View File

@ -1,6 +1,6 @@
// ignore-tidy-linelength
//@ build-fail
//@ dont-require-annotations:ERROR
//@ dont-require-annotations: ERROR
//@ dont-check-compiler-stderr
//@ aux-build:panic-runtime-unwind.rs
//@ compile-flags:-C panic=abort

View File

@ -1,6 +1,6 @@
// ignore-tidy-linelength
//@ build-fail
//@ dont-require-annotations:ERROR
//@ dont-require-annotations: ERROR
//@ dont-check-compiler-stderr
//@ aux-build:panic-runtime-unwind.rs
//@ aux-build:wants-panic-runtime-unwind.rs

View File

@ -1,3 +1,5 @@
//@ dont-require-annotations: SUGGESTION
struct S;
impl S {

View File

@ -1,5 +1,5 @@
error: expected one of `:`, `@`, or `|`, found `bar`
--> $DIR/inverted-parameters.rs:4:24
--> $DIR/inverted-parameters.rs:6:24
|
LL | fn foo(&self, &str bar) {}
| -----^^^
@ -8,7 +8,7 @@ LL | fn foo(&self, &str bar) {}
| help: declare the type after the parameter binding: `<identifier>: <type>`
error: expected one of `:`, `@`, or `|`, found `quux`
--> $DIR/inverted-parameters.rs:10:10
--> $DIR/inverted-parameters.rs:12:10
|
LL | fn baz(S quux, xyzzy: i32) {}
| --^^^^
@ -17,19 +17,19 @@ LL | fn baz(S quux, xyzzy: i32) {}
| help: declare the type after the parameter binding: `<identifier>: <type>`
error: expected one of `:`, `@`, or `|`, found `a`
--> $DIR/inverted-parameters.rs:15:12
--> $DIR/inverted-parameters.rs:17:12
|
LL | fn one(i32 a b) {}
| ^ expected one of `:`, `@`, or `|`
error: expected one of `:` or `|`, found `(`
--> $DIR/inverted-parameters.rs:18:23
--> $DIR/inverted-parameters.rs:20:23
|
LL | fn pattern((i32, i32) (a, b)) {}
| ^ expected one of `:` or `|`
error: expected one of `:`, `@`, or `|`, found `)`
--> $DIR/inverted-parameters.rs:21:12
--> $DIR/inverted-parameters.rs:23:12
|
LL | fn fizz(i32) {}
| ^ expected one of `:`, `@`, or `|`
@ -49,7 +49,7 @@ LL | fn fizz(_: i32) {}
| ++
error: expected one of `:`, `@`, or `|`, found `S`
--> $DIR/inverted-parameters.rs:27:23
--> $DIR/inverted-parameters.rs:29:23
|
LL | fn missing_colon(quux S) {}
| -----^

View File

@ -1,3 +1,5 @@
//@ dont-require-annotations: SUGGESTION
struct X(usize);
impl X {

View File

@ -1,5 +1,5 @@
error[E0594]: cannot assign to `self.0`, which is behind a `&` reference
--> $DIR/suggest-ref-mut.rs:7:9
--> $DIR/suggest-ref-mut.rs:9:9
|
LL | self.0 = 32;
| ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written
@ -10,7 +10,7 @@ LL | fn zap(&mut self) {
| +++
error[E0594]: cannot assign to `*foo`, which is behind a `&` reference
--> $DIR/suggest-ref-mut.rs:15:5
--> $DIR/suggest-ref-mut.rs:17:5
|
LL | *foo = 32;
| ^^^^^^^^^ `foo` is a `&` reference, so the data it refers to cannot be written
@ -21,7 +21,7 @@ LL | let ref mut foo = 16;
| +++
error[E0594]: cannot assign to `*bar`, which is behind a `&` reference
--> $DIR/suggest-ref-mut.rs:19:9
--> $DIR/suggest-ref-mut.rs:21:9
|
LL | *bar = 32;
| ^^^^^^^^^ `bar` is a `&` reference, so the data it refers to cannot be written
@ -32,7 +32,7 @@ LL | if let Some(ref mut bar) = Some(16) {
| +++
error[E0594]: cannot assign to `*quo`, which is behind a `&` reference
--> $DIR/suggest-ref-mut.rs:23:22
--> $DIR/suggest-ref-mut.rs:25:22
|
LL | ref quo => { *quo = 32; },
| ^^^^^^^^^ `quo` is a `&` reference, so the data it refers to cannot be written

View File

@ -1,4 +1,5 @@
//@ edition:2018
//@ dont-require-annotations: SUGGESTION
async fn hello() { //~ HELP try adding a return type
0

View File

@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/issue-90027-async-fn-return-suggestion.rs:4:5
--> $DIR/issue-90027-async-fn-return-suggestion.rs:5:5
|
LL | async fn hello() {
| - help: try adding a return type: `-> i32`
@ -7,7 +7,7 @@ LL | 0
| ^ expected `()`, found integer
error[E0308]: mismatched types
--> $DIR/issue-90027-async-fn-return-suggestion.rs:9:5
--> $DIR/issue-90027-async-fn-return-suggestion.rs:10:5
|
LL | async fn world() -> () {
| -- expected `()` because of return type
@ -15,13 +15,13 @@ LL | 0
| ^ expected `()`, found integer
error[E0308]: mismatched types
--> $DIR/issue-90027-async-fn-return-suggestion.rs:14:5
--> $DIR/issue-90027-async-fn-return-suggestion.rs:15:5
|
LL | hello()
| ^^^^^^^ expected `()`, found future
|
note: calling an async function returns a future
--> $DIR/issue-90027-async-fn-return-suggestion.rs:14:5
--> $DIR/issue-90027-async-fn-return-suggestion.rs:15:5
|
LL | hello()
| ^^^^^^^