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 std
::collections
::BTreeMap
;
16 use rustc_hir
::def_id
::DefId
;
18 use rustc_span
::Symbol
;
21 use crate::clean
::GenericArgs
as PP
;
22 use crate::clean
::WherePredicate
as WP
;
23 use crate::core
::DocContext
;
25 crate fn where_clauses(cx
: &DocContext
<'_
>, clauses
: Vec
<WP
>) -> Vec
<WP
> {
26 // First, partition the where clause into its separate components
27 let mut params
: BTreeMap
<_
, (Vec
<_
>, Vec
<_
>)> = BTreeMap
::new();
28 let mut lifetimes
= Vec
::new();
29 let mut equalities
= Vec
::new();
30 let mut tybounds
= Vec
::new();
32 for clause
in clauses
{
34 WP
::BoundPredicate { ty, bounds, bound_params }
=> match ty
{
35 clean
::Generic(s
) => {
36 let (b
, p
) = params
.entry(s
).or_default();
38 p
.extend(bound_params
);
40 t
=> tybounds
.push((t
, (bounds
, bound_params
))),
42 WP
::RegionPredicate { lifetime, bounds }
=> {
43 lifetimes
.push((lifetime
, bounds
));
45 WP
::EqPredicate { lhs, rhs }
=> equalities
.push((lhs
, rhs
)),
49 // Look for equality predicates on associated types that can be merged into
50 // general bound predicates
51 equalities
.retain(|&(ref lhs
, ref rhs
)| {
52 let (self_
, trait_did
, name
) = if let Some(p
) = lhs
.projection() {
57 let generic
= match self_
{
58 clean
::Generic(s
) => s
,
61 let (bounds
, _
) = match params
.get_mut(generic
) {
66 merge_bounds(cx
, bounds
, trait_did
, name
, rhs
)
69 // And finally, let's reassemble everything
70 let mut clauses
= Vec
::new();
72 lifetimes
.into_iter().map(|(lt
, bounds
)| WP
::RegionPredicate { lifetime: lt, bounds }
),
74 clauses
.extend(params
.into_iter().map(|(k
, (bounds
, params
))| WP
::BoundPredicate
{
75 ty
: clean
::Generic(k
),
79 clauses
.extend(tybounds
.into_iter().map(|(ty
, (bounds
, bound_params
))| WP
::BoundPredicate
{
84 clauses
.extend(equalities
.into_iter().map(|(lhs
, rhs
)| WP
::EqPredicate { lhs, rhs }
));
88 crate fn merge_bounds(
89 cx
: &clean
::DocContext
<'_
>,
90 bounds
: &mut Vec
<clean
::GenericBound
>,
95 !bounds
.iter_mut().any(|b
| {
96 let trait_ref
= match *b
{
97 clean
::GenericBound
::TraitBound(ref mut tr
, _
) => tr
,
98 clean
::GenericBound
::Outlives(..) => return false,
100 let (did
, path
) = match trait_ref
.trait_
{
101 clean
::ResolvedPath { did, ref mut path, .. }
=> (did
, path
),
104 // If this QPath's trait `trait_did` is the same as, or a supertrait
105 // of, the bound's trait `did` then we can keep going, otherwise
106 // this is just a plain old equality bound.
107 if !trait_is_same_or_supertrait(cx
, did
, trait_did
) {
110 let last
= path
.segments
.last_mut().expect("segments were empty");
112 PP
::AngleBracketed { ref mut bindings, .. }
=> {
113 bindings
.push(clean
::TypeBinding
{
115 kind
: clean
::TypeBindingKind
::Equality { ty: rhs.clone() }
,
118 PP
::Parenthesized { ref mut output, .. }
=> match output
{
119 Some(o
) => assert_eq
!(o
, rhs
),
121 if *rhs
!= clean
::Type
::Tuple(Vec
::new()) {
122 *output
= Some(rhs
.clone());
131 fn trait_is_same_or_supertrait(cx
: &DocContext
<'_
>, child
: DefId
, trait_
: DefId
) -> bool
{
135 let predicates
= cx
.tcx
.super_predicates_of(child
);
136 debug_assert
!(cx
.tcx
.generics_of(child
).has_self
);
137 let self_ty
= cx
.tcx
.types
.self_param
;
141 .filter_map(|(pred
, _
)| {
142 if let ty
::PredicateKind
::Trait(pred
, _
) = pred
.kind().skip_binder() {
143 if pred
.trait_ref
.self_ty() == self_ty { Some(pred.def_id()) }
else { None }
148 .any(|did
| trait_is_same_or_supertrait(cx
, did
, trait_
))