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.
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.
11 //! Simplification of where clauses and parameter bounds into a prettier and
12 //! more canonical form.
14 //! Currently all cross-crate-inlined function use `middle::ty` to reconstruct
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:
20 //! where T: Trait<Foo=Bar>
24 //! where T: Trait, <T as Trait>::Foo = Bar
26 //! This module attempts to reconstruct the original where and/or parameter
27 //! bounds by special casing scenarios such as these. Fun!
30 use std
::collections
::HashMap
;
32 use rustc
::middle
::subst
;
33 use rustc
::middle
::ty
;
36 use clean
::PathParameters
as PP
;
37 use clean
::WherePredicate
as WP
;
38 use clean
::{self, Clean}
;
41 pub fn where_clauses(cx
: &DocContext
, clauses
: Vec
<WP
>) -> Vec
<WP
> {
42 // First, partition the where clause into its separate components
43 let mut params
= HashMap
::new();
44 let mut lifetimes
= Vec
::new();
45 let mut equalities
= Vec
::new();
46 let mut tybounds
= Vec
::new();
47 for clause
in clauses
{
49 WP
::BoundPredicate { ty, bounds }
=> {
51 clean
::Generic(s
) => params
.entry(s
).or_insert(Vec
::new())
53 t
=> tybounds
.push((t
, ty_bounds(bounds
))),
56 WP
::RegionPredicate { lifetime, bounds }
=> {
57 lifetimes
.push((lifetime
, bounds
));
59 WP
::EqPredicate { lhs, rhs }
=> equalities
.push((lhs
, rhs
)),
63 // Simplify the type parameter bounds on all the generics
64 let mut params
= params
.into_iter().map(|(k
, v
)| {
66 }).collect
::<HashMap
<_
, _
>>();
68 // Look for equality predicates on associated types that can be merged into
69 // general bound predicates
70 equalities
.retain(|&(ref lhs
, ref rhs
)| {
71 let (self_
, trait_
, name
) = match *lhs
{
72 clean
::QPath { ref self_type, ref trait_, ref name }
=> {
73 (self_type
, trait_
, name
)
77 let generic
= match **self_
{
78 clean
::Generic(ref s
) => s
,
81 let trait_did
= match **trait_
{
82 clean
::ResolvedPath { did, .. }
=> did
,
85 let bounds
= match params
.get_mut(generic
) {
89 !bounds
.iter_mut().any(|b
| {
90 let trait_ref
= match *b
{
91 clean
::TraitBound(ref mut tr
, _
) => tr
,
92 clean
::RegionBound(..) => return false,
94 let (did
, path
) = match trait_ref
.trait_
{
95 clean
::ResolvedPath { did, ref mut path, ..}
=> (did
, path
),
98 // If this QPath's trait `trait_did` is the same as, or a supertrait
99 // of, the bound's trait `did` then we can keep going, otherwise
100 // this is just a plain old equality bound.
101 if !trait_is_same_or_supertrait(cx
, did
, trait_did
) {
104 let last
= path
.segments
.last_mut().unwrap();
106 PP
::AngleBracketed { ref mut bindings, .. }
=> {
107 bindings
.push(clean
::TypeBinding
{
112 PP
::Parenthesized { ref mut output, .. }
=> {
113 assert
!(output
.is_none());
114 *output
= Some(rhs
.clone());
121 // And finally, let's reassemble everything
122 let mut clauses
= Vec
::new();
123 clauses
.extend(lifetimes
.into_iter().map(|(lt
, bounds
)| {
124 WP
::RegionPredicate { lifetime: lt, bounds: bounds }
126 clauses
.extend(params
.into_iter().map(|(k
, v
)| {
128 ty
: clean
::Generic(k
),
132 clauses
.extend(tybounds
.into_iter().map(|(ty
, bounds
)| {
133 WP
::BoundPredicate { ty: ty, bounds: bounds }
135 clauses
.extend(equalities
.into_iter().map(|(lhs
, rhs
)| {
136 WP
::EqPredicate { lhs: lhs, rhs: rhs }
141 pub fn ty_params(mut params
: Vec
<clean
::TyParam
>) -> Vec
<clean
::TyParam
> {
142 for param
in &mut params
{
143 param
.bounds
= ty_bounds(mem
::replace(&mut param
.bounds
, Vec
::new()));
148 fn ty_bounds(bounds
: Vec
<clean
::TyParamBound
>) -> Vec
<clean
::TyParamBound
> {
152 fn trait_is_same_or_supertrait(cx
: &DocContext
, child
: ast
::DefId
,
153 trait_
: ast
::DefId
) -> bool
{
157 let def
= ty
::lookup_trait_def(cx
.tcx(), child
);
158 let predicates
= ty
::lookup_predicates(cx
.tcx(), child
);
159 let generics
= (&def
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
);
160 generics
.where_predicates
.iter().filter_map(|pred
| {
162 clean
::WherePredicate
::BoundPredicate
{
163 ty
: clean
::Generic(ref s
),
165 } if *s
== "Self" => Some(bounds
),
168 }).flat_map(|bounds
| bounds
).any(|bound
| {
169 let poly_trait
= match *bound
{
170 clean
::TraitBound(ref t
, _
) => t
,
173 match poly_trait
.trait_
{
174 clean
::ResolvedPath { did, .. }
=> {
175 trait_is_same_or_supertrait(cx
, did
, trait_
)