]>
Commit | Line | Data |
---|---|---|
9346a6ac AL |
1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Simplification of where clauses and parameter bounds into a prettier and | |
12 | //! more canonical form. | |
13 | //! | |
54a0048b | 14 | //! Currently all cross-crate-inlined function use `rustc::ty` to reconstruct |
9346a6ac AL |
15 | //! the AST (e.g. see all of `clean::inline`), but this is not always a |
16 | //! non-lossy transformation. The current format of storage for where clauses | |
17 | //! for functions and such is simply a list of predicates. One example of this | |
18 | //! is that the AST predicate of: | |
19 | //! | |
20 | //! where T: Trait<Foo=Bar> | |
21 | //! | |
22 | //! is encoded as: | |
23 | //! | |
24 | //! where T: Trait, <T as Trait>::Foo = Bar | |
25 | //! | |
26 | //! This module attempts to reconstruct the original where and/or parameter | |
27 | //! bounds by special casing scenarios such as these. Fun! | |
28 | ||
29 | use std::mem; | |
30 | use std::collections::HashMap; | |
31 | ||
54a0048b SL |
32 | use rustc::hir::def_id::DefId; |
33 | use rustc::ty::subst; | |
9346a6ac AL |
34 | |
35 | use clean::PathParameters as PP; | |
36 | use clean::WherePredicate as WP; | |
37 | use clean::{self, Clean}; | |
38 | use core::DocContext; | |
39 | ||
40 | pub fn where_clauses(cx: &DocContext, clauses: Vec<WP>) -> Vec<WP> { | |
41 | // First, partition the where clause into its separate components | |
42 | let mut params = HashMap::new(); | |
43 | let mut lifetimes = Vec::new(); | |
44 | let mut equalities = Vec::new(); | |
45 | let mut tybounds = Vec::new(); | |
46 | for clause in clauses { | |
47 | match clause { | |
48 | WP::BoundPredicate { ty, bounds } => { | |
49 | match ty { | |
50 | clean::Generic(s) => params.entry(s).or_insert(Vec::new()) | |
51 | .extend(bounds), | |
52 | t => tybounds.push((t, ty_bounds(bounds))), | |
53 | } | |
54 | } | |
55 | WP::RegionPredicate { lifetime, bounds } => { | |
56 | lifetimes.push((lifetime, bounds)); | |
57 | } | |
58 | WP::EqPredicate { lhs, rhs } => equalities.push((lhs, rhs)), | |
59 | } | |
60 | } | |
61 | ||
62 | // Simplify the type parameter bounds on all the generics | |
63 | let mut params = params.into_iter().map(|(k, v)| { | |
64 | (k, ty_bounds(v)) | |
65 | }).collect::<HashMap<_, _>>(); | |
66 | ||
67 | // Look for equality predicates on associated types that can be merged into | |
68 | // general bound predicates | |
69 | equalities.retain(|&(ref lhs, ref rhs)| { | |
70 | let (self_, trait_, name) = match *lhs { | |
71 | clean::QPath { ref self_type, ref trait_, ref name } => { | |
72 | (self_type, trait_, name) | |
73 | } | |
74 | _ => return true, | |
75 | }; | |
76 | let generic = match **self_ { | |
77 | clean::Generic(ref s) => s, | |
78 | _ => return true, | |
79 | }; | |
80 | let trait_did = match **trait_ { | |
81 | clean::ResolvedPath { did, .. } => did, | |
82 | _ => return true, | |
83 | }; | |
84 | let bounds = match params.get_mut(generic) { | |
85 | Some(bound) => bound, | |
86 | None => return true, | |
87 | }; | |
88 | !bounds.iter_mut().any(|b| { | |
89 | let trait_ref = match *b { | |
90 | clean::TraitBound(ref mut tr, _) => tr, | |
91 | clean::RegionBound(..) => return false, | |
92 | }; | |
93 | let (did, path) = match trait_ref.trait_ { | |
94 | clean::ResolvedPath { did, ref mut path, ..} => (did, path), | |
95 | _ => return false, | |
96 | }; | |
97 | // If this QPath's trait `trait_did` is the same as, or a supertrait | |
98 | // of, the bound's trait `did` then we can keep going, otherwise | |
99 | // this is just a plain old equality bound. | |
100 | if !trait_is_same_or_supertrait(cx, did, trait_did) { | |
101 | return false | |
102 | } | |
103 | let last = path.segments.last_mut().unwrap(); | |
104 | match last.params { | |
105 | PP::AngleBracketed { ref mut bindings, .. } => { | |
106 | bindings.push(clean::TypeBinding { | |
107 | name: name.clone(), | |
108 | ty: rhs.clone(), | |
109 | }); | |
110 | } | |
111 | PP::Parenthesized { ref mut output, .. } => { | |
112 | assert!(output.is_none()); | |
113 | *output = Some(rhs.clone()); | |
114 | } | |
115 | }; | |
116 | true | |
117 | }) | |
118 | }); | |
119 | ||
120 | // And finally, let's reassemble everything | |
121 | let mut clauses = Vec::new(); | |
122 | clauses.extend(lifetimes.into_iter().map(|(lt, bounds)| { | |
123 | WP::RegionPredicate { lifetime: lt, bounds: bounds } | |
124 | })); | |
125 | clauses.extend(params.into_iter().map(|(k, v)| { | |
126 | WP::BoundPredicate { | |
127 | ty: clean::Generic(k), | |
128 | bounds: v, | |
129 | } | |
130 | })); | |
131 | clauses.extend(tybounds.into_iter().map(|(ty, bounds)| { | |
132 | WP::BoundPredicate { ty: ty, bounds: bounds } | |
133 | })); | |
134 | clauses.extend(equalities.into_iter().map(|(lhs, rhs)| { | |
135 | WP::EqPredicate { lhs: lhs, rhs: rhs } | |
136 | })); | |
137 | clauses | |
138 | } | |
139 | ||
140 | pub fn ty_params(mut params: Vec<clean::TyParam>) -> Vec<clean::TyParam> { | |
62682a34 | 141 | for param in &mut params { |
9346a6ac AL |
142 | param.bounds = ty_bounds(mem::replace(&mut param.bounds, Vec::new())); |
143 | } | |
144 | return params; | |
145 | } | |
146 | ||
147 | fn ty_bounds(bounds: Vec<clean::TyParamBound>) -> Vec<clean::TyParamBound> { | |
148 | bounds | |
149 | } | |
150 | ||
e9174d1e SL |
151 | fn trait_is_same_or_supertrait(cx: &DocContext, child: DefId, |
152 | trait_: DefId) -> bool { | |
9346a6ac AL |
153 | if child == trait_ { |
154 | return true | |
155 | } | |
c1a9b12d SL |
156 | let def = cx.tcx().lookup_trait_def(child); |
157 | let predicates = cx.tcx().lookup_predicates(child); | |
9346a6ac AL |
158 | let generics = (&def.generics, &predicates, subst::TypeSpace).clean(cx); |
159 | generics.where_predicates.iter().filter_map(|pred| { | |
160 | match *pred { | |
161 | clean::WherePredicate::BoundPredicate { | |
162 | ty: clean::Generic(ref s), | |
163 | ref bounds | |
164 | } if *s == "Self" => Some(bounds), | |
165 | _ => None, | |
166 | } | |
62682a34 | 167 | }).flat_map(|bounds| bounds).any(|bound| { |
9346a6ac AL |
168 | let poly_trait = match *bound { |
169 | clean::TraitBound(ref t, _) => t, | |
170 | _ => return false, | |
171 | }; | |
172 | match poly_trait.trait_ { | |
173 | clean::ResolvedPath { did, .. } => { | |
174 | trait_is_same_or_supertrait(cx, did, trait_) | |
175 | } | |
176 | _ => false, | |
177 | } | |
178 | }) | |
179 | } |