From 5fbb1b2f4dd858f21da7ba56f9e5011b43a0af4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 15 Feb 2024 20:41:20 +0100 Subject: [PATCH] rustdoc: fix and refactor HTML rendering a bit --- src/librustdoc/html/format.rs | 454 +++++++----------- ...{bounds-in-multiple-parts.rs => bounds.rs} | 12 + .../const-generics/generic_const_exprs.rs | 2 +- 3 files changed, 186 insertions(+), 282 deletions(-) rename tests/rustdoc/{bounds-in-multiple-parts.rs => bounds.rs} (54%) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 4ba1665bdc9f1..bb68c84f529a7 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1,6 +1,6 @@ //! HTML formatting module //! -//! This module contains a large number of `fmt::Display` implementations for +//! This module contains a large number of `Display` implementations for //! various types in `rustdoc::clean`. //! //! These implementations all emit HTML. As an internal implementation detail, @@ -9,7 +9,7 @@ use std::borrow::Cow; use std::cell::Cell; -use std::fmt::{self, Write}; +use std::fmt::{self, Display, Write}; use std::iter::{self, once}; use rustc_ast as ast; @@ -150,16 +150,16 @@ impl Buffer { } } -pub(crate) fn comma_sep( +pub(crate) fn comma_sep( items: impl Iterator, space_after_comma: bool, -) -> impl fmt::Display { +) -> impl Display { display_fn(move |f| { for (i, item) in items.enumerate() { if i != 0 { write!(f, ",{}", if space_after_comma { " " } else { "" })?; } - fmt::Display::fmt(&item, f)?; + item.fmt(f)?; } Ok(()) }) @@ -168,7 +168,7 @@ pub(crate) fn comma_sep( pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( bounds: &'a [clean::GenericBound], cx: &'a Context<'tcx>, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { let mut bounds_dup = FxHashSet::default(); @@ -176,7 +176,7 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( if i > 0 { f.write_str(" + ")?; } - fmt::Display::fmt(&bound.print(cx), f)?; + bound.print(cx).fmt(f)?; } Ok(()) }) @@ -186,7 +186,7 @@ impl clean::GenericParamDef { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match &self.kind { clean::GenericParamDefKind::Lifetime { outlives } => { write!(f, "{}", self.name)?; @@ -207,35 +207,27 @@ impl clean::GenericParamDef { f.write_str(self.name.as_str())?; if !bounds.is_empty() { - if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; - } else { - write!(f, ": {}", print_generic_bounds(bounds, cx))?; - } + f.write_str(": ")?; + print_generic_bounds(bounds, cx).fmt(f)?; } if let Some(ref ty) = default { - if f.alternate() { - write!(f, " = {:#}", ty.print(cx))?; - } else { - write!(f, " = {}", ty.print(cx))?; - } + f.write_str(" = ")?; + ty.print(cx).fmt(f)?; } Ok(()) } clean::GenericParamDefKind::Const { ty, default, .. } => { - if f.alternate() { - write!(f, "const {}: {:#}", self.name, ty.print(cx))?; - } else { - write!(f, "const {}: {}", self.name, ty.print(cx))?; - } + write!(f, "const {}: ", self.name)?; + ty.print(cx).fmt(f)?; if let Some(default) = default { + f.write_str(" = ")?; if f.alternate() { - write!(f, " = {default:#}")?; + write!(f, "{default}")?; } else { - write!(f, " = {default}")?; + write!(f, "{}", Escape(default))?; } } @@ -249,7 +241,7 @@ impl clean::Generics { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); if real_params.peek().is_none() { @@ -279,63 +271,50 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( cx: &'a Context<'tcx>, indent: usize, ending: Ending, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { - let mut where_predicates = gens.where_predicates.iter().filter(|pred| { - !matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty()) - }).map(|pred| { - display_fn(move |f| { - if f.alternate() { - f.write_str(" ")?; - } else { - f.write_str("\n")?; - } - - match pred { - clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { - let ty_cx = ty.print(cx); - let generic_bounds = print_generic_bounds(bounds, cx); + let mut where_predicates = gens + .where_predicates + .iter() + .map(|pred| { + display_fn(move |f| { + if f.alternate() { + f.write_str(" ")?; + } else { + f.write_str("\n")?; + } - if bound_params.is_empty() { - if f.alternate() { - write!(f, "{ty_cx:#}: {generic_bounds:#}") - } else { - write!(f, "{ty_cx}: {generic_bounds}") + match pred { + clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { + print_higher_ranked_params_with_space(bound_params, cx).fmt(f)?; + ty.print(cx).fmt(f)?; + f.write_str(":")?; + if !bounds.is_empty() { + f.write_str(" ")?; + print_generic_bounds(bounds, cx).fmt(f)?; } - } else { + Ok(()) + } + clean::WherePredicate::RegionPredicate { lifetime, bounds } => { + // We don't need to check `alternate` since we can be certain that neither + // the lifetime nor the bounds contain any characters which need escaping. + write!(f, "{}:", lifetime.print())?; + if !bounds.is_empty() { + write!(f, " {}", print_generic_bounds(bounds, cx))?; + } + Ok(()) + } + clean::WherePredicate::EqPredicate { lhs, rhs } => { if f.alternate() { - write!( - f, - "for<{:#}> {ty_cx:#}: {generic_bounds:#}", - comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true) - ) + write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx)) } else { - write!( - f, - "for<{}> {ty_cx}: {generic_bounds}", - comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true) - ) + write!(f, "{} == {}", lhs.print(cx), rhs.print(cx)) } } } - clean::WherePredicate::RegionPredicate { lifetime, bounds } => { - let mut bounds_display = String::new(); - for bound in bounds.iter().map(|b| b.print(cx)) { - write!(bounds_display, "{bound} + ")?; - } - bounds_display.truncate(bounds_display.len() - " + ".len()); - write!(f, "{}: {bounds_display}", lifetime.print()) - } - clean::WherePredicate::EqPredicate { lhs, rhs } => { - if f.alternate() { - write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx)) - } else { - write!(f, "{} == {}", lhs.print(cx), rhs.print(cx)) - } - } - } + }) }) - }).peekable(); + .peekable(); if where_predicates.peek().is_none() { return Ok(()); @@ -392,13 +371,13 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( } impl clean::Lifetime { - pub(crate) fn print(&self) -> impl fmt::Display + '_ { + pub(crate) fn print(&self) -> impl Display + '_ { self.0.as_str() } } impl clean::Constant { - pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl fmt::Display + '_ { + pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display + '_ { let expr = self.expr(tcx); display_fn( move |f| { @@ -409,31 +388,10 @@ impl clean::Constant { } impl clean::PolyTrait { - fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { - if !self.generic_params.is_empty() { - if f.alternate() { - write!( - f, - "for<{:#}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true) - )?; - } else { - write!( - f, - "for<{}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true) - )?; - } - } - if f.alternate() { - write!(f, "{:#}", self.trait_.print(cx)) - } else { - write!(f, "{}", self.trait_.print(cx)) - } + print_higher_ranked_params_with_space(&self.generic_params, cx).fmt(f)?; + self.trait_.print(cx).fmt(f) }) } } @@ -442,32 +400,25 @@ impl clean::GenericBound { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), clean::GenericBound::TraitBound(ty, modifier) => { - let modifier_str = match modifier { + f.write_str(match modifier { hir::TraitBoundModifier::None => "", hir::TraitBoundModifier::Maybe => "?", hir::TraitBoundModifier::Negative => "!", // `const` and `~const` trait bounds are experimental; don't render them. hir::TraitBoundModifier::Const | hir::TraitBoundModifier::MaybeConst => "", - }; - if f.alternate() { - write!(f, "{modifier_str}{ty:#}", ty = ty.print(cx)) - } else { - write!(f, "{modifier_str}{ty}", ty = ty.print(cx)) - } + })?; + ty.print(cx).fmt(f) } }) } } impl clean::GenericArgs { - fn print<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { match self { clean::GenericArgs::AngleBracketed { args, bindings } => { @@ -515,11 +466,7 @@ impl clean::GenericArgs { f.write_str(", ")?; } comma = true; - if f.alternate() { - write!(f, "{:#}", ty.print(cx))?; - } else { - write!(f, "{}", ty.print(cx))?; - } + ty.print(cx).fmt(f)?; } f.write_str(")")?; if let Some(ref ty) = *output { @@ -973,31 +920,43 @@ fn primitive_link_fragment( None => {} } } - std::fmt::Display::fmt(&name, f)?; + Display::fmt(&name, f)?; if needs_termination { write!(f, "")?; } Ok(()) } -/// Helper to render type parameters fn tybounds<'a, 'tcx: 'a>( bounds: &'a [clean::PolyTrait], lt: &'a Option, cx: &'a Context<'tcx>, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { for (i, bound) in bounds.iter().enumerate() { if i > 0 { write!(f, " + ")?; } - - fmt::Display::fmt(&bound.print(cx), f)?; + bound.print(cx).fmt(f)?; } - if let Some(lt) = lt { - write!(f, " + ")?; - fmt::Display::fmt(<.print(), f)?; + // We don't need to check `alternate` since we can be certain that + // the lifetime doesn't contain any characters which need escaping. + write!(f, " + {}", lt.print())?; + } + Ok(()) + }) +} + +fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>( + params: &'a [clean::GenericParamDef], + cx: &'a Context<'tcx>, +) -> impl Display + 'a + Captures<'tcx> { + display_fn(move |f| { + if !params.is_empty() { + f.write_str(if f.alternate() { "for<" } else { "for<" })?; + comma_sep(params.iter().map(|lt| lt.print(cx)), true).fmt(f)?; + f.write_str(if f.alternate() { "> " } else { "> " })?; } Ok(()) }) @@ -1007,7 +966,7 @@ pub(crate) fn anchor<'a, 'cx: 'a>( did: DefId, text: Symbol, cx: &'cx Context<'_>, -) -> impl fmt::Display + 'a { +) -> impl Display + 'a { let parts = href(did, cx); display_fn(move |f| { if let Ok((url, short_ty, fqp)) = parts { @@ -1039,7 +998,7 @@ fn fmt_type<'cx>( } clean::DynTrait(ref bounds, ref lt) => { f.write_str("dyn ")?; - fmt::Display::fmt(&tybounds(bounds, lt, cx), f) + tybounds(bounds, lt, cx).fmt(f) } clean::Infer => write!(f, "_"), clean::Primitive(clean::PrimitiveType::Never) => { @@ -1049,80 +1008,62 @@ fn fmt_type<'cx>( primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx) } clean::BareFunction(ref decl) => { + print_higher_ranked_params_with_space(&decl.generic_params, cx).fmt(f)?; + decl.unsafety.print_with_space().fmt(f)?; + print_abi_with_space(decl.abi).fmt(f)?; if f.alternate() { - write!( - f, - "{:#}{}{:#}fn{:#}", - decl.print_hrtb_with_space(cx), - decl.unsafety.print_with_space(), - print_abi_with_space(decl.abi), - decl.decl.print(cx), - ) + f.write_str("fn")?; } else { - write!( - f, - "{}{}{}", - decl.print_hrtb_with_space(cx), - decl.unsafety.print_with_space(), - print_abi_with_space(decl.abi) - )?; primitive_link(f, PrimitiveType::Fn, format_args!("fn"), cx)?; - write!(f, "{}", decl.decl.print(cx)) } + decl.decl.print(cx).fmt(f) } - clean::Tuple(ref typs) => { - match &typs[..] { - &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx), - [one] => { - if let clean::Generic(name) = one { - primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx) - } else { - write!(f, "(")?; - // Carry `f.alternate()` into this display w/o branching manually. - fmt::Display::fmt(&one.print(cx), f)?; - write!(f, ",)") - } + clean::Tuple(ref typs) => match &typs[..] { + &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx), + [one] => { + if let clean::Generic(name) = one { + primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx) + } else { + write!(f, "(")?; + one.print(cx).fmt(f)?; + write!(f, ",)") } - many => { - let generic_names: Vec = many - .iter() - .filter_map(|t| match t { - clean::Generic(name) => Some(*name), - _ => None, - }) - .collect(); - let is_generic = generic_names.len() == many.len(); - if is_generic { - primitive_link( - f, - PrimitiveType::Tuple, - format_args!( - "({})", - generic_names.iter().map(|s| s.as_str()).join(", ") - ), - cx, - ) - } else { - write!(f, "(")?; - for (i, item) in many.iter().enumerate() { - if i != 0 { - write!(f, ", ")?; - } - // Carry `f.alternate()` into this display w/o branching manually. - fmt::Display::fmt(&item.print(cx), f)?; + } + many => { + let generic_names: Vec = many + .iter() + .filter_map(|t| match t { + clean::Generic(name) => Some(*name), + _ => None, + }) + .collect(); + let is_generic = generic_names.len() == many.len(); + if is_generic { + primitive_link( + f, + PrimitiveType::Tuple, + format_args!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")), + cx, + ) + } else { + write!(f, "(")?; + for (i, item) in many.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; } - write!(f, ")") + item.print(cx).fmt(f)?; } + write!(f, ")") } } - } + }, clean::Slice(ref t) => match **t { clean::Generic(name) => { primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx) } _ => { write!(f, "[")?; - fmt::Display::fmt(&t.print(cx), f)?; + t.print(cx).fmt(f)?; write!(f, "]") } }, @@ -1135,7 +1076,7 @@ fn fmt_type<'cx>( ), _ => { write!(f, "[")?; - fmt::Display::fmt(&t.print(cx), f)?; + t.print(cx).fmt(f)?; if f.alternate() { write!(f, "; {n}")?; } else { @@ -1175,7 +1116,7 @@ fn fmt_type<'cx>( } } else { primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?; - fmt::Display::fmt(&t.print(cx), f) + t.print(cx).fmt(f) } } clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => { @@ -1216,11 +1157,8 @@ fn fmt_type<'cx>( Ok(()) } clean::ImplTrait(ref bounds) => { - if f.alternate() { - write!(f, "impl {:#}", print_generic_bounds(bounds, cx)) - } else { - write!(f, "impl {}", print_generic_bounds(bounds, cx)) - } + f.write_str("impl ")?; + print_generic_bounds(bounds, cx).fmt(f) } clean::QPath(box clean::QPathData { ref assoc, @@ -1292,8 +1230,7 @@ fn fmt_type<'cx>( write!(f, "{}", assoc.name) }?; - // Carry `f.alternate()` into this display w/o branching manually. - fmt::Display::fmt(&assoc.args.print(cx), f) + assoc.args.print(cx).fmt(f) } } } @@ -1302,7 +1239,7 @@ impl clean::Type { pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + ) -> impl Display + 'b + Captures<'tcx> { display_fn(move |f| fmt_type(self, f, false, cx)) } } @@ -1311,7 +1248,7 @@ impl clean::Path { pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + ) -> impl Display + 'b + Captures<'tcx> { display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) } } @@ -1321,20 +1258,18 @@ impl clean::Impl { &'a self, use_absolute: bool, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { - if f.alternate() { - write!(f, "impl{:#} ", self.generics.print(cx))?; - } else { - write!(f, "impl{} ", self.generics.print(cx))?; - } + f.write_str("impl")?; + self.generics.print(cx).fmt(f)?; + f.write_str(" ")?; if let Some(ref ty) = self.trait_ { match self.polarity { ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {} ty::ImplPolarity::Negative => write!(f, "!")?, } - fmt::Display::fmt(&ty.print(cx), f)?; + ty.print(cx).fmt(f)?; write!(f, " for ")?; } @@ -1359,14 +1294,9 @@ impl clean::Impl { // Hardcoded anchor library/core/src/primitive_docs.rs // Link should match `# Trait implementations` - let hrtb = bare_fn.print_hrtb_with_space(cx); - let unsafety = bare_fn.unsafety.print_with_space(); - let abi = print_abi_with_space(bare_fn.abi); - if f.alternate() { - write!(f, "{hrtb:#}{unsafety}{abi:#}",)?; - } else { - write!(f, "{hrtb}{unsafety}{abi}",)?; - } + print_higher_ranked_params_with_space(&bare_fn.generic_params, cx).fmt(f)?; + bare_fn.unsafety.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, @@ -1386,8 +1316,7 @@ impl clean::Impl { fmt_type(&self.for_, f, use_absolute, cx)?; } - fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, Ending::Newline), f)?; - Ok(()) + print_where_clause(&self.generics, cx, 0, Ending::Newline).fmt(f) }) } } @@ -1396,16 +1325,11 @@ impl clean::Arguments { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { for (i, input) in self.values.iter().enumerate() { write!(f, "{}: ", input.name)?; - - if f.alternate() { - write!(f, "{:#}", input.type_.print(cx))?; - } else { - write!(f, "{}", input.type_.print(cx))?; - } + input.type_.print(cx).fmt(f)?; if i + 1 < self.values.len() { write!(f, ", ")?; } @@ -1415,25 +1339,6 @@ impl clean::Arguments { } } -impl clean::BareFunctionDecl { - fn print_hrtb_with_space<'a, 'tcx: 'a>( - &'a self, - cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { - display_fn(move |f| { - if !self.generic_params.is_empty() { - write!( - f, - "for<{}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true) - ) - } else { - Ok(()) - } - }) - } -} - // Implements Write but only counts the bytes "written". struct WriteCounter(usize); @@ -1447,7 +1352,7 @@ impl std::fmt::Write for WriteCounter { // Implements Display by emitting the given number of spaces. struct Indent(usize); -impl fmt::Display for Indent { +impl Display for Indent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (0..self.0).for_each(|_| { f.write_char(' ').unwrap(); @@ -1460,7 +1365,7 @@ impl clean::FnDecl { pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + ) -> impl Display + 'b + Captures<'tcx> { display_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; if f.alternate() { @@ -1494,7 +1399,7 @@ impl clean::FnDecl { header_len: usize, indent: usize, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { // First, generate the text form of the declaration, with no line wrapping, and count the bytes. let mut counter = WriteCounter(0); @@ -1554,7 +1459,7 @@ impl clean::FnDecl { } clean::SelfExplicit(ref typ) => { write!(f, "self: ")?; - fmt::Display::fmt(&typ.print(cx), f)?; + typ.print(cx).fmt(f)?; } } } else { @@ -1562,7 +1467,7 @@ impl clean::FnDecl { write!(f, "const ")?; } write!(f, "{}: ", input.name)?; - fmt::Display::fmt(&input.type_.print(cx), f)?; + input.type_.print(cx).fmt(f)?; } } @@ -1578,14 +1483,13 @@ impl clean::FnDecl { Some(n) => write!(f, "\n{})", Indent(n))?, }; - fmt::Display::fmt(&self.print_output(cx), f)?; - Ok(()) + self.print_output(cx).fmt(f) } fn print_output<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match &self.output { clean::Tuple(tys) if tys.is_empty() => Ok(()), ty if f.alternate() => { @@ -1600,7 +1504,7 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>( visibility: Option>, item_did: ItemId, cx: &'a Context<'tcx>, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl Display + 'a + Captures<'tcx> { use std::fmt::Write as _; let to_print: Cow<'static, str> = match visibility { @@ -1648,7 +1552,7 @@ pub(crate) fn visibility_to_src_with_space<'a, 'tcx: 'a>( visibility: Option>, tcx: TyCtxt<'tcx>, item_did: DefId, -) -> impl fmt::Display + 'a + Captures<'tcx> { +) -> impl Display + 'a + Captures<'tcx> { let to_print: Cow<'static, str> = match visibility { None => "".into(), Some(ty::Visibility::Public) => "pub ".into(), @@ -1727,7 +1631,7 @@ impl clean::Import { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { @@ -1751,7 +1655,7 @@ impl clean::ImportSource { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self.did { Some(did) => resolved_path(f, did, &self.path, true, false, cx), _ => { @@ -1779,29 +1683,19 @@ impl clean::TypeBinding { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { f.write_str(self.assoc.name.as_str())?; - if f.alternate() { - write!(f, "{:#}", self.assoc.args.print(cx))?; - } else { - write!(f, "{}", self.assoc.args.print(cx))?; - } + self.assoc.args.print(cx).fmt(f)?; match self.kind { clean::TypeBindingKind::Equality { ref term } => { - if f.alternate() { - write!(f, " = {:#}", term.print(cx))?; - } else { - write!(f, " = {}", term.print(cx))?; - } + f.write_str(" = ")?; + term.print(cx).fmt(f)?; } clean::TypeBindingKind::Constraint { ref bounds } => { if !bounds.is_empty() { - if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; - } else { - write!(f, ": {}", print_generic_bounds(bounds, cx))?; - } + f.write_str(": ")?; + print_generic_bounds(bounds, cx).fmt(f)?; } } } @@ -1810,7 +1704,7 @@ impl clean::TypeBinding { } } -pub(crate) fn print_abi_with_space(abi: Abi) -> impl fmt::Display { +pub(crate) fn print_abi_with_space(abi: Abi) -> impl Display { display_fn(move |f| { let quot = if f.alternate() { "\"" } else { """ }; match abi { @@ -1828,34 +1722,32 @@ impl clean::GenericArg { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self { - clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(<.print(), f), - clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f), - clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f), - clean::GenericArg::Infer => fmt::Display::fmt("_", f), + clean::GenericArg::Lifetime(lt) => lt.print().fmt(f), + clean::GenericArg::Type(ty) => ty.print(cx).fmt(f), + clean::GenericArg::Const(ct) => ct.print(cx.tcx()).fmt(f), + clean::GenericArg::Infer => Display::fmt("_", f), }) } } -impl clean::types::Term { +impl clean::Term { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self { - clean::types::Term::Type(ty) => fmt::Display::fmt(&ty.print(cx), f), - clean::types::Term::Constant(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f), + clean::Term::Type(ty) => ty.print(cx).fmt(f), + clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f), }) } } -pub(crate) fn display_fn( - f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, -) -> impl fmt::Display { +pub(crate) fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl Display { struct WithFormatter(Cell>); - impl fmt::Display for WithFormatter + impl Display for WithFormatter where F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, { diff --git a/tests/rustdoc/bounds-in-multiple-parts.rs b/tests/rustdoc/bounds.rs similarity index 54% rename from tests/rustdoc/bounds-in-multiple-parts.rs rename to tests/rustdoc/bounds.rs index 279e3c148887e..da09e3f2a528f 100644 --- a/tests/rustdoc/bounds-in-multiple-parts.rs +++ b/tests/rustdoc/bounds.rs @@ -18,3 +18,15 @@ pub trait T2 { fn f() where Self: Eq, Self: Eq2, T: Eq2; } + +// Checking that we support empty bounds (we used to crash on empty outlives-bounds). +// Note that we don't want to hide them since they have a semantic effect. +// For outlives-bounds, they force the lifetime param to be early-bound instead of late-bound. +// For trait bounds, it can affect well-formedness (see `ClauseKind::WellFormed`). +// @has 'foo/fn.empty.html' +// @has - '//pre[@class="rust item-decl"]' "empty<'a, T>()where T:, 'a:," +pub fn empty<'a, T>() + where + T:, + 'a:, +{} diff --git a/tests/rustdoc/const-generics/generic_const_exprs.rs b/tests/rustdoc/const-generics/generic_const_exprs.rs index e23b3006da6cd..2d2d31d7231a7 100644 --- a/tests/rustdoc/const-generics/generic_const_exprs.rs +++ b/tests/rustdoc/const-generics/generic_const_exprs.rs @@ -3,5 +3,5 @@ #![allow(incomplete_features)] // make sure that `ConstEvaluatable` predicates dont cause rustdoc to ICE #77647 // @has foo/struct.Ice.html '//pre[@class="rust item-decl"]' \ -// 'pub struct Ice;' +// 'pub struct Ice where [(); { _ }]:;' pub struct Ice where [(); N + 1]:;