Skip to content

Commit 6950f14

Browse files
committed
rustdoc: show tuple impls as impl Trait for (T, ...)
This commit adds a new unstable attribute, `#[doc(tuple_varadic)]`, that shows a 1-tuple as `(T, ...)` instead of just `(T,)`, and links to a section in the tuple primitive docs that talks about these.
1 parent 7a93567 commit 6950f14

File tree

15 files changed

+111
-44
lines changed

15 files changed

+111
-44
lines changed

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
401401
let msg = "`#[doc(keyword)]` is meant for internal use only";
402402
gate_feature_post!(self, rustdoc_internals, attr.span, msg);
403403
}
404+
405+
if nested_meta.has_name(sym::tuple_varadic) {
406+
let msg = "`#[doc(tuple_varadic)]` is meant for internal use only";
407+
gate_feature_post!(self, rustdoc_internals, attr.span, msg);
408+
}
404409
}
405410
}
406411

compiler/rustc_passes/src/check_attr.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,37 @@ impl CheckAttrVisitor<'_> {
804804
true
805805
}
806806

807+
fn check_doc_tuple_varadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
808+
match self.tcx.hir().find(hir_id).and_then(|node| match node {
809+
hir::Node::Item(item) => Some(&item.kind),
810+
_ => None,
811+
}) {
812+
Some(ItemKind::Impl(ref i)) => {
813+
if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) {
814+
self.tcx
815+
.sess
816+
.struct_span_err(
817+
meta.span(),
818+
"`#[doc(tuple_varadic)]` can only be used on unary tuples",
819+
)
820+
.emit();
821+
return false;
822+
}
823+
}
824+
_ => {
825+
self.tcx
826+
.sess
827+
.struct_span_err(
828+
meta.span(),
829+
"`#[doc(keyword = \"...\")]` can only be used on impl blocks",
830+
)
831+
.emit();
832+
return false;
833+
}
834+
}
835+
true
836+
}
837+
807838
/// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid.
808839
///
809840
/// A doc inlining attribute is invalid if it is applied to a non-`use` item, or
@@ -1064,6 +1095,13 @@ impl CheckAttrVisitor<'_> {
10641095
is_valid = false
10651096
}
10661097

1098+
sym::tuple_varadic
1099+
if !self.check_attr_not_crate_level(meta, hir_id, "tuple_varadic")
1100+
|| !self.check_doc_tuple_varadic(meta, hir_id) =>
1101+
{
1102+
is_valid = false
1103+
}
1104+
10671105
sym::html_favicon_url
10681106
| sym::html_logo_url
10691107
| sym::html_playground_url
@@ -1117,7 +1155,8 @@ impl CheckAttrVisitor<'_> {
11171155
| sym::no_inline
11181156
| sym::notable_trait
11191157
| sym::passes
1120-
| sym::plugins => {}
1158+
| sym::plugins
1159+
| sym::tuple_varadic => {}
11211160

11221161
sym::test => {
11231162
if !self.check_test_attr(meta, hir_id) {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,7 @@ symbols! {
14391439
tuple,
14401440
tuple_from_req,
14411441
tuple_indexing,
1442+
tuple_varadic,
14421443
two_phase,
14431444
ty,
14441445
type_alias_enum_variants,

library/core/src/fmt/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2335,6 +2335,7 @@ macro_rules! tuple {
23352335

23362336
macro_rules! maybe_tuple_doc {
23372337
($a:ident @ #[$meta:meta] $item:item) => {
2338+
#[cfg_attr(not(bootstrap), doc(tuple_varadic))]
23382339
#[doc = "This trait is implemented for tuples up to twelve items long."]
23392340
#[$meta]
23402341
$item

library/core/src/hash/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,7 @@ mod impls {
900900

901901
macro_rules! maybe_tuple_doc {
902902
($a:ident @ #[$meta:meta] $item:item) => {
903+
#[cfg_attr(not(bootstrap), doc(tuple_varadic))]
903904
#[doc = "This trait is implemented for tuples up to twelve items long."]
904905
#[$meta]
905906
$item

library/core/src/primitive_docs.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -916,24 +916,11 @@ mod prim_str {}
916916
///
917917
/// # Trait implementations
918918
///
919-
/// If every type inside a tuple implements one of the following traits, then a
920-
/// tuple itself also implements it.
921-
///
922-
/// * [`Clone`]
923-
/// * [`Copy`]
924-
/// * [`PartialEq`]
925-
/// * [`Eq`]
926-
/// * [`PartialOrd`]
927-
/// * [`Ord`]
928-
/// * [`Debug`]
929-
/// * [`Default`]
930-
/// * [`Hash`]
931-
///
932-
/// [`Debug`]: fmt::Debug
933-
/// [`Hash`]: hash::Hash
934-
///
935-
/// Due to a temporary restriction in Rust's type system, these traits are only
936-
/// implemented on tuples of arity 12 or less. In the future, this may change.
919+
/// In this documentation the shorthand `(T, ...)` is used to represent all
920+
/// tuples up to length twelve. When that is used, any trait bounds expressed
921+
/// on `T` applies to each field of the tuple independently. Note that this is
922+
/// a convenience notation to avoid repetitive documentation, not valid
923+
/// Rust syntax.
937924
///
938925
/// # Examples
939926
///
@@ -978,6 +965,7 @@ impl<T, U> (T, U) {}
978965
// Fake impl that's only really used for docs.
979966
#[cfg(doc)]
980967
#[stable(feature = "rust1", since = "1.0.0")]
968+
#[doc(tuple_varadic)]
981969
/// This trait is implemented on arbitrary-length tuples.
982970
impl<T: Clone> Clone for (T,) {
983971
fn clone(&self) -> Self {
@@ -988,6 +976,7 @@ impl<T: Clone> Clone for (T,) {
988976
// Fake impl that's only really used for docs.
989977
#[cfg(doc)]
990978
#[stable(feature = "rust1", since = "1.0.0")]
979+
#[doc(tuple_varadic)]
991980
/// This trait is implemented on arbitrary-length tuples.
992981
impl<T: Copy> Copy for (T,) {
993982
// empty

library/core/src/tuple.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ macro_rules! tuple_impls {
105105

106106
macro_rules! maybe_tuple_doc {
107107
($a:ident @ #[$meta:meta] $item:item) => {
108+
#[cfg_attr(not(bootstrap), doc(tuple_varadic))]
108109
#[doc = "This trait is implemented for tuples up to twelve items long."]
109110
#[$meta]
110111
$item

library/std/src/primitive_docs.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -916,24 +916,11 @@ mod prim_str {}
916916
///
917917
/// # Trait implementations
918918
///
919-
/// If every type inside a tuple implements one of the following traits, then a
920-
/// tuple itself also implements it.
921-
///
922-
/// * [`Clone`]
923-
/// * [`Copy`]
924-
/// * [`PartialEq`]
925-
/// * [`Eq`]
926-
/// * [`PartialOrd`]
927-
/// * [`Ord`]
928-
/// * [`Debug`]
929-
/// * [`Default`]
930-
/// * [`Hash`]
931-
///
932-
/// [`Debug`]: fmt::Debug
933-
/// [`Hash`]: hash::Hash
934-
///
935-
/// Due to a temporary restriction in Rust's type system, these traits are only
936-
/// implemented on tuples of arity 12 or less. In the future, this may change.
919+
/// In this documentation the shorthand `(T, ...)` is used to represent all
920+
/// tuples up to length twelve. When that is used, any trait bounds expressed
921+
/// on `T` applies to each field of the tuple independently. Note that this is
922+
/// a convenience notation to avoid repetitive documentation, not valid
923+
/// Rust syntax.
937924
///
938925
/// # Examples
939926
///
@@ -978,6 +965,7 @@ impl<T, U> (T, U) {}
978965
// Fake impl that's only really used for docs.
979966
#[cfg(doc)]
980967
#[stable(feature = "rust1", since = "1.0.0")]
968+
#[doc(tuple_varadic)]
981969
/// This trait is implemented on arbitrary-length tuples.
982970
impl<T: Clone> Clone for (T,) {
983971
fn clone(&self) -> Self {
@@ -988,6 +976,7 @@ impl<T: Clone> Clone for (T,) {
988976
// Fake impl that's only really used for docs.
989977
#[cfg(doc)]
990978
#[stable(feature = "rust1", since = "1.0.0")]
979+
#[doc(tuple_varadic)]
991980
/// This trait is implemented on arbitrary-length tuples.
992981
impl<T: Copy> Copy for (T,) {
993982
// empty

src/librustdoc/clean/inline.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,11 @@ pub(crate) fn build_impl(
500500
for_,
501501
items: trait_items,
502502
polarity,
503-
kind: ImplKind::Normal,
503+
kind: if utils::has_doc_flag(tcx, did, sym::tuple_varadic) {
504+
ImplKind::TupleVaradic
505+
} else {
506+
ImplKind::Normal
507+
},
504508
}),
505509
box merged_attrs,
506510
cx,

src/librustdoc/clean/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1999,7 +1999,11 @@ fn clean_impl<'tcx>(
19991999
for_,
20002000
items,
20012001
polarity: tcx.impl_polarity(def_id),
2002-
kind: ImplKind::Normal,
2002+
kind: if utils::has_doc_flag(tcx, def_id.to_def_id(), sym::tuple_varadic) {
2003+
ImplKind::TupleVaradic
2004+
} else {
2005+
ImplKind::Normal
2006+
},
20032007
});
20042008
Item::from_hir_id_and_parts(hir_id, None, kind, cx)
20052009
};

src/librustdoc/clean/types.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,6 +2394,7 @@ impl Impl {
23942394
pub(crate) enum ImplKind {
23952395
Normal,
23962396
Auto,
2397+
TupleVaradic,
23972398
Blanket(Box<Type>),
23982399
}
23992400

@@ -2406,6 +2407,10 @@ impl ImplKind {
24062407
matches!(self, ImplKind::Blanket(_))
24072408
}
24082409

2410+
pub(crate) fn is_tuple_varadic(&self) -> bool {
2411+
matches!(self, ImplKind::TupleVaradic)
2412+
}
2413+
24092414
pub(crate) fn as_blanket_ty(&self) -> Option<&Type> {
24102415
match self {
24112416
ImplKind::Blanket(ty) => Some(ty),

src/librustdoc/html/format.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,16 @@ fn primitive_link(
713713
prim: clean::PrimitiveType,
714714
name: &str,
715715
cx: &Context<'_>,
716+
) -> fmt::Result {
717+
primitive_link_fragment(f, prim, name, "", cx)
718+
}
719+
720+
fn primitive_link_fragment(
721+
f: &mut fmt::Formatter<'_>,
722+
prim: clean::PrimitiveType,
723+
name: &str,
724+
fragment: &str,
725+
cx: &Context<'_>,
716726
) -> fmt::Result {
717727
let m = &cx.cache();
718728
let mut needs_termination = false;
@@ -723,7 +733,7 @@ fn primitive_link(
723733
let len = if len == 0 { 0 } else { len - 1 };
724734
write!(
725735
f,
726-
"<a class=\"primitive\" href=\"{}primitive.{}.html\">",
736+
"<a class=\"primitive\" href=\"{}primitive.{}.html{fragment}\">",
727737
"../".repeat(len),
728738
prim.as_sym()
729739
)?;
@@ -754,7 +764,7 @@ fn primitive_link(
754764
};
755765
if let Some(mut loc) = loc {
756766
loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
757-
write!(f, "<a class=\"primitive\" href=\"{}\">", loc.finish())?;
767+
write!(f, "<a class=\"primitive\" href=\"{}{fragment}\">", loc.finish())?;
758768
needs_termination = true;
759769
}
760770
}
@@ -1064,7 +1074,11 @@ impl clean::Impl {
10641074
write!(f, " for ")?;
10651075
}
10661076

1067-
if let Some(ty) = self.kind.as_blanket_ty() {
1077+
if let clean::Type::Tuple(types) = &self.for_ &&
1078+
let [clean::Type::Generic(name)] = &types[..] &&
1079+
(self.kind.is_tuple_varadic() || self.kind.is_auto()) {
1080+
primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({name}, ...)"), "#trait-implementations-1", cx)?;
1081+
} else if let Some(ty) = self.kind.as_blanket_ty() {
10681082
fmt_type(ty, f, use_absolute, cx)?;
10691083
} else {
10701084
fmt_type(&self.for_, f, use_absolute, cx)?;

src/librustdoc/json/conversions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ impl FromWithTcx<clean::Impl> for Impl {
552552
let trait_ = trait_.map(|path| clean::Type::Path { path }.into_tcx(tcx));
553553
// FIXME: use something like ImplKind in JSON?
554554
let (synthetic, blanket_impl) = match kind {
555-
clean::ImplKind::Normal => (false, None),
555+
clean::ImplKind::Normal | clean::ImplKind::TupleVaradic => (false, None),
556556
clean::ImplKind::Auto => (true, None),
557557
clean::ImplKind::Blanket(ty) => (false, Some(*ty)),
558558
};

src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@
22
/// wonderful
33
mod foo {}
44

5+
trait Mine {}
6+
7+
#[doc(tuple_varadic)] //~ ERROR: `#[doc(tuple_varadic)]` is meant for internal use only
8+
impl<T> Mine for (T,) {}
9+
510
fn main() {}

src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ LL | #[doc(keyword = "match")]
77
= note: see issue #90418 <https://github.com/rust-lang/rust/issues/90418> for more information
88
= help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable
99

10-
error: aborting due to previous error
10+
error[E0658]: `#[doc(tuple_varadic)]` is meant for internal use only
11+
--> $DIR/feature-gate-rustdoc_internals.rs:7:1
12+
|
13+
LL | #[doc(tuple_varadic)]
14+
| ^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= note: see issue #90418 <https://github.com/rust-lang/rust/issues/90418> for more information
17+
= help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable
18+
19+
error: aborting due to 2 previous errors
1120

1221
For more information about this error, try `rustc --explain E0658`.

0 commit comments

Comments
 (0)