Skip to content

Commit b78a1b7

Browse files
committed
rustdoc: (WIP) Allows the doc generation from compiled crates.
This commit uses an existing foreign inlining facility to implement the doc generation from compiled crates. At the highest level, this is a *huge* hack since the metadata doesn't provide everything we need to generate docs. Fixes rust-lang#2206. Will fix rust-lang#15309 once integrated with Makefile.
1 parent 6f4c11b commit b78a1b7

File tree

3 files changed

+234
-12
lines changed

3 files changed

+234
-12
lines changed

src/librustdoc/clean/mod.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use rustc::middle::stability;
5050
use rustc::session::config;
5151

5252
use std::rc::Rc;
53+
use std::cell::RefCell;
5354
use std::u32;
5455
use std::str::Str as StrTrait; // Conflicts with Str variant
5556
use std::char::Char as CharTrait; // Conflicts with Char variant
@@ -63,7 +64,7 @@ use visit_ast;
6364
/// Increment this when the `Crate` and related structures change.
6465
pub static SCHEMA_VERSION: &'static str = "0.8.3";
6566

66-
mod inline;
67+
pub mod inline;
6768

6869
// extract the stability index for a node from tcx, if possible
6970
fn get_stability(cx: &DocContext, def_id: ast::DefId) -> Option<Stability> {
@@ -124,19 +125,23 @@ pub struct Crate {
124125

125126
impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
126127
fn clean(&self, cx: &DocContext) -> Crate {
127-
let mut externs = Vec::new();
128-
cx.sess().cstore.iter_crate_data(|n, meta| {
129-
externs.push((n, meta.clean(cx)));
130-
});
131-
externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
132-
133128
// Figure out the name of this crate
134129
let input = config::Input::File(cx.src.clone());
135130
let name = link::find_crate_name(None, self.attrs.as_slice(), &input);
136131

137132
// Clean the crate, translating the entire libsyntax AST to one that is
138133
// understood by rustdoc.
139-
let mut module = self.module.clean(cx);
134+
let module = self.module.clean(cx);
135+
136+
let postclean = RefCell::new(Some((name.to_string(), cx.src.clone(), module)));
137+
postclean.clean(cx)
138+
}
139+
}
140+
141+
// post-cleaning processing consumes the cleaned results.
142+
impl Clean<Crate> for RefCell<Option<(String, FsPath, Item)>> {
143+
fn clean(&self, cx: &DocContext) -> Crate {
144+
let (name, src, mut module) = self.borrow_mut().take().unwrap();
140145

141146
// Collect all inner modules which are tagged as implementations of
142147
// primitives.
@@ -194,9 +199,15 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
194199
m.items.extend(tmp.into_iter());
195200
}
196201

202+
let mut externs = Vec::new();
203+
cx.sess().cstore.iter_crate_data(|n, meta| {
204+
externs.push((n, meta.clean(cx)));
205+
});
206+
externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
207+
197208
Crate {
198-
name: name.to_string(),
199-
src: cx.src.clone(),
209+
name: name,
210+
src: src,
200211
module: Some(module),
201212
externs: externs,
202213
primitives: primitives,
@@ -1643,7 +1654,7 @@ pub struct Span {
16431654
}
16441655

16451656
impl Span {
1646-
fn empty() -> Span {
1657+
pub fn empty() -> Span {
16471658
Span {
16481659
filename: "".to_string(),
16491660
loline: 0, locol: 0,

src/librustdoc/core.rs

Lines changed: 185 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ pub use self::MaybeTyped::*;
1111

1212
use rustc_driver::driver;
1313
use rustc::session::{mod, config};
14+
use rustc::metadata::decoder;
1415
use rustc::middle::{privacy, ty};
1516
use rustc::lint;
1617
use rustc_trans::back::link;
1718

18-
use syntax::{ast, ast_map, codemap, diagnostic};
19+
use syntax::{ast, ast_map, ast_util, codemap, diagnostic};
20+
use syntax::parse::token;
21+
use syntax::ptr::P;
1922

2023
use std::cell::RefCell;
2124
use std::collections::{HashMap, HashSet};
@@ -163,3 +166,184 @@ pub fn run_core(libs: Vec<Path>, cfgs: Vec<String>, externs: Externs,
163166
*analysis.inlined.borrow_mut() = map;
164167
(krate, analysis)
165168
}
169+
170+
pub fn run_core_with_lib(libpath: &Path, srcpath: &Path) -> (clean::Crate, CrateAnalysis) {
171+
let codemap = codemap::CodeMap::new();
172+
let diagnostic_handler = diagnostic::default_handler(diagnostic::Auto, None);
173+
let span_diagnostic_handler = diagnostic::mk_span_handler(diagnostic_handler, codemap);
174+
175+
let dummy_crate_name = "__crate";
176+
let dummy_view_name = "__view";
177+
178+
let mut opts = config::basic_options();
179+
opts.externs.insert(dummy_crate_name.into_string(),
180+
vec![libpath.as_str().unwrap().into_string()]);
181+
182+
let sess = session::build_session_(opts, None, span_diagnostic_handler);
183+
184+
// dummy AST to faciliate the crate loading
185+
let dummy_crate_ident = ast::Ident::new(token::gensym(dummy_crate_name));
186+
let dummy_view_ident = ast::Ident::new(token::gensym(dummy_view_name));
187+
let krate = ast::Crate {
188+
module: ast::Mod {
189+
inner: codemap::DUMMY_SP,
190+
view_items: vec![
191+
ast::ViewItem {
192+
node: ast::ViewItemExternCrate(
193+
dummy_view_ident,
194+
Some((token::get_ident(dummy_crate_ident), ast::CookedStr)),
195+
ast::DUMMY_NODE_ID),
196+
attrs: Vec::new(),
197+
vis: ast::Inherited,
198+
span: codemap::DUMMY_SP,
199+
},
200+
ast::ViewItem {
201+
node: ast::ViewItemUse(
202+
P(codemap::dummy_spanned(ast::ViewPathSimple(
203+
dummy_crate_ident,
204+
ast::Path {
205+
span: codemap::DUMMY_SP,
206+
global: false,
207+
segments: vec![
208+
ast::PathSegment {
209+
identifier: dummy_view_ident,
210+
parameters: ast::PathParameters::none(),
211+
},
212+
],
213+
},
214+
ast::DUMMY_NODE_ID)
215+
))
216+
),
217+
attrs: Vec::new(),
218+
vis: ast::Public,
219+
span: codemap::DUMMY_SP,
220+
},
221+
],
222+
items: Vec::new(),
223+
},
224+
attrs: Vec::new(),
225+
config: Vec::new(),
226+
span: codemap::DUMMY_SP,
227+
exported_macros: Vec::new(),
228+
};
229+
230+
let mut forest = ast_map::Forest::new(krate);
231+
let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest);
232+
233+
let type_arena = TypedArena::new();
234+
let ty::CrateAnalysis {
235+
exported_items, public_items, ty_cx, ..
236+
} = driver::phase_3_run_analysis_passes(sess, ast_map, &type_arena,
237+
dummy_crate_name.into_string());
238+
239+
let ctxt = DocContext {
240+
krate: ty_cx.map.krate(),
241+
maybe_typed: Typed(ty_cx),
242+
src: srcpath.clone(),
243+
external_traits: RefCell::new(Some(HashMap::new())),
244+
external_typarams: RefCell::new(Some(HashMap::new())),
245+
external_paths: RefCell::new(Some(HashMap::new())),
246+
inlined: RefCell::new(Some(HashSet::new())),
247+
populated_crate_impls: RefCell::new(HashSet::new()),
248+
};
249+
250+
// there should be only one Def available, namely reexport
251+
let mut view_node_id = None;
252+
for (id, _) in ctxt.tcx().def_map.borrow().iter() {
253+
assert!(view_node_id.is_none(), "multiple Defs available");
254+
view_node_id = Some(*id);
255+
}
256+
let view_node_id = view_node_id.expect("no Def available");
257+
258+
// we now have all necessary environments, try to inline.
259+
let inlined = clean::inline::try_inline(&ctxt, view_node_id, None);
260+
let inlined = inlined.expect("cannot inline crate");
261+
if inlined.len() != 1 {
262+
panic!("cannot inline crate");
263+
}
264+
let inlined = inlined.into_iter().next().unwrap();
265+
266+
// we still have to fill some gaps, so get the crate data in our hands
267+
let crate_num = 1; // we don't have std injection so this should be the first
268+
let crate_data = ctxt.sess().cstore.get_crate_data(crate_num);
269+
let crate_name = crate_data.name();
270+
271+
// fix external_paths for the given crate_num
272+
{
273+
let mut external_paths = ctxt.external_paths.borrow_mut();
274+
for (def_id, &(ref mut fqn, _)) in external_paths.as_mut().unwrap().iter_mut() {
275+
if def_id.krate == crate_num {
276+
assert_eq!(fqn.head().map(|s| s.as_slice()), Some(dummy_crate_name));
277+
if fqn.len() == 1 {
278+
fqn[0] = "".into_string();
279+
} else {
280+
fqn.remove(0);
281+
}
282+
}
283+
}
284+
}
285+
286+
let postclean = RefCell::new(Some((crate_name, srcpath.clone(), inlined)));
287+
let mut krate = postclean.clean(&ctxt);
288+
289+
// why do we have both crate attributes and item attributes?!
290+
let crate_attrs = decoder::get_crate_attributes(crate_data.data());
291+
{
292+
let mut attrs = &mut krate.module.as_mut().unwrap().attrs;
293+
attrs.extend(crate_attrs.clean(&ctxt).into_iter());
294+
}
295+
296+
// the reconstructed crate doesn't have exported macros (yet)
297+
let macros = decoder::get_exported_macros(crate_data.data());
298+
{
299+
let mut module = match krate.module {
300+
Some(clean::Item { inner: clean::ModuleItem(ref mut module), .. }) => module,
301+
_ => panic!("unexpectedly cleaned crate")
302+
};
303+
for macro in macros.into_iter() {
304+
// XXX okay, this is bad. the metadata doesn't have a direct macro name.
305+
// for now we try to recognize `macro_rules!\s*([^/({\[]+)`.
306+
// hope someone doesn't come up with `macro_rules! /*screw doc*/ foo()`...
307+
let macname = {
308+
let macro = macro.trim_left();
309+
if macro.starts_with("macro_rules!") {
310+
let macro = macro.slice_from(12).trim_left();
311+
let sep = macro.find(['/', '(', '{', '['].as_slice());
312+
if let Some(sep) = sep {
313+
Some(format!("{}!", macro.slice_to(sep).trim_right()))
314+
} else {
315+
None
316+
}
317+
} else {
318+
None
319+
}
320+
};
321+
module.items.push(clean::Item {
322+
name: macname,
323+
attrs: Vec::new(),
324+
source: clean::Span::empty(),
325+
visibility: ast::Public.clean(&ctxt),
326+
stability: None,
327+
def_id: ast_util::local_def(ast::DUMMY_NODE_ID),
328+
inner: clean::MacroItem(clean::Macro {
329+
source: macro,
330+
}),
331+
});
332+
}
333+
}
334+
335+
// we need the analysis for later uses
336+
let DocContext {
337+
external_paths, external_traits, external_typarams, inlined, ..
338+
} = ctxt;
339+
let analysis = CrateAnalysis {
340+
exported_items: exported_items,
341+
public_items: public_items,
342+
external_paths: external_paths,
343+
external_traits: external_traits,
344+
external_typarams: external_typarams,
345+
inlined: inlined,
346+
};
347+
348+
(krate, analysis)
349+
}

src/librustdoc/lib.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,16 @@ fn acquire_input(input: &str,
282282
match matches.opt_str("r").as_ref().map(|s| s.as_slice()) {
283283
Some("rust") => Ok(rust_input(input, externs, matches)),
284284
Some("json") => json_input(input),
285+
Some("lib") => Ok(lib_input(input)),
285286
Some(s) => Err(format!("unknown input format: {}", s)),
286287
None => {
287288
if input.ends_with(".json") {
288289
json_input(input)
290+
} else if input.ends_with(".rlib") ||
291+
input.ends_with(".so") ||
292+
input.ends_with(".dylib") ||
293+
input.ends_with(".dll") {
294+
Ok(lib_input(input))
289295
} else {
290296
Ok(rust_input(input, externs, matches))
291297
}
@@ -418,6 +424,27 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche
418424
return Output { krate: krate, json_plugins: json, passes: passes, };
419425
}
420426

427+
/// This input format extracts the metadata from given rlib or dylib file.
428+
/// No passes are run over the deserialized output.
429+
fn lib_input(libfile: &str) -> Output {
430+
let lib = Path::new(libfile);
431+
info!("starting to run rustc");
432+
let (krate, analysis) = std::task::try(proc() {
433+
let lib = lib;
434+
core::run_core_with_lib(&lib, &lib.clone())
435+
}).map_err(|_| "rustc failed").unwrap();
436+
info!("finished with rustc");
437+
let mut analysis = Some(analysis);
438+
ANALYSISKEY.with(|s| {
439+
*s.borrow_mut() = analysis.take();
440+
});
441+
442+
// FIXME: this should read from the "plugins" field, but currently
443+
// Json doesn't implement decodable...
444+
let plugin_output = Vec::new();
445+
Output { krate: krate, json_plugins: plugin_output, passes: Vec::new() }
446+
}
447+
421448
/// This input format purely deserializes the json output file. No passes are
422449
/// run over the deserialized output.
423450
fn json_input(input: &str) -> Result<Output, String> {

0 commit comments

Comments
 (0)