diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 78965712dfa18..a2f48c164652d 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -143,11 +143,14 @@ impl Buffer {
}
}
-fn comma_sep(items: impl Iterator- ) -> impl fmt::Display {
+fn comma_sep(
+ items: impl Iterator
- ,
+ space_after_comma: bool,
+) -> impl fmt::Display {
display_fn(move |f| {
for (i, item) in items.enumerate() {
if i != 0 {
- write!(f, ", ")?;
+ write!(f, ",{}", if space_after_comma { " " } else { "" })?;
}
fmt::Display::fmt(&item, f)?;
}
@@ -248,9 +251,9 @@ impl clean::Generics {
}
if f.alternate() {
- write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx))))
+ write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx)), true))
} else {
- write!(f, "<{}>", comma_sep(real_params.map(|g| g.print(cx))))
+ write!(f, "<{}>", comma_sep(real_params.map(|g| g.print(cx)), true))
}
})
}
@@ -266,10 +269,80 @@ crate fn print_where_clause<'a, 'tcx: 'a>(
end_newline: bool,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
- if gens.where_predicates.is_empty() {
+ 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("
")?;
+ }
+
+ match pred {
+ clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
+ let bounds = bounds;
+ let for_prefix = if bound_params.is_empty() {
+ String::new()
+ } else if f.alternate() {
+ format!(
+ "for<{:#}> ",
+ comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+ )
+ } else {
+ format!(
+ "for<{}> ",
+ comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+ )
+ };
+
+ if f.alternate() {
+ write!(
+ f,
+ "{}{:#}: {:#}",
+ for_prefix,
+ ty.print(cx),
+ print_generic_bounds(bounds, cx)
+ )
+ } else {
+ write!(
+ f,
+ "{}{}: {}",
+ for_prefix,
+ ty.print(cx),
+ print_generic_bounds(bounds, cx)
+ )
+ }
+ }
+ clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
+ write!(
+ f,
+ "{}: {}",
+ lifetime.print(),
+ bounds
+ .iter()
+ .map(|b| b.print(cx).to_string())
+ .collect::>()
+ .join(" + ")
+ )
+ }
+ 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();
+
+ if where_predicates.peek().is_none() {
return Ok(());
}
+
let mut clause = String::new();
+
if f.alternate() {
clause.push_str(" where");
} else {
@@ -279,72 +352,11 @@ crate fn print_where_clause<'a, 'tcx: 'a>(
clause.push_str(" where");
}
}
- for (i, pred) in gens.where_predicates.iter().enumerate() {
- if f.alternate() {
- clause.push(' ');
- } else {
- clause.push_str("
");
- }
-
- match pred {
- clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
- let bounds = bounds;
- let for_prefix = match bound_params.len() {
- 0 => String::new(),
- _ if f.alternate() => {
- format!(
- "for<{:#}> ",
- comma_sep(bound_params.iter().map(|lt| lt.print()))
- )
- }
- _ => format!(
- "for<{}> ",
- comma_sep(bound_params.iter().map(|lt| lt.print()))
- ),
- };
-
- if f.alternate() {
- clause.push_str(&format!(
- "{}{:#}: {:#}",
- for_prefix,
- ty.print(cx),
- print_generic_bounds(bounds, cx)
- ));
- } else {
- clause.push_str(&format!(
- "{}{}: {}",
- for_prefix,
- ty.print(cx),
- print_generic_bounds(bounds, cx)
- ));
- }
- }
- clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
- clause.push_str(&format!(
- "{}: {}",
- lifetime.print(),
- bounds
- .iter()
- .map(|b| b.print(cx).to_string())
- .collect::>()
- .join(" + ")
- ));
- }
- clean::WherePredicate::EqPredicate { lhs, rhs } => {
- if f.alternate() {
- clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),));
- } else {
- clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),));
- }
- }
- }
- if i < gens.where_predicates.len() - 1 || end_newline {
- clause.push(',');
- }
- }
+ clause.push_str(&comma_sep(where_predicates, false).to_string());
if end_newline {
+ clause.push(',');
// add a space so stripping
tags and breaking spaces still renders properly
if f.alternate() {
clause.push(' ');
@@ -394,13 +406,13 @@ impl clean::PolyTrait {
write!(
f,
"for<{:#}> ",
- comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
+ 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)))
+ comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
)?;
}
}
@@ -424,7 +436,8 @@ impl clean::GenericBound {
let modifier_str = match modifier {
hir::TraitBoundModifier::None => "",
hir::TraitBoundModifier::Maybe => "?",
- hir::TraitBoundModifier::MaybeConst => "~const",
+ // ~const is experimental; do not display those bounds in rustdoc
+ hir::TraitBoundModifier::MaybeConst => "",
};
if f.alternate() {
write!(f, "{}{:#}", modifier_str, ty.print(cx))
@@ -1111,7 +1124,7 @@ impl clean::BareFunctionDecl {
write!(
f,
"for<{}> ",
- comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
+ comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
)
} else {
Ok(())
diff --git a/src/test/rustdoc/const-generics/generic_const_exprs.rs b/src/test/rustdoc/const-generics/generic_const_exprs.rs
index 35036a89360e2..215ee228eb857 100644
--- a/src/test/rustdoc/const-generics/generic_const_exprs.rs
+++ b/src/test/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 struct"]' \
-// 'pub struct Ice where [(); N + 1]: ;'
+// 'pub struct Ice;'
pub struct Ice where [(); N + 1]:;
diff --git a/src/test/rustdoc/rfc-2632-const-trait-impl.rs b/src/test/rustdoc/rfc-2632-const-trait-impl.rs
new file mode 100644
index 0000000000000..2adf69f6514cd
--- /dev/null
+++ b/src/test/rustdoc/rfc-2632-const-trait-impl.rs
@@ -0,0 +1,60 @@
+// Test that we do not currently display `~const` in rustdoc
+// as that syntax is currently provisional; `~const Drop` has
+// no effect on stable code so it should be hidden as well.
+//
+// To future blessers: make sure that `const_trait_impl` is
+// stabilized when changing `@!has` to `@has`, and please do
+// not remove this test.
+#![feature(const_trait_impl)]
+#![crate_name = "foo"]
+
+pub struct S(T);
+
+// @!has foo/trait.Tr.html '//pre[@class="rust trait"]/code/a[@class="trait"]' '~const'
+// @!has - '//pre[@class="rust trait"]/code/a[@class="trait"]' 'Drop'
+// @has - '//pre[@class="rust trait"]/code/a[@class="trait"]' 'Clone'
+// @!has - '//pre[@class="rust trait"]/code/span[@class="where"]' '~const'
+// @!has - '//pre[@class="rust trait"]/code/span[@class="where"]' 'Drop'
+// @has - '//pre[@class="rust trait"]/code/span[@class="where"]' ': Clone'
+pub trait Tr {
+ // @!has - '//div[@id="method.a"]/h4[@class="code-header"]' '~const'
+ // @!has - '//div[@id="method.a"]/h4[@class="code-header"]/a[@class="trait"]' 'Drop'
+ // @has - '//div[@id="method.a"]/h4[@class="code-header"]/a[@class="trait"]' 'Clone'
+ // @!has - '//div[@id="method.a"]/h4[@class="code-header"]/span[@class="where"]' '~const'
+ // @!has - '//div[@id="method.a"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' 'Drop'
+ // @has - '//div[@id="method.a"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' ': Clone'
+ #[default_method_body_is_const]
+ fn a() where Option: ~const Drop + ~const Clone {}
+}
+
+// @!has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]' '~const'
+// @!has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/a[@class="trait"]' 'Drop'
+// @has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/a[@class="trait"]' 'Clone'
+// @!has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/span[@class="where"]' '~const'
+// @!has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/span[@class="where fmt-newline"]' 'Drop'
+// @has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/span[@class="where fmt-newline"]' ': Clone'
+impl const Tr for T where Option: ~const Drop + ~const Clone {
+ fn a() where Option: ~const Drop + ~const Clone {}
+}
+
+// @!has foo/fn.foo.html '//pre[@class="rust fn"]/code/a[@class="trait"]' '~const'
+// @!has - '//pre[@class="rust fn"]/code/a[@class="trait"]' 'Drop'
+// @has - '//pre[@class="rust fn"]/code/a[@class="trait"]' 'Clone'
+// @!has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' '~const'
+// @!has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' 'Drop'
+// @has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' ': Clone'
+pub const fn foo() where Option: ~const Drop + ~const Clone {
+ F::a()
+}
+
+impl S {
+ // @!has foo/struct.S.html '//section[@id="method.foo"]/h4[@class="code-header"]' '~const'
+ // @!has - '//section[@id="method.foo"]/h4[@class="code-header"]/a[@class="trait"]' 'Drop'
+ // @has - '//section[@id="method.foo"]/h4[@class="code-header"]/a[@class="trait"]' 'Clone'
+ // @!has - '//section[@id="method.foo"]/h4[@class="code-header"]/span[@class="where"]' '~const'
+ // @!has - '//section[@id="method.foo"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' 'Drop'
+ // @has - '//section[@id="method.foo"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' ': Clone'
+ pub const fn foo() where B: ~const Drop + ~const Clone {
+ B::a()
+ }
+}