|
| 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 | +//! |
| 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: |
| 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 | + |
| 32 | +use clean; |
| 33 | +use clean::WherePredicate as WP; |
| 34 | +use clean::PathParameters as PP; |
| 35 | + |
| 36 | +pub fn where_clauses(clauses: Vec<WP>) -> Vec<WP> { |
| 37 | + // First, partition the where clause into its separate components |
| 38 | + let mut params = HashMap::new(); |
| 39 | + let mut lifetimes = Vec::new(); |
| 40 | + let mut equalities = Vec::new(); |
| 41 | + let mut tybounds = Vec::new(); |
| 42 | + for clause in clauses { |
| 43 | + match clause { |
| 44 | + WP::BoundPredicate { ty, bounds } => { |
| 45 | + match ty { |
| 46 | + clean::Generic(s) => params.entry(s).or_insert(Vec::new()) |
| 47 | + .extend(bounds), |
| 48 | + t => tybounds.push((t, ty_bounds(bounds))), |
| 49 | + } |
| 50 | + } |
| 51 | + WP::RegionPredicate { lifetime, bounds } => { |
| 52 | + lifetimes.push((lifetime, bounds)); |
| 53 | + } |
| 54 | + WP::EqPredicate { lhs, rhs } => equalities.push((lhs, rhs)), |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + // Simplify the type parameter bounds on all the generics |
| 59 | + let mut params = params.into_iter().map(|(k, v)| { |
| 60 | + (k, ty_bounds(v)) |
| 61 | + }).collect::<HashMap<_, _>>(); |
| 62 | + |
| 63 | + // Look for equality predicates on associated types that can be merged into |
| 64 | + // general bound predicates |
| 65 | + equalities.retain(|&(ref lhs, ref rhs)| { |
| 66 | + let (self_, trait_, name) = match *lhs { |
| 67 | + clean::QPath { ref self_type, ref trait_, ref name } => { |
| 68 | + (self_type, trait_, name) |
| 69 | + } |
| 70 | + _ => return true, |
| 71 | + }; |
| 72 | + let generic = match **self_ { |
| 73 | + clean::Generic(ref s) => s, |
| 74 | + _ => return true, |
| 75 | + }; |
| 76 | + let trait_did = match **trait_ { |
| 77 | + clean::ResolvedPath { did, .. } => did, |
| 78 | + _ => return true, |
| 79 | + }; |
| 80 | + let bounds = match params.get_mut(generic) { |
| 81 | + Some(bound) => bound, |
| 82 | + None => return true, |
| 83 | + }; |
| 84 | + !bounds.iter_mut().any(|b| { |
| 85 | + let trait_ref = match *b { |
| 86 | + clean::TraitBound(ref mut tr, _) => tr, |
| 87 | + clean::RegionBound(..) => return false, |
| 88 | + }; |
| 89 | + let (did, path) = match trait_ref.trait_ { |
| 90 | + clean::ResolvedPath { did, ref mut path, ..} => (did, path), |
| 91 | + _ => return false, |
| 92 | + }; |
| 93 | + if did != trait_did { return false } |
| 94 | + let last = path.segments.last_mut().unwrap(); |
| 95 | + let bindings = match last.params { |
| 96 | + PP::AngleBracketed { ref mut bindings, .. } => bindings, |
| 97 | + PP::Parenthesized { .. } => return false, |
| 98 | + }; |
| 99 | + bindings.push(clean::TypeBinding { |
| 100 | + name: name.clone(), |
| 101 | + ty: rhs.clone(), |
| 102 | + }); |
| 103 | + true |
| 104 | + }) |
| 105 | + }); |
| 106 | + |
| 107 | + // And finally, let's reassemble everything |
| 108 | + let mut clauses = Vec::new(); |
| 109 | + clauses.extend(lifetimes.into_iter().map(|(lt, bounds)| { |
| 110 | + WP::RegionPredicate { lifetime: lt, bounds: bounds } |
| 111 | + })); |
| 112 | + clauses.extend(params.into_iter().map(|(k, v)| { |
| 113 | + WP::BoundPredicate { |
| 114 | + ty: clean::Generic(k), |
| 115 | + bounds: v, |
| 116 | + } |
| 117 | + })); |
| 118 | + clauses.extend(tybounds.into_iter().map(|(ty, bounds)| { |
| 119 | + WP::BoundPredicate { ty: ty, bounds: bounds } |
| 120 | + })); |
| 121 | + clauses.extend(equalities.into_iter().map(|(lhs, rhs)| { |
| 122 | + WP::EqPredicate { lhs: lhs, rhs: rhs } |
| 123 | + })); |
| 124 | + clauses |
| 125 | +} |
| 126 | + |
| 127 | +pub fn ty_params(mut params: Vec<clean::TyParam>) -> Vec<clean::TyParam> { |
| 128 | + for param in params.iter_mut() { |
| 129 | + param.bounds = ty_bounds(mem::replace(&mut param.bounds, Vec::new())); |
| 130 | + } |
| 131 | + return params; |
| 132 | +} |
| 133 | + |
| 134 | +fn ty_bounds(bounds: Vec<clean::TyParamBound>) -> Vec<clean::TyParamBound> { |
| 135 | + bounds |
| 136 | +} |
0 commit comments