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 `rustc::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
::hir
::def_id
::DefId
;
35 use clean
::PathParameters
as PP
;
36 use clean
::WherePredicate
as WP
;
37 use clean
::{self, Clean}
;
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
{
48 WP
::BoundPredicate { ty, bounds }
=> {
50 clean
::Generic(s
) => params
.entry(s
).or_insert(Vec
::new())
52 t
=> tybounds
.push((t
, ty_bounds(bounds
))),
55 WP
::RegionPredicate { lifetime, bounds }
=> {
56 lifetimes
.push((lifetime
, bounds
));
58 WP
::EqPredicate { lhs, rhs }
=> equalities
.push((lhs
, rhs
)),
62 // Simplify the type parameter bounds on all the generics
63 let mut params
= params
.into_iter().map(|(k
, v
)| {
65 }).collect
::<HashMap
<_
, _
>>();
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
)
76 let generic
= match **self_
{
77 clean
::Generic(ref s
) => s
,
80 let trait_did
= match **trait_
{
81 clean
::ResolvedPath { did, .. }
=> did
,
84 let bounds
= match params
.get_mut(generic
) {
88 !bounds
.iter_mut().any(|b
| {
89 let trait_ref
= match *b
{
90 clean
::TraitBound(ref mut tr
, _
) => tr
,
91 clean
::RegionBound(..) => return false,
93 let (did
, path
) = match trait_ref
.trait_
{
94 clean
::ResolvedPath { did, ref mut path, ..}
=> (did
, path
),
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
) {
103 let last
= path
.segments
.last_mut().unwrap();
105 PP
::AngleBracketed { ref mut bindings, .. }
=> {
106 bindings
.push(clean
::TypeBinding
{
111 PP
::Parenthesized { ref mut output, .. }
=> {
112 assert
!(output
.is_none());
113 *output
= Some(rhs
.clone());
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 }
125 clauses
.extend(params
.into_iter().map(|(k
, v
)| {
127 ty
: clean
::Generic(k
),
131 clauses
.extend(tybounds
.into_iter().map(|(ty
, bounds
)| {
132 WP
::BoundPredicate { ty: ty, bounds: bounds }
134 clauses
.extend(equalities
.into_iter().map(|(lhs
, rhs
)| {
135 WP
::EqPredicate { lhs: lhs, rhs: rhs }
140 pub fn ty_params(mut params
: Vec
<clean
::TyParam
>) -> Vec
<clean
::TyParam
> {
141 for param
in &mut params
{
142 param
.bounds
= ty_bounds(mem
::replace(&mut param
.bounds
, Vec
::new()));
147 fn ty_bounds(bounds
: Vec
<clean
::TyParamBound
>) -> Vec
<clean
::TyParamBound
> {
151 fn trait_is_same_or_supertrait(cx
: &DocContext
, child
: DefId
,
152 trait_
: DefId
) -> bool
{
156 let def
= cx
.tcx().lookup_trait_def(child
);
157 let predicates
= cx
.tcx().lookup_predicates(child
);
158 let generics
= (&def
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
);
159 generics
.where_predicates
.iter().filter_map(|pred
| {
161 clean
::WherePredicate
::BoundPredicate
{
162 ty
: clean
::Generic(ref s
),
164 } if *s
== "Self" => Some(bounds
),
167 }).flat_map(|bounds
| bounds
).any(|bound
| {
168 let poly_trait
= match *bound
{
169 clean
::TraitBound(ref t
, _
) => t
,
172 match poly_trait
.trait_
{
173 clean
::ResolvedPath { did, .. }
=> {
174 trait_is_same_or_supertrait(cx
, did
, trait_
)