From af45040acd86d8881fd3dfbfdb08525627d63347 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume.gomez@huawei.com> Date: Fri, 2 Dec 2022 19:36:20 +0100 Subject: [PATCH 1/3] Merge generics and where predicates and prevent duplicates in where predicates --- src/librustdoc/clean/mod.rs | 124 ++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 33 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 80909919ba2b6..2a2a9470d25c0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -12,7 +12,7 @@ pub(crate) mod utils; use rustc_ast as ast; use rustc_attr as attr; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -598,47 +598,105 @@ pub(crate) fn clean_generics<'tcx>( }) .collect::<Vec<_>>(); + let mut bound_predicates = FxIndexMap::default(); + let mut region_predicates = FxIndexMap::default(); + let mut eq_predicates = ThinVec::default(); + for pred in gens.predicates.iter().filter_map(|x| clean_where_predicate(x, cx)) { + match pred { + WherePredicate::BoundPredicate { ty, bounds, bound_params } => { + match bound_predicates.entry(ty) { + IndexEntry::Vacant(v) => { + v.insert((bounds, bound_params)); + } + IndexEntry::Occupied(mut o) => { + // we merge both bounds. + for bound in bounds { + if !o.get().0.contains(&bound) { + o.get_mut().0.push(bound); + } + } + for bound_param in bound_params { + if !o.get().1.contains(&bound_param) { + o.get_mut().1.push(bound_param); + } + } + } + } + } + WherePredicate::RegionPredicate { lifetime, bounds } => { + match region_predicates.entry(lifetime) { + IndexEntry::Vacant(v) => { + v.insert(bounds); + } + IndexEntry::Occupied(mut o) => { + // we merge both bounds. + for bound in bounds { + if !o.get().contains(&bound) { + o.get_mut().push(bound); + } + } + } + } + } + WherePredicate::EqPredicate { lhs, rhs, bound_params } => { + eq_predicates.push(WherePredicate::EqPredicate { lhs, rhs, bound_params }); + } + } + } + let mut params = ThinVec::with_capacity(gens.params.len()); + // In this loop, we gather the generic parameters (`<'a, B: 'a>`) and check if they have + // bounds in the where predicates. If so, we move their bounds into the where predicates + // while also preventing duplicates. for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) { - let p = clean_generic_param(cx, Some(gens), p); + let mut p = clean_generic_param(cx, Some(gens), p); + match &mut p.kind { + GenericParamDefKind::Lifetime { ref mut outlives } => { + if let Some(region_pred) = region_predicates.get_mut(&Lifetime(p.name)) { + // We merge bounds in the `where` clause. + for outlive in outlives.drain(..) { + let outlive = GenericBound::Outlives(outlive); + if !region_pred.contains(&outlive) { + region_pred.push(outlive); + } + } + } + } + GenericParamDefKind::Type { bounds, synthetic: false, .. } => { + if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(p.name)) { + // We merge bounds in the `where` clause. + for bound in bounds.drain(..) { + if !bound_pred.0.contains(&bound) { + bound_pred.0.push(bound); + } + } + } + } + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + // nothing to do here. + } + } params.push(p); } params.extend(impl_trait_params); - let mut generics = Generics { + Generics { params, - where_predicates: gens - .predicates - .iter() - .filter_map(|x| clean_where_predicate(x, cx)) + where_predicates: bound_predicates + .into_iter() + .map(|(ty, (bounds, bound_params))| WherePredicate::BoundPredicate { + ty, + bounds, + bound_params, + }) + .chain( + region_predicates + .into_iter() + .map(|(lifetime, bounds)| WherePredicate::RegionPredicate { lifetime, bounds }), + ) + .chain(eq_predicates.into_iter()) .collect(), - }; - - // Some duplicates are generated for ?Sized bounds between type params and where - // predicates. The point in here is to move the bounds definitions from type params - // to where predicates when such cases occur. - for where_pred in &mut generics.where_predicates { - match *where_pred { - WherePredicate::BoundPredicate { ty: Generic(ref name), ref mut bounds, .. } => { - if bounds.is_empty() { - for param in &mut generics.params { - match param.kind { - GenericParamDefKind::Lifetime { .. } => {} - GenericParamDefKind::Type { bounds: ref mut ty_bounds, .. } => { - if ¶m.name == name { - mem::swap(bounds, ty_bounds); - break; - } - } - GenericParamDefKind::Const { .. } => {} - } - } - } - } - _ => continue, - } } - generics } fn clean_ty_generics<'tcx>( From 93d7aa146d8a91c281c32cd050fceba8312ed9a6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume.gomez@huawei.com> Date: Fri, 2 Dec 2022 19:36:57 +0100 Subject: [PATCH 2/3] Add rustdoc test for bounds de-duplication and merge --- src/test/rustdoc/bounds-in-multiple-parts.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/rustdoc/bounds-in-multiple-parts.rs diff --git a/src/test/rustdoc/bounds-in-multiple-parts.rs b/src/test/rustdoc/bounds-in-multiple-parts.rs new file mode 100644 index 0000000000000..279e3c148887e --- /dev/null +++ b/src/test/rustdoc/bounds-in-multiple-parts.rs @@ -0,0 +1,20 @@ +#![crate_name = "foo"] + +pub trait Eq {} +pub trait Eq2 {} + +// Checking that "where predicates" and "generics params" are merged. +// @has 'foo/trait.T.html' +// @has - "//*[@id='tymethod.f']/h4" "fn f<'a, 'b, 'c, T>()where Self: Eq, T: Eq + 'a, 'c: 'b + 'a," +pub trait T { + fn f<'a, 'b, 'c: 'a, T: Eq + 'a>() + where Self: Eq, Self: Eq, T: Eq, 'c: 'b; +} + +// Checking that a duplicated "where predicate" is removed. +// @has 'foo/trait.T2.html' +// @has - "//*[@id='tymethod.f']/h4" "fn f<T>()where Self: Eq + Eq2, T: Eq2 + Eq," +pub trait T2 { + fn f<T: Eq>() + where Self: Eq, Self: Eq2, T: Eq2; +} From 269704a4a09502ae64f3916058b157d94e6cba99 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume.gomez@huawei.com> Date: Fri, 2 Dec 2022 19:37:04 +0100 Subject: [PATCH 3/3] Update existing rustdoc tests --- src/test/rustdoc/impl-parts.rs | 4 ++-- src/test/rustdoc/rfc-2632-const-trait-impl.rs | 2 +- src/test/rustdoc/whitespace-after-where-clause.enum.html | 2 +- src/test/rustdoc/whitespace-after-where-clause.struct.html | 2 +- src/test/rustdoc/whitespace-after-where-clause.union.html | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/rustdoc/impl-parts.rs b/src/test/rustdoc/impl-parts.rs index 7b931727e446d..90cbb77cb6b60 100644 --- a/src/test/rustdoc/impl-parts.rs +++ b/src/test/rustdoc/impl-parts.rs @@ -6,7 +6,7 @@ pub auto trait AnAutoTrait {} pub struct Foo<T> { field: T } // @has impl_parts/struct.Foo.html '//*[@class="impl has-srclink"]//h3[@class="code-header"]' \ -// "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync," +// "impl<T> !AnAutoTrait for Foo<T>where T: Sync + Clone," // @has impl_parts/trait.AnAutoTrait.html '//*[@id="implementors-list"]//h3[@class="code-header"]' \ -// "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync," +// "impl<T> !AnAutoTrait for Foo<T>where T: Sync + Clone," impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync {} diff --git a/src/test/rustdoc/rfc-2632-const-trait-impl.rs b/src/test/rustdoc/rfc-2632-const-trait-impl.rs index 602ee1b1b1fca..7ed9d6729b647 100644 --- a/src/test/rustdoc/rfc-2632-const-trait-impl.rs +++ b/src/test/rustdoc/rfc-2632-const-trait-impl.rs @@ -61,7 +61,7 @@ impl<T> S<T> { // @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"]' ': Clone' - pub const fn foo<B: ~const Clone + ~const Destruct>() + pub const fn foo<B, C: ~const Clone + ~const Destruct>() where B: ~const Clone + ~const Destruct, { diff --git a/src/test/rustdoc/whitespace-after-where-clause.enum.html b/src/test/rustdoc/whitespace-after-where-clause.enum.html index c74866f4a10b8..f7663e4616ae6 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.enum.html +++ b/src/test/rustdoc/whitespace-after-where-clause.enum.html @@ -1,4 +1,4 @@ -<div class="item-decl"><pre class="rust enum"><code>pub enum Cow<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>>,</span>{ +<div class="item-decl"><pre class="rust enum"><code>pub enum Cow<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{ Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B), Whatever(<a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>), }</code></pre></div> \ No newline at end of file diff --git a/src/test/rustdoc/whitespace-after-where-clause.struct.html b/src/test/rustdoc/whitespace-after-where-clause.struct.html index 1ba1367d20f72..fa3f224e7ad0f 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.struct.html +++ b/src/test/rustdoc/whitespace-after-where-clause.struct.html @@ -1,4 +1,4 @@ -<div class="item-decl"><pre class="rust struct"><code>pub struct Struct<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>>,</span>{ +<div class="item-decl"><pre class="rust struct"><code>pub struct Struct<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{ pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B, pub b: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>, }</code></pre></div> \ No newline at end of file diff --git a/src/test/rustdoc/whitespace-after-where-clause.union.html b/src/test/rustdoc/whitespace-after-where-clause.union.html index 0dfb6407d45f1..7bb177debc3a8 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.union.html +++ b/src/test/rustdoc/whitespace-after-where-clause.union.html @@ -1,3 +1,3 @@ -<div class="item-decl"><pre class="rust union"><code>pub union Union<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>>,</span>{ +<div class="item-decl"><pre class="rust union"><code>pub union Union<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{ /* private fields */ }</code></pre></div> \ No newline at end of file