diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 4024f883f3798..37c4f665415fc 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -401,6 +401,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { let msg = "`#[doc(keyword)]` is meant for internal use only"; gate_feature_post!(self, rustdoc_internals, attr.span, msg); } + + if nested_meta.has_name(sym::tuple_variadic) { + let msg = "`#[doc(tuple_variadic)]` is meant for internal use only"; + gate_feature_post!(self, rustdoc_internals, attr.span, msg); + } } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6c19ec915b169..4ef0f590a1f3d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -805,6 +805,37 @@ impl CheckAttrVisitor<'_> { true } + fn check_doc_tuple_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { + match self.tcx.hir().find(hir_id).and_then(|node| match node { + hir::Node::Item(item) => Some(&item.kind), + _ => None, + }) { + Some(ItemKind::Impl(ref i)) => { + if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) { + self.tcx + .sess + .struct_span_err( + meta.span(), + "`#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity", + ) + .emit(); + return false; + } + } + _ => { + self.tcx + .sess + .struct_span_err( + meta.span(), + "`#[doc(keyword = \"...\")]` can only be used on impl blocks", + ) + .emit(); + return false; + } + } + true + } + /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid. /// /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or @@ -1065,6 +1096,13 @@ impl CheckAttrVisitor<'_> { is_valid = false } + sym::tuple_variadic + if !self.check_attr_not_crate_level(meta, hir_id, "tuple_variadic") + || !self.check_doc_tuple_variadic(meta, hir_id) => + { + is_valid = false + } + sym::html_favicon_url | sym::html_logo_url | sym::html_playground_url @@ -1118,7 +1156,8 @@ impl CheckAttrVisitor<'_> { | sym::no_inline | sym::notable_trait | sym::passes - | sym::plugins => {} + | sym::plugins + | sym::tuple_variadic => {} sym::test => { if !self.check_test_attr(meta, hir_id) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 11ca017ed9571..6daf811e26f14 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1449,6 +1449,7 @@ symbols! { tuple, tuple_from_req, tuple_indexing, + tuple_variadic, two_phase, ty, type_alias_enum_variants, diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 70f3fe853d97a..fd5624812f554 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -95,7 +95,6 @@ use crate::marker::Destruct; /// /// * Function item types (i.e., the distinct types defined for each function) /// * Function pointer types (e.g., `fn() -> i32`) -/// * Tuple types, if each component also implements `Clone` (e.g., `()`, `(i32, bool)`) /// * Closure types, if they capture no value from the environment /// or if all such captured values implement `Clone` themselves. /// Note that variables captured by shared reference always implement `Clone` diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 63655ae8a240b..1c66089fad6e6 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -2313,29 +2313,46 @@ macro_rules! peel { macro_rules! tuple { () => (); ( $($name:ident,)+ ) => ( - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($name:Debug),+> Debug for ($($name,)+) where last_type!($($name,)+): ?Sized { - #[allow(non_snake_case, unused_assignments)] - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let mut builder = f.debug_tuple(""); - let ($(ref $name,)+) = *self; - $( - builder.field(&$name); - )+ - - builder.finish() + maybe_tuple_doc! { + $($name)+ @ + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($name:Debug),+> Debug for ($($name,)+) where last_type!($($name,)+): ?Sized { + #[allow(non_snake_case, unused_assignments)] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let mut builder = f.debug_tuple(""); + let ($(ref $name,)+) = *self; + $( + builder.field(&$name); + )+ + + builder.finish() + } } } peel! { $($name,)+ } ) } +macro_rules! maybe_tuple_doc { + ($a:ident @ #[$meta:meta] $item:item) => { + #[cfg_attr(not(bootstrap), doc(tuple_variadic))] + #[doc = "This trait is implemented for tuples up to twelve items long."] + #[$meta] + $item + }; + ($a:ident $($rest_a:ident)+ @ #[$meta:meta] $item:item) => { + #[doc(hidden)] + #[$meta] + $item + }; +} + macro_rules! last_type { ($a:ident,) => { $a }; ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; } -tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } +tuple! { E, D, C, B, A, Z, Y, X, W, V, U, T, } #[stable(feature = "rust1", since = "1.0.0")] impl Debug for [T] { diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index 3d168f62a09f5..2c152fe1b2c18 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -883,36 +883,53 @@ mod impls { ); ( $($name:ident)+) => ( - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($name: Hash),+> Hash for ($($name,)+) where last_type!($($name,)+): ?Sized { - #[allow(non_snake_case)] - #[inline] - fn hash(&self, state: &mut S) { - let ($(ref $name,)+) = *self; - $($name.hash(state);)+ + maybe_tuple_doc! { + $($name)+ @ + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($name: Hash),+> Hash for ($($name,)+) where last_type!($($name,)+): ?Sized { + #[allow(non_snake_case)] + #[inline] + fn hash(&self, state: &mut S) { + let ($(ref $name,)+) = *self; + $($name.hash(state);)+ + } } } ); } + macro_rules! maybe_tuple_doc { + ($a:ident @ #[$meta:meta] $item:item) => { + #[cfg_attr(not(bootstrap), doc(tuple_variadic))] + #[doc = "This trait is implemented for tuples up to twelve items long."] + #[$meta] + $item + }; + ($a:ident $($rest_a:ident)+ @ #[$meta:meta] $item:item) => { + #[doc(hidden)] + #[$meta] + $item + }; + } + macro_rules! last_type { ($a:ident,) => { $a }; ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; } impl_hash_tuple! {} - impl_hash_tuple! { A } - impl_hash_tuple! { A B } - impl_hash_tuple! { A B C } - impl_hash_tuple! { A B C D } - impl_hash_tuple! { A B C D E } - impl_hash_tuple! { A B C D E F } - impl_hash_tuple! { A B C D E F G } - impl_hash_tuple! { A B C D E F G H } - impl_hash_tuple! { A B C D E F G H I } - impl_hash_tuple! { A B C D E F G H I J } - impl_hash_tuple! { A B C D E F G H I J K } - impl_hash_tuple! { A B C D E F G H I J K L } + impl_hash_tuple! { T } + impl_hash_tuple! { T B } + impl_hash_tuple! { T B C } + impl_hash_tuple! { T B C D } + impl_hash_tuple! { T B C D E } + impl_hash_tuple! { T B C D E F } + impl_hash_tuple! { T B C D E F G } + impl_hash_tuple! { T B C D E F G H } + impl_hash_tuple! { T B C D E F G H I } + impl_hash_tuple! { T B C D E F G H I J } + impl_hash_tuple! { T B C D E F G H I J K } + impl_hash_tuple! { T B C D E F G H I J K L } #[stable(feature = "rust1", since = "1.0.0")] impl Hash for [T] { diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 4a90ef9545d68..2c57897956fcd 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -359,7 +359,6 @@ pub trait StructuralEq { /// /// * Function item types (i.e., the distinct types defined for each function) /// * Function pointer types (e.g., `fn() -> i32`) -/// * Tuple types, if each component also implements `Copy` (e.g., `()`, `(i32, bool)`) /// * Closure types, if they capture no value from the environment /// or if all such captured values implement `Copy` themselves. /// Note that variables captured by shared reference always implement `Copy` diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 688ab63bf1366..00793f7f9204d 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -439,6 +439,27 @@ mod prim_char {} #[stable(feature = "rust1", since = "1.0.0")] mod prim_unit {} +// Required to make auto trait impls render. +// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls +#[doc(hidden)] +impl () {} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for () { + fn clone(&self) -> Self { + loop {} + } +} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +impl Copy for () { + // empty +} + #[doc(primitive = "pointer")] #[doc(alias = "ptr")] #[doc(alias = "*")] @@ -893,13 +914,18 @@ mod prim_str {} /// /// For more about tuples, see [the book](../book/ch03-02-data-types.html#the-tuple-type). /// +// Hardcoded anchor in src/librustdoc/html/format.rs +// linked to as `#trait-implementations-1` /// # Trait implementations /// -/// If every type inside a tuple implements one of the following traits, then a -/// tuple itself also implements it. +/// In this documentation the shorthand `(T₁, T₂, …, Tₙ)` is used to represent tuples of varying +/// length. When that is used, any trait bound expressed on `T` applies to each element of the +/// tuple independently. Note that this is a convenience notation to avoid repetitive +/// documentation, not valid Rust syntax. +/// +/// Due to a temporary restriction in Rust’s type system, the following traits are only +/// implemented on tuples of arity 12 or less. In the future, this may change: /// -/// * [`Clone`] -/// * [`Copy`] /// * [`PartialEq`] /// * [`Eq`] /// * [`PartialOrd`] @@ -911,8 +937,21 @@ mod prim_str {} /// [`Debug`]: fmt::Debug /// [`Hash`]: hash::Hash /// -/// Due to a temporary restriction in Rust's type system, these traits are only -/// implemented on tuples of arity 12 or less. In the future, this may change. +/// The following traits are implemented for tuples of any length. These traits have +/// implementations that are automatically generated by the compiler, so are not limited by +/// missing language features. +/// +/// * [`Clone`] +/// * [`Copy`] +/// * [`Send`] +/// * [`Sync`] +/// * [`Unpin`] +/// * [`UnwindSafe`] +/// * [`RefUnwindSafe`] +/// +/// [`Unpin`]: marker::Unpin +/// [`UnwindSafe`]: panic::UnwindSafe +/// [`RefUnwindSafe`]: panic::RefUnwindSafe /// /// # Examples /// @@ -949,6 +988,31 @@ mod prim_str {} #[stable(feature = "rust1", since = "1.0.0")] mod prim_tuple {} +// Required to make auto trait impls render. +// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls +#[doc(hidden)] +impl (T,) {} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), doc(tuple_variadic))] +/// This trait is implemented on arbitrary-length tuples. +impl Clone for (T,) { + fn clone(&self) -> Self { + loop {} + } +} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), doc(tuple_variadic))] +/// This trait is implemented on arbitrary-length tuples. +impl Copy for (T,) { + // empty +} + #[doc(primitive = "f32")] /// A 32-bit floating point type (specifically, the "binary32" type defined in IEEE 754-2008). /// diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 181717f35bd25..ab3763cbc4196 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -19,75 +19,106 @@ macro_rules! tuple_impls { }; // "Private" internal implementation (@impl $( $T:ident )+) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:PartialEq),+> PartialEq for ($($T,)+) - where - last_type!($($T,)+): ?Sized - { - #[inline] - fn eq(&self, other: &($($T,)+)) -> bool { - $( ${ignore(T)} self.${index()} == other.${index()} )&&+ - } - #[inline] - fn ne(&self, other: &($($T,)+)) -> bool { - $( ${ignore(T)} self.${index()} != other.${index()} )||+ + maybe_tuple_doc! { + $($T)+ @ + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:PartialEq),+> PartialEq for ($($T,)+) + where + last_type!($($T,)+): ?Sized + { + #[inline] + fn eq(&self, other: &($($T,)+)) -> bool { + $( ${ignore(T)} self.${index()} == other.${index()} )&&+ + } + #[inline] + fn ne(&self, other: &($($T,)+)) -> bool { + $( ${ignore(T)} self.${index()} != other.${index()} )||+ + } } } - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:Eq),+> Eq for ($($T,)+) - where - last_type!($($T,)+): ?Sized - {} + maybe_tuple_doc! { + $($T)+ @ + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:Eq),+> Eq for ($($T,)+) + where + last_type!($($T,)+): ?Sized + {} + } - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:PartialOrd + PartialEq),+> PartialOrd for ($($T,)+) - where - last_type!($($T,)+): ?Sized - { - #[inline] - fn partial_cmp(&self, other: &($($T,)+)) -> Option { - lexical_partial_cmp!($( ${ignore(T)} self.${index()}, other.${index()} ),+) - } - #[inline] - fn lt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(lt, $( ${ignore(T)} self.${index()}, other.${index()} ),+) - } - #[inline] - fn le(&self, other: &($($T,)+)) -> bool { - lexical_ord!(le, $( ${ignore(T)} self.${index()}, other.${index()} ),+) - } - #[inline] - fn ge(&self, other: &($($T,)+)) -> bool { - lexical_ord!(ge, $( ${ignore(T)} self.${index()}, other.${index()} ),+) - } - #[inline] - fn gt(&self, other: &($($T,)+)) -> bool { - lexical_ord!(gt, $( ${ignore(T)} self.${index()}, other.${index()} ),+) + maybe_tuple_doc! { + $($T)+ @ + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:PartialOrd + PartialEq),+> PartialOrd for ($($T,)+) + where + last_type!($($T,)+): ?Sized + { + #[inline] + fn partial_cmp(&self, other: &($($T,)+)) -> Option { + lexical_partial_cmp!($( ${ignore(T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn lt(&self, other: &($($T,)+)) -> bool { + lexical_ord!(lt, $( ${ignore(T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn le(&self, other: &($($T,)+)) -> bool { + lexical_ord!(le, $( ${ignore(T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn ge(&self, other: &($($T,)+)) -> bool { + lexical_ord!(ge, $( ${ignore(T)} self.${index()}, other.${index()} ),+) + } + #[inline] + fn gt(&self, other: &($($T,)+)) -> bool { + lexical_ord!(gt, $( ${ignore(T)} self.${index()}, other.${index()} ),+) + } } } - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:Ord),+> Ord for ($($T,)+) - where - last_type!($($T,)+): ?Sized - { - #[inline] - fn cmp(&self, other: &($($T,)+)) -> Ordering { - lexical_cmp!($( ${ignore(T)} self.${index()}, other.${index()} ),+) + maybe_tuple_doc! { + $($T)+ @ + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:Ord),+> Ord for ($($T,)+) + where + last_type!($($T,)+): ?Sized + { + #[inline] + fn cmp(&self, other: &($($T,)+)) -> Ordering { + lexical_cmp!($( ${ignore(T)} self.${index()}, other.${index()} ),+) + } } } - #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:Default),+> Default for ($($T,)+) { - #[inline] - fn default() -> ($($T,)+) { - ($({ let x: $T = Default::default(); x},)+) + maybe_tuple_doc! { + $($T)+ @ + #[stable(feature = "rust1", since = "1.0.0")] + impl<$($T:Default),+> Default for ($($T,)+) { + #[inline] + fn default() -> ($($T,)+) { + ($({ let x: $T = Default::default(); x},)+) + } } } } } +// If this is a unary tuple, it adds a doc comment. +// Otherwise, it hides the docs entirely. +macro_rules! maybe_tuple_doc { + ($a:ident @ #[$meta:meta] $item:item) => { + #[cfg_attr(not(bootstrap), doc(tuple_variadic))] + #[doc = "This trait is implemented for tuples up to twelve items long."] + #[$meta] + $item + }; + ($a:ident $($rest_a:ident)+ @ #[$meta:meta] $item:item) => { + #[doc(hidden)] + #[$meta] + $item + }; +} + // Constructs an expression that performs a lexical ordering using method $rel. // The values are interleaved, so the macro invocation for // `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, a1, b1, a2, b2, @@ -125,4 +156,4 @@ macro_rules! last_type { ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; } -tuple_impls!(A B C D E F G H I J K L); +tuple_impls!(E D C B A Z Y X W V U T); diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 688ab63bf1366..00793f7f9204d 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -439,6 +439,27 @@ mod prim_char {} #[stable(feature = "rust1", since = "1.0.0")] mod prim_unit {} +// Required to make auto trait impls render. +// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls +#[doc(hidden)] +impl () {} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for () { + fn clone(&self) -> Self { + loop {} + } +} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +impl Copy for () { + // empty +} + #[doc(primitive = "pointer")] #[doc(alias = "ptr")] #[doc(alias = "*")] @@ -893,13 +914,18 @@ mod prim_str {} /// /// For more about tuples, see [the book](../book/ch03-02-data-types.html#the-tuple-type). /// +// Hardcoded anchor in src/librustdoc/html/format.rs +// linked to as `#trait-implementations-1` /// # Trait implementations /// -/// If every type inside a tuple implements one of the following traits, then a -/// tuple itself also implements it. +/// In this documentation the shorthand `(T₁, T₂, …, Tₙ)` is used to represent tuples of varying +/// length. When that is used, any trait bound expressed on `T` applies to each element of the +/// tuple independently. Note that this is a convenience notation to avoid repetitive +/// documentation, not valid Rust syntax. +/// +/// Due to a temporary restriction in Rust’s type system, the following traits are only +/// implemented on tuples of arity 12 or less. In the future, this may change: /// -/// * [`Clone`] -/// * [`Copy`] /// * [`PartialEq`] /// * [`Eq`] /// * [`PartialOrd`] @@ -911,8 +937,21 @@ mod prim_str {} /// [`Debug`]: fmt::Debug /// [`Hash`]: hash::Hash /// -/// Due to a temporary restriction in Rust's type system, these traits are only -/// implemented on tuples of arity 12 or less. In the future, this may change. +/// The following traits are implemented for tuples of any length. These traits have +/// implementations that are automatically generated by the compiler, so are not limited by +/// missing language features. +/// +/// * [`Clone`] +/// * [`Copy`] +/// * [`Send`] +/// * [`Sync`] +/// * [`Unpin`] +/// * [`UnwindSafe`] +/// * [`RefUnwindSafe`] +/// +/// [`Unpin`]: marker::Unpin +/// [`UnwindSafe`]: panic::UnwindSafe +/// [`RefUnwindSafe`]: panic::RefUnwindSafe /// /// # Examples /// @@ -949,6 +988,31 @@ mod prim_str {} #[stable(feature = "rust1", since = "1.0.0")] mod prim_tuple {} +// Required to make auto trait impls render. +// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls +#[doc(hidden)] +impl (T,) {} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), doc(tuple_variadic))] +/// This trait is implemented on arbitrary-length tuples. +impl Clone for (T,) { + fn clone(&self) -> Self { + loop {} + } +} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), doc(tuple_variadic))] +/// This trait is implemented on arbitrary-length tuples. +impl Copy for (T,) { + // empty +} + #[doc(primitive = "f32")] /// A 32-bit floating point type (specifically, the "binary32" type defined in IEEE 754-2008). /// diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index a82abe6692655..d4c38f34b5b11 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -500,7 +500,11 @@ pub(crate) fn build_impl( for_, items: trait_items, polarity, - kind: ImplKind::Normal, + kind: if utils::has_doc_flag(tcx, did, sym::tuple_variadic) { + ImplKind::TupleVaradic + } else { + ImplKind::Normal + }, }), box merged_attrs, cx, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f3070fb35f1d0..fd30691c32489 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1999,7 +1999,11 @@ fn clean_impl<'tcx>( for_, items, polarity: tcx.impl_polarity(def_id), - kind: ImplKind::Normal, + kind: if utils::has_doc_flag(tcx, def_id.to_def_id(), sym::tuple_variadic) { + ImplKind::TupleVaradic + } else { + ImplKind::Normal + }, }); Item::from_hir_id_and_parts(hir_id, None, kind, cx) }; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 4605793d0df94..83ab9acd3005a 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1750,7 +1750,7 @@ pub(crate) enum PrimitiveType { Never, } -type SimplifiedTypes = FxHashMap>; +type SimplifiedTypes = FxHashMap>; impl PrimitiveType { pub(crate) fn from_hir(prim: hir::PrimTy) -> PrimitiveType { use ast::{FloatTy, IntTy, UintTy}; @@ -1839,10 +1839,10 @@ impl PrimitiveType { // // Either manually update this arrayvec at this point // or start with a more complex refactoring. - Tuple => [TupleSimplifiedType(2), TupleSimplifiedType(3)].into(), + Tuple => [TupleSimplifiedType(1), TupleSimplifiedType(2), TupleSimplifiedType(3)].into(), Unit => single(TupleSimplifiedType(0)), - RawPointer => [PtrSimplifiedType(Mutability::Not), PtrSimplifiedType(Mutability::Mut)].into(), - Reference => [RefSimplifiedType(Mutability::Not), RefSimplifiedType(Mutability::Mut)].into(), + RawPointer => [PtrSimplifiedType(Mutability::Not), PtrSimplifiedType(Mutability::Mut)].into_iter().collect(), + Reference => [RefSimplifiedType(Mutability::Not), RefSimplifiedType(Mutability::Mut)].into_iter().collect(), // FIXME: This will be wrong if we ever add inherent impls // for function pointers. Fn => ArrayVec::new(), @@ -2394,6 +2394,7 @@ impl Impl { pub(crate) enum ImplKind { Normal, Auto, + TupleVaradic, Blanket(Box), } @@ -2406,6 +2407,10 @@ impl ImplKind { matches!(self, ImplKind::Blanket(_)) } + pub(crate) fn is_tuple_variadic(&self) -> bool { + matches!(self, ImplKind::TupleVaradic) + } + pub(crate) fn as_blanket_ty(&self) -> Option<&Type> { match self { ImplKind::Blanket(ty) => Some(ty), diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 394db2d0cda6b..0c0920ae63e4e 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -713,6 +713,16 @@ fn primitive_link( prim: clean::PrimitiveType, name: &str, cx: &Context<'_>, +) -> fmt::Result { + primitive_link_fragment(f, prim, name, "", cx) +} + +fn primitive_link_fragment( + f: &mut fmt::Formatter<'_>, + prim: clean::PrimitiveType, + name: &str, + fragment: &str, + cx: &Context<'_>, ) -> fmt::Result { let m = &cx.cache(); let mut needs_termination = false; @@ -723,7 +733,7 @@ fn primitive_link( let len = if len == 0 { 0 } else { len - 1 }; write!( f, - "", + "", "../".repeat(len), prim.as_sym() )?; @@ -754,7 +764,7 @@ fn primitive_link( }; if let Some(mut loc) = loc { loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym())); - write!(f, "", loc.finish())?; + write!(f, "", loc.finish())?; needs_termination = true; } } @@ -1039,7 +1049,13 @@ impl clean::Impl { write!(f, " for ")?; } - if let Some(ty) = self.kind.as_blanket_ty() { + if let clean::Type::Tuple(types) = &self.for_ && + let [clean::Type::Generic(name)] = &types[..] && + (self.kind.is_tuple_variadic() || self.kind.is_auto()) { + // Hardcoded anchor library/core/src/primitive_docs.rs + // Link should match `# Trait implementations` + primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?; + } else if let Some(ty) = self.kind.as_blanket_ty() { fmt_type(ty, f, use_absolute, cx)?; } else { fmt_type(&self.for_, f, use_absolute, cx)?; diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 51a2abc50bc2b..4fde63c99d4b9 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -552,7 +552,7 @@ impl FromWithTcx for Impl { let trait_ = trait_.map(|path| clean::Type::Path { path }.into_tcx(tcx)); // FIXME: use something like ImplKind in JSON? let (synthetic, blanket_impl) = match kind { - clean::ImplKind::Normal => (false, None), + clean::ImplKind::Normal | clean::ImplKind::TupleVaradic => (false, None), clean::ImplKind::Auto => (true, None), clean::ImplKind::Blanket(ty) => (false, Some(*ty)), }; diff --git a/src/test/rustdoc-ui/tuple-variadic-check.rs b/src/test/rustdoc-ui/tuple-variadic-check.rs new file mode 100644 index 0000000000000..11ce2dbe28050 --- /dev/null +++ b/src/test/rustdoc-ui/tuple-variadic-check.rs @@ -0,0 +1,15 @@ +#![feature(rustdoc_internals)] + +trait Mine {} + +// This one is fine +#[doc(tuple_variadic)] +impl Mine for (T,) {} + +trait Mine2 {} + +// This one is not +#[doc(tuple_variadic)] //~ ERROR +impl Mine for (T,U) {} + +fn main() {} diff --git a/src/test/rustdoc-ui/tuple-variadic-check.stderr b/src/test/rustdoc-ui/tuple-variadic-check.stderr new file mode 100644 index 0000000000000..358d06d6a42dd --- /dev/null +++ b/src/test/rustdoc-ui/tuple-variadic-check.stderr @@ -0,0 +1,8 @@ +error: `#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity + --> $DIR/tuple-variadic-check.rs:12:7 + | +LL | #[doc(tuple_variadic)] + | ^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/rustdoc/primitive-tuple-auto-trait.rs b/src/test/rustdoc/primitive-tuple-auto-trait.rs new file mode 100644 index 0000000000000..71b0b07700958 --- /dev/null +++ b/src/test/rustdoc/primitive-tuple-auto-trait.rs @@ -0,0 +1,22 @@ +// compile-flags: --crate-type lib --edition 2018 + +#![crate_name = "foo"] +#![feature(rustdoc_internals)] + +// @has foo/primitive.tuple.html '//a[@class="primitive"]' 'tuple' +// @has - '//span[@class="in-band"]' 'Primitive Type tuple' +// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!' +// @has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations' +// @has - '//div[@id="synthetic-implementations-list"]//h3' 'Send' +// @has - '//div[@id="synthetic-implementations-list"]//h3' 'Sync' +#[doc(primitive = "tuple")] +/// this is a test! +/// +// Hardcoded anchor to header written in library/core/src/primitive_docs.rs +// @has - '//h2[@id="trait-implementations-1"]' 'Trait implementations' +/// # Trait implementations +/// +/// This header is hard-coded in the HTML format linking for `#[doc(tuple_variadics)]`. +/// To make sure it gets linked correctly, we need to make sure the hardcoded anchor +/// in the code matches what rustdoc generates for the header. +mod tuple_prim {} diff --git a/src/test/rustdoc/primitive-tuple-variadic.rs b/src/test/rustdoc/primitive-tuple-variadic.rs new file mode 100644 index 0000000000000..4fd6254f6740d --- /dev/null +++ b/src/test/rustdoc/primitive-tuple-variadic.rs @@ -0,0 +1,18 @@ +// compile-flags: --crate-type lib --edition 2018 + +#![crate_name = "foo"] +#![feature(rustdoc_internals)] + +pub trait Foo {} + +// @has foo/trait.Foo.html +// @has - '//section[@id="impl-Foo-for-(T%2C)"]/h3' 'impl Foo for (T₁, T₂, …, Tₙ)' +#[doc(tuple_variadic)] +impl Foo for (T,) {} + +pub trait Bar {} + +// @has foo/trait.Bar.html +// @has - '//section[@id="impl-Bar-for-(U%2C)"]/h3' 'impl Bar for (U₁, U₂, …, Uₙ)' +#[doc(tuple_variadic)] +impl Bar for (U,) {} diff --git a/src/test/rustdoc/primitive-unit-auto-trait.rs b/src/test/rustdoc/primitive-unit-auto-trait.rs new file mode 100644 index 0000000000000..76182622ef5fb --- /dev/null +++ b/src/test/rustdoc/primitive-unit-auto-trait.rs @@ -0,0 +1,14 @@ +// compile-flags: --crate-type lib --edition 2018 + +#![crate_name = "foo"] +#![feature(rustdoc_internals)] + +// @has foo/primitive.unit.html '//a[@class="primitive"]' 'unit' +// @has - '//span[@class="in-band"]' 'Primitive Type unit' +// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!' +// @has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations' +// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl Send for ()' +// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl Sync for ()' +#[doc(primitive = "unit")] +/// this is a test! +mod unit_prim {} diff --git a/src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs b/src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs index d2ff4f6200986..6a144412d0751 100644 --- a/src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs +++ b/src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs @@ -2,4 +2,9 @@ /// wonderful mod foo {} +trait Mine {} + +#[doc(tuple_variadic)] //~ ERROR: `#[doc(tuple_variadic)]` is meant for internal use only +impl Mine for (T,) {} + fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr b/src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr index e96461ac38acb..9fe08afd4f05e 100644 --- a/src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr +++ b/src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr @@ -7,6 +7,15 @@ LL | #[doc(keyword = "match")] = note: see issue #90418 for more information = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable -error: aborting due to previous error +error[E0658]: `#[doc(tuple_variadic)]` is meant for internal use only + --> $DIR/feature-gate-rustdoc_internals.rs:7:1 + | +LL | #[doc(tuple_variadic)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #90418 for more information + = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`.