Skip to content

Commit 3100bc5

Browse files
committed
rustdoc: Move inlining to its own module
1 parent bd339ce commit 3100bc5

File tree

9 files changed

+406
-352
lines changed

9 files changed

+406
-352
lines changed

src/doc/rustdoc.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ pub fn recalibrate() {
4141
# }
4242
~~~
4343

44-
Documentation can also be controlled via the `doc` attribute on items.
44+
Documentation can also be controlled via the `doc` attribute on items. This is
45+
implicitly done by the compiler when using the above form of doc comments
46+
(converting the slash-based comments to `#[doc]` attributes).
4547

4648
~~~
4749
#[doc = "
@@ -50,6 +52,7 @@ Calculates the factorial of a number.
5052
Given the input integer `n`, this function will calculate `n!` and return it.
5153
"]
5254
pub fn factorial(n: int) -> int { if n < 2 {1} else {n * factorial(n)} }
55+
# fn main() {}
5356
~~~
5457

5558
The `doc` attribute can also be used to control how rustdoc emits documentation
@@ -60,6 +63,7 @@ in some cases.
6063
// `pub use` reaches across crates, but this behavior can also be disabled.
6164
#[doc(no_inline)]
6265
pub use std::option::Option;
66+
# fn main() {}
6367
```
6468

6569
Doc comments are markdown, and are currently parsed with the

src/librustc/metadata/csearch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ pub fn get_missing_lang_items(cstore: &cstore::CStore, cnum: ast::CrateNum)
308308
}
309309

310310
pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
311-
-> Vec<StrBuf>
311+
-> Vec<String>
312312
{
313313
let cdata = cstore.get_crate_data(did.krate);
314314
decoder::get_method_arg_names(&*cdata, did.node)

src/librustc/metadata/decoder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,7 @@ pub fn get_missing_lang_items(cdata: Cmd)
13101310
return result;
13111311
}
13121312

1313-
pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<StrBuf> {
1313+
pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
13141314
let mut ret = Vec::new();
13151315
let method_doc = lookup_item(id, cdata.data());
13161316
match reader::maybe_get_doc(method_doc, tag_method_argument_names) {

src/librustdoc/clean/inline.rs

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
// Copyright 2012-2013 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+
//! Support for inlining external documentation into the current AST.
12+
13+
use syntax::ast;
14+
use syntax::ast_util;
15+
use syntax::attr::AttrMetaMethods;
16+
17+
use rustc::metadata::csearch;
18+
use rustc::metadata::decoder;
19+
use rustc::middle::ty;
20+
21+
use core;
22+
use doctree;
23+
use clean;
24+
25+
use super::Clean;
26+
27+
/// Attempt to inline the definition of a local node id into this AST.
28+
///
29+
/// This function will fetch the definition of the id specified, and if it is
30+
/// from another crate it will attempt to inline the documentation from the
31+
/// other crate into this crate.
32+
///
33+
/// This is primarily used for `pub use` statements which are, in general,
34+
/// implementation details. Inlining the documentation should help provide a
35+
/// better experience when reading the documentation in this use case.
36+
///
37+
/// The returned value is `None` if the `id` could not be inlined, and `Some`
38+
/// of a vector of items if it was successfully expanded.
39+
pub fn try_inline(id: ast::NodeId) -> Option<Vec<clean::Item>> {
40+
let cx = ::ctxtkey.get().unwrap();
41+
let tcx = match cx.maybe_typed {
42+
core::Typed(ref tycx) => tycx,
43+
core::NotTyped(_) => return None,
44+
};
45+
let def = match tcx.def_map.borrow().find(&id) {
46+
Some(def) => *def,
47+
None => return None,
48+
};
49+
let did = ast_util::def_id_of_def(def);
50+
if ast_util::is_local(did) { return None }
51+
try_inline_def(&**cx, tcx, def)
52+
}
53+
54+
fn try_inline_def(cx: &core::DocContext,
55+
tcx: &ty::ctxt,
56+
def: ast::Def) -> Option<Vec<clean::Item>> {
57+
let mut ret = Vec::new();
58+
let did = ast_util::def_id_of_def(def);
59+
let inner = match def {
60+
ast::DefTrait(did) => {
61+
record_extern_fqn(cx, did, clean::TypeTrait);
62+
clean::TraitItem(build_external_trait(tcx, did))
63+
}
64+
ast::DefFn(did, style) => {
65+
record_extern_fqn(cx, did, clean::TypeFunction);
66+
clean::FunctionItem(build_external_function(tcx, did, style))
67+
}
68+
ast::DefStruct(did) => {
69+
record_extern_fqn(cx, did, clean::TypeStruct);
70+
ret.extend(build_impls(tcx, did).move_iter());
71+
clean::StructItem(build_struct(tcx, did))
72+
}
73+
ast::DefTy(did) => {
74+
record_extern_fqn(cx, did, clean::TypeEnum);
75+
ret.extend(build_impls(tcx, did).move_iter());
76+
build_type(tcx, did)
77+
}
78+
// Assume that the enum type is reexported next to the variant, and
79+
// variants don't show up in documentation specially.
80+
ast::DefVariant(..) => return Some(Vec::new()),
81+
ast::DefMod(did) => {
82+
record_extern_fqn(cx, did, clean::TypeModule);
83+
clean::ModuleItem(build_module(cx, tcx, did))
84+
}
85+
_ => return None,
86+
};
87+
let fqn = csearch::get_item_path(tcx, did);
88+
ret.push(clean::Item {
89+
source: clean::Span::empty(),
90+
name: Some(fqn.last().unwrap().to_str().to_strbuf()),
91+
attrs: load_attrs(tcx, did),
92+
inner: inner,
93+
visibility: Some(ast::Public),
94+
def_id: did,
95+
});
96+
Some(ret)
97+
}
98+
99+
pub fn load_attrs(tcx: &ty::ctxt, did: ast::DefId) -> Vec<clean::Attribute> {
100+
let mut attrs = Vec::new();
101+
csearch::get_item_attrs(&tcx.sess.cstore, did, |v| {
102+
attrs.extend(v.move_iter().map(|mut a| {
103+
// FIXME this isn't quite always true, it's just true about 99% of
104+
// the time when dealing with documentation. For example,
105+
// this would treat doc comments of the form `#[doc = "foo"]`
106+
// incorrectly.
107+
if a.name().get() == "doc" && a.value_str().is_some() {
108+
a.node.is_sugared_doc = true;
109+
}
110+
a.clean()
111+
}));
112+
});
113+
attrs
114+
}
115+
116+
/// Record an external fully qualified name in the external_paths cache.
117+
///
118+
/// These names are used later on by HTML rendering to generate things like
119+
/// source links back to the original item.
120+
pub fn record_extern_fqn(cx: &core::DocContext,
121+
did: ast::DefId,
122+
kind: clean::TypeKind) {
123+
match cx.maybe_typed {
124+
core::Typed(ref tcx) => {
125+
let fqn = csearch::get_item_path(tcx, did);
126+
let fqn = fqn.move_iter().map(|i| i.to_str().to_strbuf()).collect();
127+
cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
128+
}
129+
core::NotTyped(..) => {}
130+
}
131+
}
132+
133+
pub fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> clean::Trait {
134+
let def = ty::lookup_trait_def(tcx, did);
135+
let methods = ty::trait_methods(tcx, did);
136+
clean::Trait {
137+
generics: def.generics.clean(),
138+
methods: methods.iter().map(|i| i.clean()).collect(),
139+
parents: Vec::new(), // FIXME: this is likely wrong
140+
}
141+
}
142+
143+
fn build_external_function(tcx: &ty::ctxt,
144+
did: ast::DefId,
145+
style: ast::FnStyle) -> clean::Function {
146+
let t = ty::lookup_item_type(tcx, did);
147+
clean::Function {
148+
decl: match ty::get(t.ty).sty {
149+
ty::ty_bare_fn(ref f) => (did, &f.sig).clean(),
150+
_ => fail!("bad function"),
151+
},
152+
generics: t.generics.clean(),
153+
fn_style: style,
154+
}
155+
}
156+
157+
fn build_struct(tcx: &ty::ctxt, did: ast::DefId) -> clean::Struct {
158+
use syntax::parse::token::special_idents::unnamed_field;
159+
160+
let t = ty::lookup_item_type(tcx, did);
161+
let fields = ty::lookup_struct_fields(tcx, did);
162+
163+
clean::Struct {
164+
struct_type: match fields.as_slice() {
165+
[] => doctree::Unit,
166+
[ref f] if f.name == unnamed_field.name => doctree::Newtype,
167+
[ref f, ..] if f.name == unnamed_field.name => doctree::Tuple,
168+
_ => doctree::Plain,
169+
},
170+
generics: t.generics.clean(),
171+
fields: fields.iter().map(|f| f.clean()).collect(),
172+
fields_stripped: false,
173+
}
174+
}
175+
176+
fn build_type(tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEnum {
177+
let t = ty::lookup_item_type(tcx, did);
178+
match ty::get(t.ty).sty {
179+
ty::ty_enum(edid, _) => {
180+
return clean::EnumItem(clean::Enum {
181+
generics: t.generics.clean(),
182+
variants_stripped: false,
183+
variants: ty::enum_variants(tcx, edid).clean(),
184+
})
185+
}
186+
_ => {}
187+
}
188+
189+
clean::TypedefItem(clean::Typedef {
190+
type_: t.ty.clean(),
191+
generics: t.generics.clean(),
192+
})
193+
}
194+
195+
fn build_impls(tcx: &ty::ctxt,
196+
did: ast::DefId) -> Vec<clean::Item> {
197+
ty::populate_implementations_for_type_if_necessary(tcx, did);
198+
let mut impls = Vec::new();
199+
200+
match tcx.inherent_impls.borrow().find(&did) {
201+
None => {}
202+
Some(i) => {
203+
impls.extend(i.borrow().iter().map(|&did| { build_impl(tcx, did) }));
204+
}
205+
}
206+
207+
impls
208+
}
209+
210+
fn build_impl(tcx: &ty::ctxt, did: ast::DefId) -> clean::Item {
211+
let associated_trait = csearch::get_impl_trait(tcx, did);
212+
let attrs = load_attrs(tcx, did);
213+
let ty = ty::lookup_item_type(tcx, did);
214+
let methods = csearch::get_impl_methods(&tcx.sess.cstore, did).iter().map(|did| {
215+
let mut item = match ty::method(tcx, *did).clean() {
216+
clean::Provided(item) => item,
217+
clean::Required(item) => item,
218+
};
219+
item.inner = match item.inner.clone() {
220+
clean::TyMethodItem(clean::TyMethod {
221+
fn_style, decl, self_, generics
222+
}) => {
223+
clean::MethodItem(clean::Method {
224+
fn_style: fn_style,
225+
decl: decl,
226+
self_: self_,
227+
generics: generics,
228+
})
229+
}
230+
_ => fail!("not a tymethod"),
231+
};
232+
item
233+
}).collect();
234+
clean::Item {
235+
inner: clean::ImplItem(clean::Impl {
236+
derived: clean::detect_derived(attrs.as_slice()),
237+
trait_: associated_trait.clean().map(|bound| {
238+
match bound {
239+
clean::TraitBound(ty) => ty,
240+
clean::RegionBound => unreachable!(),
241+
}
242+
}),
243+
for_: ty.ty.clean(),
244+
generics: ty.generics.clean(),
245+
methods: methods,
246+
}),
247+
source: clean::Span::empty(),
248+
name: None,
249+
attrs: attrs,
250+
visibility: Some(ast::Inherited),
251+
def_id: did,
252+
}
253+
}
254+
255+
fn build_module(cx: &core::DocContext, tcx: &ty::ctxt,
256+
did: ast::DefId) -> clean::Module {
257+
let mut items = Vec::new();
258+
259+
// FIXME: this doesn't handle reexports inside the module itself.
260+
// Should they be handled?
261+
csearch::each_child_of_item(&tcx.sess.cstore, did, |def, _, _| {
262+
match def {
263+
decoder::DlDef(def) => {
264+
match try_inline_def(cx, tcx, def) {
265+
Some(i) => items.extend(i.move_iter()),
266+
None => {}
267+
}
268+
}
269+
decoder::DlImpl(did) => items.push(build_impl(tcx, did)),
270+
decoder::DlField => fail!("unimplemented field"),
271+
}
272+
});
273+
274+
clean::Module {
275+
items: items,
276+
is_crate: false,
277+
}
278+
}

0 commit comments

Comments
 (0)