simplify.rs 5.8 KB
Newer Older
A
Alexander Regueiro 已提交
1
//! Simplification of where-clauses and parameter bounds into a prettier and
2 3
//! more canonical form.
//!
4
//! Currently all cross-crate-inlined function use `rustc::ty` to reconstruct
5
//! the AST (e.g., see all of `clean::inline`), but this is not always a
A
Alexander Regueiro 已提交
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
A
Alexander Regueiro 已提交
8
//! is that the AST predicate of: `where T: Trait<Foo = Bar>` is encoded as:
M
Mark Simulacrum 已提交
9
//! `where T: Trait, <T as Trait>::Foo = Bar`.
10 11 12 13 14
//!
//! This module attempts to reconstruct the original where and/or parameter
//! bounds by special casing scenarios such as these. Fun!

use std::mem;
15
use std::collections::BTreeMap;
16

17
use rustc::hir::def_id::DefId;
18
use rustc::ty;
19

20 21 22 23
use crate::clean::GenericArgs as PP;
use crate::clean::WherePredicate as WP;
use crate::clean;
use crate::core::DocContext;
24

25
pub 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<_>> = BTreeMap::new();
28 29 30
    let mut lifetimes = Vec::new();
    let mut equalities = Vec::new();
    let mut tybounds = Vec::new();
31

32 33 34 35
    for clause in clauses {
        match clause {
            WP::BoundPredicate { ty, bounds } => {
                match ty {
36
                    clean::Generic(s) => params.entry(s).or_default()
37 38 39 40 41 42 43 44 45 46 47 48 49 50
                                               .extend(bounds),
                    t => tybounds.push((t, ty_bounds(bounds))),
                }
            }
            WP::RegionPredicate { lifetime, bounds } => {
                lifetimes.push((lifetime, bounds));
            }
            WP::EqPredicate { lhs, rhs } => equalities.push((lhs, rhs)),
        }
    }

    // Simplify the type parameter bounds on all the generics
    let mut params = params.into_iter().map(|(k, v)| {
        (k, ty_bounds(v))
51
    }).collect::<BTreeMap<_, _>>();
52 53 54 55

    // Look for equality predicates on associated types that can be merged into
    // general bound predicates
    equalities.retain(|&(ref lhs, ref rhs)| {
56 57 58 59
        let (self_, trait_did, name) = if let Some(p) = lhs.projection() {
            p
        } else {
            return true;
60
        };
61 62
        let generic = match self_ {
            clean::Generic(s) => s,
63 64 65 66 67 68
            _ => return true,
        };
        let bounds = match params.get_mut(generic) {
            Some(bound) => bound,
            None => return true,
        };
69 70

        merge_bounds(cx, bounds, trait_did, name, rhs)
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
    });

    // And finally, let's reassemble everything
    let mut clauses = Vec::new();
    clauses.extend(lifetimes.into_iter().map(|(lt, bounds)| {
        WP::RegionPredicate { lifetime: lt, bounds: bounds }
    }));
    clauses.extend(params.into_iter().map(|(k, v)| {
        WP::BoundPredicate {
            ty: clean::Generic(k),
            bounds: v,
        }
    }));
    clauses.extend(tybounds.into_iter().map(|(ty, bounds)| {
        WP::BoundPredicate { ty: ty, bounds: bounds }
    }));
    clauses.extend(equalities.into_iter().map(|(lhs, rhs)| {
        WP::EqPredicate { lhs: lhs, rhs: rhs }
    }));
    clauses
}

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
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
    })
}

136
pub fn ty_params(mut params: Vec<clean::GenericParamDef>) -> Vec<clean::GenericParamDef> {
137
    for param in &mut params {
138 139
        match param.kind {
            clean::GenericParamDefKind::Type { ref mut bounds, .. } => {
C
Chris Gregory 已提交
140
                *bounds = ty_bounds(mem::take(bounds));
141 142 143
            }
            _ => panic!("expected only type parameters"),
        }
144
    }
C
Corey Farwell 已提交
145
    params
146 147
}

V
varkor 已提交
148
fn ty_bounds(bounds: Vec<clean::GenericBound>) -> Vec<clean::GenericBound> {
149 150
    bounds
}
151

152
fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId,
N
Niko Matsakis 已提交
153
                               trait_: DefId) -> bool {
154 155 156
    if child == trait_ {
        return true
    }
157
    let predicates = cx.tcx.super_predicates_of(child);
158
    debug_assert!(cx.tcx.generics_of(child).has_self);
159
    let self_ty = cx.tcx.types.self_param;
160
    predicates.predicates.iter().filter_map(|(pred, _)| {
161
        if let ty::Predicate::Trait(ref pred) = *pred {
162
            if pred.skip_binder().trait_ref.self_ty() == self_ty {
163 164 165
                Some(pred.def_id())
            } else {
                None
166
            }
167 168
        } else {
            None
169
        }
170
    }).any(|did| trait_is_same_or_supertrait(cx, did, trait_))
171
}