Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustdoc: render generic params & where-clauses of cross-crate assoc tys in impls #112920

Merged
merged 1 commit into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
88 changes: 45 additions & 43 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,21 +1360,53 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
}
}

let mut predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates;
if let ty::TraitContainer = assoc_item.container {
let bounds = tcx
.explicit_item_bounds(assoc_item.def_id)
.subst_identity_iter_copied()
.map(|(c, s)| (c.as_predicate(), s));
let predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates;
let predicates =
tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied()));
let mut generics = clean_ty_generics(
cx,
tcx.generics_of(assoc_item.def_id),
ty::GenericPredicates { parent: None, predicates },
);
// Filter out the bounds that are (likely?) directly attached to the associated type,
// as opposed to being located in the where clause.
predicates = tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied()));
}
let mut generics = clean_ty_generics(
cx,
tcx.generics_of(assoc_item.def_id),
ty::GenericPredicates { parent: None, predicates },
);
// Move bounds that are (likely) directly attached to the parameters of the
// (generic) associated type from the where clause to the respective parameter.
// There is no guarantee that this is what the user actually wrote but we have
// no way of knowing.
let mut where_predicates = ThinVec::new();
for mut pred in generics.where_predicates {
if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Type { bounds: param_bounds, .. },
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
param_bounds.append(bounds);
} else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
param_bounds.extend(bounds.drain(..).map(|bound| match bound {
GenericBound::Outlives(lifetime) => lifetime,
_ => unreachable!(),
}));
} else {
where_predicates.push(pred);
}
}
generics.where_predicates = where_predicates;

if let ty::TraitContainer = assoc_item.container {
// Move bounds that are (likely) directly attached to the associated type
// from the where-clause to the associated type.
// There is no guarantee that this is what the user actually wrote but we have
// no way of knowing.
let mut bounds: Vec<GenericBound> = Vec::new();
generics.where_predicates.retain_mut(|pred| match *pred {
WherePredicate::BoundPredicate {
Expand Down Expand Up @@ -1431,33 +1463,6 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
}
None => bounds.push(GenericBound::maybe_sized(cx)),
}
// Move bounds that are (likely) directly attached to the parameters of the
// (generic) associated type from the where clause to the respective parameter.
// There is no guarantee that this is what the user actually wrote but we have
// no way of knowing.
let mut where_predicates = ThinVec::new();
for mut pred in generics.where_predicates {
if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Type { bounds: param_bounds, .. },
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
param_bounds.append(bounds);
} else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
..
}) = generics.params.iter_mut().find(|param| &param.name == arg) {
param_bounds.extend(bounds.drain(..).map(|bound| match bound {
GenericBound::Outlives(lifetime) => lifetime,
_ => unreachable!(),
}));
} else {
where_predicates.push(pred);
}
}
generics.where_predicates = where_predicates;

if tcx.defaultness(assoc_item.def_id).has_value() {
AssocTypeItem(
Expand All @@ -1469,7 +1474,6 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
None,
),
generics,
// FIXME: should we obtain the Type from HIR and pass it on here?
item_type: None,
}),
bounds,
Expand All @@ -1478,7 +1482,6 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
TyAssocTypeItem(generics, bounds)
}
} else {
// FIXME: when could this happen? Associated items in inherent impls?
AssocTypeItem(
Box::new(Typedef {
type_: clean_middle_ty(
Expand All @@ -1487,12 +1490,11 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
Some(assoc_item.def_id),
None,
),
generics: Generics {
params: ThinVec::new(),
where_predicates: ThinVec::new(),
},
generics,
item_type: None,
}),
// Associated types inside trait or inherent impls are not allowed to have
// item bounds. Thus we don't attempt to move any bounds there.
Vec::new(),
)
}
Expand Down
12 changes: 12 additions & 0 deletions tests/rustdoc/inline_cross/assoc_item_trait_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,15 @@ pub use aux::Main;
// @has main/trait.Aid.html
// @has - '//*[@id="associatedtype.Result"]' "type Result<'inter: 'src>"
pub use aux::Aid;

// Below, ensure that we correctly display generic parameters and where-clauses on
// associated types inside trait *impls*. More particularly, check that we don't render
// any bounds (here `Self::Alias<T>: ...`) as item bounds unlike all the trait test cases above.

// @has main/struct.Implementor.html
// @has - '//*[@id="associatedtype.Alias"]' \
// "type Alias<T: Eq> = T \
// where \
// String: From<T>, \
// <Implementor as Implementee>::Alias<T>: From<<Implementor as Implementee>::Alias<T>>"
pub use aux::Implementor;
17 changes: 17 additions & 0 deletions tests/rustdoc/inline_cross/auxiliary/assoc_item_trait_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,20 @@ pub trait Helper {
pub trait Aid<'src> {
type Result<'inter: 'src>;
}

pub trait Implementee {
type Alias<T: Eq>
where
String: From<T>;
}

pub struct Implementor;

impl Implementee for Implementor {
type Alias<T: Eq> = T
where
String: From<T>,
// We will check that this bound doesn't get turned into an item bound since
// associated types in impls are not allowed to have any.
Self::Alias<T>: From<Self::Alias<T>>;
}