From 9beff38382a88ceafcb6e83636535c07eacad345 Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Mon, 8 Jul 2019 17:59:26 +0900 Subject: [PATCH] Associated type bound for inlined impl Trait doc --- src/librustdoc/clean/mod.rs | 93 +++++++++++++----- src/librustdoc/clean/simplify.rs | 96 ++++++++++--------- .../inline_cross/auxiliary/impl_trait_aux.rs | 4 + src/test/rustdoc/inline_cross/impl_trait.rs | 6 ++ 4 files changed, 131 insertions(+), 68 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d3065f16793..bde1826c7fd 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1698,7 +1698,7 @@ fn clean(&self, cx: &DocContext<'_>) -> Generics { // Don't populate `cx.impl_trait_bounds` before `clean`ning `where` clauses, // since `Clean for ty::Predicate` would consume them. - let mut impl_trait = FxHashMap::>::default(); + let mut impl_trait = FxHashMap::>::default(); // Bounds in the type_params and lifetimes fields are repeated in the // predicates field (see rustc_typeck::collect::ty_generics), so remove @@ -1720,41 +1720,73 @@ fn clean(&self, cx: &DocContext<'_>) -> Generics { ty::GenericParamDefKind::Const { .. } => None, }).collect::>(); + // (param index, def id of trait) -> (name, type) + let mut impl_trait_proj = FxHashMap::<(u32, DefId), Vec<(String, Type)>>::default(); + let mut where_predicates = preds.predicates.iter() .flat_map(|(p, _)| { - let param_idx = if let Some(trait_ref) = p.to_opt_poly_trait_ref() { - if let ty::Param(param) = trait_ref.self_ty().sty { - Some(param.index) - } else { - None - } - } else if let Some(outlives) = p.to_opt_type_outlives() { - if let ty::Param(param) = outlives.skip_binder().0.sty { - Some(param.index) - } else { - None + let param_idx = (|| { + if let Some(trait_ref) = p.to_opt_poly_trait_ref() { + if let ty::Param(param) = trait_ref.self_ty().sty { + return Some(param.index); + } + } else if let Some(outlives) = p.to_opt_type_outlives() { + if let ty::Param(param) = outlives.skip_binder().0.sty { + return Some(param.index); + } + } else if let ty::Predicate::Projection(proj) = p { + if let ty::Param(param) = proj.skip_binder().projection_ty.self_ty().sty { + return Some(param.index); + } } - } else { + None - }; + })(); let p = p.clean(cx)?; - if let Some(b) = param_idx.and_then(|i| impl_trait.get_mut(&i.into())) { - b.extend( - p.get_bounds() - .into_iter() - .flatten() - .cloned() - .filter(|b| !b.is_sized_bound(cx)) - ); - return None; + if let Some(param_idx) = param_idx { + if let Some(b) = impl_trait.get_mut(¶m_idx.into()) { + b.extend( + p.get_bounds() + .into_iter() + .flatten() + .cloned() + .filter(|b| !b.is_sized_bound(cx)) + ); + + let proj = match &p { + WherePredicate::EqPredicate { lhs, rhs } => Some((lhs, rhs)) + .and_then(|(lhs, rhs)| Some((lhs.projection()?, rhs))), + _ => None, + }; + if let Some(((_, trait_did, name), rhs)) = proj { + impl_trait_proj + .entry((param_idx, trait_did)) + .or_default() + .push((name.to_string(), rhs.clone())); + } + + return None; + } } Some(p) }) .collect::>(); + for ((param_idx, trait_did), bounds) in impl_trait_proj { + for (name, rhs) in bounds { + simplify::merge_bounds( + cx, + impl_trait.get_mut(¶m_idx.into()).unwrap(), + trait_did, + &name, + &rhs, + ); + } + } + // Move `TraitPredicate`s to the front. for (_, bounds) in impl_trait.iter_mut() { bounds.sort_by_key(|b| if let GenericBound::TraitBound(..) = b { @@ -2664,6 +2696,21 @@ pub fn is_full_generic(&self) -> bool { _ => false, } } + + pub fn projection(&self) -> Option<(&Type, DefId, &str)> { + let (self_, trait_, name) = match self { + QPath { ref self_type, ref trait_, ref name } => { + (self_type, trait_, name) + } + _ => return None, + }; + let trait_did = match **trait_ { + ResolvedPath { did, .. } => did, + _ => return None, + }; + Some((&self_, trait_did, name)) + } + } impl GetDefId for Type { diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 3801c42307f..8758ab19691 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -53,58 +53,21 @@ pub fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> Vec { // Look for equality predicates on associated types that can be merged into // general bound predicates equalities.retain(|&(ref lhs, ref rhs)| { - let (self_, trait_, name) = match *lhs { - clean::QPath { ref self_type, ref trait_, ref name } => { - (self_type, trait_, name) - } - _ => return true, - }; - let generic = match **self_ { - clean::Generic(ref s) => s, - _ => return true, + let (self_, trait_did, name) = if let Some(p) = lhs.projection() { + p + } else { + return true; }; - let trait_did = match **trait_ { - clean::ResolvedPath { did, .. } => did, + let generic = match self_ { + clean::Generic(s) => s, _ => return true, }; let bounds = match params.get_mut(generic) { Some(bound) => bound, None => return true, }; - !bounds.iter_mut().any(|b| { - let trait_ref = match *b { - clean::GenericBound::TraitBound(ref mut tr, _) => tr, - clean::GenericBound::Outlives(..) => return false, - }; - let (did, path) = match trait_ref.trait_ { - clean::ResolvedPath { did, ref mut path, ..} => (did, path), - _ => return false, - }; - // If this QPath's trait `trait_did` is the same as, or a supertrait - // of, the bound's trait `did` then we can keep going, otherwise - // this is just a plain old equality bound. - if !trait_is_same_or_supertrait(cx, did, trait_did) { - return false - } - let last = path.segments.last_mut().expect("segments were empty"); - match last.args { - PP::AngleBracketed { ref mut bindings, .. } => { - bindings.push(clean::TypeBinding { - name: name.clone(), - kind: clean::TypeBindingKind::Equality { - ty: rhs.clone(), - }, - }); - } - PP::Parenthesized { ref mut output, .. } => { - assert!(output.is_none()); - if *rhs != clean::Type::Tuple(Vec::new()) { - *output = Some(rhs.clone()); - } - } - }; - true - }) + + merge_bounds(cx, bounds, trait_did, name, rhs) }); // And finally, let's reassemble everything @@ -127,6 +90,49 @@ pub fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> Vec { clauses } +pub fn merge_bounds( + cx: &clean::DocContext<'_>, + bounds: &mut Vec, + trait_did: DefId, + name: &str, + rhs: &clean::Type, +) -> bool { + !bounds.iter_mut().any(|b| { + let trait_ref = match *b { + clean::GenericBound::TraitBound(ref mut tr, _) => tr, + clean::GenericBound::Outlives(..) => return false, + }; + let (did, path) = match trait_ref.trait_ { + clean::ResolvedPath { did, ref mut path, ..} => (did, path), + _ => return false, + }; + // If this QPath's trait `trait_did` is the same as, or a supertrait + // of, the bound's trait `did` then we can keep going, otherwise + // this is just a plain old equality bound. + if !trait_is_same_or_supertrait(cx, did, trait_did) { + return false + } + let last = path.segments.last_mut().expect("segments were empty"); + match last.args { + PP::AngleBracketed { ref mut bindings, .. } => { + bindings.push(clean::TypeBinding { + name: name.to_string(), + kind: clean::TypeBindingKind::Equality { + ty: rhs.clone(), + }, + }); + } + PP::Parenthesized { ref mut output, .. } => { + assert!(output.is_none()); + if *rhs != clean::Type::Tuple(Vec::new()) { + *output = Some(rhs.clone()); + } + } + }; + true + }) +} + pub fn ty_params(mut params: Vec) -> Vec { for param in &mut params { match param.kind { diff --git a/src/test/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs b/src/test/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs index 7807acbc4d6..7b6e665b85f 100644 --- a/src/test/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs +++ b/src/test/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs @@ -1,5 +1,9 @@ +use std::ops::Deref; + pub fn func<'a>(_x: impl Clone + Into> + 'a) {} +pub fn func2(_x: impl Deref> + Iterator, _y: impl Iterator) {} + pub struct Foo; impl Foo { diff --git a/src/test/rustdoc/inline_cross/impl_trait.rs b/src/test/rustdoc/inline_cross/impl_trait.rs index 091baa9773e..20d193aad16 100644 --- a/src/test/rustdoc/inline_cross/impl_trait.rs +++ b/src/test/rustdoc/inline_cross/impl_trait.rs @@ -7,6 +7,12 @@ // @!has - '//pre[@class="rust fn"]' 'where' pub use impl_trait_aux::func; +// @has impl_trait/fn.func2.html +// @has - '//pre[@class="rust fn"]' "_x: impl Deref> + Iterator," +// @has - '//pre[@class="rust fn"]' "_y: impl Iterator)" +// @!has - '//pre[@class="rust fn"]' 'where' +pub use impl_trait_aux::func2; + // @has impl_trait/struct.Foo.html // @has - '//code[@id="method.v"]' "pub fn method<'a>(_x: impl Clone + Into> + 'a)" // @!has - '//code[@id="method.v"]' 'where' -- GitLab