1 //! Simplification of where-clauses and parameter bounds into a prettier and
2 //! more canonical form.
4 //! Currently all cross-crate-inlined function use `rustc_middle::ty` to reconstruct
5 //! the AST (e.g., see all of `clean::inline`), but this is not always a
6 //! non-lossy transformation. The current format of storage for where-clauses
7 //! for functions and such is simply a list of predicates. One example of this
8 //! is that the AST predicate of: `where T: Trait<Foo = Bar>` is encoded as:
9 //! `where T: Trait, <T as Trait>::Foo = Bar`.
11 //! This module attempts to reconstruct the original where and/or parameter
12 //! bounds by special casing scenarios such as these. Fun!
14 use rustc_data_structures
::fx
::FxIndexMap
;
15 use rustc_hir
::def_id
::DefId
;
17 use rustc_span
::Symbol
;
20 use crate::clean
::GenericArgs
as PP
;
21 use crate::clean
::WherePredicate
as WP
;
22 use crate::core
::DocContext
;
24 pub(crate) fn where_clauses(cx
: &DocContext
<'_
>, clauses
: Vec
<WP
>) -> Vec
<WP
> {
25 // First, partition the where clause into its separate components.
27 // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to
28 // the order of the generated bounds.
29 let mut params
: FxIndexMap
<Symbol
, (Vec
<_
>, Vec
<_
>)> = FxIndexMap
::default();
30 let mut lifetimes
= Vec
::new();
31 let mut equalities
= Vec
::new();
32 let mut tybounds
= Vec
::new();
34 for clause
in clauses
{
36 WP
::BoundPredicate { ty, bounds, bound_params }
=> match ty
{
37 clean
::Generic(s
) => {
38 let (b
, p
) = params
.entry(s
).or_default();
40 p
.extend(bound_params
);
42 t
=> tybounds
.push((t
, (bounds
, bound_params
))),
44 WP
::RegionPredicate { lifetime, bounds }
=> {
45 lifetimes
.push((lifetime
, bounds
));
47 WP
::EqPredicate { lhs, rhs }
=> equalities
.push((lhs
, rhs
)),
51 // Look for equality predicates on associated types that can be merged into
52 // general bound predicates
53 equalities
.retain(|&(ref lhs
, ref rhs
)| {
54 let Some((self_
, trait_did
, name
)) = lhs
.projection() else {
57 let clean
::Generic(generic
) = self_
else { return true }
;
58 let Some((bounds
, _
)) = params
.get_mut(generic
) else { return true }
;
60 merge_bounds(cx
, bounds
, trait_did
, name
, rhs
)
63 // And finally, let's reassemble everything
64 let mut clauses
= Vec
::new();
66 lifetimes
.into_iter().map(|(lt
, bounds
)| WP
::RegionPredicate { lifetime: lt, bounds }
),
68 clauses
.extend(params
.into_iter().map(|(k
, (bounds
, params
))| WP
::BoundPredicate
{
69 ty
: clean
::Generic(k
),
73 clauses
.extend(tybounds
.into_iter().map(|(ty
, (bounds
, bound_params
))| WP
::BoundPredicate
{
78 clauses
.extend(equalities
.into_iter().map(|(lhs
, rhs
)| WP
::EqPredicate { lhs, rhs }
));
82 pub(crate) fn merge_bounds(
83 cx
: &clean
::DocContext
<'_
>,
84 bounds
: &mut Vec
<clean
::GenericBound
>,
86 assoc
: clean
::PathSegment
,
89 !bounds
.iter_mut().any(|b
| {
90 let trait_ref
= match *b
{
91 clean
::GenericBound
::TraitBound(ref mut tr
, _
) => tr
,
92 clean
::GenericBound
::Outlives(..) => return false,
94 // If this QPath's trait `trait_did` is the same as, or a supertrait
95 // of, the bound's trait `did` then we can keep going, otherwise
96 // this is just a plain old equality bound.
97 if !trait_is_same_or_supertrait(cx
, trait_ref
.trait_
.def_id(), trait_did
) {
100 let last
= trait_ref
.trait_
.segments
.last_mut().expect("segments were empty");
102 PP
::AngleBracketed { ref mut bindings, .. }
=> {
103 bindings
.push(clean
::TypeBinding
{
104 assoc
: assoc
.clone(),
105 kind
: clean
::TypeBindingKind
::Equality { term: rhs.clone() }
,
108 PP
::Parenthesized { ref mut output, .. }
=> match output
{
109 Some(o
) => assert_eq
!(&clean
::Term
::Type(o
.as_ref().clone()), rhs
),
111 if *rhs
!= clean
::Term
::Type(clean
::Type
::Tuple(Vec
::new())) {
112 *output
= Some(Box
::new(rhs
.ty().unwrap().clone()));
121 fn trait_is_same_or_supertrait(cx
: &DocContext
<'_
>, child
: DefId
, trait_
: DefId
) -> bool
{
125 let predicates
= cx
.tcx
.super_predicates_of(child
);
126 debug_assert
!(cx
.tcx
.generics_of(child
).has_self
);
127 let self_ty
= cx
.tcx
.types
.self_param
;
131 .filter_map(|(pred
, _
)| {
132 if let ty
::PredicateKind
::Trait(pred
) = pred
.kind().skip_binder() {
133 if pred
.trait_ref
.self_ty() == self_ty { Some(pred.def_id()) }
else { None }
138 .any(|did
| trait_is_same_or_supertrait(cx
, did
, trait_
))