diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index f048f6fe8ad4b..4f2d59b4c6632 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -185,6 +185,10 @@ lint_cfg_attr_no_attributes = lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` +lint_closure_returning_async_block = closure returning async block can be made into an async closure + .label = this async block can be removed, and the closure can be turned into an async closure + .suggestion = turn this into an async closure + lint_command_line_source = `forbid` lint level was set on command line lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as identifiers, which look alike diff --git a/compiler/rustc_lint/src/async_closures.rs b/compiler/rustc_lint/src/async_closures.rs new file mode 100644 index 0000000000000..33cc5738262f6 --- /dev/null +++ b/compiler/rustc_lint/src/async_closures.rs @@ -0,0 +1,129 @@ +use rustc_hir as hir; +use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::Span; + +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `closure_returning_async_block` lint detects cases where users + /// write a closure that returns an async block. + /// + /// ### Example + /// + /// ```rust + /// #![warn(closure_returning_async_block)] + /// let c = |x: &str| async {}; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Using an async closure is preferable over a closure that returns an + /// async block, since async closures are less restrictive in how its + /// captures are allowed to be used. + /// + /// For example, this code does not work with a closure returning an async + /// block: + /// + /// ```rust,compile_fail + /// async fn callback(x: &str) {} + /// + /// let captured_str = String::new(); + /// let c = move || async { + /// callback(&captured_str).await; + /// }; + /// ``` + /// + /// But it does work with async closures: + /// + /// ```rust + /// #![feature(async_closure)] + /// + /// async fn callback(x: &str) {} + /// + /// let captured_str = String::new(); + /// let c = async move || { + /// callback(&captured_str).await; + /// }; + /// ``` + pub CLOSURE_RETURNING_ASYNC_BLOCK, + Allow, + "closure that returns `async {}` could be rewritten as an async closure", + @feature_gate = async_closure; +} + +declare_lint_pass!( + /// Lint for potential usages of async closures and async fn trait bounds. + AsyncClosureUsage => [CLOSURE_RETURNING_ASYNC_BLOCK] +); + +impl<'tcx> LateLintPass<'tcx> for AsyncClosureUsage { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Closure(&hir::Closure { + body, + kind: hir::ClosureKind::Closure, + fn_decl_span, + .. + }) = expr.kind + else { + return; + }; + + let mut body = cx.tcx.hir().body(body).value; + + // Only peel blocks that have no expressions. + while let hir::ExprKind::Block(&hir::Block { stmts: [], expr: Some(tail), .. }, None) = + body.kind + { + body = tail; + } + + let hir::ExprKind::Closure(&hir::Closure { + kind: + hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Block, + )), + fn_decl_span: async_decl_span, + .. + }) = body.kind + else { + return; + }; + + let deletion_span = cx.tcx.sess.source_map().span_extend_while_whitespace(async_decl_span); + + cx.tcx.emit_node_span_lint( + CLOSURE_RETURNING_ASYNC_BLOCK, + expr.hir_id, + fn_decl_span, + ClosureReturningAsyncBlock { + async_decl_span, + sugg: AsyncClosureSugg { + deletion_span, + insertion_span: fn_decl_span.shrink_to_lo(), + }, + }, + ); + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_closure_returning_async_block)] +struct ClosureReturningAsyncBlock { + #[label] + async_decl_span: Span, + #[subdiagnostic] + sugg: AsyncClosureSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] +struct AsyncClosureSugg { + #[suggestion_part(code = "")] + deletion_span: Span, + #[suggestion_part(code = "async ")] + insertion_span: Span, +} diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index d4f6d388d9fe3..0860413190c3d 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use crate::fluent_generated as fluent; use crate::{LateContext, LateLintPass}; @@ -57,7 +57,7 @@ declare_lint! { pub IMPL_TRAIT_OVERCAPTURES, Allow, "`impl Trait` will capture more lifetimes than possibly intended in edition 2024", - @feature_gate = sym::precise_capturing; + @feature_gate = precise_capturing; //@future_incompatible = FutureIncompatibleInfo { // reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), // reference: "<FIXME>", @@ -91,7 +91,7 @@ declare_lint! { pub IMPL_TRAIT_REDUNDANT_CAPTURES, Warn, "redundant precise-capturing `use<...>` syntax on an `impl Trait`", - @feature_gate = sym::precise_capturing; + @feature_gate = precise_capturing; } declare_lint_pass!( diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 868a44a980a0f..b6927cf60b69e 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -41,6 +41,7 @@ #![feature(trait_upcasting)] // tidy-alphabetical-end +mod async_closures; mod async_fn_in_trait; pub mod builtin; mod context; @@ -87,6 +88,7 @@ use rustc_hir::def_id::LocalModDefId; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; +use async_closures::AsyncClosureUsage; use async_fn_in_trait::AsyncFnInTrait; use builtin::*; use deref_into_dyn_supertrait::*; @@ -229,6 +231,7 @@ late_lint_methods!( MapUnitFn: MapUnitFn, MissingDebugImplementations: MissingDebugImplementations, MissingDoc: MissingDoc, + AsyncClosureUsage: AsyncClosureUsage, AsyncFnInTrait: AsyncFnInTrait, NonLocalDefinitions: NonLocalDefinitions::default(), ImplTraitOvercaptures: ImplTraitOvercaptures, diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 445dcd41e5d52..93dd5e764c67d 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -2,7 +2,6 @@ use crate::{LateContext, LateLintPass, LintContext}; use rustc_hir as hir; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::sym; declare_lint! { /// The `multiple_supertrait_upcastable` lint detects when an object-safe trait has multiple @@ -30,7 +29,7 @@ declare_lint! { pub MULTIPLE_SUPERTRAIT_UPCASTABLE, Allow, "detect when an object-safe trait has multiple supertraits", - @feature_gate = sym::multiple_supertrait_upcastable; + @feature_gate = multiple_supertrait_upcastable; } declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTABLE]); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 48e20a586c6d0..aa7844f40121b 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -9,7 +9,6 @@ use crate::{declare_lint, declare_lint_pass, FutureIncompatibilityReason}; use rustc_span::edition::Edition; -use rustc_span::symbol::sym; declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s @@ -463,7 +462,7 @@ declare_lint! { pub MUST_NOT_SUSPEND, Allow, "use of a `#[must_not_suspend]` value across a yield point", - @feature_gate = rustc_span::symbol::sym::must_not_suspend; + @feature_gate = must_not_suspend; } declare_lint! { @@ -1647,7 +1646,7 @@ declare_lint! { pub RUST_2024_INCOMPATIBLE_PAT, Allow, "detects patterns whose meaning will change in Rust 2024", - @feature_gate = sym::ref_pat_eat_one_layer_2024; + @feature_gate = ref_pat_eat_one_layer_2024; // FIXME uncomment below upon stabilization /*@future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), @@ -2695,7 +2694,7 @@ declare_lint! { pub FUZZY_PROVENANCE_CASTS, Allow, "a fuzzy integer to pointer cast is used", - @feature_gate = sym::strict_provenance; + @feature_gate = strict_provenance; } declare_lint! { @@ -2741,7 +2740,7 @@ declare_lint! { pub LOSSY_PROVENANCE_CASTS, Allow, "a lossy pointer to integer cast is used", - @feature_gate = sym::strict_provenance; + @feature_gate = strict_provenance; } declare_lint! { @@ -3925,7 +3924,7 @@ declare_lint! { pub NON_EXHAUSTIVE_OMITTED_PATTERNS, Allow, "detect when patterns of types marked `non_exhaustive` are missed", - @feature_gate = sym::non_exhaustive_omitted_patterns_lint; + @feature_gate = non_exhaustive_omitted_patterns_lint; } declare_lint! { @@ -4045,7 +4044,7 @@ declare_lint! { pub TEST_UNSTABLE_LINT, Deny, "this unstable lint is only for testing", - @feature_gate = sym::test_unstable_lint; + @feature_gate = test_unstable_lint; } declare_lint! { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index b44eb25216770..f87f19e170005 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -865,7 +865,7 @@ macro_rules! declare_lint { ); ); ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, - $(@feature_gate = $gate:expr;)? + $(@feature_gate = $gate:ident;)? $(@future_incompatible = FutureIncompatibleInfo { reason: $reason:expr, $($field:ident : $val:expr),* $(,)* @@ -879,7 +879,7 @@ macro_rules! declare_lint { desc: $desc, is_externally_loaded: false, $($v: true,)* - $(feature_gate: Some($gate),)? + $(feature_gate: Some(rustc_span::symbol::sym::$gate),)? $(future_incompatible: Some($crate::FutureIncompatibleInfo { reason: $reason, $($field: $val,)* @@ -895,21 +895,21 @@ macro_rules! declare_lint { macro_rules! declare_tool_lint { ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr - $(, @feature_gate = $gate:expr;)? + $(, @feature_gate = $gate:ident;)? ) => ( $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false $(, @feature_gate = $gate;)?} ); ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, report_in_external_macro: $rep:expr - $(, @feature_gate = $gate:expr;)? + $(, @feature_gate = $gate:ident;)? ) => ( $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep $(, @feature_gate = $gate;)?} ); ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, $external:expr - $(, @feature_gate = $gate:expr;)? + $(, @feature_gate = $gate:ident;)? ) => ( $(#[$attr])* $vis static $NAME: &$crate::Lint = &$crate::Lint { @@ -920,7 +920,7 @@ macro_rules! declare_tool_lint { report_in_external_macro: $external, future_incompatible: None, is_externally_loaded: true, - $(feature_gate: Some($gate),)? + $(feature_gate: Some(rustc_span::symbol::sym::$gate),)? crate_level_only: false, ..$crate::Lint::default_fields_for_macro() }; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 66aeb35aceec0..d13e38889e1d5 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -684,6 +684,9 @@ pub use core::{ module_path, option_env, stringify, trace_macros, }; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub use crate::sys::anonymous_pipe as pipe; + #[unstable( feature = "concat_bytes", issue = "87555", diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 63455a274faaa..b4f6bb71c79c4 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -386,7 +386,7 @@ fn test_interior_nul_in_env_value_is_error() { fn test_creation_flags() { use crate::os::windows::process::CommandExt; use crate::sys::c::{BOOL, DWORD, INFINITE}; - #[repr(C, packed)] + #[repr(C)] struct DEBUG_EVENT { pub event_code: DWORD, pub process_id: DWORD, diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/anonymous_pipe/mod.rs new file mode 100644 index 0000000000000..118bcbaa75bf7 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/mod.rs @@ -0,0 +1,141 @@ +//! Module for annoymous pipe +//! +//! ``` +//! #![feature(anonymous_pipe)] +//! # fn main() -> std::io::Result<()> { +//! let (reader, writer) = std::pipe::pipe()?; +//! # Ok(()) +//! # } +//! ``` + +use crate::{io, process::Stdio, sys::pipe::AnonPipe}; + +/// Create annoymous pipe that is close-on-exec and blocking. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + cfg_if::cfg_if! { + if #[cfg(unix)] { + unix::pipe() + } else if #[cfg(windows)] { + windows::pipe() + } else { + Err(io::Error::UNSUPPORTED_PLATFORM) + } + } +} + +/// Read end of the annoymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(AnonPipe); + +/// Write end of the annoymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result<Self> { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result<Self> { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[cfg(unix)] +mod unix; + +#[cfg(windows)] +mod windows; + +#[cfg(test)] +mod tests; diff --git a/library/std/src/sys/anonymous_pipe/tests.rs b/library/std/src/sys/anonymous_pipe/tests.rs new file mode 100644 index 0000000000000..8f17422901d62 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/tests.rs @@ -0,0 +1,29 @@ +use super::*; +use crate::io::{Read, Write}; + +#[test] +fn pipe_creation_and_rw() { + let (mut rx, mut tx) = pipe().unwrap(); + tx.write_all(b"12345").unwrap(); + drop(tx); + + let mut s = String::new(); + rx.read_to_string(&mut s).unwrap(); + assert_eq!(s, "12345"); +} + +#[test] +fn pipe_try_clone_and_rw() { + let (mut rx, mut tx) = pipe().unwrap(); + tx.try_clone().unwrap().write_all(b"12").unwrap(); + tx.write_all(b"345").unwrap(); + drop(tx); + + let mut s = String::new(); + rx.try_clone().unwrap().take(3).read_to_string(&mut s).unwrap(); + assert_eq!(s, "123"); + + s.clear(); + rx.read_to_string(&mut s).unwrap(); + assert_eq!(s, "45"); +} diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs new file mode 100644 index 0000000000000..18252f0183d3b --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/unix.rs @@ -0,0 +1,107 @@ +use super::*; + +use crate::{ + os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}, + sys::{ + fd::FileDesc, + pipe::{anon_pipe, AnonPipe}, + }, + sys_common::{FromInner, IntoInner}, +}; + +#[inline] +pub(super) fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + anon_pipe().map(|(rx, tx)| (PipeReader(rx), PipeWriter(tx))) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeReader { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeReader { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeReader> for OwnedFd { + fn from(pipe: PipeReader) -> Self { + FileDesc::into_inner(AnonPipe::into_inner(pipe.0)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeReader { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(AnonPipe::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeReader { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeReader> for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeWriter { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeWriter { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeWriter> for OwnedFd { + fn from(pipe: PipeWriter) -> Self { + FileDesc::into_inner(AnonPipe::into_inner(pipe.0)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeWriter { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(AnonPipe::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeWriter { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeWriter> for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +fn convert_to_pipe(owned_fd: OwnedFd) -> AnonPipe { + AnonPipe::from_inner(FileDesc::from_inner(OwnedFd::from(owned_fd))) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<OwnedFd> for PipeReader { + fn from(owned_fd: OwnedFd) -> Self { + Self(convert_to_pipe(owned_fd)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<OwnedFd> for PipeWriter { + fn from(owned_fd: OwnedFd) -> Self { + Self(convert_to_pipe(owned_fd)) + } +} diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs new file mode 100644 index 0000000000000..592e2f05a9ebf --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -0,0 +1,113 @@ +use super::*; + +use crate::{ + os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, + }, + sys::{ + handle::Handle, + pipe::{anon_pipe, AnonPipe, Pipes}, + }, + sys_common::{FromInner, IntoInner}, +}; + +#[inline] +pub(super) fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + anon_pipe(true, false).map(|Pipes { ours, theirs }| (PipeReader(ours), PipeWriter(theirs))) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeReader { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.handle().as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeReader { + fn as_raw_handle(&self) -> RawHandle { + self.0.handle().as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeReader { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(AnonPipe::from_inner(Handle::from_raw_handle(raw_handle))) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeReader { + fn into_raw_handle(self) -> RawHandle { + self.0.into_handle().into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeReader> for OwnedHandle { + fn from(pipe: PipeReader) -> Self { + Handle::into_inner(AnonPipe::into_inner(pipe.0)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeReader> for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeWriter { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.handle().as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeWriter { + fn as_raw_handle(&self) -> RawHandle { + self.0.handle().as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeWriter { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(AnonPipe::from_inner(Handle::from_raw_handle(raw_handle))) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeWriter { + fn into_raw_handle(self) -> RawHandle { + self.0.into_handle().into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeWriter> for OwnedHandle { + fn from(pipe: PipeWriter) -> Self { + Handle::into_inner(AnonPipe::into_inner(pipe.0)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeWriter> for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +fn convert_to_pipe(owned_handle: OwnedHandle) -> AnonPipe { + AnonPipe::from_inner(Handle::from_inner(owned_handle)) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<OwnedHandle> for PipeReader { + fn from(owned_handle: OwnedHandle) -> Self { + Self(convert_to_pipe(owned_handle)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<OwnedHandle> for PipeWriter { + fn from(owned_handle: OwnedHandle) -> Self { + Self(convert_to_pipe(owned_handle)) + } +} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 8aa35c40fe052..f7b41a2d4064a 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -5,6 +5,8 @@ mod pal; mod personality; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod os_str; diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index 33db24e77e4da..8762af614f17e 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -9,6 +9,7 @@ use crate::sys_common::{FromInner, IntoInner}; // Anonymous pipes //////////////////////////////////////////////////////////////////////////////// +#[derive(Debug)] pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { @@ -46,6 +47,10 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } impl AnonPipe { + pub fn try_clone(&self) -> io::Result<Self> { + self.0.duplicate().map(Self) + } + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } @@ -79,6 +84,10 @@ impl AnonPipe { pub fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() } + + pub fn as_file_desc(&self) -> &FileDesc { + &self.0 + } } impl IntoInner<FileDesc> for AnonPipe { diff --git a/library/std/src/sys/pal/unsupported/pipe.rs b/library/std/src/sys/pal/unsupported/pipe.rs index d7d8f297ae586..eaa95594db0b3 100644 --- a/library/std/src/sys/pal/unsupported/pipe.rs +++ b/library/std/src/sys/pal/unsupported/pipe.rs @@ -3,6 +3,10 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; pub struct AnonPipe(!); impl AnonPipe { + pub fn try_clone(&self) -> io::Result<Self> { + self.0 + } + pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> { self.0 } diff --git a/library/std/src/sys/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs index 3f85bb0a099a9..5aa2141571953 100644 --- a/library/std/src/sys/pal/windows/handle.rs +++ b/library/std/src/sys/pal/windows/handle.rs @@ -17,6 +17,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// An owned container for `HANDLE` object, closing them on Drop. /// /// All methods are inherited through a `Deref` impl to `RawHandle` +#[derive(Debug)] pub struct Handle(OwnedHandle); impl Handle { diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 67ef3ca82da02..6e6ba721d00fa 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -19,6 +19,7 @@ use crate::sys_common::{FromInner, IntoInner}; // Anonymous pipes //////////////////////////////////////////////////////////////////////////////// +#[derive(Debug)] pub struct AnonPipe { inner: Handle, } @@ -182,7 +183,7 @@ pub fn spawn_pipe_relay( their_handle_inheritable: bool, ) -> io::Result<AnonPipe> { // We need this handle to live for the lifetime of the thread spawned below. - let source = source.duplicate()?; + let source = source.try_clone()?; // create a new pair of anon pipes. let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?; @@ -238,7 +239,8 @@ impl AnonPipe { pub fn into_handle(self) -> Handle { self.inner } - fn duplicate(&self) -> io::Result<Self> { + + pub fn try_clone(&self) -> io::Result<Self> { self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) } diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 8eaca70eaff48..5a8fd3ebb9c83 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -66,7 +66,7 @@ where macro_rules! declare_rustdoc_lint { ( $(#[$attr:meta])* $name: ident, $level: ident, $descr: literal $(,)? - $(@feature_gate = $gate:expr;)? + $(@feature_gate = $gate:ident;)? ) => { declare_tool_lint! { $(#[$attr])* pub rustdoc::$name, $level, $descr @@ -128,7 +128,7 @@ declare_rustdoc_lint! { MISSING_DOC_CODE_EXAMPLES, Allow, "detects publicly-exported items without code samples in their documentation", - @feature_gate = rustc_span::symbol::sym::rustdoc_missing_doc_code_examples; + @feature_gate = rustdoc_missing_doc_code_examples; } declare_rustdoc_lint! { diff --git a/tests/ui/async-await/async-closures/lint-closure-returning-async-block.rs b/tests/ui/async-await/async-closures/lint-closure-returning-async-block.rs new file mode 100644 index 0000000000000..3e2ab8321a890 --- /dev/null +++ b/tests/ui/async-await/async-closures/lint-closure-returning-async-block.rs @@ -0,0 +1,21 @@ +//@ edition: 2021 + +#![feature(async_closure)] +#![deny(closure_returning_async_block)] + +fn main() { + let x = || async {}; + //~^ ERROR closure returning async block can be made into an async closure + + let x = || async move {}; + //~^ ERROR closure returning async block can be made into an async closure + + let x = move || async move {}; + //~^ ERROR closure returning async block can be made into an async closure + + let x = move || async {}; + //~^ ERROR closure returning async block can be made into an async closure + + let x = || {{ async {} }}; + //~^ ERROR closure returning async block can be made into an async closure +} diff --git a/tests/ui/async-await/async-closures/lint-closure-returning-async-block.stderr b/tests/ui/async-await/async-closures/lint-closure-returning-async-block.stderr new file mode 100644 index 0000000000000..4c0c4d797d8ef --- /dev/null +++ b/tests/ui/async-await/async-closures/lint-closure-returning-async-block.stderr @@ -0,0 +1,67 @@ +error: closure returning async block can be made into an async closure + --> $DIR/lint-closure-returning-async-block.rs:7:13 + | +LL | let x = || async {}; + | ^^ ----- this async block can be removed, and the closure can be turned into an async closure + | +note: the lint level is defined here + --> $DIR/lint-closure-returning-async-block.rs:4:9 + | +LL | #![deny(closure_returning_async_block)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: turn this into an async closure + | +LL - let x = || async {}; +LL + let x = async || {}; + | + +error: closure returning async block can be made into an async closure + --> $DIR/lint-closure-returning-async-block.rs:10:13 + | +LL | let x = || async move {}; + | ^^ ---------- this async block can be removed, and the closure can be turned into an async closure + | +help: turn this into an async closure + | +LL - let x = || async move {}; +LL + let x = async || {}; + | + +error: closure returning async block can be made into an async closure + --> $DIR/lint-closure-returning-async-block.rs:13:13 + | +LL | let x = move || async move {}; + | ^^^^^^^ ---------- this async block can be removed, and the closure can be turned into an async closure + | +help: turn this into an async closure + | +LL - let x = move || async move {}; +LL + let x = async move || {}; + | + +error: closure returning async block can be made into an async closure + --> $DIR/lint-closure-returning-async-block.rs:16:13 + | +LL | let x = move || async {}; + | ^^^^^^^ ----- this async block can be removed, and the closure can be turned into an async closure + | +help: turn this into an async closure + | +LL - let x = move || async {}; +LL + let x = async move || {}; + | + +error: closure returning async block can be made into an async closure + --> $DIR/lint-closure-returning-async-block.rs:19:13 + | +LL | let x = || {{ async {} }}; + | ^^ ----- this async block can be removed, and the closure can be turned into an async closure + | +help: turn this into an async closure + | +LL - let x = || {{ async {} }}; +LL + let x = async || {{ {} }}; + | + +error: aborting due to 5 previous errors +