Skip to content

Commit 3d693d7

Browse files
committed
rustdoc: Use privacy visibility for pruning
This commit ends rustdoc's approximation of privacy and instead uses the result of the various compiler passes instead. Closes #9827
1 parent a7e8957 commit 3d693d7

File tree

5 files changed

+166
-87
lines changed

5 files changed

+166
-87
lines changed

src/librustc/driver/driver.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ pub fn phase_2_configure_and_expand(sess: Session,
199199

200200
pub struct CrateAnalysis {
201201
exp_map2: middle::resolve::ExportMap2,
202+
exported_items: middle::privacy::ExportedItems,
202203
ty_cx: ty::ctxt,
203204
maps: astencode::Maps,
204205
reachable: @mut HashSet<ast::NodeId>
@@ -310,6 +311,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,
310311
CrateAnalysis {
311312
exp_map2: exp_map2,
312313
ty_cx: ty_cx,
314+
exported_items: exported_items,
313315
maps: astencode::Maps {
314316
root_map: root_map,
315317
method_map: method_map,

src/librustdoc/core.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@
1010

1111
use rustc;
1212
use rustc::{driver, middle};
13+
use rustc::middle::privacy;
1314

1415
use syntax::ast;
16+
use syntax::ast_util::is_local;
1517
use syntax::diagnostic;
1618
use syntax::parse;
1719
use syntax;
1820

1921
use std::os;
2022
use std::local_data;
23+
use std::hashmap::HashMap;
2124

2225
use visit_ast::RustdocVisitor;
2326
use clean;
@@ -29,10 +32,19 @@ pub struct DocContext {
2932
sess: driver::session::Session
3033
}
3134

35+
pub struct CrateAnalysis {
36+
exported_items: privacy::ExportedItems,
37+
reexports: HashMap<ast::NodeId, ~[ast::NodeId]>,
38+
}
39+
3240
/// Parses, resolves, and typechecks the given crate
33-
fn get_ast_and_resolve(cpath: &Path, libs: ~[Path]) -> DocContext {
41+
fn get_ast_and_resolve(cpath: &Path,
42+
libs: ~[Path]) -> (DocContext, CrateAnalysis) {
3443
use syntax::codemap::dummy_spanned;
35-
use rustc::driver::driver::*;
44+
use rustc::driver::driver::{file_input, build_configuration,
45+
phase_1_parse_input,
46+
phase_2_configure_and_expand,
47+
phase_3_run_analysis_passes};
3648

3749
let parsesess = parse::new_parse_sess(None);
3850
let input = file_input(cpath.clone());
@@ -60,14 +72,26 @@ fn get_ast_and_resolve(cpath: &Path, libs: ~[Path]) -> DocContext {
6072

6173
let mut crate = phase_1_parse_input(sess, cfg.clone(), &input);
6274
crate = phase_2_configure_and_expand(sess, cfg, crate);
63-
let analysis = phase_3_run_analysis_passes(sess, &crate);
75+
let driver::driver::CrateAnalysis {
76+
exported_items, ty_cx, exp_map2, _
77+
} = phase_3_run_analysis_passes(sess, &crate);
78+
79+
let mut reexports = HashMap::new();
80+
for (&module, nodes) in exp_map2.iter() {
81+
reexports.insert(module, nodes.iter()
82+
.filter(|e| e.reexport && is_local(e.def_id))
83+
.map(|e| e.def_id.node)
84+
.to_owned_vec());
85+
}
6486

6587
debug2!("crate: {:?}", crate);
66-
DocContext { crate: crate, tycx: analysis.ty_cx, sess: sess }
88+
return (DocContext { crate: crate, tycx: ty_cx, sess: sess },
89+
CrateAnalysis { reexports: reexports, exported_items: exported_items });
6790
}
6891

69-
pub fn run_core (libs: ~[Path], path: &Path) -> clean::Crate {
70-
let ctxt = @get_ast_and_resolve(path, libs);
92+
pub fn run_core (libs: ~[Path], path: &Path) -> (clean::Crate, CrateAnalysis) {
93+
let (ctxt, analysis) = get_ast_and_resolve(path, libs);
94+
let ctxt = @ctxt;
7195
debug2!("defmap:");
7296
for (k, v) in ctxt.tycx.def_map.iter() {
7397
debug2!("{:?}: {:?}", k, v);
@@ -77,5 +101,5 @@ pub fn run_core (libs: ~[Path], path: &Path) -> clean::Crate {
77101
let v = @mut RustdocVisitor::new();
78102
v.visit(&ctxt.crate);
79103

80-
v.clean()
104+
(v.clean(), analysis)
81105
}

src/librustdoc/html/render.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ impl DocFolder for Cache {
582582
clean::StructItem(*) | clean::EnumItem(*) |
583583
clean::TypedefItem(*) | clean::TraitItem(*) |
584584
clean::FunctionItem(*) | clean::ModuleItem(*) |
585-
clean::VariantItem(*) => {
585+
clean::ForeignFunctionItem(*) | clean::VariantItem(*) => {
586586
self.paths.insert(item.id, (self.stack.clone(), shortty(&item)));
587587
}
588588
_ => {}

src/librustdoc/passes.rs

Lines changed: 128 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
// except according to those terms.
1010

1111
use std::num;
12+
use std::cell::Cell;
1213
use std::uint;
1314
use std::hashmap::HashSet;
15+
use std::local_data;
1416

1517
use syntax::ast;
1618

19+
use core;
1720
use clean;
1821
use clean::Item;
1922
use plugins;
@@ -51,110 +54,157 @@ pub fn strip_hidden(crate: clean::Crate) -> plugins::PluginResult {
5154

5255
/// Strip private items from the point of view of a crate or externally from a
5356
/// crate, specified by the `xcrate` flag.
54-
pub fn strip_private(mut crate: clean::Crate) -> plugins::PluginResult {
57+
pub fn strip_private(crate: clean::Crate) -> plugins::PluginResult {
5558
// This stripper collects all *retained* nodes.
56-
struct Stripper<'self>(&'self mut HashSet<ast::NodeId>);
57-
impl<'self> fold::DocFolder for Stripper<'self> {
58-
fn fold_item(&mut self, i: Item) -> Option<Item> {
59-
match i.inner {
60-
// These items can all get re-exported
61-
clean::TypedefItem(*) | clean::StaticItem(*) |
62-
clean::StructItem(*) | clean::EnumItem(*) |
63-
clean::TraitItem(*) | clean::FunctionItem(*) |
64-
clean::ViewItemItem(*) | clean::MethodItem(*) |
65-
clean::ForeignFunctionItem(*) | clean::ForeignStaticItem(*) => {
66-
// XXX: re-exported items should get surfaced in the docs as
67-
// well (using the output of resolve analysis)
68-
if i.visibility != Some(ast::public) {
69-
return None;
70-
}
71-
}
59+
let mut retained = HashSet::new();
60+
let crate = Cell::new(crate);
61+
let exported_items = do local_data::get(super::analysiskey) |analysis| {
62+
let analysis = analysis.unwrap();
63+
let mut exported_items = analysis.exported_items.clone();
64+
{
65+
let mut finder = ExportedItemsFinder {
66+
exported_items: &mut exported_items,
67+
analysis: analysis,
68+
};
69+
let c = finder.fold_crate(crate.take());
70+
crate.put_back(c);
71+
}
72+
exported_items
73+
};
74+
let mut crate = crate.take();
7275

73-
// These are public-by-default (if the enum/struct was public)
74-
clean::VariantItem(*) | clean::StructFieldItem(*) => {
75-
if i.visibility == Some(ast::private) {
76-
return None;
76+
// strip all private items
77+
{
78+
let mut stripper = Stripper {
79+
retained: &mut retained,
80+
exported_items: &exported_items,
81+
};
82+
crate = stripper.fold_crate(crate);
83+
}
84+
85+
// strip all private implementations of traits
86+
{
87+
let mut stripper = ImplStripper(&retained);
88+
crate = stripper.fold_crate(crate);
89+
}
90+
(crate, None)
91+
}
92+
93+
struct ExportedItemsFinder<'self> {
94+
exported_items: &'self mut HashSet<ast::NodeId>,
95+
analysis: &'self core::CrateAnalysis,
96+
}
97+
98+
impl<'self> fold::DocFolder for ExportedItemsFinder<'self> {
99+
fn fold_item(&mut self, i: Item) -> Option<Item> {
100+
match i.inner {
101+
clean::ModuleItem(*) => {
102+
if self.analysis.exported_items.contains(&i.id) {
103+
match self.analysis.reexports.find(&i.id) {
104+
Some(l) => {
105+
for &id in l.iter() {
106+
self.exported_items.insert(id);
107+
}
108+
}
109+
None => {}
77110
}
78111
}
112+
}
113+
_ => {}
114+
}
115+
return self.fold_item_recur(i);
116+
}
117+
}
118+
119+
struct Stripper<'self> {
120+
retained: &'self mut HashSet<ast::NodeId>,
121+
exported_items: &'self HashSet<ast::NodeId>,
122+
}
79123

80-
// handled below
81-
clean::ModuleItem(*) => {}
124+
impl<'self> fold::DocFolder for Stripper<'self> {
125+
fn fold_item(&mut self, i: Item) -> Option<Item> {
126+
match i.inner {
127+
// These items can all get re-exported
128+
clean::TypedefItem(*) | clean::StaticItem(*) |
129+
clean::StructItem(*) | clean::EnumItem(*) |
130+
clean::TraitItem(*) | clean::FunctionItem(*) |
131+
clean::VariantItem(*) | clean::MethodItem(*) |
132+
clean::ForeignFunctionItem(*) | clean::ForeignStaticItem(*) => {
133+
if !self.exported_items.contains(&i.id) {
134+
return None;
135+
}
136+
}
82137

83-
// impls/tymethods have no control over privacy
84-
clean::ImplItem(*) | clean::TyMethodItem(*) => {}
138+
clean::ViewItemItem(*) | clean::StructFieldItem(*) => {
139+
if i.visibility != Some(ast::public) {
140+
return None;
141+
}
85142
}
86143

87-
let fastreturn = match i.inner {
88-
// nothing left to do for traits (don't want to filter their
89-
// methods out, visibility controlled by the trait)
90-
clean::TraitItem(*) => true,
144+
// handled below
145+
clean::ModuleItem(*) => {}
91146

92-
// implementations of traits are always public.
93-
clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
147+
// impls/tymethods have no control over privacy
148+
clean::ImplItem(*) | clean::TyMethodItem(*) => {}
149+
}
94150

95-
_ => false,
96-
};
151+
let fastreturn = match i.inner {
152+
// nothing left to do for traits (don't want to filter their
153+
// methods out, visibility controlled by the trait)
154+
clean::TraitItem(*) => true,
97155

98-
let i = if fastreturn {
99-
self.insert(i.id);
100-
return Some(i);
101-
} else {
102-
self.fold_item_recur(i)
103-
};
156+
// implementations of traits are always public.
157+
clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
104158

105-
match i {
106-
Some(i) => {
107-
match i.inner {
108-
// emptied modules/impls have no need to exist
109-
clean::ModuleItem(ref m) if m.items.len() == 0 => None,
110-
clean::ImplItem(ref i) if i.methods.len() == 0 => None,
111-
_ => {
112-
self.insert(i.id);
113-
Some(i)
114-
}
159+
_ => false,
160+
};
161+
162+
let i = if fastreturn {
163+
self.retained.insert(i.id);
164+
return Some(i);
165+
} else {
166+
self.fold_item_recur(i)
167+
};
168+
169+
match i {
170+
Some(i) => {
171+
match i.inner {
172+
// emptied modules/impls have no need to exist
173+
clean::ModuleItem(ref m) if m.items.len() == 0 => None,
174+
clean::ImplItem(ref i) if i.methods.len() == 0 => None,
175+
_ => {
176+
self.retained.insert(i.id);
177+
Some(i)
115178
}
116179
}
117-
None => None,
118180
}
181+
None => None,
119182
}
120183
}
184+
}
121185

122-
// This stripper discards all private impls of traits
123-
struct ImplStripper<'self>(&'self HashSet<ast::NodeId>);
124-
impl<'self> fold::DocFolder for ImplStripper<'self> {
125-
fn fold_item(&mut self, i: Item) -> Option<Item> {
126-
match i.inner {
127-
clean::ImplItem(ref imp) => {
128-
match imp.trait_ {
129-
Some(clean::ResolvedPath{ id, _ }) => {
130-
if !self.contains(&id) {
131-
return None;
132-
}
186+
// This stripper discards all private impls of traits
187+
struct ImplStripper<'self>(&'self HashSet<ast::NodeId>);
188+
impl<'self> fold::DocFolder for ImplStripper<'self> {
189+
fn fold_item(&mut self, i: Item) -> Option<Item> {
190+
match i.inner {
191+
clean::ImplItem(ref imp) => {
192+
match imp.trait_ {
193+
Some(clean::ResolvedPath{ id, _ }) => {
194+
if !self.contains(&id) {
195+
return None;
133196
}
134-
Some(*) | None => {}
135197
}
198+
Some(*) | None => {}
136199
}
137-
_ => {}
138200
}
139-
self.fold_item_recur(i)
201+
_ => {}
140202
}
203+
self.fold_item_recur(i)
141204
}
142-
143-
let mut retained = HashSet::new();
144-
// First, strip all private items
145-
{
146-
let mut stripper = Stripper(&mut retained);
147-
crate = stripper.fold_crate(crate);
148-
}
149-
150-
// Next, strip all private implementations of traits
151-
{
152-
let mut stripper = ImplStripper(&retained);
153-
crate = stripper.fold_crate(crate);
154-
}
155-
(crate, None)
156205
}
157206

207+
158208
pub fn unindent_comments(crate: clean::Crate) -> plugins::PluginResult {
159209
struct CommentCleaner;
160210
impl fold::DocFolder for CommentCleaner {

src/librustdoc/rustdoc.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern mod rustc;
2424
extern mod extra;
2525

2626
use std::cell::Cell;
27+
use std::local_data;
2728
use std::rt::io::Writer;
2829
use std::rt::io::file::FileInfo;
2930
use std::rt::io;
@@ -73,6 +74,7 @@ static DEFAULT_PASSES: &'static [&'static str] = &[
7374
];
7475

7576
local_data_key!(pub ctxtkey: @core::DocContext)
77+
local_data_key!(pub analysiskey: core::CrateAnalysis)
7678

7779
type Output = (clean::Crate, ~[plugins::PluginJson]);
7880

@@ -191,11 +193,12 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output {
191193
let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s)));
192194
let cr = Cell::new(Path(cratefile));
193195
info2!("starting to run rustc");
194-
let crate = do std::task::try {
196+
let (crate, analysis) = do std::task::try {
195197
let cr = cr.take();
196198
core::run_core(libs.take(), &cr)
197199
}.unwrap();
198200
info2!("finished with rustc");
201+
local_data::set(analysiskey, analysis);
199202

200203
// Process all of the crate attributes, extracting plugin metadata along
201204
// with the passes which we are supposed to run.

0 commit comments

Comments
 (0)