Skip to content

Commit 4aba7de

Browse files
Clean generic impls code
1 parent 579adf8 commit 4aba7de

File tree

8 files changed

+508
-435
lines changed

8 files changed

+508
-435
lines changed

src/Cargo.lock

Lines changed: 41 additions & 122 deletions
Large diffs are not rendered by default.

src/librustdoc/clean/auto_trait.rs

Lines changed: 24 additions & 290 deletions
Large diffs are not rendered by default.

src/librustdoc/clean/blanket_impl.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright 2018 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+
use rustc::hir;
12+
use rustc::traits;
13+
use rustc::ty::ToPredicate;
14+
use rustc::ty::subst::Subst;
15+
use rustc::infer::InferOk;
16+
use syntax_pos::DUMMY_SP;
17+
18+
use core::DocAccessLevels;
19+
20+
use super::*;
21+
22+
use self::def_ctor::{get_def_ctor_from_def_id, get_def_ctor_from_node_id};
23+
use self::finder_trait::Finder;
24+
25+
pub struct BlanketImplFinder<'a, 'tcx: 'a, 'rcx: 'a> {
26+
pub cx: &'a core::DocContext<'a, 'tcx, 'rcx>,
27+
}
28+
29+
impl<'a, 'tcx, 'rcx> BlanketImplFinder <'a, 'tcx, 'rcx> {
30+
pub fn new(cx: &'a core::DocContext<'a, 'tcx, 'rcx>) -> Self {
31+
BlanketImplFinder { cx }
32+
}
33+
34+
pub fn get_with_def_id(&self, def_id: DefId) -> Vec<Item> {
35+
get_def_ctor_from_def_id(&self.cx, def_id, &|def_ctor| {
36+
self.get_blanket_impls(def_id, &def_ctor, None)
37+
})
38+
}
39+
40+
pub fn get_with_node_id(&self, id: ast::NodeId, name: String) -> Vec<Item> {
41+
get_def_ctor_from_node_id(&self.cx, id, name, &|def_ctor, name| {
42+
let did = self.cx.tcx.hir.local_def_id(id);
43+
self.get_blanket_impls(did, &def_ctor, Some(name))
44+
})
45+
}
46+
47+
pub fn get_blanket_impls<F>(
48+
&self,
49+
def_id: DefId,
50+
def_ctor: &F,
51+
name: Option<String>,
52+
) -> Vec<Item>
53+
where F: Fn(DefId) -> Def {
54+
let mut impls = Vec::new();
55+
if self.cx
56+
.tcx
57+
.get_attrs(def_id)
58+
.lists("doc")
59+
.has_word("hidden")
60+
{
61+
debug!(
62+
"get_blanket_impls(def_id={:?}, def_ctor=...): item has doc('hidden'), \
63+
aborting",
64+
def_id
65+
);
66+
return impls;
67+
}
68+
if self.cx.access_levels.borrow().is_doc_reachable(def_id) {
69+
let generics = self.cx.tcx.generics_of(def_id);
70+
let ty = self.cx.tcx.type_of(def_id);
71+
let real_name = name.clone().map(|name| Ident::from_str(&name));
72+
let param_env = self.cx.tcx.param_env(def_id);
73+
for &trait_def_id in self.cx.all_traits.iter() {
74+
if !self.cx.access_levels.borrow().is_doc_reachable(trait_def_id) ||
75+
self.cx.generated_synthetics
76+
.borrow_mut()
77+
.get(&(def_id, trait_def_id))
78+
.is_some() {
79+
continue
80+
}
81+
self.cx.tcx.for_each_relevant_impl(trait_def_id, ty, |impl_def_id| {
82+
self.cx.tcx.infer_ctxt().enter(|infcx| {
83+
let t_generics = infcx.tcx.generics_of(impl_def_id);
84+
let trait_ref = infcx.tcx.impl_trait_ref(impl_def_id)
85+
.expect("Cannot get impl trait");
86+
87+
match infcx.tcx.type_of(impl_def_id).sty {
88+
::rustc::ty::TypeVariants::TyParam(_) => {},
89+
_ => return,
90+
}
91+
92+
let substs = infcx.fresh_substs_for_item(DUMMY_SP, def_id);
93+
let ty = ty.subst(infcx.tcx, substs);
94+
let param_env = param_env.subst(infcx.tcx, substs);
95+
96+
let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
97+
let trait_ref = trait_ref.subst(infcx.tcx, impl_substs);
98+
99+
// Require the type the impl is implemented on to match
100+
// our type, and ignore the impl if there was a mismatch.
101+
let cause = traits::ObligationCause::dummy();
102+
let eq_result = infcx.at(&cause, param_env)
103+
.eq(trait_ref.self_ty(), ty);
104+
if let Ok(InferOk { value: (), obligations }) = eq_result {
105+
// FIXME(eddyb) ignoring `obligations` might cause false positives.
106+
drop(obligations);
107+
108+
let may_apply = infcx.predicate_may_hold(&traits::Obligation::new(
109+
cause.clone(),
110+
param_env,
111+
trait_ref.to_predicate(),
112+
));
113+
if !may_apply {
114+
return
115+
}
116+
self.cx.generated_synthetics.borrow_mut()
117+
.insert((def_id, trait_def_id));
118+
let trait_ = hir::TraitRef {
119+
path: get_path_for_type(infcx.tcx,
120+
trait_def_id,
121+
hir::def::Def::Trait),
122+
ref_id: ast::DUMMY_NODE_ID,
123+
};
124+
let provided_trait_methods =
125+
infcx.tcx.provided_trait_methods(trait_def_id)
126+
.into_iter()
127+
.map(|meth| meth.ident.to_string())
128+
.collect();
129+
130+
let ty = self.get_real_ty(def_id, def_ctor, &real_name, generics);
131+
let predicates = infcx.tcx.predicates_of(impl_def_id);
132+
133+
impls.push(Item {
134+
source: infcx.tcx.def_span(impl_def_id).clean(self.cx),
135+
name: None,
136+
attrs: Default::default(),
137+
visibility: None,
138+
def_id: self.next_def_id(impl_def_id.krate),
139+
stability: None,
140+
deprecation: None,
141+
inner: ImplItem(Impl {
142+
unsafety: hir::Unsafety::Normal,
143+
generics: (t_generics, &predicates).clean(self.cx),
144+
provided_trait_methods,
145+
trait_: Some(trait_.clean(self.cx)),
146+
for_: ty.clean(self.cx),
147+
items: infcx.tcx.associated_items(impl_def_id)
148+
.collect::<Vec<_>>()
149+
.clean(self.cx),
150+
polarity: None,
151+
synthetic: false,
152+
blanket_impl: Some(infcx.tcx.type_of(impl_def_id)
153+
.clean(self.cx)),
154+
}),
155+
});
156+
debug!("{:?} => {}", trait_ref, may_apply);
157+
}
158+
});
159+
});
160+
}
161+
}
162+
impls
163+
}
164+
}
165+
166+
impl<'a, 'tcx: 'a, 'rcx: 'a> Finder<'a, 'tcx, 'rcx> for BlanketImplFinder<'a, 'tcx, 'rcx> {
167+
fn get_cx(&self) -> &DocContext<'a, 'tcx, 'rcx> {
168+
&self.cx
169+
}
170+
}

src/librustdoc/clean/def_ctor.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2018 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+
use core::DocContext;
12+
13+
use super::*;
14+
15+
pub fn get_def_ctor_from_def_id<F>(cx: &DocContext,
16+
def_id: DefId,
17+
callback: &F,
18+
) -> Vec<Item>
19+
where F: Fn(&Fn(DefId) -> Def) -> Vec<Item> {
20+
let ty = cx.tcx.type_of(def_id);
21+
22+
match ty.sty {
23+
ty::TyAdt(adt, _) => callback(&match adt.adt_kind() {
24+
AdtKind::Struct => Def::Struct,
25+
AdtKind::Enum => Def::Enum,
26+
AdtKind::Union => Def::Union,
27+
}),
28+
ty::TyInt(_) |
29+
ty::TyUint(_) |
30+
ty::TyFloat(_) |
31+
ty::TyStr |
32+
ty::TyBool |
33+
ty::TyChar => callback(&move |_: DefId| {
34+
match ty.sty {
35+
ty::TyInt(x) => Def::PrimTy(hir::TyInt(x)),
36+
ty::TyUint(x) => Def::PrimTy(hir::TyUint(x)),
37+
ty::TyFloat(x) => Def::PrimTy(hir::TyFloat(x)),
38+
ty::TyStr => Def::PrimTy(hir::TyStr),
39+
ty::TyBool => Def::PrimTy(hir::TyBool),
40+
ty::TyChar => Def::PrimTy(hir::TyChar),
41+
_ => unreachable!(),
42+
}
43+
}),
44+
_ => {
45+
debug!("Unexpected type {:?}", def_id);
46+
Vec::new()
47+
}
48+
}
49+
}
50+
51+
pub fn get_def_ctor_from_node_id<F>(cx: &DocContext,
52+
id: ast::NodeId,
53+
name: String,
54+
callback: &F,
55+
) -> Vec<Item>
56+
where F: Fn(&Fn(DefId) -> Def, String) -> Vec<Item> {
57+
let item = &cx.tcx.hir.expect_item(id).node;
58+
59+
callback(&match *item {
60+
hir::ItemKind::Struct(_, _) => Def::Struct,
61+
hir::ItemKind::Union(_, _) => Def::Union,
62+
hir::ItemKind::Enum(_, _) => Def::Enum,
63+
_ => panic!("Unexpected type {:?} {:?}", item, id),
64+
}, name)
65+
}

src/librustdoc/clean/finder_trait.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// Copyright 2018 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+
use rustc::hir;
12+
use syntax_pos::DUMMY_SP;
13+
14+
use super::*;
15+
16+
pub trait Finder<'a, 'tcx: 'a, 'rcx: 'a> {
17+
fn get_cx(&self) -> &DocContext<'a, 'tcx, 'rcx>;
18+
19+
// This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly
20+
// refactoring either librustdoc or librustc. In particular, allowing new DefIds to be
21+
// registered after the AST is constructed would require storing the defid mapping in a
22+
// RefCell, decreasing the performance for normal compilation for very little gain.
23+
//
24+
// Instead, we construct 'fake' def ids, which start immediately after the last DefId in
25+
// DefIndexAddressSpace::Low. In the Debug impl for clean::Item, we explicitly check for fake
26+
// def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds
27+
fn next_def_id(&self, crate_num: CrateNum) -> DefId {
28+
let start_def_id = {
29+
let next_id = if crate_num == LOCAL_CRATE {
30+
self.get_cx()
31+
.tcx
32+
.hir
33+
.definitions()
34+
.def_path_table()
35+
.next_id(DefIndexAddressSpace::Low)
36+
} else {
37+
self.get_cx()
38+
.cstore
39+
.def_path_table(crate_num)
40+
.next_id(DefIndexAddressSpace::Low)
41+
};
42+
43+
DefId {
44+
krate: crate_num,
45+
index: next_id,
46+
}
47+
};
48+
49+
let mut fake_ids = self.get_cx().fake_def_ids.borrow_mut();
50+
51+
let def_id = fake_ids.entry(crate_num).or_insert(start_def_id).clone();
52+
fake_ids.insert(
53+
crate_num,
54+
DefId {
55+
krate: crate_num,
56+
index: DefIndex::from_array_index(
57+
def_id.index.as_array_index() + 1,
58+
def_id.index.address_space(),
59+
),
60+
},
61+
);
62+
63+
MAX_DEF_ID.with(|m| {
64+
m.borrow_mut()
65+
.entry(def_id.krate.clone())
66+
.or_insert(start_def_id);
67+
});
68+
69+
self.get_cx().all_fake_def_ids.borrow_mut().insert(def_id);
70+
71+
def_id.clone()
72+
}
73+
74+
fn get_real_ty<F>(&self,
75+
def_id: DefId,
76+
def_ctor: &F,
77+
real_name: &Option<Ident>,
78+
generics: &ty::Generics,
79+
) -> hir::Ty
80+
where F: Fn(DefId) -> Def {
81+
let path = get_path_for_type(self.get_cx().tcx, def_id, def_ctor);
82+
let mut segments = path.segments.into_vec();
83+
let last = segments.pop().expect("segments were empty");
84+
85+
segments.push(hir::PathSegment::new(
86+
real_name.unwrap_or(last.ident),
87+
self.generics_to_path_params(generics.clone()),
88+
false,
89+
));
90+
91+
let new_path = hir::Path {
92+
span: path.span,
93+
def: path.def,
94+
segments: HirVec::from_vec(segments),
95+
};
96+
97+
hir::Ty {
98+
id: ast::DUMMY_NODE_ID,
99+
node: hir::TyKind::Path(hir::QPath::Resolved(None, P(new_path))),
100+
span: DUMMY_SP,
101+
hir_id: hir::DUMMY_HIR_ID,
102+
}
103+
}
104+
105+
fn generics_to_path_params(&self, generics: ty::Generics) -> hir::GenericArgs {
106+
let mut args = vec![];
107+
108+
for param in generics.params.iter() {
109+
match param.kind {
110+
ty::GenericParamDefKind::Lifetime => {
111+
let name = if param.name == "" {
112+
hir::ParamName::Plain(keywords::StaticLifetime.ident())
113+
} else {
114+
hir::ParamName::Plain(ast::Ident::from_interned_str(param.name))
115+
};
116+
117+
args.push(hir::GenericArg::Lifetime(hir::Lifetime {
118+
id: ast::DUMMY_NODE_ID,
119+
span: DUMMY_SP,
120+
name: hir::LifetimeName::Param(name),
121+
}));
122+
}
123+
ty::GenericParamDefKind::Type {..} => {
124+
args.push(hir::GenericArg::Type(self.ty_param_to_ty(param.clone())));
125+
}
126+
}
127+
}
128+
129+
hir::GenericArgs {
130+
args: HirVec::from_vec(args),
131+
bindings: HirVec::new(),
132+
parenthesized: false,
133+
}
134+
}
135+
136+
fn ty_param_to_ty(&self, param: ty::GenericParamDef) -> hir::Ty {
137+
debug!("ty_param_to_ty({:?}) {:?}", param, param.def_id);
138+
hir::Ty {
139+
id: ast::DUMMY_NODE_ID,
140+
node: hir::TyKind::Path(hir::QPath::Resolved(
141+
None,
142+
P(hir::Path {
143+
span: DUMMY_SP,
144+
def: Def::TyParam(param.def_id),
145+
segments: HirVec::from_vec(vec![
146+
hir::PathSegment::from_ident(Ident::from_interned_str(param.name))
147+
]),
148+
}),
149+
)),
150+
span: DUMMY_SP,
151+
hir_id: hir::DUMMY_HIR_ID,
152+
}
153+
}
154+
}

0 commit comments

Comments
 (0)