Skip to content

Commit

Permalink
Auto merge of #61613 - sinkuu:impl_trait_inline, r=ollie27
Browse files Browse the repository at this point in the history
Support `impl Trait` in inlined documentation

`impl Trait` in argument position was not properly rendered when inlined from other crates. ([a live example on docs.rs](https://docs.rs/libp2p/0.8.1/libp2p/floodsub/struct.Floodsub.html#method.unsubscribe))

![old](https://user-images.githubusercontent.com/7091080/59089838-14ba9900-8946-11e9-830b-53b317bdecb4.png)
↓
![new](https://user-images.githubusercontent.com/7091080/59089844-16845c80-8946-11e9-9fe3-8998af9d73ce.png)
  • Loading branch information
bors committed Aug 25, 2019
2 parents b1522e6 + 1fe6160 commit 521d784
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 67 deletions.
9 changes: 6 additions & 3 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,9 @@ fn build_external_function(cx: &DocContext<'_>, did: DefId) -> clean::Function {
};

let predicates = cx.tcx.predicates_of(did);
let generics = (cx.tcx.generics_of(did), &predicates).clean(cx);
let decl = (did, sig).clean(cx);
let (generics, decl) = clean::enter_impl_trait(cx, || {
((cx.tcx.generics_of(did), &predicates).clean(cx), (did, sig).clean(cx))
});
let (all_types, ret_types) = clean::get_all_types(&generics, &decl, cx);
clean::Function {
decl,
Expand Down Expand Up @@ -372,7 +373,9 @@ pub fn build_impl(cx: &DocContext<'_>, did: DefId, attrs: Option<Attrs<'_>>,
None
}
}).collect::<Vec<_>>(),
(tcx.generics_of(did), &predicates).clean(cx),
clean::enter_impl_trait(cx, || {
(tcx.generics_of(did), &predicates).clean(cx)
}),
)
};
let polarity = tcx.impl_polarity(did);
Expand Down
152 changes: 135 additions & 17 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use std::cell::RefCell;
use std::sync::Arc;
use std::u32;

use crate::core::{self, DocContext};
use crate::core::{self, DocContext, ImplTraitParam};
use crate::doctree;
use crate::html::render::{cache, ExternalLocation};
use crate::html::item_type::ItemType;
Expand Down Expand Up @@ -1540,7 +1540,7 @@ impl Clean<GenericParamDef> for ty::GenericParamDef {
ty::GenericParamDefKind::Lifetime => {
(self.name.to_string(), GenericParamDefKind::Lifetime)
}
ty::GenericParamDefKind::Type { has_default, .. } => {
ty::GenericParamDefKind::Type { has_default, synthetic, .. } => {
cx.renderinfo.borrow_mut().external_param_names
.insert(self.def_id, self.name.clean(cx));
let default = if has_default {
Expand All @@ -1552,7 +1552,7 @@ impl Clean<GenericParamDef> for ty::GenericParamDef {
did: self.def_id,
bounds: vec![], // These are filled in from the where-clauses.
default,
synthetic: None,
synthetic,
})
}
ty::GenericParamDefKind::Const { .. } => {
Expand Down Expand Up @@ -1641,7 +1641,7 @@ impl Clean<Generics> for hir::Generics {
match param.kind {
GenericParamDefKind::Lifetime => unreachable!(),
GenericParamDefKind::Type { did, ref bounds, .. } => {
cx.impl_trait_bounds.borrow_mut().insert(did, bounds.clone());
cx.impl_trait_bounds.borrow_mut().insert(did.into(), bounds.clone());
}
GenericParamDefKind::Const { .. } => unreachable!(),
}
Expand Down Expand Up @@ -1693,26 +1693,123 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics,
&'a &'tcx ty::GenericPredicates<'tcx>) {
fn clean(&self, cx: &DocContext<'_>) -> Generics {
use self::WherePredicate as WP;
use std::collections::BTreeMap;

let (gens, preds) = *self;

// Don't populate `cx.impl_trait_bounds` before `clean`ning `where` clauses,
// since `Clean for ty::Predicate` would consume them.
let mut impl_trait = BTreeMap::<ImplTraitParam, Vec<GenericBound>>::default();

// Bounds in the type_params and lifetimes fields are repeated in the
// predicates field (see rustc_typeck::collect::ty_generics), so remove
// them.
let stripped_typarams = gens.params.iter().filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => None,
ty::GenericParamDefKind::Type { .. } => {
if param.name.as_symbol() == kw::SelfUpper {
assert_eq!(param.index, 0);
return None;
let stripped_typarams = gens.params.iter()
.filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => None,
ty::GenericParamDefKind::Type { synthetic, .. } => {
if param.name.as_symbol() == kw::SelfUpper {
assert_eq!(param.index, 0);
return None;
}
if synthetic == Some(hir::SyntheticTyParamKind::ImplTrait) {
impl_trait.insert(param.index.into(), vec![]);
return None;
}
Some(param.clean(cx))
}
ty::GenericParamDefKind::Const { .. } => None,
}).collect::<Vec<GenericParamDef>>();

// param index -> [(DefId of trait, associated type name, type)]
let mut impl_trait_proj =
FxHashMap::<u32, Vec<(DefId, String, Ty<'tcx>)>>::default();

let where_predicates = preds.predicates.iter()
.flat_map(|(p, _)| {
let mut projection = 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(p) = p {
if let ty::Param(param) = p.skip_binder().projection_ty.self_ty().sty {
projection = Some(p);
return Some(param.index);
}
}

None
})();

if let Some(param_idx) = param_idx {
if let Some(b) = impl_trait.get_mut(&param_idx.into()) {
let p = p.clean(cx)?;

b.extend(
p.get_bounds()
.into_iter()
.flatten()
.cloned()
.filter(|b| !b.is_sized_bound(cx))
);

let proj = projection
.map(|p| (p.skip_binder().projection_ty.clean(cx), p.skip_binder().ty));
if let Some(((_, trait_did, name), rhs)) =
proj.as_ref().and_then(|(lhs, rhs)| Some((lhs.projection()?, rhs)))
{
impl_trait_proj
.entry(param_idx)
.or_default()
.push((trait_did, name.to_string(), rhs));
}

return None;
}
}

Some(p)
})
.collect::<Vec<_>>();

for (param, mut bounds) in impl_trait {
// Move trait bounds to the front.
bounds.sort_by_key(|b| if let GenericBound::TraitBound(..) = b {
false
} else {
true
});

if let crate::core::ImplTraitParam::ParamIndex(idx) = param {
if let Some(proj) = impl_trait_proj.remove(&idx) {
for (trait_did, name, rhs) in proj {
simplify::merge_bounds(
cx,
&mut bounds,
trait_did,
&name,
&rhs.clean(cx),
);
}
}
Some(param.clean(cx))
} else {
unreachable!();
}
ty::GenericParamDefKind::Const { .. } => None,
}).collect::<Vec<GenericParamDef>>();

let mut where_predicates = preds.predicates.iter()
.flat_map(|(p, _)| p.clean(cx))
cx.impl_trait_bounds.borrow_mut().insert(param, bounds);
}

// Now that `cx.impl_trait_bounds` is populated, we can process
// remaining predicates which could contain `impl Trait`.
let mut where_predicates = where_predicates
.into_iter()
.flat_map(|p| p.clean(cx))
.collect::<Vec<_>>();

// Type parameters and have a Sized bound by default unless removed with
Expand Down Expand Up @@ -2613,6 +2710,21 @@ impl Type {
_ => 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 {
Expand Down Expand Up @@ -2791,7 +2903,7 @@ impl Clean<Type> for hir::Ty {
if let Some(new_ty) = cx.ty_substs.borrow().get(&did).cloned() {
return new_ty;
}
if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&did) {
if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&did.into()) {
return ImplTrait(bounds);
}
}
Expand Down Expand Up @@ -3082,7 +3194,13 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {

ty::Projection(ref data) => data.clean(cx),

ty::Param(ref p) => Generic(p.name.to_string()),
ty::Param(ref p) => {
if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&p.index.into()) {
ImplTrait(bounds)
} else {
Generic(p.name.to_string())
}
}

ty::Opaque(def_id, substs) => {
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
Expand Down
96 changes: 51 additions & 45 deletions src/librustdoc/clean/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,58 +53,21 @@ pub fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
// 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
Expand All @@ -127,6 +90,49 @@ pub fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
clauses
}

pub fn merge_bounds(
cx: &clean::DocContext<'_>,
bounds: &mut Vec<clean::GenericBound>,
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<clean::GenericParamDef>) -> Vec<clean::GenericParamDef> {
for param in &mut params {
match param.kind {
Expand Down
24 changes: 22 additions & 2 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ pub struct DocContext<'tcx> {
pub lt_substs: RefCell<FxHashMap<DefId, clean::Lifetime>>,
/// Table `DefId` of const parameter -> substituted const
pub ct_substs: RefCell<FxHashMap<DefId, clean::Constant>>,
/// Table DefId of `impl Trait` in argument position -> bounds
pub impl_trait_bounds: RefCell<FxHashMap<DefId, Vec<clean::GenericBound>>>,
/// Table synthetic type parameter for `impl Trait` in argument position -> bounds
pub impl_trait_bounds: RefCell<FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>>,
pub fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
pub all_fake_def_ids: RefCell<FxHashSet<DefId>>,
/// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
Expand Down Expand Up @@ -459,3 +459,23 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
})
})
}

/// `DefId` or parameter index (`ty::ParamTy.index`) of a synthetic type parameter
/// for `impl Trait` in argument position.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ImplTraitParam {
DefId(DefId),
ParamIndex(u32),
}

impl From<DefId> for ImplTraitParam {
fn from(did: DefId) -> Self {
ImplTraitParam::DefId(did)
}
}

impl From<u32> for ImplTraitParam {
fn from(idx: u32) -> Self {
ImplTraitParam::ParamIndex(idx)
}
}
Loading

0 comments on commit 521d784

Please sign in to comment.