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.
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.
11 //! Support for inlining external documentation into the current AST.
15 use syntax
::attr
::AttrMetaMethods
;
17 use rustc
::metadata
::csearch
;
18 use rustc
::metadata
::decoder
;
19 use rustc
::middle
::def
;
20 use rustc
::middle
::ty
;
21 use rustc
::middle
::subst
;
22 use rustc
::middle
::stability
;
30 /// Attempt to inline the definition of a local node id into this AST.
32 /// This function will fetch the definition of the id specified, and if it is
33 /// from another crate it will attempt to inline the documentation from the
34 /// other crate into this crate.
36 /// This is primarily used for `pub use` statements which are, in general,
37 /// implementation details. Inlining the documentation should help provide a
38 /// better experience when reading the documentation in this use case.
40 /// The returned value is `None` if the `id` could not be inlined, and `Some`
41 /// of a vector of items if it was successfully expanded.
42 pub fn try_inline(cx
: &DocContext
, id
: ast
::NodeId
, into
: Option
<ast
::Ident
>)
43 -> Option
<Vec
<clean
::Item
>> {
44 let tcx
= match cx
.tcx_opt() {
48 let def
= match tcx
.def_map
.borrow().get(&id
) {
52 let did
= def
.def_id();
53 if ast_util
::is_local(did
) { return None }
54 try_inline_def(cx
, tcx
, def
).map(|vec
| {
55 vec
.into_iter().map(|mut item
| {
57 Some(into
) if item
.name
.is_some() => {
58 item
.name
= Some(into
.clean(cx
));
67 fn try_inline_def(cx
: &DocContext
, tcx
: &ty
::ctxt
,
68 def
: def
::Def
) -> Option
<Vec
<clean
::Item
>> {
69 let mut ret
= Vec
::new();
70 let did
= def
.def_id();
71 let inner
= match def
{
72 def
::DefTrait(did
) => {
73 record_extern_fqn(cx
, did
, clean
::TypeTrait
);
74 clean
::TraitItem(build_external_trait(cx
, tcx
, did
))
76 def
::DefFn(did
, false) => {
77 // If this function is a tuple struct constructor, we just skip it
78 record_extern_fqn(cx
, did
, clean
::TypeFunction
);
79 clean
::FunctionItem(build_external_function(cx
, tcx
, did
))
81 def
::DefStruct(did
) => {
82 record_extern_fqn(cx
, did
, clean
::TypeStruct
);
83 ret
.extend(build_impls(cx
, tcx
, did
).into_iter());
84 clean
::StructItem(build_struct(cx
, tcx
, did
))
86 def
::DefTy(did
, false) => {
87 record_extern_fqn(cx
, did
, clean
::TypeTypedef
);
88 ret
.extend(build_impls(cx
, tcx
, did
).into_iter());
89 build_type(cx
, tcx
, did
)
91 def
::DefTy(did
, true) => {
92 record_extern_fqn(cx
, did
, clean
::TypeEnum
);
93 ret
.extend(build_impls(cx
, tcx
, did
).into_iter());
94 build_type(cx
, tcx
, did
)
96 // Assume that the enum type is reexported next to the variant, and
97 // variants don't show up in documentation specially.
98 def
::DefVariant(..) => return Some(Vec
::new()),
100 record_extern_fqn(cx
, did
, clean
::TypeModule
);
101 clean
::ModuleItem(build_module(cx
, tcx
, did
))
103 def
::DefStatic(did
, mtbl
) => {
104 record_extern_fqn(cx
, did
, clean
::TypeStatic
);
105 clean
::StaticItem(build_static(cx
, tcx
, did
, mtbl
))
107 def
::DefConst(did
) => {
108 record_extern_fqn(cx
, did
, clean
::TypeConst
);
109 clean
::ConstantItem(build_const(cx
, tcx
, did
))
113 let fqn
= csearch
::get_item_path(tcx
, did
);
114 cx
.inlined
.borrow_mut().as_mut().unwrap().insert(did
);
115 ret
.push(clean
::Item
{
116 source
: clean
::Span
::empty(),
117 name
: Some(fqn
.last().unwrap().to_string()),
118 attrs
: load_attrs(cx
, tcx
, did
),
120 visibility
: Some(ast
::Public
),
121 stability
: stability
::lookup(tcx
, did
).clean(cx
),
127 pub fn load_attrs(cx
: &DocContext
, tcx
: &ty
::ctxt
,
128 did
: ast
::DefId
) -> Vec
<clean
::Attribute
> {
129 let mut attrs
= Vec
::new();
130 csearch
::get_item_attrs(&tcx
.sess
.cstore
, did
, |v
| {
131 attrs
.extend(v
.into_iter().map(|a
| {
138 /// Record an external fully qualified name in the external_paths cache.
140 /// These names are used later on by HTML rendering to generate things like
141 /// source links back to the original item.
142 pub fn record_extern_fqn(cx
: &DocContext
, did
: ast
::DefId
, kind
: clean
::TypeKind
) {
145 let fqn
= csearch
::get_item_path(tcx
, did
);
146 let fqn
= fqn
.into_iter().map(|i
| i
.to_string()).collect();
147 cx
.external_paths
.borrow_mut().as_mut().unwrap().insert(did
, (fqn
, kind
));
153 pub fn build_external_trait(cx
: &DocContext
, tcx
: &ty
::ctxt
,
154 did
: ast
::DefId
) -> clean
::Trait
{
155 let def
= ty
::lookup_trait_def(tcx
, did
);
156 let trait_items
= ty
::trait_items(tcx
, did
).clean(cx
);
157 let provided
= ty
::provided_trait_methods(tcx
, did
);
158 let items
= trait_items
.into_iter().map(|trait_item
| {
159 if provided
.iter().any(|a
| a
.def_id
== trait_item
.def_id
) {
160 clean
::ProvidedMethod(trait_item
)
162 clean
::RequiredMethod(trait_item
)
165 let trait_def
= ty
::lookup_trait_def(tcx
, did
);
166 let bounds
= trait_def
.bounds
.clean(cx
);
168 unsafety
: def
.unsafety
,
169 generics
: (&def
.generics
, subst
::TypeSpace
).clean(cx
),
170 items
: items
.collect(),
175 fn build_external_function(cx
: &DocContext
, tcx
: &ty
::ctxt
, did
: ast
::DefId
) -> clean
::Function
{
176 let t
= ty
::lookup_item_type(tcx
, did
);
177 let (decl
, style
) = match t
.ty
.sty
{
178 ty
::ty_bare_fn(_
, ref f
) => ((did
, &f
.sig
).clean(cx
), f
.unsafety
),
179 _
=> panic
!("bad function"),
183 generics
: (&t
.generics
, subst
::FnSpace
).clean(cx
),
188 fn build_struct(cx
: &DocContext
, tcx
: &ty
::ctxt
, did
: ast
::DefId
) -> clean
::Struct
{
189 use syntax
::parse
::token
::special_idents
::unnamed_field
;
191 let t
= ty
::lookup_item_type(tcx
, did
);
192 let fields
= ty
::lookup_struct_fields(tcx
, did
);
195 struct_type
: match fields
.as_slice() {
197 [ref f
] if f
.name
== unnamed_field
.name
=> doctree
::Newtype
,
198 [ref f
, ..] if f
.name
== unnamed_field
.name
=> doctree
::Tuple
,
201 generics
: (&t
.generics
, subst
::TypeSpace
).clean(cx
),
202 fields
: fields
.clean(cx
),
203 fields_stripped
: false,
207 fn build_type(cx
: &DocContext
, tcx
: &ty
::ctxt
, did
: ast
::DefId
) -> clean
::ItemEnum
{
208 let t
= ty
::lookup_item_type(tcx
, did
);
210 ty
::ty_enum(edid
, _
) if !csearch
::is_typedef(&tcx
.sess
.cstore
, did
) => {
211 return clean
::EnumItem(clean
::Enum
{
212 generics
: (&t
.generics
, subst
::TypeSpace
).clean(cx
),
213 variants_stripped
: false,
214 variants
: ty
::enum_variants(tcx
, edid
).clean(cx
),
220 clean
::TypedefItem(clean
::Typedef
{
221 type_
: t
.ty
.clean(cx
),
222 generics
: (&t
.generics
, subst
::TypeSpace
).clean(cx
),
226 fn build_impls(cx
: &DocContext
, tcx
: &ty
::ctxt
,
227 did
: ast
::DefId
) -> Vec
<clean
::Item
> {
228 ty
::populate_implementations_for_type_if_necessary(tcx
, did
);
229 let mut impls
= Vec
::new();
231 match tcx
.inherent_impls
.borrow().get(&did
) {
234 impls
.extend(i
.iter().map(|&did
| { build_impl(cx, tcx, did) }
));
238 // If this is the first time we've inlined something from this crate, then
239 // we inline *all* impls from the crate into this crate. Note that there's
240 // currently no way for us to filter this based on type, and we likely need
241 // many impls for a variety of reasons.
243 // Primarily, the impls will be used to populate the documentation for this
244 // type being inlined, but impls can also be used when generating
245 // documentation for primitives (no way to find those specifically).
246 if cx
.populated_crate_impls
.borrow_mut().insert(did
.krate
) {
247 csearch
::each_top_level_item_of_crate(&tcx
.sess
.cstore
,
250 populate_impls(cx
, tcx
, def
, &mut impls
)
253 fn populate_impls(cx
: &DocContext
, tcx
: &ty
::ctxt
,
254 def
: decoder
::DefLike
,
255 impls
: &mut Vec
<Option
<clean
::Item
>>) {
257 decoder
::DlImpl(did
) => impls
.push(build_impl(cx
, tcx
, did
)),
258 decoder
::DlDef(def
::DefMod(did
)) => {
259 csearch
::each_child_of_item(&tcx
.sess
.cstore
,
262 populate_impls(cx
, tcx
, def
, impls
)
270 impls
.into_iter().filter_map(|a
| a
).collect()
273 fn build_impl(cx
: &DocContext
, tcx
: &ty
::ctxt
,
274 did
: ast
::DefId
) -> Option
<clean
::Item
> {
275 if !cx
.inlined
.borrow_mut().as_mut().unwrap().insert(did
) {
279 let associated_trait
= csearch
::get_impl_trait(tcx
, did
);
280 // If this is an impl for a #[doc(hidden)] trait, be sure to not inline it.
281 match associated_trait
{
283 let trait_attrs
= load_attrs(cx
, tcx
, t
.def_id
);
284 if trait_attrs
.iter().any(|a
| is_doc_hidden(a
)) {
291 let attrs
= load_attrs(cx
, tcx
, did
);
292 let ty
= ty
::lookup_item_type(tcx
, did
);
293 let trait_items
= csearch
::get_impl_items(&tcx
.sess
.cstore
, did
)
296 let did
= did
.def_id();
297 let impl_item
= ty
::impl_or_trait_item(tcx
, did
);
299 ty
::MethodTraitItem(method
) => {
300 if method
.vis
!= ast
::Public
&& associated_trait
.is_none() {
303 let mut item
= method
.clean(cx
);
304 item
.inner
= match item
.inner
.clone() {
305 clean
::TyMethodItem(clean
::TyMethod
{
306 unsafety
, decl
, self_
, generics
308 clean
::MethodItem(clean
::Method
{
315 _
=> panic
!("not a tymethod"),
319 ty
::TypeTraitItem(_
) => {
320 // FIXME(pcwalton): Implement.
325 return Some(clean
::Item
{
326 inner
: clean
::ImplItem(clean
::Impl
{
327 derived
: clean
::detect_derived(attrs
.as_slice()),
328 trait_
: associated_trait
.clean(cx
).map(|bound
| {
330 clean
::TraitBound(polyt
, _
) => polyt
.trait_
,
331 clean
::RegionBound(..) => unreachable
!(),
334 for_
: ty
.ty
.clean(cx
),
335 generics
: (&ty
.generics
, subst
::TypeSpace
).clean(cx
),
338 source
: clean
::Span
::empty(),
341 visibility
: Some(ast
::Inherited
),
342 stability
: stability
::lookup(tcx
, did
).clean(cx
),
346 fn is_doc_hidden(a
: &clean
::Attribute
) -> bool
{
348 clean
::List(ref name
, ref inner
) if *name
== "doc" => {
349 inner
.iter().any(|a
| {
351 clean
::Word(ref s
) => *s
== "hidden",
361 fn build_module(cx
: &DocContext
, tcx
: &ty
::ctxt
,
362 did
: ast
::DefId
) -> clean
::Module
{
363 let mut items
= Vec
::new();
364 fill_in(cx
, tcx
, did
, &mut items
);
365 return clean
::Module
{
370 // FIXME: this doesn't handle reexports inside the module itself.
371 // Should they be handled?
372 fn fill_in(cx
: &DocContext
, tcx
: &ty
::ctxt
, did
: ast
::DefId
,
373 items
: &mut Vec
<clean
::Item
>) {
374 csearch
::each_child_of_item(&tcx
.sess
.cstore
, did
, |def
, _
, vis
| {
376 decoder
::DlDef(def
::DefForeignMod(did
)) => {
377 fill_in(cx
, tcx
, did
, items
);
379 decoder
::DlDef(def
) if vis
== ast
::Public
=> {
380 match try_inline_def(cx
, tcx
, def
) {
381 Some(i
) => items
.extend(i
.into_iter()),
385 decoder
::DlDef(..) => {}
386 // All impls were inlined above
387 decoder
::DlImpl(..) => {}
388 decoder
::DlField
=> panic
!("unimplemented field"),
394 fn build_const(cx
: &DocContext
, tcx
: &ty
::ctxt
,
395 did
: ast
::DefId
) -> clean
::Constant
{
396 use rustc
::middle
::const_eval
;
397 use syntax
::print
::pprust
;
399 let expr
= const_eval
::lookup_const_by_id(tcx
, did
).unwrap_or_else(|| {
400 panic
!("expected lookup_const_by_id to succeed for {:?}", did
);
402 debug
!("converting constant expr {:?} to snippet", expr
);
403 let sn
= pprust
::expr_to_string(expr
);
404 debug
!("got snippet {}", sn
);
407 type_
: ty
::lookup_item_type(tcx
, did
).ty
.clean(cx
),
412 fn build_static(cx
: &DocContext
, tcx
: &ty
::ctxt
,
414 mutable
: bool
) -> clean
::Static
{
416 type_
: ty
::lookup_item_type(tcx
, did
).ty
.clean(cx
),
417 mutability
: if mutable {clean::Mutable}
else {clean::Immutable}
,
418 expr
: "\n\n\n".to_string(), // trigger the "[definition]" links