Skip to content

Commit faf1a7f

Browse files
authored
Rollup merge of #93412 - fee1-dead:improve-rustdoc-const-bounds, r=GuillaumeGomez
Improve rustdoc const bounds - Rustdoc no longer displays `~const` in trait bounds, because it currently means nothing for stable users, and because we still haven't decided on the final syntax yet. - Rustdoc will hide trait bounds where the trait is `Drop` AND it is `~const`, i.e. `~const Drop` bounds because it has no effect on stable users as well. - Because of additional logic that hides the whole `where` statement where it consists of `~const Drop` bounds (so it doesn't display `struct Foo<T>() where ;` like that), bounds that have no trait e.g. `where [T; N+1]: ;` are also hidden. Cherry-picked from #92433.
2 parents ad0d1d7 + 484936b commit faf1a7f

File tree

3 files changed

+146
-73
lines changed

3 files changed

+146
-73
lines changed

src/librustdoc/html/format.rs

+85-72
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,14 @@ impl Buffer {
143143
}
144144
}
145145

146-
fn comma_sep<T: fmt::Display>(items: impl Iterator<Item = T>) -> impl fmt::Display {
146+
fn comma_sep<T: fmt::Display>(
147+
items: impl Iterator<Item = T>,
148+
space_after_comma: bool,
149+
) -> impl fmt::Display {
147150
display_fn(move |f| {
148151
for (i, item) in items.enumerate() {
149152
if i != 0 {
150-
write!(f, ", ")?;
153+
write!(f, ",{}", if space_after_comma { " " } else { "" })?;
151154
}
152155
fmt::Display::fmt(&item, f)?;
153156
}
@@ -248,9 +251,9 @@ impl clean::Generics {
248251
}
249252

250253
if f.alternate() {
251-
write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx))))
254+
write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx)), true))
252255
} else {
253-
write!(f, "&lt;{}&gt;", comma_sep(real_params.map(|g| g.print(cx))))
256+
write!(f, "&lt;{}&gt;", comma_sep(real_params.map(|g| g.print(cx)), true))
254257
}
255258
})
256259
}
@@ -266,10 +269,80 @@ crate fn print_where_clause<'a, 'tcx: 'a>(
266269
end_newline: bool,
267270
) -> impl fmt::Display + 'a + Captures<'tcx> {
268271
display_fn(move |f| {
269-
if gens.where_predicates.is_empty() {
272+
let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
273+
!matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
274+
}).map(|pred| {
275+
display_fn(move |f| {
276+
if f.alternate() {
277+
f.write_str(" ")?;
278+
} else {
279+
f.write_str("<br>")?;
280+
}
281+
282+
match pred {
283+
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
284+
let bounds = bounds;
285+
let for_prefix = if bound_params.is_empty() {
286+
String::new()
287+
} else if f.alternate() {
288+
format!(
289+
"for&lt;{:#}&gt; ",
290+
comma_sep(bound_params.iter().map(|lt| lt.print()), true)
291+
)
292+
} else {
293+
format!(
294+
"for&lt;{}&gt; ",
295+
comma_sep(bound_params.iter().map(|lt| lt.print()), true)
296+
)
297+
};
298+
299+
if f.alternate() {
300+
write!(
301+
f,
302+
"{}{:#}: {:#}",
303+
for_prefix,
304+
ty.print(cx),
305+
print_generic_bounds(bounds, cx)
306+
)
307+
} else {
308+
write!(
309+
f,
310+
"{}{}: {}",
311+
for_prefix,
312+
ty.print(cx),
313+
print_generic_bounds(bounds, cx)
314+
)
315+
}
316+
}
317+
clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
318+
write!(
319+
f,
320+
"{}: {}",
321+
lifetime.print(),
322+
bounds
323+
.iter()
324+
.map(|b| b.print(cx).to_string())
325+
.collect::<Vec<_>>()
326+
.join(" + ")
327+
)
328+
}
329+
clean::WherePredicate::EqPredicate { lhs, rhs } => {
330+
if f.alternate() {
331+
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx),)
332+
} else {
333+
write!(f, "{} == {}", lhs.print(cx), rhs.print(cx),)
334+
}
335+
}
336+
}
337+
})
338+
}).peekable();
339+
340+
if where_predicates.peek().is_none() {
270341
return Ok(());
271342
}
343+
272344
let mut clause = String::new();
345+
273346
if f.alternate() {
274347
clause.push_str(" where");
275348
} else {
@@ -279,72 +352,11 @@ crate fn print_where_clause<'a, 'tcx: 'a>(
279352
clause.push_str(" <span class=\"where\">where");
280353
}
281354
}
282-
for (i, pred) in gens.where_predicates.iter().enumerate() {
283-
if f.alternate() {
284-
clause.push(' ');
285-
} else {
286-
clause.push_str("<br>");
287-
}
288-
289-
match pred {
290-
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
291-
let bounds = bounds;
292-
let for_prefix = match bound_params.len() {
293-
0 => String::new(),
294-
_ if f.alternate() => {
295-
format!(
296-
"for&lt;{:#}&gt; ",
297-
comma_sep(bound_params.iter().map(|lt| lt.print()))
298-
)
299-
}
300-
_ => format!(
301-
"for&lt;{}&gt; ",
302-
comma_sep(bound_params.iter().map(|lt| lt.print()))
303-
),
304-
};
305-
306-
if f.alternate() {
307-
clause.push_str(&format!(
308-
"{}{:#}: {:#}",
309-
for_prefix,
310-
ty.print(cx),
311-
print_generic_bounds(bounds, cx)
312-
));
313-
} else {
314-
clause.push_str(&format!(
315-
"{}{}: {}",
316-
for_prefix,
317-
ty.print(cx),
318-
print_generic_bounds(bounds, cx)
319-
));
320-
}
321-
}
322-
clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
323-
clause.push_str(&format!(
324-
"{}: {}",
325-
lifetime.print(),
326-
bounds
327-
.iter()
328-
.map(|b| b.print(cx).to_string())
329-
.collect::<Vec<_>>()
330-
.join(" + ")
331-
));
332-
}
333-
clean::WherePredicate::EqPredicate { lhs, rhs } => {
334-
if f.alternate() {
335-
clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),));
336-
} else {
337-
clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),));
338-
}
339-
}
340-
}
341355

342-
if i < gens.where_predicates.len() - 1 || end_newline {
343-
clause.push(',');
344-
}
345-
}
356+
clause.push_str(&comma_sep(where_predicates, false).to_string());
346357

347358
if end_newline {
359+
clause.push(',');
348360
// add a space so stripping <br> tags and breaking spaces still renders properly
349361
if f.alternate() {
350362
clause.push(' ');
@@ -394,13 +406,13 @@ impl clean::PolyTrait {
394406
write!(
395407
f,
396408
"for<{:#}> ",
397-
comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
409+
comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
398410
)?;
399411
} else {
400412
write!(
401413
f,
402414
"for&lt;{}&gt; ",
403-
comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
415+
comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
404416
)?;
405417
}
406418
}
@@ -424,7 +436,8 @@ impl clean::GenericBound {
424436
let modifier_str = match modifier {
425437
hir::TraitBoundModifier::None => "",
426438
hir::TraitBoundModifier::Maybe => "?",
427-
hir::TraitBoundModifier::MaybeConst => "~const",
439+
// ~const is experimental; do not display those bounds in rustdoc
440+
hir::TraitBoundModifier::MaybeConst => "",
428441
};
429442
if f.alternate() {
430443
write!(f, "{}{:#}", modifier_str, ty.print(cx))
@@ -1111,7 +1124,7 @@ impl clean::BareFunctionDecl {
11111124
write!(
11121125
f,
11131126
"for&lt;{}&gt; ",
1114-
comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
1127+
comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
11151128
)
11161129
} else {
11171130
Ok(())

src/test/rustdoc/const-generics/generic_const_exprs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
#![allow(incomplete_features)]
44
// make sure that `ConstEvaluatable` predicates dont cause rustdoc to ICE #77647
55
// @has foo/struct.Ice.html '//pre[@class="rust struct"]' \
6-
// 'pub struct Ice<const N: usize> where [(); N + 1]: ;'
6+
// 'pub struct Ice<const N: usize>;'
77
pub struct Ice<const N: usize> where [(); N + 1]:;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Test that we do not currently display `~const` in rustdoc
2+
// as that syntax is currently provisional; `~const Drop` has
3+
// no effect on stable code so it should be hidden as well.
4+
//
5+
// To future blessers: make sure that `const_trait_impl` is
6+
// stabilized when changing `@!has` to `@has`, and please do
7+
// not remove this test.
8+
#![feature(const_trait_impl)]
9+
#![crate_name = "foo"]
10+
11+
pub struct S<T>(T);
12+
13+
// @!has foo/trait.Tr.html '//pre[@class="rust trait"]/code/a[@class="trait"]' '~const'
14+
// @!has - '//pre[@class="rust trait"]/code/a[@class="trait"]' 'Drop'
15+
// @has - '//pre[@class="rust trait"]/code/a[@class="trait"]' 'Clone'
16+
// @!has - '//pre[@class="rust trait"]/code/span[@class="where"]' '~const'
17+
// @!has - '//pre[@class="rust trait"]/code/span[@class="where"]' 'Drop'
18+
// @has - '//pre[@class="rust trait"]/code/span[@class="where"]' ': Clone'
19+
pub trait Tr<T> {
20+
// @!has - '//div[@id="method.a"]/h4[@class="code-header"]' '~const'
21+
// @!has - '//div[@id="method.a"]/h4[@class="code-header"]/a[@class="trait"]' 'Drop'
22+
// @has - '//div[@id="method.a"]/h4[@class="code-header"]/a[@class="trait"]' 'Clone'
23+
// @!has - '//div[@id="method.a"]/h4[@class="code-header"]/span[@class="where"]' '~const'
24+
// @!has - '//div[@id="method.a"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' 'Drop'
25+
// @has - '//div[@id="method.a"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' ': Clone'
26+
#[default_method_body_is_const]
27+
fn a<A: ~const Drop + ~const Clone>() where Option<A>: ~const Drop + ~const Clone {}
28+
}
29+
30+
// @!has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]' '~const'
31+
// @!has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/a[@class="trait"]' 'Drop'
32+
// @has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/a[@class="trait"]' 'Clone'
33+
// @!has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/span[@class="where"]' '~const'
34+
// @!has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/span[@class="where fmt-newline"]' 'Drop'
35+
// @has - '//section[@id="impl-Tr%3CT%3E"]/h3[@class="code-header in-band"]/span[@class="where fmt-newline"]' ': Clone'
36+
impl<T: ~const Drop + ~const Clone> const Tr<T> for T where Option<T>: ~const Drop + ~const Clone {
37+
fn a<A: ~const Drop + ~const Clone>() where Option<A>: ~const Drop + ~const Clone {}
38+
}
39+
40+
// @!has foo/fn.foo.html '//pre[@class="rust fn"]/code/a[@class="trait"]' '~const'
41+
// @!has - '//pre[@class="rust fn"]/code/a[@class="trait"]' 'Drop'
42+
// @has - '//pre[@class="rust fn"]/code/a[@class="trait"]' 'Clone'
43+
// @!has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' '~const'
44+
// @!has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' 'Drop'
45+
// @has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' ': Clone'
46+
pub const fn foo<F: ~const Drop + ~const Clone>() where Option<F>: ~const Drop + ~const Clone {
47+
F::a()
48+
}
49+
50+
impl<T> S<T> {
51+
// @!has foo/struct.S.html '//section[@id="method.foo"]/h4[@class="code-header"]' '~const'
52+
// @!has - '//section[@id="method.foo"]/h4[@class="code-header"]/a[@class="trait"]' 'Drop'
53+
// @has - '//section[@id="method.foo"]/h4[@class="code-header"]/a[@class="trait"]' 'Clone'
54+
// @!has - '//section[@id="method.foo"]/h4[@class="code-header"]/span[@class="where"]' '~const'
55+
// @!has - '//section[@id="method.foo"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' 'Drop'
56+
// @has - '//section[@id="method.foo"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' ': Clone'
57+
pub const fn foo<B: ~const Drop + ~const Clone>() where B: ~const Drop + ~const Clone {
58+
B::a()
59+
}
60+
}

0 commit comments

Comments
 (0)