diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 77dd4ccd64e..1777438d304 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_varadic) { + let msg = "`#[doc(tuple_varadic)]` 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 5cc97d326d3..ea8c1b45a60 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -804,6 +804,37 @@ impl CheckAttrVisitor<'_> { true } + fn check_doc_tuple_varadic(&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_varadic)]` can only be used on unary tuples", + ) + .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 @@ -1064,6 +1095,13 @@ impl CheckAttrVisitor<'_> { is_valid = false } + sym::tuple_varadic + if !self.check_attr_not_crate_level(meta, hir_id, "tuple_varadic") + || !self.check_doc_tuple_varadic(meta, hir_id) => + { + is_valid = false + } + sym::html_favicon_url | sym::html_logo_url | sym::html_playground_url @@ -1117,7 +1155,8 @@ impl CheckAttrVisitor<'_> { | sym::no_inline | sym::notable_trait | sym::passes - | sym::plugins => {} + | sym::plugins + | sym::tuple_varadic => {} 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 bcaf53639cc..37e905f7f56 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1439,6 +1439,7 @@ symbols! { tuple, tuple_from_req, tuple_indexing, + tuple_varadic, two_phase, ty, type_alias_enum_variants, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 46b2b0c92bf..6c76e148839 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -2335,6 +2335,7 @@ macro_rules! tuple { macro_rules! maybe_tuple_doc { ($a:ident @ #[$meta:meta] $item:item) => { + #[cfg_attr(not(bootstrap), doc(tuple_varadic))] #[doc = "This trait is implemented for tuples up to twelve items long."] #[$meta] $item diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index ae3799d2a96..d79258d2ed9 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -900,6 +900,7 @@ mod impls { macro_rules! maybe_tuple_doc { ($a:ident @ #[$meta:meta] $item:item) => { + #[cfg_attr(not(bootstrap), doc(tuple_varadic))] #[doc = "This trait is implemented for tuples up to twelve items long."] #[$meta] $item diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index db2f7ec0e0e..26f9528dd22 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -916,24 +916,11 @@ mod prim_str {} /// /// # Trait implementations /// -/// If every type inside a tuple implements one of the following traits, then a -/// tuple itself also implements it. -/// -/// * [`Clone`] -/// * [`Copy`] -/// * [`PartialEq`] -/// * [`Eq`] -/// * [`PartialOrd`] -/// * [`Ord`] -/// * [`Debug`] -/// * [`Default`] -/// * [`Hash`] -/// -/// [`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. +/// In this documentation the shorthand `(T, ...)` is used to represent all +/// tuples up to length twelve. When that is used, any trait bounds expressed +/// on `T` applies to each field of the tuple independently. Note that this is +/// a convenience notation to avoid repetitive documentation, not valid +/// Rust syntax. /// /// # Examples /// @@ -978,6 +965,7 @@ impl (T, U) {} // Fake impl that's only really used for docs. #[cfg(doc)] #[stable(feature = "rust1", since = "1.0.0")] +#[doc(tuple_varadic)] /// This trait is implemented on arbitrary-length tuples. impl Clone for (T,) { fn clone(&self) -> Self { @@ -988,6 +976,7 @@ impl Clone for (T,) { // Fake impl that's only really used for docs. #[cfg(doc)] #[stable(feature = "rust1", since = "1.0.0")] +#[doc(tuple_varadic)] /// This trait is implemented on arbitrary-length tuples. impl Copy for (T,) { // empty diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 61fa0eea78a..9ddefc651ba 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -105,6 +105,7 @@ macro_rules! tuple_impls { macro_rules! maybe_tuple_doc { ($a:ident @ #[$meta:meta] $item:item) => { + #[cfg_attr(not(bootstrap), doc(tuple_varadic))] #[doc = "This trait is implemented for tuples up to twelve items long."] #[$meta] $item diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index db2f7ec0e0e..26f9528dd22 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -916,24 +916,11 @@ mod prim_str {} /// /// # Trait implementations /// -/// If every type inside a tuple implements one of the following traits, then a -/// tuple itself also implements it. -/// -/// * [`Clone`] -/// * [`Copy`] -/// * [`PartialEq`] -/// * [`Eq`] -/// * [`PartialOrd`] -/// * [`Ord`] -/// * [`Debug`] -/// * [`Default`] -/// * [`Hash`] -/// -/// [`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. +/// In this documentation the shorthand `(T, ...)` is used to represent all +/// tuples up to length twelve. When that is used, any trait bounds expressed +/// on `T` applies to each field of the tuple independently. Note that this is +/// a convenience notation to avoid repetitive documentation, not valid +/// Rust syntax. /// /// # Examples /// @@ -978,6 +965,7 @@ impl (T, U) {} // Fake impl that's only really used for docs. #[cfg(doc)] #[stable(feature = "rust1", since = "1.0.0")] +#[doc(tuple_varadic)] /// This trait is implemented on arbitrary-length tuples. impl Clone for (T,) { fn clone(&self) -> Self { @@ -988,6 +976,7 @@ impl Clone for (T,) { // Fake impl that's only really used for docs. #[cfg(doc)] #[stable(feature = "rust1", since = "1.0.0")] +#[doc(tuple_varadic)] /// This trait is implemented on arbitrary-length tuples. impl Copy for (T,) { // empty diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index a82abe66926..d8f6a9c3ff0 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_varadic) { + ImplKind::TupleVaradic + } else { + ImplKind::Normal + }, }), box merged_attrs, cx, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f3070fb35f1..b15ef424cb6 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_varadic) { + 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 4605793d0df..95c84e68b83 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -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_varadic(&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 b7789493df6..2f433c2313b 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; } } @@ -1064,7 +1074,11 @@ 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_varadic() || self.kind.is_auto()) { + primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({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 51a2abc50bc..4fde63c99d4 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/ui/feature-gates/feature-gate-rustdoc_internals.rs b/src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs index d2ff4f62009..e40a3044b94 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_varadic)] //~ ERROR: `#[doc(tuple_varadic)]` 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 e96461ac38a..00dab359e73 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_varadic)]` is meant for internal use only + --> $DIR/feature-gate-rustdoc_internals.rs:7:1 + | +LL | #[doc(tuple_varadic)] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = 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`.