Auto merge of #84814 - Stupremee:properly-render-hrtbs, r=GuillaumeGomez
Properly render HRTBs ```rust pub fn test<T>() where for<'a> &'a T: Iterator, {} ``` This will now render properly including the `for<'a>`  I do not know if this covers all cases, it only covers everything that I could think of that includes `for` and lifetimes in where bounds. Also someone need to mentor me on how to add a proper rustdoc test for this. Resolves #78482
This commit is contained in:
commit
831ae3c136
12 changed files with 233 additions and 118 deletions
|
@ -353,12 +353,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
|
||||||
let (poly_trait, output) =
|
let (poly_trait, output) =
|
||||||
(data.0.as_ref().expect("as_ref failed").clone(), data.1.as_ref().cloned());
|
(data.0.as_ref().expect("as_ref failed").clone(), data.1.as_ref().cloned());
|
||||||
let new_ty = match poly_trait.trait_ {
|
let new_ty = match poly_trait.trait_ {
|
||||||
Type::ResolvedPath {
|
Type::ResolvedPath { ref path, ref did, ref is_generic } => {
|
||||||
ref path,
|
|
||||||
ref param_names,
|
|
||||||
ref did,
|
|
||||||
ref is_generic,
|
|
||||||
} => {
|
|
||||||
let mut new_path = path.clone();
|
let mut new_path = path.clone();
|
||||||
let last_segment =
|
let last_segment =
|
||||||
new_path.segments.pop().expect("segments were empty");
|
new_path.segments.pop().expect("segments were empty");
|
||||||
|
@ -395,7 +390,6 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
|
||||||
|
|
||||||
Type::ResolvedPath {
|
Type::ResolvedPath {
|
||||||
path: new_path,
|
path: new_path,
|
||||||
param_names: param_names.clone(),
|
|
||||||
did: *did,
|
did: *did,
|
||||||
is_generic: *is_generic,
|
is_generic: *is_generic,
|
||||||
}
|
}
|
||||||
|
@ -414,7 +408,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
|
||||||
let mut bounds_vec = bounds.into_iter().collect();
|
let mut bounds_vec = bounds.into_iter().collect();
|
||||||
self.sort_where_bounds(&mut bounds_vec);
|
self.sort_where_bounds(&mut bounds_vec);
|
||||||
|
|
||||||
Some(WherePredicate::BoundPredicate { ty, bounds: bounds_vec })
|
Some(WherePredicate::BoundPredicate {
|
||||||
|
ty,
|
||||||
|
bounds: bounds_vec,
|
||||||
|
bound_params: Vec::new(),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.chain(
|
.chain(
|
||||||
lifetime_to_bounds.into_iter().filter(|&(_, ref bounds)| !bounds.is_empty()).map(
|
lifetime_to_bounds.into_iter().filter(|&(_, ref bounds)| !bounds.is_empty()).map(
|
||||||
|
@ -492,7 +490,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
let p = p.unwrap();
|
let p = p.unwrap();
|
||||||
match p {
|
match p {
|
||||||
WherePredicate::BoundPredicate { ty, mut bounds } => {
|
WherePredicate::BoundPredicate { ty, mut bounds, .. } => {
|
||||||
// Writing a projection trait bound of the form
|
// Writing a projection trait bound of the form
|
||||||
// <T as Trait>::Name : ?Sized
|
// <T as Trait>::Name : ?Sized
|
||||||
// is illegal, because ?Sized bounds can only
|
// is illegal, because ?Sized bounds can only
|
||||||
|
@ -566,7 +564,6 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
|
||||||
match **trait_ {
|
match **trait_ {
|
||||||
Type::ResolvedPath {
|
Type::ResolvedPath {
|
||||||
path: ref trait_path,
|
path: ref trait_path,
|
||||||
ref param_names,
|
|
||||||
ref did,
|
ref did,
|
||||||
ref is_generic,
|
ref is_generic,
|
||||||
} => {
|
} => {
|
||||||
|
@ -613,7 +610,6 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
|
||||||
PolyTrait {
|
PolyTrait {
|
||||||
trait_: Type::ResolvedPath {
|
trait_: Type::ResolvedPath {
|
||||||
path: new_trait_path,
|
path: new_trait_path,
|
||||||
param_names: param_names.clone(),
|
|
||||||
did: *did,
|
did: *did,
|
||||||
is_generic: *is_generic,
|
is_generic: *is_generic,
|
||||||
},
|
},
|
||||||
|
|
|
@ -583,9 +583,11 @@ fn build_macro(cx: &mut DocContext<'_>, did: DefId, name: Symbol) -> clean::Item
|
||||||
fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics {
|
fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics {
|
||||||
for pred in &mut g.where_predicates {
|
for pred in &mut g.where_predicates {
|
||||||
match *pred {
|
match *pred {
|
||||||
clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref mut bounds }
|
clean::WherePredicate::BoundPredicate {
|
||||||
if *s == kw::SelfUpper =>
|
ty: clean::Generic(ref s),
|
||||||
{
|
ref mut bounds,
|
||||||
|
..
|
||||||
|
} if *s == kw::SelfUpper => {
|
||||||
bounds.retain(|bound| match *bound {
|
bounds.retain(|bound| match *bound {
|
||||||
clean::GenericBound::TraitBound(
|
clean::GenericBound::TraitBound(
|
||||||
clean::PolyTrait { trait_: clean::ResolvedPath { did, .. }, .. },
|
clean::PolyTrait { trait_: clean::ResolvedPath { did, .. }, .. },
|
||||||
|
@ -608,6 +610,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
ref bounds,
|
ref bounds,
|
||||||
|
..
|
||||||
} => !(bounds.is_empty() || *s == kw::SelfUpper && did == trait_did),
|
} => !(bounds.is_empty() || *s == kw::SelfUpper && did == trait_did),
|
||||||
_ => true,
|
_ => true,
|
||||||
});
|
});
|
||||||
|
@ -622,7 +625,7 @@ fn separate_supertrait_bounds(
|
||||||
) -> (clean::Generics, Vec<clean::GenericBound>) {
|
) -> (clean::Generics, Vec<clean::GenericBound>) {
|
||||||
let mut ty_bounds = Vec::new();
|
let mut ty_bounds = Vec::new();
|
||||||
g.where_predicates.retain(|pred| match *pred {
|
g.where_predicates.retain(|pred| match *pred {
|
||||||
clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds }
|
clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds, .. }
|
||||||
if *s == kw::SelfUpper =>
|
if *s == kw::SelfUpper =>
|
||||||
{
|
{
|
||||||
ty_bounds.extend(bounds.iter().cloned());
|
ty_bounds.extend(bounds.iter().cloned());
|
||||||
|
|
|
@ -180,7 +180,7 @@ impl Clean<Type> for (ty::TraitRef<'_>, &[TypeBinding]) {
|
||||||
|
|
||||||
debug!("ty::TraitRef\n subst: {:?}\n", trait_ref.substs);
|
debug!("ty::TraitRef\n subst: {:?}\n", trait_ref.substs);
|
||||||
|
|
||||||
ResolvedPath { path, param_names: None, did: trait_ref.def_id, is_generic: false }
|
ResolvedPath { path, did: trait_ref.def_id, is_generic: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,6 +330,7 @@ impl Clean<WherePredicate> for hir::WherePredicate<'_> {
|
||||||
hir::WherePredicate::BoundPredicate(ref wbp) => WherePredicate::BoundPredicate {
|
hir::WherePredicate::BoundPredicate(ref wbp) => WherePredicate::BoundPredicate {
|
||||||
ty: wbp.bounded_ty.clean(cx),
|
ty: wbp.bounded_ty.clean(cx),
|
||||||
bounds: wbp.bounds.clean(cx),
|
bounds: wbp.bounds.clean(cx),
|
||||||
|
bound_params: wbp.bound_generic_params.into_iter().map(|x| x.clean(cx)).collect(),
|
||||||
},
|
},
|
||||||
|
|
||||||
hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate {
|
hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate {
|
||||||
|
@ -370,6 +371,7 @@ impl<'a> Clean<WherePredicate> for ty::PolyTraitPredicate<'a> {
|
||||||
WherePredicate::BoundPredicate {
|
WherePredicate::BoundPredicate {
|
||||||
ty: poly_trait_ref.skip_binder().self_ty().clean(cx),
|
ty: poly_trait_ref.skip_binder().self_ty().clean(cx),
|
||||||
bounds: vec![poly_trait_ref.clean(cx)],
|
bounds: vec![poly_trait_ref.clean(cx)],
|
||||||
|
bound_params: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -402,6 +404,7 @@ impl<'tcx> Clean<Option<WherePredicate>> for ty::OutlivesPredicate<Ty<'tcx>, ty:
|
||||||
Some(WherePredicate::BoundPredicate {
|
Some(WherePredicate::BoundPredicate {
|
||||||
ty: ty.clean(cx),
|
ty: ty.clean(cx),
|
||||||
bounds: vec![GenericBound::Outlives(lt.clean(cx).expect("failed to clean lifetimes"))],
|
bounds: vec![GenericBound::Outlives(lt.clean(cx).expect("failed to clean lifetimes"))],
|
||||||
|
bound_params: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -567,7 +570,9 @@ impl Clean<Generics> for hir::Generics<'_> {
|
||||||
// to where predicates when such cases occur.
|
// to where predicates when such cases occur.
|
||||||
for where_pred in &mut generics.where_predicates {
|
for where_pred in &mut generics.where_predicates {
|
||||||
match *where_pred {
|
match *where_pred {
|
||||||
WherePredicate::BoundPredicate { ty: Generic(ref name), ref mut bounds } => {
|
WherePredicate::BoundPredicate {
|
||||||
|
ty: Generic(ref name), ref mut bounds, ..
|
||||||
|
} => {
|
||||||
if bounds.is_empty() {
|
if bounds.is_empty() {
|
||||||
for param in &mut generics.params {
|
for param in &mut generics.params {
|
||||||
match param.kind {
|
match param.kind {
|
||||||
|
@ -721,7 +726,7 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics, ty::GenericPredicates<'tcx
|
||||||
// handled in cleaning associated types
|
// handled in cleaning associated types
|
||||||
let mut sized_params = FxHashSet::default();
|
let mut sized_params = FxHashSet::default();
|
||||||
where_predicates.retain(|pred| match *pred {
|
where_predicates.retain(|pred| match *pred {
|
||||||
WP::BoundPredicate { ty: Generic(ref g), ref bounds } => {
|
WP::BoundPredicate { ty: Generic(ref g), ref bounds, .. } => {
|
||||||
if bounds.iter().any(|b| b.is_sized_bound(cx)) {
|
if bounds.iter().any(|b| b.is_sized_bound(cx)) {
|
||||||
sized_params.insert(*g);
|
sized_params.insert(*g);
|
||||||
false
|
false
|
||||||
|
@ -741,6 +746,7 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics, ty::GenericPredicates<'tcx
|
||||||
where_predicates.push(WP::BoundPredicate {
|
where_predicates.push(WP::BoundPredicate {
|
||||||
ty: Type::Generic(tp.name),
|
ty: Type::Generic(tp.name),
|
||||||
bounds: vec![GenericBound::maybe_sized(cx)],
|
bounds: vec![GenericBound::maybe_sized(cx)],
|
||||||
|
bound_params: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1117,6 +1123,7 @@ impl Clean<Item> for ty::AssocItem {
|
||||||
WherePredicate::BoundPredicate {
|
WherePredicate::BoundPredicate {
|
||||||
ty: QPath { ref name, ref self_type, ref trait_, .. },
|
ty: QPath { ref name, ref self_type, ref trait_, .. },
|
||||||
ref bounds,
|
ref bounds,
|
||||||
|
..
|
||||||
} => (name, self_type, trait_, bounds),
|
} => (name, self_type, trait_, bounds),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
@ -1371,24 +1378,9 @@ impl Clean<Type> for hir::Ty<'_> {
|
||||||
}
|
}
|
||||||
TyKind::Path(_) => clean_qpath(&self, cx),
|
TyKind::Path(_) => clean_qpath(&self, cx),
|
||||||
TyKind::TraitObject(ref bounds, ref lifetime, _) => {
|
TyKind::TraitObject(ref bounds, ref lifetime, _) => {
|
||||||
match bounds[0].clean(cx).trait_ {
|
let bounds = bounds.iter().map(|bound| bound.clean(cx)).collect();
|
||||||
ResolvedPath { path, param_names: None, did, is_generic } => {
|
let lifetime = if !lifetime.is_elided() { Some(lifetime.clean(cx)) } else { None };
|
||||||
let mut bounds: Vec<self::GenericBound> = bounds[1..]
|
DynTrait(bounds, lifetime)
|
||||||
.iter()
|
|
||||||
.map(|bound| {
|
|
||||||
self::GenericBound::TraitBound(
|
|
||||||
bound.clean(cx),
|
|
||||||
hir::TraitBoundModifier::None,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
if !lifetime.is_elided() {
|
|
||||||
bounds.push(self::GenericBound::Outlives(lifetime.clean(cx)));
|
|
||||||
}
|
|
||||||
ResolvedPath { path, param_names: Some(bounds), did, is_generic }
|
|
||||||
}
|
|
||||||
_ => Infer, // shouldn't happen
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TyKind::BareFn(ref barefn) => BareFunction(box barefn.clean(cx)),
|
TyKind::BareFn(ref barefn) => BareFunction(box barefn.clean(cx)),
|
||||||
TyKind::Infer | TyKind::Err => Infer,
|
TyKind::Infer | TyKind::Err => Infer,
|
||||||
|
@ -1471,7 +1463,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
|
||||||
};
|
};
|
||||||
inline::record_extern_fqn(cx, did, kind);
|
inline::record_extern_fqn(cx, did, kind);
|
||||||
let path = external_path(cx, cx.tcx.item_name(did), None, false, vec![], substs);
|
let path = external_path(cx, cx.tcx.item_name(did), None, false, vec![], substs);
|
||||||
ResolvedPath { path, param_names: None, did, is_generic: false }
|
ResolvedPath { path, did, is_generic: false }
|
||||||
}
|
}
|
||||||
ty::Foreign(did) => {
|
ty::Foreign(did) => {
|
||||||
inline::record_extern_fqn(cx, did, ItemType::ForeignType);
|
inline::record_extern_fqn(cx, did, ItemType::ForeignType);
|
||||||
|
@ -1483,7 +1475,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
|
||||||
vec![],
|
vec![],
|
||||||
InternalSubsts::empty(),
|
InternalSubsts::empty(),
|
||||||
);
|
);
|
||||||
ResolvedPath { path, param_names: None, did, is_generic: false }
|
ResolvedPath { path, did, is_generic: false }
|
||||||
}
|
}
|
||||||
ty::Dynamic(ref obj, ref reg) => {
|
ty::Dynamic(ref obj, ref reg) => {
|
||||||
// HACK: pick the first `did` as the `did` of the trait object. Someone
|
// HACK: pick the first `did` as the `did` of the trait object. Someone
|
||||||
|
@ -1501,28 +1493,19 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
|
||||||
|
|
||||||
inline::record_extern_fqn(cx, did, ItemType::Trait);
|
inline::record_extern_fqn(cx, did, ItemType::Trait);
|
||||||
|
|
||||||
let mut param_names = vec![];
|
let lifetime = reg.clean(cx);
|
||||||
if let Some(b) = reg.clean(cx) {
|
let mut bounds = vec![];
|
||||||
param_names.push(GenericBound::Outlives(b));
|
|
||||||
}
|
|
||||||
for did in dids {
|
for did in dids {
|
||||||
let empty = cx.tcx.intern_substs(&[]);
|
let empty = cx.tcx.intern_substs(&[]);
|
||||||
let path =
|
let path =
|
||||||
external_path(cx, cx.tcx.item_name(did), Some(did), false, vec![], empty);
|
external_path(cx, cx.tcx.item_name(did), Some(did), false, vec![], empty);
|
||||||
inline::record_extern_fqn(cx, did, ItemType::Trait);
|
inline::record_extern_fqn(cx, did, ItemType::Trait);
|
||||||
let bound = GenericBound::TraitBound(
|
let bound = PolyTrait {
|
||||||
PolyTrait {
|
trait_: ResolvedPath { path, did, is_generic: false },
|
||||||
trait_: ResolvedPath {
|
|
||||||
path,
|
|
||||||
param_names: None,
|
|
||||||
did,
|
|
||||||
is_generic: false,
|
|
||||||
},
|
|
||||||
generic_params: Vec::new(),
|
generic_params: Vec::new(),
|
||||||
},
|
};
|
||||||
hir::TraitBoundModifier::None,
|
bounds.push(bound);
|
||||||
);
|
|
||||||
param_names.push(bound);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bindings = vec![];
|
let mut bindings = vec![];
|
||||||
|
@ -1535,7 +1518,15 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
|
||||||
|
|
||||||
let path =
|
let path =
|
||||||
external_path(cx, cx.tcx.item_name(did), Some(did), false, bindings, substs);
|
external_path(cx, cx.tcx.item_name(did), Some(did), false, bindings, substs);
|
||||||
ResolvedPath { path, param_names: Some(param_names), did, is_generic: false }
|
bounds.insert(
|
||||||
|
0,
|
||||||
|
PolyTrait {
|
||||||
|
trait_: ResolvedPath { path, did, is_generic: false },
|
||||||
|
generic_params: Vec::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
DynTrait(bounds, lifetime)
|
||||||
}
|
}
|
||||||
ty::Tuple(ref t) => {
|
ty::Tuple(ref t) => {
|
||||||
Tuple(t.iter().map(|t| t.expect_ty()).collect::<Vec<_>>().clean(cx))
|
Tuple(t.iter().map(|t| t.expect_ty()).collect::<Vec<_>>().clean(cx))
|
||||||
|
@ -2239,14 +2230,9 @@ impl From<GenericBound> for SimpleBound {
|
||||||
match bound.clone() {
|
match bound.clone() {
|
||||||
GenericBound::Outlives(l) => SimpleBound::Outlives(l),
|
GenericBound::Outlives(l) => SimpleBound::Outlives(l),
|
||||||
GenericBound::TraitBound(t, mod_) => match t.trait_ {
|
GenericBound::TraitBound(t, mod_) => match t.trait_ {
|
||||||
Type::ResolvedPath { path, param_names, .. } => SimpleBound::TraitBound(
|
Type::ResolvedPath { path, .. } => {
|
||||||
path.segments,
|
SimpleBound::TraitBound(path.segments, Vec::new(), t.generic_params, mod_)
|
||||||
param_names.map_or_else(Vec::new, |v| {
|
}
|
||||||
v.iter().map(|p| SimpleBound::from(p.clone())).collect()
|
|
||||||
}),
|
|
||||||
t.generic_params,
|
|
||||||
mod_,
|
|
||||||
),
|
|
||||||
_ => panic!("Unexpected bound {:?}", bound),
|
_ => panic!("Unexpected bound {:?}", bound),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,16 +24,20 @@ use crate::core::DocContext;
|
||||||
|
|
||||||
crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
|
crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
|
||||||
// First, partition the where clause into its separate components
|
// First, partition the where clause into its separate components
|
||||||
let mut params: BTreeMap<_, Vec<_>> = BTreeMap::new();
|
let mut params: BTreeMap<_, (Vec<_>, Vec<_>)> = BTreeMap::new();
|
||||||
let mut lifetimes = Vec::new();
|
let mut lifetimes = Vec::new();
|
||||||
let mut equalities = Vec::new();
|
let mut equalities = Vec::new();
|
||||||
let mut tybounds = Vec::new();
|
let mut tybounds = Vec::new();
|
||||||
|
|
||||||
for clause in clauses {
|
for clause in clauses {
|
||||||
match clause {
|
match clause {
|
||||||
WP::BoundPredicate { ty, bounds } => match ty {
|
WP::BoundPredicate { ty, bounds, bound_params } => match ty {
|
||||||
clean::Generic(s) => params.entry(s).or_default().extend(bounds),
|
clean::Generic(s) => {
|
||||||
t => tybounds.push((t, bounds)),
|
let (b, p) = params.entry(s).or_default();
|
||||||
|
b.extend(bounds);
|
||||||
|
p.extend(bound_params);
|
||||||
|
}
|
||||||
|
t => tybounds.push((t, (bounds, bound_params))),
|
||||||
},
|
},
|
||||||
WP::RegionPredicate { lifetime, bounds } => {
|
WP::RegionPredicate { lifetime, bounds } => {
|
||||||
lifetimes.push((lifetime, bounds));
|
lifetimes.push((lifetime, bounds));
|
||||||
|
@ -54,7 +58,7 @@ crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
|
||||||
clean::Generic(s) => s,
|
clean::Generic(s) => s,
|
||||||
_ => return true,
|
_ => return true,
|
||||||
};
|
};
|
||||||
let bounds = match params.get_mut(generic) {
|
let (bounds, _) = match params.get_mut(generic) {
|
||||||
Some(bound) => bound,
|
Some(bound) => bound,
|
||||||
None => return true,
|
None => return true,
|
||||||
};
|
};
|
||||||
|
@ -67,10 +71,16 @@ crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
|
||||||
clauses.extend(
|
clauses.extend(
|
||||||
lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }),
|
lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }),
|
||||||
);
|
);
|
||||||
clauses.extend(
|
clauses.extend(params.into_iter().map(|(k, (bounds, params))| WP::BoundPredicate {
|
||||||
params.into_iter().map(|(k, v)| WP::BoundPredicate { ty: clean::Generic(k), bounds: v }),
|
ty: clean::Generic(k),
|
||||||
);
|
bounds,
|
||||||
clauses.extend(tybounds.into_iter().map(|(ty, bounds)| WP::BoundPredicate { ty, bounds }));
|
bound_params: params,
|
||||||
|
}));
|
||||||
|
clauses.extend(tybounds.into_iter().map(|(ty, (bounds, bound_params))| WP::BoundPredicate {
|
||||||
|
ty,
|
||||||
|
bounds,
|
||||||
|
bound_params,
|
||||||
|
}));
|
||||||
clauses.extend(equalities.into_iter().map(|(lhs, rhs)| WP::EqPredicate { lhs, rhs }));
|
clauses.extend(equalities.into_iter().map(|(lhs, rhs)| WP::EqPredicate { lhs, rhs }));
|
||||||
clauses
|
clauses
|
||||||
}
|
}
|
||||||
|
|
|
@ -1168,7 +1168,7 @@ impl GenericBound {
|
||||||
inline::record_extern_fqn(cx, did, ItemType::Trait);
|
inline::record_extern_fqn(cx, did, ItemType::Trait);
|
||||||
GenericBound::TraitBound(
|
GenericBound::TraitBound(
|
||||||
PolyTrait {
|
PolyTrait {
|
||||||
trait_: ResolvedPath { path, param_names: None, did, is_generic: false },
|
trait_: ResolvedPath { path, did, is_generic: false },
|
||||||
generic_params: Vec::new(),
|
generic_params: Vec::new(),
|
||||||
},
|
},
|
||||||
hir::TraitBoundModifier::Maybe,
|
hir::TraitBoundModifier::Maybe,
|
||||||
|
@ -1220,7 +1220,7 @@ impl Lifetime {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
crate enum WherePredicate {
|
crate enum WherePredicate {
|
||||||
BoundPredicate { ty: Type, bounds: Vec<GenericBound> },
|
BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<Lifetime> },
|
||||||
RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
|
RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
|
||||||
EqPredicate { lhs: Type, rhs: Type },
|
EqPredicate { lhs: Type, rhs: Type },
|
||||||
}
|
}
|
||||||
|
@ -1434,11 +1434,12 @@ crate enum Type {
|
||||||
/// Structs/enums/traits (most that would be an `hir::TyKind::Path`).
|
/// Structs/enums/traits (most that would be an `hir::TyKind::Path`).
|
||||||
ResolvedPath {
|
ResolvedPath {
|
||||||
path: Path,
|
path: Path,
|
||||||
param_names: Option<Vec<GenericBound>>,
|
|
||||||
did: DefId,
|
did: DefId,
|
||||||
/// `true` if is a `T::Name` path for associated types.
|
/// `true` if is a `T::Name` path for associated types.
|
||||||
is_generic: bool,
|
is_generic: bool,
|
||||||
},
|
},
|
||||||
|
/// `dyn for<'a> Trait<'a> + Send + 'static`
|
||||||
|
DynTrait(Vec<PolyTrait>, Option<Lifetime>),
|
||||||
/// For parameterized types, so the consumer of the JSON don't go
|
/// For parameterized types, so the consumer of the JSON don't go
|
||||||
/// looking for types which don't exist anywhere.
|
/// looking for types which don't exist anywhere.
|
||||||
Generic(Symbol),
|
Generic(Symbol),
|
||||||
|
@ -1625,6 +1626,7 @@ impl Type {
|
||||||
fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
|
fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
|
||||||
let t: PrimitiveType = match *self {
|
let t: PrimitiveType = match *self {
|
||||||
ResolvedPath { did, .. } => return Some(did.into()),
|
ResolvedPath { did, .. } => return Some(did.into()),
|
||||||
|
DynTrait(ref bounds, _) => return bounds[0].trait_.inner_def_id(cache),
|
||||||
Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()),
|
Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()),
|
||||||
BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference,
|
BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference,
|
||||||
BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache),
|
BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache),
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::clean::auto_trait::AutoTraitFinder;
|
||||||
use crate::clean::blanket_impl::BlanketImplFinder;
|
use crate::clean::blanket_impl::BlanketImplFinder;
|
||||||
use crate::clean::{
|
use crate::clean::{
|
||||||
inline, Clean, Crate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime,
|
inline, Clean, Crate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime,
|
||||||
Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type, TypeBinding,
|
Path, PathSegment, PolyTrait, Primitive, PrimitiveType, ResolvedPath, Type, TypeBinding,
|
||||||
};
|
};
|
||||||
use crate::core::DocContext;
|
use crate::core::DocContext;
|
||||||
use crate::formats::item_type::ItemType;
|
use crate::formats::item_type::ItemType;
|
||||||
|
@ -163,8 +163,18 @@ pub(super) fn external_path(
|
||||||
|
|
||||||
crate fn strip_type(ty: Type) -> Type {
|
crate fn strip_type(ty: Type) -> Type {
|
||||||
match ty {
|
match ty {
|
||||||
Type::ResolvedPath { path, param_names, did, is_generic } => {
|
Type::ResolvedPath { path, did, is_generic } => {
|
||||||
Type::ResolvedPath { path: strip_path(&path), param_names, did, is_generic }
|
Type::ResolvedPath { path: strip_path(&path), did, is_generic }
|
||||||
|
}
|
||||||
|
Type::DynTrait(mut bounds, lt) => {
|
||||||
|
let first = bounds.remove(0);
|
||||||
|
let stripped_trait = strip_type(first.trait_);
|
||||||
|
|
||||||
|
bounds.insert(
|
||||||
|
0,
|
||||||
|
PolyTrait { trait_: stripped_trait, generic_params: first.generic_params },
|
||||||
|
);
|
||||||
|
Type::DynTrait(bounds, lt)
|
||||||
}
|
}
|
||||||
Type::Tuple(inner_tys) => {
|
Type::Tuple(inner_tys) => {
|
||||||
Type::Tuple(inner_tys.iter().map(|t| strip_type(t.clone())).collect())
|
Type::Tuple(inner_tys.iter().map(|t| strip_type(t.clone())).collect())
|
||||||
|
@ -431,7 +441,7 @@ crate fn resolve_type(cx: &mut DocContext<'_>, path: Path, id: hir::HirId) -> Ty
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
let did = register_res(cx, path.res);
|
let did = register_res(cx, path.res);
|
||||||
ResolvedPath { path, param_names: None, did, is_generic }
|
ResolvedPath { path, did, is_generic }
|
||||||
}
|
}
|
||||||
|
|
||||||
crate fn get_auto_trait_and_blanket_impls(
|
crate fn get_auto_trait_and_blanket_impls(
|
||||||
|
|
|
@ -402,6 +402,15 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
|
||||||
self.cache.parent_stack.push(did);
|
self.cache.parent_stack.push(did);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
clean::DynTrait(ref bounds, _)
|
||||||
|
| clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => {
|
||||||
|
if let Some(did) = bounds[0].trait_.def_id() {
|
||||||
|
self.cache.parent_stack.push(did);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
ref t => {
|
ref t => {
|
||||||
let prim_did = t
|
let prim_did = t
|
||||||
.primitive_type()
|
.primitive_type()
|
||||||
|
@ -432,6 +441,12 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
|
||||||
| clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => {
|
| clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => {
|
||||||
dids.insert(did);
|
dids.insert(did);
|
||||||
}
|
}
|
||||||
|
clean::DynTrait(ref bounds, _)
|
||||||
|
| clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => {
|
||||||
|
if let Some(did) = bounds[0].trait_.def_id() {
|
||||||
|
dids.insert(did);
|
||||||
|
}
|
||||||
|
}
|
||||||
ref t => {
|
ref t => {
|
||||||
let did = t
|
let did = t
|
||||||
.primitive_type()
|
.primitive_type()
|
||||||
|
|
|
@ -250,17 +250,33 @@ crate fn print_where_clause<'a, 'tcx: 'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
match pred {
|
match pred {
|
||||||
clean::WherePredicate::BoundPredicate { ty, bounds } => {
|
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
|
||||||
let bounds = bounds;
|
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() {
|
if f.alternate() {
|
||||||
clause.push_str(&format!(
|
clause.push_str(&format!(
|
||||||
"{:#}: {:#}",
|
"{}{:#}: {:#}",
|
||||||
|
for_prefix,
|
||||||
ty.print(cx),
|
ty.print(cx),
|
||||||
print_generic_bounds(bounds, cx)
|
print_generic_bounds(bounds, cx)
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
clause.push_str(&format!(
|
clause.push_str(&format!(
|
||||||
"{}: {}",
|
"{}{}: {}",
|
||||||
|
for_prefix,
|
||||||
ty.print(cx),
|
ty.print(cx),
|
||||||
print_generic_bounds(bounds, cx)
|
print_generic_bounds(bounds, cx)
|
||||||
));
|
));
|
||||||
|
@ -631,18 +647,24 @@ fn primitive_link(
|
||||||
|
|
||||||
/// Helper to render type parameters
|
/// Helper to render type parameters
|
||||||
fn tybounds<'a, 'tcx: 'a>(
|
fn tybounds<'a, 'tcx: 'a>(
|
||||||
param_names: &'a Option<Vec<clean::GenericBound>>,
|
bounds: &'a Vec<clean::PolyTrait>,
|
||||||
|
lt: &'a Option<clean::Lifetime>,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| match *param_names {
|
display_fn(move |f| {
|
||||||
Some(ref params) => {
|
for (i, bound) in bounds.iter().enumerate() {
|
||||||
for param in params {
|
if i > 0 {
|
||||||
write!(f, " + ")?;
|
write!(f, " + ")?;
|
||||||
fmt::Display::fmt(¶m.print(cx), f)?;
|
}
|
||||||
|
|
||||||
|
fmt::Display::fmt(&bound.print(cx), f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(lt) = lt {
|
||||||
|
write!(f, " + ")?;
|
||||||
|
fmt::Display::fmt(<.print(), f)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
None => Ok(()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,13 +701,13 @@ fn fmt_type<'cx>(
|
||||||
|
|
||||||
match *t {
|
match *t {
|
||||||
clean::Generic(name) => write!(f, "{}", name),
|
clean::Generic(name) => write!(f, "{}", name),
|
||||||
clean::ResolvedPath { did, ref param_names, ref path, is_generic } => {
|
clean::ResolvedPath { did, ref path, is_generic } => {
|
||||||
if param_names.is_some() {
|
|
||||||
f.write_str("dyn ")?;
|
|
||||||
}
|
|
||||||
// Paths like `T::Output` and `Self::Output` should be rendered with all segments.
|
// Paths like `T::Output` and `Self::Output` should be rendered with all segments.
|
||||||
resolved_path(f, did, path, is_generic, use_absolute, cx)?;
|
resolved_path(f, did, path, is_generic, use_absolute, cx)
|
||||||
fmt::Display::fmt(&tybounds(param_names, cx), f)
|
}
|
||||||
|
clean::DynTrait(ref bounds, ref lt) => {
|
||||||
|
f.write_str("dyn ")?;
|
||||||
|
fmt::Display::fmt(&tybounds(bounds, lt, cx), f)
|
||||||
}
|
}
|
||||||
clean::Infer => write!(f, "_"),
|
clean::Infer => write!(f, "_"),
|
||||||
clean::Primitive(prim) => primitive_link(f, prim, &*prim.as_sym().as_str(), cx),
|
clean::Primitive(prim) => primitive_link(f, prim, &*prim.as_sym().as_str(), cx),
|
||||||
|
@ -820,7 +842,9 @@ fn fmt_type<'cx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => {
|
clean::DynTrait(ref bounds, ref trait_lt)
|
||||||
|
if bounds.len() > 1 || trait_lt.is_some() =>
|
||||||
|
{
|
||||||
write!(f, "{}{}{}(", amp, lt, m)?;
|
write!(f, "{}{}{}(", amp, lt, m)?;
|
||||||
fmt_type(&ty, f, use_absolute, cx)?;
|
fmt_type(&ty, f, use_absolute, cx)?;
|
||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
|
@ -881,7 +905,7 @@ fn fmt_type<'cx>(
|
||||||
// the ugliness comes from inlining across crates where
|
// the ugliness comes from inlining across crates where
|
||||||
// everything comes in as a fully resolved QPath (hard to
|
// everything comes in as a fully resolved QPath (hard to
|
||||||
// look at).
|
// look at).
|
||||||
box clean::ResolvedPath { did, ref param_names, .. } => {
|
box clean::ResolvedPath { did, .. } => {
|
||||||
match href(did.into(), cx) {
|
match href(did.into(), cx) {
|
||||||
Some((ref url, _, ref path)) if !f.alternate() => {
|
Some((ref url, _, ref path)) if !f.alternate() => {
|
||||||
write!(
|
write!(
|
||||||
|
@ -896,9 +920,6 @@ fn fmt_type<'cx>(
|
||||||
}
|
}
|
||||||
_ => write!(f, "{}", name)?,
|
_ => write!(f, "{}", name)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: `param_names` are not rendered, and this seems bad?
|
|
||||||
drop(param_names);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => write!(f, "{}", name),
|
_ => write!(f, "{}", name),
|
||||||
|
|
|
@ -234,6 +234,7 @@ fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option
|
||||||
});
|
});
|
||||||
Some(path_segment.name)
|
Some(path_segment.name)
|
||||||
}
|
}
|
||||||
|
clean::DynTrait(ref bounds, _) => get_index_type_name(&bounds[0].trait_, accept_generic),
|
||||||
clean::Generic(s) if accept_generic => Some(s),
|
clean::Generic(s) if accept_generic => Some(s),
|
||||||
clean::Primitive(ref p) => Some(p.as_sym()),
|
clean::Primitive(ref p) => Some(p.as_sym()),
|
||||||
clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
|
clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
|
||||||
|
|
|
@ -328,9 +328,10 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
|
||||||
fn from_tcx(predicate: clean::WherePredicate, tcx: TyCtxt<'_>) -> Self {
|
fn from_tcx(predicate: clean::WherePredicate, tcx: TyCtxt<'_>) -> Self {
|
||||||
use clean::WherePredicate::*;
|
use clean::WherePredicate::*;
|
||||||
match predicate {
|
match predicate {
|
||||||
BoundPredicate { ty, bounds } => WherePredicate::BoundPredicate {
|
BoundPredicate { ty, bounds, .. } => WherePredicate::BoundPredicate {
|
||||||
ty: ty.into_tcx(tcx),
|
ty: ty.into_tcx(tcx),
|
||||||
bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
|
bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
|
||||||
|
// FIXME: add `bound_params` to rustdoc-json-params?
|
||||||
},
|
},
|
||||||
RegionPredicate { lifetime, bounds } => WherePredicate::RegionPredicate {
|
RegionPredicate { lifetime, bounds } => WherePredicate::RegionPredicate {
|
||||||
lifetime: lifetime.0.to_string(),
|
lifetime: lifetime.0.to_string(),
|
||||||
|
@ -372,14 +373,35 @@ impl FromWithTcx<clean::Type> for Type {
|
||||||
fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self {
|
fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self {
|
||||||
use clean::Type::*;
|
use clean::Type::*;
|
||||||
match ty {
|
match ty {
|
||||||
ResolvedPath { path, param_names, did, is_generic: _ } => Type::ResolvedPath {
|
ResolvedPath { path, did, is_generic: _ } => Type::ResolvedPath {
|
||||||
name: path.whole_name(),
|
name: path.whole_name(),
|
||||||
id: from_def_id(did.into()),
|
id: from_def_id(did.into()),
|
||||||
args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))),
|
args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))),
|
||||||
param_names: param_names
|
param_names: Vec::new(),
|
||||||
.map(|v| v.into_iter().map(|x| x.into_tcx(tcx)).collect())
|
|
||||||
.unwrap_or_default(),
|
|
||||||
},
|
},
|
||||||
|
DynTrait(mut bounds, lt) => {
|
||||||
|
let (path, id) = match bounds.remove(0).trait_ {
|
||||||
|
ResolvedPath { path, did, .. } => (path, did),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Type::ResolvedPath {
|
||||||
|
name: path.whole_name(),
|
||||||
|
id: from_def_id(id.into()),
|
||||||
|
args: path
|
||||||
|
.segments
|
||||||
|
.last()
|
||||||
|
.map(|args| Box::new(args.clone().args.into_tcx(tcx))),
|
||||||
|
param_names: bounds
|
||||||
|
.into_iter()
|
||||||
|
.map(|t| {
|
||||||
|
clean::GenericBound::TraitBound(t, rustc_hir::TraitBoundModifier::None)
|
||||||
|
})
|
||||||
|
.chain(lt.into_iter().map(|lt| clean::GenericBound::Outlives(lt)))
|
||||||
|
.map(|bound| bound.into_tcx(tcx))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
Generic(s) => Type::Generic(s.to_string()),
|
Generic(s) => Type::Generic(s.to_string()),
|
||||||
Primitive(p) => Type::Primitive(p.as_sym().to_string()),
|
Primitive(p) => Type::Primitive(p.as_sym().to_string()),
|
||||||
BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))),
|
BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))),
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
#![crate_name = "foo"]
|
|
||||||
#![crate_type = "lib"]
|
|
||||||
|
|
||||||
pub struct Foo {
|
|
||||||
pub some_func: for<'a> fn(val: &'a i32) -> i32,
|
|
||||||
pub some_trait: dyn for<'a> Trait<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// @has foo/struct.Foo.html '//span[@id="structfield.some_func"]' "some_func: for<'a> fn(val: &'a i32) -> i32"
|
|
||||||
// @has foo/struct.Foo.html '//span[@id="structfield.some_trait"]' "some_trait: dyn Trait<'a>"
|
|
||||||
|
|
||||||
pub trait Trait<'a> {}
|
|
61
src/test/rustdoc/higher-ranked-trait-bounds.rs
Normal file
61
src/test/rustdoc/higher-ranked-trait-bounds.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#![crate_name = "foo"]
|
||||||
|
|
||||||
|
// @has foo/trait.Trait.html
|
||||||
|
pub trait Trait<'x> {}
|
||||||
|
|
||||||
|
// @has foo/fn.test1.html
|
||||||
|
// @has - '//pre' "pub fn test1<T>() where for<'a> &'a T: Iterator,"
|
||||||
|
pub fn test1<T>()
|
||||||
|
where
|
||||||
|
for<'a> &'a T: Iterator,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// @has foo/fn.test2.html
|
||||||
|
// @has - '//pre' "pub fn test2<T>() where for<'a, 'b> &'a T: Trait<'b>,"
|
||||||
|
pub fn test2<T>()
|
||||||
|
where
|
||||||
|
for<'a, 'b> &'a T: Trait<'b>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// @has foo/fn.test3.html
|
||||||
|
// @has - '//pre' "pub fn test3<F>() where F: for<'a, 'b> Fn(&'a u8, &'b u8),"
|
||||||
|
pub fn test3<F>()
|
||||||
|
where
|
||||||
|
F: for<'a, 'b> Fn(&'a u8, &'b u8),
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// @has foo/struct.Foo.html
|
||||||
|
pub struct Foo<'a> {
|
||||||
|
_x: &'a u8,
|
||||||
|
pub some_trait: &'a dyn for<'b> Trait<'b>,
|
||||||
|
pub some_func: for<'c> fn(val: &'c i32) -> i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// @has - '//span[@id="structfield.some_func"]' "some_func: for<'c> fn(val: &'c i32) -> i32"
|
||||||
|
// @has - '//span[@id="structfield.some_trait"]' "some_trait: &'a dyn for<'b> Trait<'b>"
|
||||||
|
|
||||||
|
impl<'a> Foo<'a> {
|
||||||
|
// @has - '//code' "pub fn bar<T>() where T: Trait<'a>,"
|
||||||
|
pub fn bar<T>()
|
||||||
|
where
|
||||||
|
T: Trait<'a>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @has foo/trait.B.html
|
||||||
|
pub trait B<'x> {}
|
||||||
|
|
||||||
|
// @has - '//code[@class="in-band"]' "impl<'a> B<'a> for dyn for<'b> Trait<'b>"
|
||||||
|
impl<'a> B<'a> for dyn for<'b> Trait<'b> {}
|
||||||
|
|
||||||
|
// @has foo/struct.Bar.html
|
||||||
|
// @has - '//span[@id="structfield.bar"]' "bar: &'a (dyn for<'b> Trait<'b> + Unpin)"
|
||||||
|
// @has - '//span[@id="structfield.baz"]' "baz: &'a (dyn Unpin + for<'b> Trait<'b>)"
|
||||||
|
pub struct Bar<'a> {
|
||||||
|
pub bar: &'a (dyn for<'b> Trait<'b> + Unpin),
|
||||||
|
pub baz: &'a (dyn Unpin + for<'b> Trait<'b>),
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue