Skip to content

Commit 78fabae

Browse files
committed
rustdoc: Handle impl method visibility correctly. rust-lang#5533
1 parent 064e03d commit 78fabae

File tree

1 file changed

+161
-13
lines changed

1 file changed

+161
-13
lines changed

src/librustdoc/prune_private_pass.rs

Lines changed: 161 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
1313
use core::prelude::*;
1414

15+
use extract;
16+
use syntax::ast;
17+
use syntax::ast_map;
1518
use astsrv;
1619
use doc;
1720
use fold::Fold;
@@ -28,12 +31,73 @@ pub fn mk_pass() -> Pass {
2831
}
2932

3033
pub fn run(srv: astsrv::Srv, doc: doc::Doc) -> doc::Doc {
34+
// First strip private methods out of impls
35+
let fold = Fold {
36+
ctxt: srv.clone(),
37+
fold_impl: fold_impl,
38+
.. fold::default_any_fold(srv.clone())
39+
};
40+
let doc = (fold.fold_doc)(&fold, doc);
41+
42+
// Then strip private items and empty impls
3143
let fold = Fold {
3244
ctxt: srv.clone(),
3345
fold_mod: fold_mod,
3446
.. fold::default_any_fold(srv)
3547
};
36-
(fold.fold_doc)(&fold, doc)
48+
let doc = (fold.fold_doc)(&fold, doc);
49+
50+
return doc;
51+
}
52+
53+
fn fold_impl(
54+
fold: &fold::Fold<astsrv::Srv>,
55+
doc: doc::ImplDoc
56+
) -> doc::ImplDoc {
57+
let doc = fold::default_seq_fold_impl(fold, doc);
58+
59+
do astsrv::exec(fold.ctxt.clone()) |ctxt| {
60+
match ctxt.ast_map.get(&doc.item.id) {
61+
ast_map::node_item(item, _) => {
62+
match item.node {
63+
ast::item_impl(_, None, _, ref methods) => {
64+
// Associated impls have complex rules for method visibility
65+
strip_priv_methods(copy doc, *methods, item.vis)
66+
}
67+
ast::item_impl(_, Some(_), _ ,_) => {
68+
// Trait impls don't
69+
copy doc
70+
}
71+
_ => fail!()
72+
}
73+
}
74+
_ => fail!()
75+
}
76+
}
77+
}
78+
79+
fn strip_priv_methods(
80+
doc: doc::ImplDoc,
81+
methods: &[@ast::method],
82+
item_vis: ast::visibility
83+
) -> doc::ImplDoc {
84+
let methods = do (&doc.methods).filtered |method| {
85+
let ast_method = do methods.find |m| {
86+
extract::to_str(m.ident) == method.name
87+
};
88+
fail_unless!(ast_method.is_some());
89+
let ast_method = ast_method.unwrap();
90+
match ast_method.vis {
91+
ast::public => true,
92+
ast::private => false,
93+
ast::inherited => item_vis == ast::public
94+
}
95+
};
96+
97+
doc::ImplDoc {
98+
methods: methods,
99+
.. doc
100+
}
37101
}
38102

39103
fn fold_mod(
@@ -44,28 +108,40 @@ fn fold_mod(
44108

45109
doc::ModDoc {
46110
items: doc.items.filtered(|ItemTag| {
47-
is_visible(fold.ctxt.clone(), ItemTag.item())
111+
match ItemTag {
112+
&doc::ImplTag(ref doc) => {
113+
if doc.trait_types.is_empty() {
114+
// This is an associated impl. We have already pruned the
115+
// non-visible methods. If there are any left then
116+
// retain the impl, otherwise throw it away
117+
!doc.methods.is_empty()
118+
} else {
119+
// This is a trait implementation, make it visible
120+
// NOTE: This is not quite right since this could be an impl
121+
// of a private trait. We can't know that without running
122+
// resolve though.
123+
true
124+
}
125+
}
126+
_ => {
127+
is_visible(fold.ctxt.clone(), ItemTag.item())
128+
}
129+
}
48130
}),
49131
.. doc
50132
}
51133
}
52134

53135
fn is_visible(srv: astsrv::Srv, doc: doc::ItemDoc) -> bool {
54-
use syntax::ast_map;
55-
use syntax::ast;
56-
57136
let id = doc.id;
58137

59138
do astsrv::exec(srv) |ctxt| {
60139
match ctxt.ast_map.get(&id) {
61140
ast_map::node_item(item, _) => {
62-
match item.node {
63-
ast::item_impl(_, Some(_), _, _) => {
64-
// This is a trait implementation, make it visible
65-
// NOTE: This is not quite right since this could be an impl
66-
// of a private trait. We can't know that without running
67-
// resolve though.
68-
true
141+
match &item.node {
142+
&ast::item_impl(*) => {
143+
// Impls handled elsewhere
144+
fail!()
69145
}
70146
_ => {
71147
// Otherwise just look at the visibility
@@ -85,7 +161,8 @@ fn should_prune_items_without_pub_modifier() {
85161
}
86162

87163
#[test]
88-
fn unless_they_are_trait_impls() {
164+
fn should_not_prune_trait_impls() {
165+
// Impls are more complicated
89166
let doc = test::mk_doc(
90167
~" \
91168
trait Foo { } \
@@ -94,16 +171,87 @@ fn unless_they_are_trait_impls() {
94171
fail_unless!(!doc.cratemod().impls().is_empty());
95172
}
96173
174+
#[test]
175+
fn should_prune_associated_methods_without_vis_modifier_on_impls_without_vis_modifier() {
176+
let doc = test::mk_doc(
177+
~"impl Foo {\
178+
pub fn bar() { }\
179+
fn baz() { }\
180+
}");
181+
fail_unless!(doc.cratemod().impls()[0].methods.len() == 1);
182+
}
183+
184+
#[test]
185+
fn should_prune_priv_associated_methods_on_impls_without_vis_modifier() {
186+
let doc = test::mk_doc(
187+
~"impl Foo {\
188+
pub fn bar() { }\
189+
priv fn baz() { }\
190+
}");
191+
fail_unless!(doc.cratemod().impls()[0].methods.len() == 1);
192+
}
193+
194+
#[test]
195+
fn should_prune_priv_associated_methods_on_pub_impls() {
196+
let doc = test::mk_doc(
197+
~"pub impl Foo {\
198+
fn bar() { }\
199+
priv fn baz() { }\
200+
}");
201+
fail_unless!(doc.cratemod().impls()[0].methods.len() == 1);
202+
}
203+
204+
#[test]
205+
fn should_prune_associated_methods_without_vis_modifier_on_priv_impls() {
206+
let doc = test::mk_doc(
207+
~"priv impl Foo {\
208+
pub fn bar() { }\
209+
fn baz() { }\
210+
}");
211+
fail_unless!(doc.cratemod().impls()[0].methods.len() == 1);
212+
}
213+
214+
#[test]
215+
fn should_prune_priv_associated_methods_on_priv_impls() {
216+
let doc = test::mk_doc(
217+
~"priv impl Foo {\
218+
pub fn bar() { }\
219+
priv fn baz() { }\
220+
}");
221+
fail_unless!(doc.cratemod().impls()[0].methods.len() == 1);
222+
}
223+
224+
#[test]
225+
fn should_prune_associated_impls_with_no_pub_methods() {
226+
let doc = test::mk_doc(
227+
~"priv impl Foo {\
228+
fn baz() { }\
229+
}");
230+
fail_unless!(doc.cratemod().impls().is_empty());
231+
}
232+
233+
#[test]
234+
fn should_not_prune_associated_impls_with_pub_methods() {
235+
let doc = test::mk_doc(
236+
~" \
237+
impl Foo { pub fn bar() { } } \
238+
");
239+
fail_unless!(!doc.cratemod().impls().is_empty());
240+
}
241+
242+
97243
#[cfg(test)]
98244
pub mod test {
99245
use astsrv;
100246
use doc;
101247
use extract;
248+
use tystr_pass;
102249
use prune_private_pass::run;
103250
104251
pub fn mk_doc(source: ~str) -> doc::Doc {
105252
do astsrv::from_str(copy source) |srv| {
106253
let doc = extract::from_srv(srv.clone(), ~"");
254+
let doc = tystr_pass::run(srv.clone(), doc);
107255
run(srv.clone(), doc)
108256
}
109257
}

0 commit comments

Comments
 (0)