about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs5
-rw-r--r--compiler/rustc_passes/src/check_attr.rs41
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/fmt/mod.rs1
-rw-r--r--library/core/src/hash/mod.rs1
-rw-r--r--library/core/src/primitive_docs.rs25
-rw-r--r--library/core/src/tuple.rs1
-rw-r--r--library/std/src/primitive_docs.rs25
-rw-r--r--src/librustdoc/clean/inline.rs6
-rw-r--r--src/librustdoc/clean/mod.rs6
-rw-r--r--src/librustdoc/clean/types.rs5
-rw-r--r--src/librustdoc/html/format.rs20
-rw-r--r--src/librustdoc/json/conversions.rs2
-rw-r--r--src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs5
-rw-r--r--src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr11
15 files changed, 111 insertions, 44 deletions
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> (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<T: Clone> Clone for (T,) {
     fn clone(&self) -> Self {
@@ -988,6 +976,7 @@ impl<T: Clone> 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<T: Copy> 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> (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<T: Clone> Clone for (T,) {
     fn clone(&self) -> Self {
@@ -988,6 +976,7 @@ impl<T: Clone> 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<T: Copy> 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<Type>),
 }
 
@@ -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
@@ -714,6 +714,16 @@ fn primitive_link(
     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;
     if !f.alternate() {
@@ -723,7 +733,7 @@ fn primitive_link(
                 let len = if len == 0 { 0 } else { len - 1 };
                 write!(
                     f,
-                    "<a class=\"primitive\" href=\"{}primitive.{}.html\">",
+                    "<a class=\"primitive\" href=\"{}primitive.{}.html{fragment}\">",
                     "../".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, "<a class=\"primitive\" href=\"{}\">", loc.finish())?;
+                    write!(f, "<a class=\"primitive\" href=\"{}{fragment}\">", 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<clean::Impl> 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<T> 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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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`.