diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9ca4bb09b3e17..d1dac9ca6daff 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -952,6 +952,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { bare_fn_ty.decl.inputs.len() == 1 } else { false + } + || if let Some(&[hir::GenericArg::Type(ty)]) = i + .of_trait + .as_ref() + .and_then(|trait_ref| trait_ref.path.segments.last()) + .map(|last_segment| last_segment.args().args) + { + matches!(&ty.kind, hir::TyKind::Tup([_])) + } else { + false }; if !is_valid { self.dcx().emit_err(errors::DocFakeVariadicNotValid { span: meta.span() }); diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 65d4d5cf2ce41..9ec92e2845034 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -122,23 +122,29 @@ macro_rules! tuple_impls { } } - #[stable(feature = "array_tuple_conv", since = "1.71.0")] - impl From<[T; ${count($T)}]> for ($(${ignore($T)} T,)+) { - #[inline] - #[allow(non_snake_case)] - fn from(array: [T; ${count($T)}]) -> Self { - let [$($T,)+] = array; - ($($T,)+) + maybe_tuple_doc! { + $($T)+ @ + #[stable(feature = "array_tuple_conv", since = "1.71.0")] + impl From<[T; ${count($T)}]> for ($(${ignore($T)} T,)+) { + #[inline] + #[allow(non_snake_case)] + fn from(array: [T; ${count($T)}]) -> Self { + let [$($T,)+] = array; + ($($T,)+) + } } } - #[stable(feature = "array_tuple_conv", since = "1.71.0")] - impl From<($(${ignore($T)} T,)+)> for [T; ${count($T)}] { - #[inline] - #[allow(non_snake_case)] - fn from(tuple: ($(${ignore($T)} T,)+)) -> Self { - let ($($T,)+) = tuple; - [$($T,)+] + maybe_tuple_doc! { + $($T)+ @ + #[stable(feature = "array_tuple_conv", since = "1.71.0")] + impl From<($(${ignore($T)} T,)+)> for [T; ${count($T)}] { + #[inline] + #[allow(non_snake_case)] + fn from(tuple: ($(${ignore($T)} T,)+)) -> Self { + let ($($T,)+) = tuple; + [$($T,)+] + } } } } @@ -148,7 +154,7 @@ macro_rules! tuple_impls { // Otherwise, it hides the docs entirely. macro_rules! maybe_tuple_doc { ($a:ident @ #[$meta:meta] $item:item) => { - #[doc(fake_variadic)] + #[cfg_attr(not(bootstrap), doc(fake_variadic))] #[doc = "This trait is implemented for tuples up to twelve items long."] #[$meta] $item diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 6357cfee141fd..5cbf8c5e19f45 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1288,56 +1288,90 @@ impl clean::Impl { if self.is_negative_trait_impl() { write!(f, "!")?; } - ty.print(cx).fmt(f)?; + if self.kind.is_fake_variadic() + && let generics = ty.generics() + && let &[inner_type] = generics.as_ref().map_or(&[][..], |v| &v[..]) + { + let last = ty.last(); + if f.alternate() { + write!(f, "{}<", last)?; + self.print_type(inner_type, f, use_absolute, cx)?; + write!(f, ">")?; + } else { + write!(f, "{}<", anchor(ty.def_id(), last, cx).to_string())?; + self.print_type(inner_type, f, use_absolute, cx)?; + write!(f, ">")?; + } + } else { + ty.print(cx).fmt(f)?; + } write!(f, " for ")?; } - if let clean::Type::Tuple(types) = &self.for_ - && let [clean::Type::Generic(name)] = &types[..] - && (self.kind.is_fake_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_args!("({name}₁, {name}₂, …, {name}ₙ)"), - "#trait-implementations-1", - cx, - )?; - } else if let clean::BareFunction(bare_fn) = &self.for_ - && let [clean::Argument { type_: clean::Type::Generic(name), .. }] = - &bare_fn.decl.inputs.values[..] - && (self.kind.is_fake_variadic() || self.kind.is_auto()) - { - // Hardcoded anchor library/core/src/primitive_docs.rs - // Link should match `# Trait implementations` - - print_higher_ranked_params_with_space(&bare_fn.generic_params, cx).fmt(f)?; - bare_fn.safety.print_with_space().fmt(f)?; - print_abi_with_space(bare_fn.abi).fmt(f)?; - let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" }; - primitive_link_fragment( - f, - PrimitiveType::Tuple, - format_args!("fn({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), - "#trait-implementations-1", - cx, - )?; - // Write output. - if !bare_fn.decl.output.is_unit() { - write!(f, " -> ")?; - fmt_type(&bare_fn.decl.output, f, use_absolute, cx)?; - } - } else if let Some(ty) = self.kind.as_blanket_ty() { + 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)?; + self.print_type(&self.for_, f, use_absolute, cx)?; } print_where_clause(&self.generics, cx, 0, Ending::Newline).fmt(f) }) } + fn print_type<'a, 'tcx: 'a>( + &self, + type_: &clean::Type, + f: &mut fmt::Formatter<'_>, + use_absolute: bool, + cx: &'a Context<'tcx>, + ) -> Result<(), fmt::Error> { + if let clean::Type::Tuple(types) = type_ + && let [clean::Type::Generic(name)] = &types[..] + && (self.kind.is_fake_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_args!("({name}₁, {name}₂, …, {name}ₙ)"), + "#trait-implementations-1", + cx, + )?; + } else if let clean::Type::Array(ty, len) = type_ + && let clean::Type::Generic(name) = &**ty + && &len[..] == "1" + && (self.kind.is_fake_variadic() || self.kind.is_auto()) + { + primitive_link(f, PrimitiveType::Array, format_args!("[{name}; N]"), cx)?; + } else if let clean::BareFunction(bare_fn) = &type_ + && let [clean::Argument { type_: clean::Type::Generic(name), .. }] = + &bare_fn.decl.inputs.values[..] + && (self.kind.is_fake_variadic() || self.kind.is_auto()) + { + // Hardcoded anchor library/core/src/primitive_docs.rs + // Link should match `# Trait implementations` + + print_higher_ranked_params_with_space(&bare_fn.generic_params, cx).fmt(f)?; + bare_fn.safety.print_with_space().fmt(f)?; + print_abi_with_space(bare_fn.abi).fmt(f)?; + let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" }; + primitive_link_fragment( + f, + PrimitiveType::Tuple, + format_args!("fn({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), + "#trait-implementations-1", + cx, + )?; + // Write output. + if !bare_fn.decl.output.is_unit() { + write!(f, " -> ")?; + fmt_type(&bare_fn.decl.output, f, use_absolute, cx)?; + } + } else { + fmt_type(&type_, f, use_absolute, cx)?; + } + Ok(()) + } } impl clean::Arguments { diff --git a/tests/rustdoc/primitive-tuple-variadic.rs b/tests/rustdoc/primitive-tuple-variadic.rs index 4b2fb786a8950..b15e996f929a9 100644 --- a/tests/rustdoc/primitive-tuple-variadic.rs +++ b/tests/rustdoc/primitive-tuple-variadic.rs @@ -16,3 +16,20 @@ pub trait Bar {} //@ has - '//section[@id="impl-Bar-for-(U,)"]/h3' 'impl Bar for (U₁, U₂, …, Uₙ)' #[doc(fake_variadic)] impl Bar for (U,) {} + +pub trait Baz { fn baz(&self) -> T { todo!() } } + +//@ has foo/trait.Baz.html +//@ has - '//section[@id="impl-Baz%3C(T,)%3E-for-%5BT;+1%5D"]/h3' 'impl Baz<(T₁, T₂, …, Tₙ)> for [T; N]' +#[doc(fake_variadic)] +impl Baz<(T,)> for [T; 1] {} + +//@ has foo/trait.Baz.html +//@ has - '//section[@id="impl-Baz%3C%5BT;+1%5D%3E-for-(T,)"]/h3' 'impl Baz<[T; N]> for (T₁, T₂, …, Tₙ)' +#[doc(fake_variadic)] +impl Baz<[T; 1]> for (T,) {} + +//@ has foo/trait.Baz.html +//@ has - '//section[@id="impl-Baz%3CT%3E-for-(T,)"]/h3' 'impl Baz for (T₁, T₂, …, Tₙ)' +#[doc(fake_variadic)] +impl Baz for (T,) {}