Auto merge of #148012 - Zalathar:rollup-ed6cnn8, r=Zalathar

Rollup of 4 pull requests

Successful merges:

 - rust-lang/rust#115501 (Add new inherit_handles flag to CommandExt trait)
 - rust-lang/rust#146629 (std: reorganize the UNIX-internal `weak` module)
 - rust-lang/rust#147762 (feat(rustdoc): `--emit=depinfo` output to stdout via `-`)
 - rust-lang/rust#148001 (fix: Don't add diff symbol to unchanged lines)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-10-23 02:01:36 +00:00
commit 7838ce1a3a
23 changed files with 417 additions and 343 deletions

View file

@ -2698,8 +2698,7 @@ impl HumanEmitter {
[SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => {
buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
}
[] => {
// FIXME: needed? Doesn't get exercised in any test.
[] | [SubstitutionHighlight { start: 0, end: 0 }] => {
self.draw_col_separator_no_space(buffer, *row_num, max_line_num_len + 1);
}
_ => {

View file

@ -1111,7 +1111,7 @@ impl Input {
}
}
#[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq, Encodable, Decodable)]
#[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq, Eq, Encodable, Decodable)]
pub enum OutFileName {
Real(PathBuf),
Stdout,

View file

@ -365,6 +365,20 @@ pub trait CommandExt: Sealed {
/// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
#[unstable(feature = "windows_process_extensions_startupinfo", issue = "141010")]
fn startupinfo_force_feedback(&mut self, enabled: Option<bool>) -> &mut process::Command;
/// If this flag is set to `true`, each inheritable handle in the calling
/// process is inherited by the new process. If the flag is `false`, the
/// handles are not inherited.
///
/// The default value for this flag is `true`.
///
/// **Note** that inherited handles have the same value and access rights
/// as the original handles. For additional discussion of inheritable handles,
/// see the [Remarks][1] section of the `CreateProcessW` documentation.
///
/// [1]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#remarks
#[unstable(feature = "windows_process_extensions_inherit_handles", issue = "146407")]
fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command;
}
#[stable(feature = "windows_process_extensions", since = "1.16.0")]
@ -421,6 +435,11 @@ impl CommandExt for process::Command {
self.as_inner_mut().startupinfo_force_feedback(enabled);
self
}
fn inherit_handles(&mut self, inherit_handles: bool) -> &mut process::Command {
self.as_inner_mut().inherit_handles(inherit_handles);
self
}
}
#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]

View file

@ -2,10 +2,6 @@
use crate::io::ErrorKind;
#[cfg(not(target_os = "espidf"))]
#[macro_use]
pub mod weak;
#[cfg(target_os = "fuchsia")]
pub mod fuchsia;
pub mod futex;
@ -19,6 +15,7 @@ pub mod stack_overflow;
pub mod sync;
pub mod thread_parking;
pub mod time;
pub mod weak;
#[cfg(target_os = "espidf")]
pub fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}

View file

@ -69,7 +69,6 @@ mod imp {
use super::Handler;
use super::thread_info::{delete_current_info, set_current_info, with_current_info};
use crate::ops::Range;
use crate::sync::OnceLock;
use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
use crate::sys::pal::unix::os;
use crate::{io, mem, ptr};
@ -406,6 +405,10 @@ mod imp {
} else if cfg!(all(target_os = "linux", target_env = "musl")) {
install_main_guard_linux_musl(page_size)
} else if cfg!(target_os = "freebsd") {
#[cfg(not(target_os = "freebsd"))]
return None;
// The FreeBSD code cannot be checked on non-BSDs.
#[cfg(target_os = "freebsd")]
install_main_guard_freebsd(page_size)
} else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) {
install_main_guard_bsds(page_size)
@ -442,6 +445,7 @@ mod imp {
}
#[forbid(unsafe_op_in_unsafe_fn)]
#[cfg(target_os = "freebsd")]
unsafe fn install_main_guard_freebsd(page_size: usize) -> Option<Range<usize>> {
// FreeBSD's stack autogrows, and optionally includes a guard page
// at the bottom. If we try to remap the bottom of the stack
@ -453,38 +457,23 @@ mod imp {
// by the security.bsd.stack_guard_page sysctl.
// By default it is 1, checking once is enough since it is
// a boot time config value.
static PAGES: OnceLock<usize> = OnceLock::new();
static PAGES: crate::sync::OnceLock<usize> = crate::sync::OnceLock::new();
let pages = PAGES.get_or_init(|| {
use crate::sys::weak::dlsym;
dlsym!(
fn sysctlbyname(
name: *const libc::c_char,
oldp: *mut libc::c_void,
oldlenp: *mut libc::size_t,
newp: *const libc::c_void,
newlen: libc::size_t,
) -> libc::c_int;
);
let mut guard: usize = 0;
let mut size = size_of_val(&guard);
let oid = c"security.bsd.stack_guard_page";
match sysctlbyname.get() {
Some(fcn)
if unsafe {
fcn(
oid.as_ptr(),
(&raw mut guard).cast(),
&raw mut size,
ptr::null_mut(),
0,
) == 0
} =>
{
guard
}
_ => 1,
}
let r = unsafe {
libc::sysctlbyname(
oid.as_ptr(),
(&raw mut guard).cast(),
&raw mut size,
ptr::null_mut(),
0,
)
};
if r == 0 { guard } else { 1 }
});
Some(guardaddr..guardaddr + pages * page_size)
}

View file

@ -1,225 +0,0 @@
//! Support for "weak linkage" to symbols on Unix
//!
//! Some I/O operations we do in std require newer versions of OSes but we need
//! to maintain binary compatibility with older releases for now. In order to
//! use the new functionality when available we use this module for detection.
//!
//! One option to use here is weak linkage, but that is unfortunately only
//! really workable with ELF. Otherwise, use dlsym to get the symbol value at
//! runtime. This is also done for compatibility with older versions of glibc,
//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
//! we've been dynamically linked to the library the symbol comes from, but that
//! is currently always the case for things like libpthread/libc.
//!
//! A long time ago this used weak linkage for the __pthread_get_minstack
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
//! dependency on libc6 (#23628) because it is GLIBC_PRIVATE. We now use `dlsym`
//! for a runtime lookup of that symbol to avoid the ELF versioned dependency.
// There are a variety of `#[cfg]`s controlling which targets are involved in
// each instance of `weak!` and `syscall!`. Rather than trying to unify all of
// that, we'll just allow that some unix targets don't use this module at all.
#![allow(dead_code, unused_macros)]
#![forbid(unsafe_op_in_unsafe_fn)]
use crate::ffi::{CStr, c_char, c_void};
use crate::marker::{FnPtr, PhantomData};
use crate::sync::atomic::{Atomic, AtomicPtr, Ordering};
use crate::{mem, ptr};
// We currently only test `dlsym!`, but that doesn't work on all platforms, so
// we gate the tests to only the platforms where it is actually used.
//
// FIXME(joboet): add more tests, reorganise the whole module and get rid of
// `#[allow(dead_code, unused_macros)]`.
#[cfg(any(
target_vendor = "apple",
all(target_os = "linux", target_env = "gnu"),
target_os = "freebsd",
))]
#[cfg(test)]
mod tests;
// We can use true weak linkage on ELF targets.
#[cfg(all(unix, not(target_vendor = "apple")))]
pub(crate) macro weak {
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
unsafe extern "C" {
#[linkage = "extern_weak"]
static $name: Option<unsafe extern "C" fn($($t),*) -> $ret>;
}
#[allow(unused_unsafe)]
ExternWeak::new(unsafe { $name })
};
)
}
// On non-ELF targets, use the dlsym approximation of weak linkage.
#[cfg(target_vendor = "apple")]
pub(crate) use self::dlsym as weak;
pub(crate) struct ExternWeak<F: Copy> {
weak_ptr: Option<F>,
}
impl<F: Copy> ExternWeak<F> {
#[inline]
pub(crate) fn new(weak_ptr: Option<F>) -> Self {
ExternWeak { weak_ptr }
}
#[inline]
pub(crate) fn get(&self) -> Option<F> {
self.weak_ptr
}
}
pub(crate) macro dlsym {
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
dlsym!(
#[link_name = stringify!($name)]
fn $name($($param : $t),*) -> $ret;
);
),
(
#[link_name = $sym:expr]
fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;
) => (
static DLSYM: DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
let Ok(name) = CStr::from_bytes_with_nul(concat!($sym, '\0').as_bytes()) else {
panic!("symbol name may not contain NUL")
};
// SAFETY: Whoever calls the function pointer returned by `get()`
// is responsible for ensuring that the signature is correct. Just
// like with extern blocks, this is syntactically enforced by making
// the function pointer be unsafe.
unsafe { DlsymWeak::new(name) }
};
let $name = &DLSYM;
)
}
pub(crate) struct DlsymWeak<F> {
/// A pointer to the nul-terminated name of the symbol.
// Use a pointer instead of `&'static CStr` to save space.
name: *const c_char,
func: Atomic<*mut libc::c_void>,
_marker: PhantomData<F>,
}
impl<F: FnPtr> DlsymWeak<F> {
/// # Safety
///
/// If the signature of `F` does not match the signature of the symbol (if
/// it exists), calling the function pointer returned by `get()` is
/// undefined behaviour.
pub(crate) const unsafe fn new(name: &'static CStr) -> Self {
DlsymWeak {
name: name.as_ptr(),
func: AtomicPtr::new(ptr::without_provenance_mut(1)),
_marker: PhantomData,
}
}
#[inline]
pub(crate) fn get(&self) -> Option<F> {
// The caller is presumably going to read through this value
// (by calling the function we've dlsymed). This means we'd
// need to have loaded it with at least C11's consume
// ordering in order to be guaranteed that the data we read
// from the pointer isn't from before the pointer was
// stored. Rust has no equivalent to memory_order_consume,
// so we use an acquire load (sorry, ARM).
//
// Now, in practice this likely isn't needed even on CPUs
// where relaxed and consume mean different things. The
// symbols we're loading are probably present (or not) at
// init, and even if they aren't the runtime dynamic loader
// is extremely likely have sufficient barriers internally
// (possibly implicitly, for example the ones provided by
// invoking `mprotect`).
//
// That said, none of that's *guaranteed*, so we use acquire.
match self.func.load(Ordering::Acquire) {
func if func.addr() == 1 => self.initialize(),
func if func.is_null() => None,
// SAFETY:
// `func` is not null and `F` implements `FnPtr`, thus this
// transmutation is well-defined. It is the responsibility of the
// creator of this `DlsymWeak` to ensure that calling the resulting
// function pointer does not result in undefined behaviour (though
// the `dlsym!` macro delegates this responsibility to the caller
// of the function by using `unsafe` function pointers).
// FIXME: use `transmute` once it stops complaining about generics.
func => Some(unsafe { mem::transmute_copy::<*mut c_void, F>(&func) }),
}
}
// Cold because it should only happen during first-time initialization.
#[cold]
fn initialize(&self) -> Option<F> {
// SAFETY: `self.name` was created from a `&'static CStr` and is
// therefore a valid C string pointer.
let val = unsafe { libc::dlsym(libc::RTLD_DEFAULT, self.name) };
// This synchronizes with the acquire load in `get`.
self.func.store(val, Ordering::Release);
if val.is_null() {
None
} else {
// SAFETY: see the comment in `get`.
// FIXME: use `transmute` once it stops complaining about generics.
Some(unsafe { mem::transmute_copy::<*mut libc::c_void, F>(&val) })
}
}
}
unsafe impl<F> Send for DlsymWeak<F> {}
unsafe impl<F> Sync for DlsymWeak<F> {}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub(crate) macro syscall {
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
unsafe fn $name($($param: $t),*) -> $ret {
weak!(fn $name($($param: $t),*) -> $ret;);
if let Some(fun) = $name.get() {
unsafe { fun($($param),*) }
} else {
super::os::set_errno(libc::ENOSYS);
-1
}
}
)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) macro syscall {
(
fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;
) => (
unsafe fn $name($($param: $t),*) -> $ret {
weak!(fn $name($($param: $t),*) -> $ret;);
// Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
// interposition, but if it's not found just use a raw syscall.
if let Some(fun) = $name.get() {
unsafe { fun($($param),*) }
} else {
unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret }
}
}
)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) macro raw_syscall {
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
unsafe fn $name($($param: $t),*) -> $ret {
unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret }
}
)
}

View file

@ -0,0 +1,104 @@
use crate::ffi::{CStr, c_char, c_void};
use crate::marker::{FnPtr, PhantomData};
use crate::sync::atomic::{Atomic, AtomicPtr, Ordering};
use crate::{mem, ptr};
#[cfg(test)]
#[path = "./tests.rs"]
mod tests;
pub(crate) macro weak {
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
static DLSYM: DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
let Ok(name) = CStr::from_bytes_with_nul(concat!(stringify!($name), '\0').as_bytes()) else {
panic!("symbol name may not contain NUL")
};
// SAFETY: Whoever calls the function pointer returned by `get()`
// is responsible for ensuring that the signature is correct. Just
// like with extern blocks, this is syntactically enforced by making
// the function pointer be unsafe.
unsafe { DlsymWeak::new(name) }
};
let $name = &DLSYM;
)
}
pub(crate) struct DlsymWeak<F> {
/// A pointer to the nul-terminated name of the symbol.
// Use a pointer instead of `&'static CStr` to save space.
name: *const c_char,
func: Atomic<*mut libc::c_void>,
_marker: PhantomData<F>,
}
impl<F: FnPtr> DlsymWeak<F> {
/// # Safety
///
/// If the signature of `F` does not match the signature of the symbol (if
/// it exists), calling the function pointer returned by `get()` is
/// undefined behaviour.
pub const unsafe fn new(name: &'static CStr) -> Self {
DlsymWeak {
name: name.as_ptr(),
func: AtomicPtr::new(ptr::without_provenance_mut(1)),
_marker: PhantomData,
}
}
#[inline]
pub fn get(&self) -> Option<F> {
// The caller is presumably going to read through this value
// (by calling the function we've dlsymed). This means we'd
// need to have loaded it with at least C11's consume
// ordering in order to be guaranteed that the data we read
// from the pointer isn't from before the pointer was
// stored. Rust has no equivalent to memory_order_consume,
// so we use an acquire load (sorry, ARM).
//
// Now, in practice this likely isn't needed even on CPUs
// where relaxed and consume mean different things. The
// symbols we're loading are probably present (or not) at
// init, and even if they aren't the runtime dynamic loader
// is extremely likely have sufficient barriers internally
// (possibly implicitly, for example the ones provided by
// invoking `mprotect`).
//
// That said, none of that's *guaranteed*, so we use acquire.
match self.func.load(Ordering::Acquire) {
func if func.addr() == 1 => self.initialize(),
func if func.is_null() => None,
// SAFETY:
// `func` is not null and `F` implements `FnPtr`, thus this
// transmutation is well-defined. It is the responsibility of the
// creator of this `DlsymWeak` to ensure that calling the resulting
// function pointer does not result in undefined behaviour (though
// the `weak!` macro delegates this responsibility to the caller
// of the function by using `unsafe` function pointers).
// FIXME: use `transmute` once it stops complaining about generics.
func => Some(unsafe { mem::transmute_copy::<*mut c_void, F>(&func) }),
}
}
// Cold because it should only happen during first-time initialization.
#[cold]
fn initialize(&self) -> Option<F> {
// SAFETY: `self.name` was created from a `&'static CStr` and is
// therefore a valid C string pointer.
let val = unsafe { libc::dlsym(libc::RTLD_DEFAULT, self.name) };
// This synchronizes with the acquire load in `get`.
self.func.store(val, Ordering::Release);
if val.is_null() {
None
} else {
// SAFETY: see the comment in `get`.
// FIXME: use `transmute` once it stops complaining about generics.
Some(unsafe { mem::transmute_copy::<*mut libc::c_void, F>(&val) })
}
}
}
unsafe impl<F> Send for DlsymWeak<F> {}
unsafe impl<F> Sync for DlsymWeak<F> {}

View file

@ -0,0 +1,52 @@
//! Support for "weak linkage" to symbols on Unix
//!
//! Some I/O operations we do in std require newer versions of OSes but we need
//! to maintain binary compatibility with older releases for now. In order to
//! use the new functionality when available we use this module for detection.
//!
//! One option to use here is weak linkage, but that is unfortunately only
//! really workable with ELF. Otherwise, use dlsym to get the symbol value at
//! runtime. This is also done for compatibility with older versions of glibc,
//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
//! we've been dynamically linked to the library the symbol comes from, but that
//! is currently always the case for things like libpthread/libc.
//!
//! A long time ago this used weak linkage for the __pthread_get_minstack
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
//! dependency on libc6 (#23628) because it is GLIBC_PRIVATE. We now use `dlsym`
//! for a runtime lookup of that symbol to avoid the ELF versioned dependency.
#![forbid(unsafe_op_in_unsafe_fn)]
cfg_select! {
// On non-ELF targets, use the dlsym approximation of weak linkage.
target_vendor = "apple" => {
mod dlsym;
pub(crate) use dlsym::weak;
}
// Some targets don't need and support weak linkage at all...
target_os = "espidf" => {}
// ... but ELF targets support true weak linkage.
_ => {
// There are a variety of `#[cfg]`s controlling which targets are involved in
// each instance of `weak!`. Rather than trying to unify all of
// that, we'll just allow that some unix targets don't use this macro at all.
#[cfg_attr(not(target_os = "linux"), allow(unused_macros, dead_code))]
mod weak_linkage;
#[cfg_attr(not(target_os = "linux"), allow(unused_imports))]
pub(crate) use weak_linkage::weak;
}
}
// GNU/Linux needs the `dlsym` variant to avoid linking to private glibc symbols.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod dlsym;
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(crate) use dlsym::weak as dlsym;
#[cfg(any(target_os = "android", target_os = "linux"))]
mod syscall;
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) use syscall::syscall;

View file

@ -0,0 +1,19 @@
use super::weak;
pub(crate) macro syscall {
(
fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;
) => (
unsafe fn $name($($param: $t),*) -> $ret {
weak!(fn $name($($param: $t),*) -> $ret;);
// Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
// interposition, but if it's not found just use a raw syscall.
if let Some(fun) = $name.get() {
unsafe { fun($($param),*) }
} else {
unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret }
}
}
)
}

View file

@ -1,30 +1,24 @@
use super::*;
// This file is included by both implementations of `weak!`.
use super::weak;
use crate::ffi::{CStr, c_char};
#[test]
fn dlsym_existing() {
fn weak_existing() {
const TEST_STRING: &'static CStr = c"Ferris!";
// Try to find a symbol that definitely exists.
dlsym! {
weak! {
fn strlen(cs: *const c_char) -> usize;
}
dlsym! {
#[link_name = "strlen"]
fn custom_name(cs: *const c_char) -> usize;
}
let strlen = strlen.get().unwrap();
assert_eq!(unsafe { strlen(TEST_STRING.as_ptr()) }, TEST_STRING.count_bytes());
let custom_name = custom_name.get().unwrap();
assert_eq!(unsafe { custom_name(TEST_STRING.as_ptr()) }, TEST_STRING.count_bytes());
}
#[test]
fn dlsym_missing() {
fn weak_missing() {
// Try to find a symbol that definitely does not exist.
dlsym! {
weak! {
fn test_symbol_that_does_not_exist() -> i32;
}

View file

@ -0,0 +1,32 @@
#[cfg(test)]
#[path = "./tests.rs"]
mod tests;
pub(crate) macro weak {
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
unsafe extern "C" {
#[linkage = "extern_weak"]
static $name: Option<unsafe extern "C" fn($($t),*) -> $ret>;
}
#[allow(unused_unsafe)]
ExternWeak::new(unsafe { $name })
};
)
}
pub(crate) struct ExternWeak<F: Copy> {
weak_ptr: Option<F>,
}
impl<F: Copy> ExternWeak<F> {
#[inline]
pub fn new(weak_ptr: Option<F>) -> Self {
ExternWeak { weak_ptr }
}
#[inline]
pub fn get(&self) -> Option<F> {
self.weak_ptr
}
}

View file

@ -159,6 +159,7 @@ pub struct Command {
startupinfo_fullscreen: bool,
startupinfo_untrusted_source: bool,
startupinfo_force_feedback: Option<bool>,
inherit_handles: bool,
}
pub enum Stdio {
@ -187,6 +188,7 @@ impl Command {
startupinfo_fullscreen: false,
startupinfo_untrusted_source: false,
startupinfo_force_feedback: None,
inherit_handles: true,
}
}
@ -252,6 +254,10 @@ impl Command {
self.cwd.as_ref().map(Path::new)
}
pub fn inherit_handles(&mut self, inherit_handles: bool) {
self.inherit_handles = inherit_handles;
}
pub fn spawn(
&mut self,
default: Stdio,
@ -310,6 +316,7 @@ impl Command {
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
}
let inherit_handles = self.inherit_handles as c::BOOL;
let (envp, _data) = make_envp(maybe_env)?;
let (dirp, _data) = make_dirp(self.cwd.as_ref())?;
let mut pi = zeroed_process_information();
@ -401,7 +408,7 @@ impl Command {
cmd_str.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
c::TRUE,
inherit_handles,
flags,
envp,
dirp,

View file

@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::DiagCtxtHandle;
use rustc_session::config::{
self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns,
OptionsTargetModifiers, Sysroot, UnstableOptions, get_cmd_lint_options, nightly_options,
parse_crate_types_from_list, parse_externs, parse_target_triple,
OptionsTargetModifiers, OutFileName, Sysroot, UnstableOptions, get_cmd_lint_options,
nightly_options, parse_crate_types_from_list, parse_externs, parse_target_triple,
};
use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath;
@ -314,7 +314,7 @@ pub(crate) enum EmitType {
Unversioned,
Toolchain,
InvocationSpecific,
DepInfo(Option<PathBuf>),
DepInfo(Option<OutFileName>),
}
impl FromStr for EmitType {
@ -326,13 +326,11 @@ impl FromStr for EmitType {
"toolchain-shared-resources" => Ok(Self::Toolchain),
"invocation-specific" => Ok(Self::InvocationSpecific),
"dep-info" => Ok(Self::DepInfo(None)),
option => {
if let Some(file) = option.strip_prefix("dep-info=") {
Ok(Self::DepInfo(Some(Path::new(file).into())))
} else {
Err(())
}
}
option => match option.strip_prefix("dep-info=") {
Some("-") => Ok(Self::DepInfo(Some(OutFileName::Stdout))),
Some(f) => Ok(Self::DepInfo(Some(OutFileName::Real(f.into())))),
None => Err(()),
},
}
}
}
@ -342,10 +340,10 @@ impl RenderOptions {
self.emit.is_empty() || self.emit.contains(&EmitType::InvocationSpecific)
}
pub(crate) fn dep_info(&self) -> Option<Option<&Path>> {
pub(crate) fn dep_info(&self) -> Option<Option<&OutFileName>> {
for emit in &self.emit {
if let EmitType::DepInfo(file) = emit {
return Some(file.as_deref());
return Some(file.as_ref());
}
}
None

View file

@ -19,7 +19,7 @@ use rustc_lint::{MissingDoc, late_lint_mod};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
use rustc_session::config::{
self, CrateType, ErrorOutputType, Input, OutFileName, OutputType, OutputTypes, ResolveDocLinks,
self, CrateType, ErrorOutputType, Input, OutputType, OutputTypes, ResolveDocLinks,
};
pub(crate) use rustc_session::config::{Options, UnstableOptions};
use rustc_session::{Session, lint};
@ -271,10 +271,7 @@ pub(crate) fn create_config(
test,
remap_path_prefix,
output_types: if let Some(file) = render_options.dep_info() {
OutputTypes::new(&[(
OutputType::DepInfo,
file.map(|f| OutFileName::Real(f.to_path_buf())),
)])
OutputTypes::new(&[(OutputType::DepInfo, file.cloned())])
} else {
OutputTypes::new(&[])
},

View file

@ -14,7 +14,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ struct FooDefault<'a> {
LL | struct FooDefault<'a> {
|
error: this `impl` can be derived
@ -31,7 +31,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ struct TupleDefault(bool, i32, u64);
LL | struct TupleDefault(bool, i32, u64);
|
error: this `impl` can be derived
@ -48,7 +48,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ struct StrDefault<'a>(&'a str);
LL | struct StrDefault<'a>(&'a str);
|
error: this `impl` can be derived
@ -65,7 +65,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ struct Y(u32);
LL | struct Y(u32);
|
error: this `impl` can be derived
@ -82,7 +82,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ struct WithoutSelfCurly {
LL | struct WithoutSelfCurly {
|
error: this `impl` can be derived
@ -99,7 +99,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ struct WithoutSelfParan(bool);
LL | struct WithoutSelfParan(bool);
|
error: this `impl` can be derived
@ -115,7 +115,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ pub struct DirectDefaultDefaultCall {
LL | pub struct DirectDefaultDefaultCall {
|
error: this `impl` can be derived
@ -131,7 +131,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ pub struct EquivalentToDefaultDefaultCallVec {
LL | pub struct EquivalentToDefaultDefaultCallVec {
|
error: this `impl` can be derived
@ -147,7 +147,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ pub struct EquivalentToDefaultDefaultCallLocal {
LL | pub struct EquivalentToDefaultDefaultCallLocal {
|
error: this `impl` can be derived
@ -164,7 +164,7 @@ LL | | }
help: replace the manual implementation with a derive attribute
|
LL + #[derive(Default)]
LL ~ pub struct RepeatDefault1 {
LL | pub struct RepeatDefault1 {
|
error: this `impl` can be derived
@ -181,7 +181,7 @@ LL | | }
help: replace the manual implementation with a derive attribute and mark the default variant
|
LL + #[derive(Default)]
LL ~ pub enum SimpleEnum {
LL | pub enum SimpleEnum {
LL | Foo,
LL ~ #[default]
LL ~ Bar,

View file

@ -45,4 +45,15 @@ fn main() {
assert!(!path("precedence1.d").exists());
assert!(!path("precedence2.d").exists());
assert!(path("precedence3.d").exists());
// stdout (-) also wins if being the last.
let result = rustdoc()
.input("lib.rs")
.arg("-Zunstable-options")
.emit("dep-info=precedence1.d")
.emit("dep-info=-")
.run();
assert!(!path("precedence1.d").exists());
assert!(!path("-").exists()); // `-` shouldn't be treated as a file path
assert!(!result.stdout().is_empty()); // Something emitted to stdout
}

View file

@ -8,7 +8,7 @@ LL | 0..5+1 => errors_only.push(x),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = 5+1;
LL ~ match x as i32 {
LL | match x as i32 {
LL ~ 0..VAL => errors_only.push(x),
|

View file

@ -13,7 +13,7 @@ LL + val if val == tmp[0] => {}
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = tmp[0];
LL ~ match z {
LL | match z {
LL ~ VAL => {}
|

View file

@ -13,7 +13,7 @@ LL + val if val == x.y => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x.y;
LL ~ match 0 {
LL | match 0 {
LL | x => (),
LL ~ VAL => (),
|
@ -33,7 +33,7 @@ LL + val if val == x.0 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x.0;
LL ~ match 0 {
LL | match 0 {
LL | x => (),
LL | x.y => (),
LL ~ VAL => (),
@ -54,7 +54,7 @@ LL + val if val == x._0 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x._0;
LL ~ match 0 {
LL | match 0 {
LL | x => (),
LL | x.y => (),
LL | x.0 => (),
@ -76,7 +76,7 @@ LL + val if val == x.0.1 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x.0.1;
LL ~ match 0 {
LL | match 0 {
LL | x => (),
...
LL | x._0 => (),
@ -98,7 +98,7 @@ LL + val if val == x.4.y.17.__z => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x.4.y.17.__z;
LL ~ match 0 {
LL | match 0 {
LL | x => (),
...
LL | x.0.1 => (),
@ -150,7 +150,7 @@ LL + val if val == x[0] => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x[0];
LL ~ match 0 {
LL | match 0 {
LL ~ VAL => (),
|
@ -169,7 +169,7 @@ LL + val if val == x[..] => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x[..];
LL ~ match 0 {
LL | match 0 {
LL | x[0] => (),
LL ~ VAL => (),
|
@ -216,7 +216,7 @@ LL + val if val == x.f() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x.f();
LL ~ match 0 {
LL | match 0 {
LL ~ VAL => (),
|
@ -235,7 +235,7 @@ LL + val if val == x._f() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x._f();
LL ~ match 0 {
LL | match 0 {
LL | x.f() => (),
LL ~ VAL => (),
|
@ -255,7 +255,7 @@ LL + val if val == x? => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x?;
LL ~ match 0 {
LL | match 0 {
LL | x.f() => (),
LL | x._f() => (),
LL ~ VAL => (),
@ -276,7 +276,7 @@ LL + val if val == ().f() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = ().f();
LL ~ match 0 {
LL | match 0 {
LL | x.f() => (),
LL | x._f() => (),
LL | x? => (),
@ -298,7 +298,7 @@ LL + val if val == (0, x)?.f() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = (0, x)?.f();
LL ~ match 0 {
LL | match 0 {
LL | x.f() => (),
...
LL | ().f() => (),
@ -320,7 +320,7 @@ LL + val if val == x.f().g() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x.f().g();
LL ~ match 0 {
LL | match 0 {
LL | x.f() => (),
...
LL | (0, x)?.f() => (),
@ -342,7 +342,7 @@ LL + val if val == 0.f()?.g()?? => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = 0.f()?.g()??;
LL ~ match 0 {
LL | match 0 {
LL | x.f() => (),
...
LL | x.f().g() => (),
@ -364,7 +364,7 @@ LL + val if val == x as usize => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x as usize;
LL ~ match 0 {
LL | match 0 {
LL ~ VAL => (),
|
@ -383,7 +383,7 @@ LL + val if val == 0 as usize => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = 0 as usize;
LL ~ match 0 {
LL | match 0 {
LL | x as usize => (),
LL ~ VAL => (),
|
@ -403,7 +403,7 @@ LL + val if val == x.f().0.4 as f32 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x.f().0.4 as f32;
LL ~ match 0 {
LL | match 0 {
LL | x as usize => (),
LL | 0 as usize => (),
LL ~ VAL => (),
@ -424,7 +424,7 @@ LL + val if val == 1 + 1 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = 1 + 1;
LL ~ match 0 {
LL | match 0 {
LL ~ VAL => (),
|
@ -443,7 +443,7 @@ LL + val if val == (1 + 2) * 3 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = (1 + 2) * 3;
LL ~ match 0 {
LL | match 0 {
LL | 1 + 1 => (),
LL ~ VAL => (),
|
@ -463,7 +463,7 @@ LL + val if val == (x.0 > 2) => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x.0 > 2;
LL ~ match 0 {
LL | match 0 {
LL | 1 + 1 => (),
...
LL |
@ -485,7 +485,7 @@ LL + val if val == (x.0 == 2) => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = x.0 == 2;
LL ~ match 0 {
LL | match 0 {
LL | 1 + 1 => (),
...
LL | x.0 > 2 => (),
@ -507,7 +507,7 @@ LL + (x, val) if x != 0 && val == (y.0 > 2) => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = y.0 > 2;
LL ~ match (0, 0) {
LL | match (0, 0) {
LL ~ (x, VAL) if x != 0 => (),
|
@ -526,7 +526,7 @@ LL + (x, val) if (x != 0 || x != 1) && val == (y.0 > 2) => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = y.0 > 2;
LL ~ match (0, 0) {
LL | match (0, 0) {
LL | (x, y.0 > 2) if x != 0 => (),
LL ~ (x, VAL) if x != 0 || x != 1 => (),
|
@ -563,7 +563,7 @@ LL + val if val == u8::MAX.abs() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = u8::MAX.abs();
LL ~ match u8::MAX {
LL | match u8::MAX {
LL ~ VAL => (),
|
@ -582,7 +582,7 @@ LL + z @ w @ val if val == v.u() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = v.u();
LL ~ match u8::MAX {
LL | match u8::MAX {
LL | u8::MAX.abs() => (),
...
LL |
@ -604,7 +604,7 @@ LL + val if val == y.ilog(3) => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = y.ilog(3);
LL ~ match u8::MAX {
LL | match u8::MAX {
LL | u8::MAX.abs() => (),
...
LL |
@ -626,7 +626,7 @@ LL + val if val == n + 1 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = n + 1;
LL ~ match u8::MAX {
LL | match u8::MAX {
LL | u8::MAX.abs() => (),
...
LL |
@ -648,7 +648,7 @@ LL + (val) if val == "".f() + 14 * 8 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = "".f() + 14 * 8;
LL ~ match u8::MAX {
LL | match u8::MAX {
LL | u8::MAX.abs() => (),
...
LL |
@ -670,7 +670,7 @@ LL + val if val == f?() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = f?();
LL ~ match u8::MAX {
LL | match u8::MAX {
LL | u8::MAX.abs() => (),
...
LL | 0 | ((1) | 2) | 3 => (),

View file

@ -13,7 +13,7 @@ LL + Foo(val) if val == "hi".to_owned() => true,
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = "hi".to_owned();
LL ~ match foo {
LL | match foo {
LL ~ Foo(VAL) => true,
|
@ -32,7 +32,7 @@ LL + Bar { baz } if baz == "hi".to_owned() => true,
help: consider extracting the expression into a `const`
|
LL + const BAZ: /* Type */ = "hi".to_owned();
LL ~ match bar {
LL | match bar {
LL ~ Bar { baz: BAZ } => true,
|
@ -51,7 +51,7 @@ LL + &[val] if val == "foo".to_string() => {}
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = "foo".to_string();
LL ~ match foo.as_slice() {
LL | match foo.as_slice() {
LL ~ &[VAL] => {}
|

View file

@ -92,7 +92,7 @@ LL | ..=1 + 2 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = 1 + 2;
LL ~ match -1 {
LL | match -1 {
LL | 0..=1 => (),
...
LL |
@ -109,7 +109,7 @@ LL | (-4 + 0).. => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = -4 + 0;
LL ~ match -1 {
LL | match -1 {
LL | 0..=1 => (),
...
LL |
@ -126,7 +126,7 @@ LL | (1 + 4)...1 * 2 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = 1 + 4;
LL ~ match -1 {
LL | match -1 {
LL | 0..=1 => (),
...
LL |
@ -143,7 +143,7 @@ LL | (1 + 4)...1 * 2 => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = 1 * 2;
LL ~ match -1 {
LL | match -1 {
LL | 0..=1 => (),
...
LL |
@ -160,7 +160,7 @@ LL | 0.x()..="y".z() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = 0.x();
LL ~ match -1 {
LL | match -1 {
LL | 0..=1 => (),
...
LL |
@ -177,7 +177,7 @@ LL | 0.x()..="y".z() => (),
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = "y".z();
LL ~ match -1 {
LL | match -1 {
LL | 0..=1 => (),
...
LL |

View file

@ -81,7 +81,7 @@ LL | 4..=(2 + _) => ()
help: consider extracting the expression into a `const`
|
LL + const VAL: /* Type */ = 2 + _;
LL ~ match 9 {
LL | match 9 {
LL ~ 4..=(VAL) => ()
|

View file

@ -0,0 +1,81 @@
// Tests `inherit_handles` by spawning a child process and checking its handle
// count to be greater than when not setting the option.
//@ run-pass
//@ only-windows
//@ needs-subprocess
//@ edition: 2024
#![feature(windows_process_extensions_inherit_handles)]
use std::os::windows::io::AsRawHandle;
use std::os::windows::process::CommandExt;
use std::process::{Command, Stdio};
use std::time::Duration;
use std::{env, io, thread};
fn main() {
if std::env::args().skip(1).any(|s| s == "--child") {
child();
} else {
parent();
}
}
fn parent() {
let with_inherit_count = child_handle_count(true);
let without_inherit_count = child_handle_count(false);
// Only compare the two values instead of only expecting a hard 1 for
// robustness, although only 1 has ever been observed here.
assert!(
with_inherit_count > without_inherit_count,
"Child process handle count unexpectedly smaller when inheriting handles compared to when \
not: {} <= {}",
with_inherit_count,
without_inherit_count,
);
}
/// Spawns the current program as a child process and returns its handle count.
fn child_handle_count(inherit_handles: bool) -> u32 {
let mut child_proc = Command::new(&env::current_exe().unwrap())
.arg("--child")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.inherit_handles(inherit_handles)
.spawn()
.unwrap();
let mut handle_count = 0;
let ret = unsafe { GetProcessHandleCount(child_proc.as_raw_handle(), &raw mut handle_count) };
assert_ne!(
ret,
0,
"GetProcessHandleCount failed: {:?}",
io::Error::last_os_error(),
);
// Cleanup.
child_proc.kill().unwrap();
child_proc.wait().unwrap();
handle_count
}
/// A process that stays running until killed.
fn child() {
// Don't wait forever if something goes wrong.
thread::sleep(Duration::from_secs(10));
}
// Windows API
mod winapi {
use std::os::windows::raw::HANDLE;
#[link(name = "kernel32")]
unsafe extern "system" {
pub fn GetProcessHandleCount(hprocess: HANDLE, pdwhandlecount: *mut u32) -> i32;
}
}
use winapi::*;