Skip to content

Commit 11f26f9

Browse files
committed
rustdoc: Simplify cross-crate where clauses
Add a custom module to rustdoc which simplifies the output of `middle::ty` into a more readable form which tends to be written down anyway! Closes #20646
1 parent 8874fd4 commit 11f26f9

File tree

4 files changed

+199
-10
lines changed

4 files changed

+199
-10
lines changed

src/librustdoc/clean/mod.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ use visit_ast;
5858
pub static SCHEMA_VERSION: &'static str = "0.8.3";
5959

6060
mod inline;
61+
mod simplify;
6162

6263
// extract the stability index for a node from tcx, if possible
6364
fn get_stability(cx: &DocContext, def_id: ast::DefId) -> Option<Stability> {
@@ -891,8 +892,9 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>,
891892

892893
let (gens, preds, space) = *self;
893894

894-
// Bounds in the type_params and lifetimes fields are repeated in the predicates
895-
// field (see rustc_typeck::collect::ty_generics), so remove them.
895+
// Bounds in the type_params and lifetimes fields are repeated in the
896+
// predicates field (see rustc_typeck::collect::ty_generics), so remove
897+
// them.
896898
let stripped_typarams = gens.types.get_slice(space).iter().map(|tp| {
897899
tp.clean(cx)
898900
}).collect::<Vec<_>>();
@@ -902,11 +904,12 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>,
902904
srp.clean(cx)
903905
}).collect::<Vec<_>>();
904906

905-
let where_predicates = preds.predicates.get_slice(space).to_vec().clean(cx);
907+
let where_predicates = preds.predicates.get_slice(space)
908+
.to_vec().clean(cx);
906909

907-
// Type parameters have a Sized bound by default unless removed with ?Sized.
908-
// Scan through the predicates and mark any type parameter with a Sized
909-
// bound, removing the bounds as we find them.
910+
// Type parameters have a Sized bound by default unless removed with
911+
// ?Sized. Scan through the predicates and mark any type parameter with
912+
// a Sized bound, removing the bounds as we find them.
910913
let mut sized_params = HashSet::new();
911914
let mut where_predicates = where_predicates.into_iter().filter_map(|pred| {
912915
if let WP::BoundPredicate { ty: Type::Generic(ref g), ref bounds } = pred {
@@ -918,8 +921,8 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>,
918921
Some(pred)
919922
}).collect::<Vec<_>>();
920923

921-
// Finally, run through the type parameters again and insert a ?Sized unbound for
922-
// any we didn't find to be Sized.
924+
// Finally, run through the type parameters again and insert a ?Sized
925+
// unbound for any we didn't find to be Sized.
923926
for tp in &stripped_typarams {
924927
if !sized_params.contains(&tp.name) {
925928
let mut sized_bound = ty::BuiltinBound::BoundSized.clean(cx);
@@ -938,9 +941,9 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>,
938941
// and instead see `where T: Foo + Bar + Sized + 'a`
939942

940943
Generics {
941-
type_params: stripped_typarams,
944+
type_params: simplify::ty_params(stripped_typarams),
942945
lifetimes: stripped_lifetimes,
943-
where_predicates: where_predicates
946+
where_predicates: simplify::where_clauses(where_predicates),
944947
}
945948
}
946949
}

src/librustdoc/clean/simplify.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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+
}

src/test/auxiliary/issue-20646.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
pub trait Trait {
12+
type Output;
13+
}
14+
15+
pub fn fun<T>(_: T) where T: Trait<Output=i32> {}

src/test/rustdoc/issue-20646.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
// aux-build:issue-20646.rs
12+
13+
#![feature(associated_types)]
14+
15+
extern crate issue_20646;
16+
17+
// @has issue_20646/trait.Trait.html \
18+
// '//*[@id="associatedtype.Output"]' \
19+
// 'type Output'
20+
pub trait Trait {
21+
type Output;
22+
}
23+
24+
// @has issue_20646/fn.fun.html \
25+
// '//*[@class="rust fn"]' 'where T: Trait<Output=i32>'
26+
pub fn fun<T>(_: T) where T: Trait<Output=i32> {}
27+
28+
pub mod reexport {
29+
// @has issue_20646/reexport/trait.Trait.html \
30+
// '//*[@id="associatedtype.Output"]' \
31+
// 'type Output'
32+
// @has issue_20646/reexport/fn.fun.html \
33+
// '//*[@class="rust fn"]' 'where T: Trait<Output=i32>'
34+
pub use issue_20646::{Trait, fun};
35+
}

0 commit comments

Comments
 (0)