diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 002c6d7460bf2cb33ffbc2ad939ff29c20269312..630dc18dca79f3e56c62296fa01a487348c80c5a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -58,6 +58,7 @@ pub static SCHEMA_VERSION: &'static str = "0.8.3"; mod inline; +mod simplify; // extract the stability index for a node from tcx, if possible fn get_stability(cx: &DocContext, def_id: ast::DefId) -> Option { @@ -891,8 +892,9 @@ fn has_sized_bound(bounds: &[TyParamBound], cx: &DocContext) -> bool { let (gens, preds, space) = *self; - // Bounds in the type_params and lifetimes fields are repeated in the predicates - // field (see rustc_typeck::collect::ty_generics), so remove them. + // Bounds in the type_params and lifetimes fields are repeated in the + // predicates field (see rustc_typeck::collect::ty_generics), so remove + // them. let stripped_typarams = gens.types.get_slice(space).iter().map(|tp| { tp.clean(cx) }).collect::>(); @@ -902,11 +904,12 @@ fn has_sized_bound(bounds: &[TyParamBound], cx: &DocContext) -> bool { srp.clean(cx) }).collect::>(); - let where_predicates = preds.predicates.get_slice(space).to_vec().clean(cx); + let where_predicates = preds.predicates.get_slice(space) + .to_vec().clean(cx); - // Type parameters have a Sized bound by default unless removed with ?Sized. - // Scan through the predicates and mark any type parameter with a Sized - // bound, removing the bounds as we find them. + // Type parameters have a Sized bound by default unless removed with + // ?Sized. Scan through the predicates and mark any type parameter with + // a Sized bound, removing the bounds as we find them. let mut sized_params = HashSet::new(); let mut where_predicates = where_predicates.into_iter().filter_map(|pred| { if let WP::BoundPredicate { ty: Type::Generic(ref g), ref bounds } = pred { @@ -918,8 +921,8 @@ fn has_sized_bound(bounds: &[TyParamBound], cx: &DocContext) -> bool { Some(pred) }).collect::>(); - // Finally, run through the type parameters again and insert a ?Sized unbound for - // any we didn't find to be Sized. + // Finally, run through the type parameters again and insert a ?Sized + // unbound for any we didn't find to be Sized. for tp in &stripped_typarams { if !sized_params.contains(&tp.name) { let mut sized_bound = ty::BuiltinBound::BoundSized.clean(cx); @@ -938,9 +941,9 @@ fn has_sized_bound(bounds: &[TyParamBound], cx: &DocContext) -> bool { // and instead see `where T: Foo + Bar + Sized + 'a` Generics { - type_params: stripped_typarams, + type_params: simplify::ty_params(stripped_typarams), lifetimes: stripped_lifetimes, - where_predicates: where_predicates + where_predicates: simplify::where_clauses(where_predicates), } } } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs new file mode 100644 index 0000000000000000000000000000000000000000..367551fa1491c3e88978ab9c070e664f7264a12a --- /dev/null +++ b/src/librustdoc/clean/simplify.rs @@ -0,0 +1,136 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Simplification of where clauses and parameter bounds into a prettier and +//! more canonical form. +//! +//! Currently all cross-crate-inlined function use `middle::ty` to reconstruct +//! the AST (e.g. see all of `clean::inline`), but this is not always a +//! non-lossy transformation. The current format of storage for where clauses +//! for functions and such is simply a list of predicates. One example of this +//! is that the AST predicate of: +//! +//! where T: Trait +//! +//! is encoded as: +//! +//! where T: Trait, ::Foo = Bar +//! +//! This module attempts to reconstruct the original where and/or parameter +//! bounds by special casing scenarios such as these. Fun! + +use std::mem; +use std::collections::HashMap; + +use clean; +use clean::WherePredicate as WP; +use clean::PathParameters as PP; + +pub fn where_clauses(clauses: Vec) -> Vec { + // First, partition the where clause into its separate components + let mut params = HashMap::new(); + let mut lifetimes = Vec::new(); + let mut equalities = Vec::new(); + let mut tybounds = Vec::new(); + for clause in clauses { + match clause { + WP::BoundPredicate { ty, bounds } => { + match ty { + clean::Generic(s) => params.entry(s).or_insert(Vec::new()) + .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)) + }).collect::>(); + + // Look for equality predicates on associated types that can be merged into + // general bound predicates + equalities.retain(|&(ref lhs, ref rhs)| { + let (self_, trait_, name) = match *lhs { + clean::QPath { ref self_type, ref trait_, ref name } => { + (self_type, trait_, name) + } + _ => return true, + }; + let generic = match **self_ { + clean::Generic(ref s) => s, + _ => return true, + }; + let trait_did = match **trait_ { + clean::ResolvedPath { did, .. } => did, + _ => return true, + }; + let bounds = match params.get_mut(generic) { + Some(bound) => bound, + None => return true, + }; + !bounds.iter_mut().any(|b| { + let trait_ref = match *b { + clean::TraitBound(ref mut tr, _) => tr, + clean::RegionBound(..) => return false, + }; + let (did, path) = match trait_ref.trait_ { + clean::ResolvedPath { did, ref mut path, ..} => (did, path), + _ => return false, + }; + if did != trait_did { return false } + let last = path.segments.last_mut().unwrap(); + let bindings = match last.params { + PP::AngleBracketed { ref mut bindings, .. } => bindings, + PP::Parenthesized { .. } => return false, + }; + bindings.push(clean::TypeBinding { + name: name.clone(), + ty: rhs.clone(), + }); + true + }) + }); + + // 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 +} + +pub fn ty_params(mut params: Vec) -> Vec { + for param in params.iter_mut() { + param.bounds = ty_bounds(mem::replace(&mut param.bounds, Vec::new())); + } + return params; +} + +fn ty_bounds(bounds: Vec) -> Vec { + bounds +} diff --git a/src/test/auxiliary/issue-20646.rs b/src/test/auxiliary/issue-20646.rs new file mode 100644 index 0000000000000000000000000000000000000000..150d8018f088831ef4d4a24c0862983b50e3ec32 --- /dev/null +++ b/src/test/auxiliary/issue-20646.rs @@ -0,0 +1,15 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub trait Trait { + type Output; +} + +pub fn fun(_: T) where T: Trait {} diff --git a/src/test/rustdoc/issue-20646.rs b/src/test/rustdoc/issue-20646.rs new file mode 100644 index 0000000000000000000000000000000000000000..d2ba85651d8ebaeea559950b8aed81425a1675cc --- /dev/null +++ b/src/test/rustdoc/issue-20646.rs @@ -0,0 +1,35 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:issue-20646.rs + +#![feature(associated_types)] + +extern crate issue_20646; + +// @has issue_20646/trait.Trait.html \ +// '//*[@id="associatedtype.Output"]' \ +// 'type Output' +pub trait Trait { + type Output; +} + +// @has issue_20646/fn.fun.html \ +// '//*[@class="rust fn"]' 'where T: Trait' +pub fn fun(_: T) where T: Trait {} + +pub mod reexport { + // @has issue_20646/reexport/trait.Trait.html \ + // '//*[@id="associatedtype.Output"]' \ + // 'type Output' + // @has issue_20646/reexport/fn.fun.html \ + // '//*[@class="rust fn"]' 'where T: Trait' + pub use issue_20646::{Trait, fun}; +}