]>
git.proxmox.com Git - rustc.git/blob - src/librustdoc/clean/inline.rs
53063c6247efb8e21ab16f359e89e74624305659
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.
13 use std
::collections
::HashSet
;
17 use syntax
::attr
::AttrMetaMethods
;
19 use rustc
::metadata
::csearch
;
20 use rustc
::metadata
::decoder
;
21 use rustc
::middle
::def
;
22 use rustc
::middle
::ty
;
23 use rustc
::middle
::subst
;
24 use rustc
::middle
::stability
;
25 use rustc
::middle
::const_eval
;
31 use super::{Clean, ToSource}
;
33 /// Attempt to inline the definition of a local node id into this AST.
35 /// This function will fetch the definition of the id specified, and if it is
36 /// from another crate it will attempt to inline the documentation from the
37 /// other crate into this crate.
39 /// This is primarily used for `pub use` statements which are, in general,
40 /// implementation details. Inlining the documentation should help provide a
41 /// better experience when reading the documentation in this use case.
43 /// The returned value is `None` if the `id` could not be inlined, and `Some`
44 /// of a vector of items if it was successfully expanded.
45 pub fn try_inline(cx
: &DocContext
, id
: ast
::NodeId
, into
: Option
<ast
::Ident
>)
46 -> Option
<Vec
<clean
::Item
>> {
47 let tcx
= match cx
.tcx_opt() {
51 let def
= match tcx
.def_map
.borrow().get(&id
) {
52 Some(d
) => d
.full_def(),
55 let did
= def
.def_id();
56 if ast_util
::is_local(did
) { return None }
57 try_inline_def(cx
, tcx
, def
).map(|vec
| {
58 vec
.into_iter().map(|mut item
| {
60 Some(into
) if item
.name
.is_some() => {
61 item
.name
= Some(into
.clean(cx
));
70 fn try_inline_def(cx
: &DocContext
, tcx
: &ty
::ctxt
,
71 def
: def
::Def
) -> Option
<Vec
<clean
::Item
>> {
72 let mut ret
= Vec
::new();
73 let did
= def
.def_id();
74 let inner
= match def
{
75 def
::DefTrait(did
) => {
76 record_extern_fqn(cx
, did
, clean
::TypeTrait
);
77 clean
::TraitItem(build_external_trait(cx
, tcx
, did
))
79 def
::DefFn(did
, false) => {
80 // If this function is a tuple struct constructor, we just skip it
81 record_extern_fqn(cx
, did
, clean
::TypeFunction
);
82 clean
::FunctionItem(build_external_function(cx
, tcx
, did
))
84 def
::DefStruct(did
) => {
85 record_extern_fqn(cx
, did
, clean
::TypeStruct
);
86 ret
.extend(build_impls(cx
, tcx
, did
));
87 clean
::StructItem(build_struct(cx
, tcx
, did
))
89 def
::DefTy(did
, false) => {
90 record_extern_fqn(cx
, did
, clean
::TypeTypedef
);
91 ret
.extend(build_impls(cx
, tcx
, did
));
92 build_type(cx
, tcx
, did
)
94 def
::DefTy(did
, true) => {
95 record_extern_fqn(cx
, did
, clean
::TypeEnum
);
96 ret
.extend(build_impls(cx
, tcx
, did
));
97 build_type(cx
, tcx
, did
)
99 // Assume that the enum type is reexported next to the variant, and
100 // variants don't show up in documentation specially.
101 def
::DefVariant(..) => return Some(Vec
::new()),
102 def
::DefMod(did
) => {
103 record_extern_fqn(cx
, did
, clean
::TypeModule
);
104 clean
::ModuleItem(build_module(cx
, tcx
, did
))
106 def
::DefStatic(did
, mtbl
) => {
107 record_extern_fqn(cx
, did
, clean
::TypeStatic
);
108 clean
::StaticItem(build_static(cx
, tcx
, did
, mtbl
))
110 def
::DefConst(did
) | def
::DefAssociatedConst(did
, _
) => {
111 record_extern_fqn(cx
, did
, clean
::TypeConst
);
112 clean
::ConstantItem(build_const(cx
, tcx
, did
))
116 let fqn
= csearch
::get_item_path(tcx
, did
);
117 cx
.inlined
.borrow_mut().as_mut().unwrap().insert(did
);
118 ret
.push(clean
::Item
{
119 source
: clean
::Span
::empty(),
120 name
: Some(fqn
.last().unwrap().to_string()),
121 attrs
: load_attrs(cx
, tcx
, did
),
123 visibility
: Some(ast
::Public
),
124 stability
: stability
::lookup(tcx
, did
).clean(cx
),
130 pub fn load_attrs(cx
: &DocContext
, tcx
: &ty
::ctxt
,
131 did
: ast
::DefId
) -> Vec
<clean
::Attribute
> {
132 let attrs
= csearch
::get_item_attrs(&tcx
.sess
.cstore
, did
);
133 attrs
.into_iter().map(|a
| a
.clean(cx
)).collect()
136 /// Record an external fully qualified name in the external_paths cache.
138 /// These names are used later on by HTML rendering to generate things like
139 /// source links back to the original item.
140 pub fn record_extern_fqn(cx
: &DocContext
, did
: ast
::DefId
, kind
: clean
::TypeKind
) {
143 let fqn
= csearch
::get_item_path(tcx
, did
);
144 let fqn
= fqn
.into_iter().map(|i
| i
.to_string()).collect();
145 cx
.external_paths
.borrow_mut().as_mut().unwrap().insert(did
, (fqn
, kind
));
151 pub fn build_external_trait(cx
: &DocContext
, tcx
: &ty
::ctxt
,
152 did
: ast
::DefId
) -> clean
::Trait
{
153 let def
= ty
::lookup_trait_def(tcx
, did
);
154 let trait_items
= ty
::trait_items(tcx
, did
).clean(cx
);
155 let predicates
= ty
::lookup_predicates(tcx
, did
);
156 let generics
= (&def
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
);
157 let generics
= filter_non_trait_generics(did
, generics
);
158 let (generics
, supertrait_bounds
) = separate_supertrait_bounds(generics
);
160 unsafety
: def
.unsafety
,
163 bounds
: supertrait_bounds
,
167 fn build_external_function(cx
: &DocContext
, tcx
: &ty
::ctxt
, did
: ast
::DefId
) -> clean
::Function
{
168 let t
= ty
::lookup_item_type(tcx
, did
);
169 let (decl
, style
, abi
) = match t
.ty
.sty
{
170 ty
::TyBareFn(_
, ref f
) => ((did
, &f
.sig
).clean(cx
), f
.unsafety
, f
.abi
),
171 _
=> panic
!("bad function"),
173 let predicates
= ty
::lookup_predicates(tcx
, did
);
176 generics
: (&t
.generics
, &predicates
, subst
::FnSpace
).clean(cx
),
178 constness
: ast
::Constness
::NotConst
,
183 fn build_struct(cx
: &DocContext
, tcx
: &ty
::ctxt
, did
: ast
::DefId
) -> clean
::Struct
{
184 use syntax
::parse
::token
::special_idents
::unnamed_field
;
186 let t
= ty
::lookup_item_type(tcx
, did
);
187 let predicates
= ty
::lookup_predicates(tcx
, did
);
188 let fields
= ty
::lookup_struct_fields(tcx
, did
);
191 struct_type
: match &*fields
{
193 [ref f
] if f
.name
== unnamed_field
.name
=> doctree
::Newtype
,
194 [ref f
, ..] if f
.name
== unnamed_field
.name
=> doctree
::Tuple
,
197 generics
: (&t
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
),
198 fields
: fields
.clean(cx
),
199 fields_stripped
: false,
203 fn build_type(cx
: &DocContext
, tcx
: &ty
::ctxt
, did
: ast
::DefId
) -> clean
::ItemEnum
{
204 let t
= ty
::lookup_item_type(tcx
, did
);
205 let predicates
= ty
::lookup_predicates(tcx
, did
);
207 ty
::TyEnum(edid
, _
) if !csearch
::is_typedef(&tcx
.sess
.cstore
, did
) => {
208 return clean
::EnumItem(clean
::Enum
{
209 generics
: (&t
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
),
210 variants_stripped
: false,
211 variants
: ty
::enum_variants(tcx
, edid
).clean(cx
),
217 clean
::TypedefItem(clean
::Typedef
{
218 type_
: t
.ty
.clean(cx
),
219 generics
: (&t
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
),
223 pub fn build_impls(cx
: &DocContext
, tcx
: &ty
::ctxt
,
224 did
: ast
::DefId
) -> Vec
<clean
::Item
> {
225 ty
::populate_inherent_implementations_for_type_if_necessary(tcx
, did
);
226 let mut impls
= Vec
::new();
228 match tcx
.inherent_impls
.borrow().get(&did
) {
231 for &did
in i
.iter() {
232 build_impl(cx
, tcx
, did
, &mut impls
);
237 // If this is the first time we've inlined something from this crate, then
238 // we inline *all* impls from the crate into this crate. Note that there's
239 // currently no way for us to filter this based on type, and we likely need
240 // many impls for a variety of reasons.
242 // Primarily, the impls will be used to populate the documentation for this
243 // type being inlined, but impls can also be used when generating
244 // documentation for primitives (no way to find those specifically).
245 if cx
.populated_crate_impls
.borrow_mut().insert(did
.krate
) {
246 csearch
::each_top_level_item_of_crate(&tcx
.sess
.cstore
,
249 populate_impls(cx
, tcx
, def
, &mut impls
)
252 fn populate_impls(cx
: &DocContext
, tcx
: &ty
::ctxt
,
253 def
: decoder
::DefLike
,
254 impls
: &mut Vec
<clean
::Item
>) {
256 decoder
::DlImpl(did
) => build_impl(cx
, tcx
, did
, impls
),
257 decoder
::DlDef(def
::DefMod(did
)) => {
258 csearch
::each_child_of_item(&tcx
.sess
.cstore
,
261 populate_impls(cx
, tcx
, def
, impls
)
272 pub fn build_impl(cx
: &DocContext
,
275 ret
: &mut Vec
<clean
::Item
>) {
276 if !cx
.inlined
.borrow_mut().as_mut().unwrap().insert(did
) {
280 let attrs
= load_attrs(cx
, tcx
, did
);
281 let associated_trait
= csearch
::get_impl_trait(tcx
, did
);
282 if let Some(ref t
) = associated_trait
{
283 // If this is an impl for a #[doc(hidden)] trait, be sure to not inline
284 let trait_attrs
= load_attrs(cx
, tcx
, t
.def_id
);
285 if trait_attrs
.iter().any(|a
| is_doc_hidden(a
)) {
290 // If this is a defaulted impl, then bail out early here
291 if csearch
::is_default_impl(&tcx
.sess
.cstore
, did
) {
292 return ret
.push(clean
::Item
{
293 inner
: clean
::DefaultImplItem(clean
::DefaultImpl
{
294 // FIXME: this should be decoded
295 unsafety
: ast
::Unsafety
::Normal
,
296 trait_
: match associated_trait
.as_ref().unwrap().clean(cx
) {
297 clean
::TraitBound(polyt
, _
) => polyt
.trait_
,
298 clean
::RegionBound(..) => unreachable
!(),
301 source
: clean
::Span
::empty(),
304 visibility
: Some(ast
::Inherited
),
305 stability
: stability
::lookup(tcx
, did
).clean(cx
),
310 let predicates
= ty
::lookup_predicates(tcx
, did
);
311 let trait_items
= csearch
::get_impl_items(&tcx
.sess
.cstore
, did
)
314 let did
= did
.def_id();
315 let impl_item
= ty
::impl_or_trait_item(tcx
, did
);
317 ty
::ConstTraitItem(ref assoc_const
) => {
318 let did
= assoc_const
.def_id
;
319 let type_scheme
= ty
::lookup_item_type(tcx
, did
);
320 let default = match assoc_const
.default {
321 Some(_
) => Some(const_eval
::lookup_const_by_id(tcx
, did
, None
)
322 .unwrap().span
.to_src(cx
)),
326 name
: Some(assoc_const
.name
.clean(cx
)),
327 inner
: clean
::AssociatedConstItem(
328 type_scheme
.ty
.clean(cx
),
331 source
: clean
::Span
::empty(),
334 stability
: stability
::lookup(tcx
, did
).clean(cx
),
338 ty
::MethodTraitItem(method
) => {
339 if method
.vis
!= ast
::Public
&& associated_trait
.is_none() {
342 if method
.provided_source
.is_some() {
345 let mut item
= method
.clean(cx
);
346 item
.inner
= match item
.inner
.clone() {
347 clean
::TyMethodItem(clean
::TyMethod
{
348 unsafety
, decl
, self_
, generics
, abi
350 clean
::MethodItem(clean
::Method
{
352 constness
: ast
::Constness
::NotConst
,
359 _
=> panic
!("not a tymethod"),
363 ty
::TypeTraitItem(ref assoc_ty
) => {
364 let did
= assoc_ty
.def_id
;
365 let type_scheme
= ty
::TypeScheme
{
366 ty
: assoc_ty
.ty
.unwrap(),
367 generics
: ty
::Generics
::empty()
369 // Not sure the choice of ParamSpace actually matters here,
370 // because an associated type won't have generics on the LHS
371 let typedef
= (type_scheme
, ty
::GenericPredicates
::empty(),
372 subst
::ParamSpace
::TypeSpace
).clean(cx
);
374 name
: Some(assoc_ty
.name
.clean(cx
)),
375 inner
: clean
::TypedefItem(typedef
, true),
376 source
: clean
::Span
::empty(),
379 stability
: stability
::lookup(tcx
, did
).clean(cx
),
384 }).collect
::<Vec
<_
>>();
385 let polarity
= csearch
::get_impl_polarity(tcx
, did
);
386 let ty
= ty
::lookup_item_type(tcx
, did
);
387 let trait_
= associated_trait
.clean(cx
).map(|bound
| {
389 clean
::TraitBound(polyt
, _
) => polyt
.trait_
,
390 clean
::RegionBound(..) => unreachable
!(),
393 if let Some(clean
::ResolvedPath { did, .. }
) = trait_
{
394 if Some(did
) == cx
.deref_trait_did
.get() {
395 super::build_deref_target_impls(cx
, &trait_items
, ret
);
398 ret
.push(clean
::Item
{
399 inner
: clean
::ImplItem(clean
::Impl
{
400 unsafety
: ast
::Unsafety
::Normal
, // FIXME: this should be decoded
401 derived
: clean
::detect_derived(&attrs
),
403 for_
: ty
.ty
.clean(cx
),
404 generics
: (&ty
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
),
406 polarity
: polarity
.map(|p
| { p.clean(cx) }
),
408 source
: clean
::Span
::empty(),
411 visibility
: Some(ast
::Inherited
),
412 stability
: stability
::lookup(tcx
, did
).clean(cx
),
416 fn is_doc_hidden(a
: &clean
::Attribute
) -> bool
{
418 clean
::List(ref name
, ref inner
) if *name
== "doc" => {
419 inner
.iter().any(|a
| {
421 clean
::Word(ref s
) => *s
== "hidden",
431 fn build_module(cx
: &DocContext
, tcx
: &ty
::ctxt
,
432 did
: ast
::DefId
) -> clean
::Module
{
433 let mut items
= Vec
::new();
434 fill_in(cx
, tcx
, did
, &mut items
);
435 return clean
::Module
{
440 fn fill_in(cx
: &DocContext
, tcx
: &ty
::ctxt
, did
: ast
::DefId
,
441 items
: &mut Vec
<clean
::Item
>) {
442 // If we're reexporting a reexport it may actually reexport something in
443 // two namespaces, so the target may be listed twice. Make sure we only
444 // visit each node at most once.
445 let mut visited
= HashSet
::new();
446 csearch
::each_child_of_item(&tcx
.sess
.cstore
, did
, |def
, _
, vis
| {
448 decoder
::DlDef(def
::DefForeignMod(did
)) => {
449 fill_in(cx
, tcx
, did
, items
);
451 decoder
::DlDef(def
) if vis
== ast
::Public
=> {
452 if !visited
.insert(def
) { return }
453 match try_inline_def(cx
, tcx
, def
) {
454 Some(i
) => items
.extend(i
),
458 decoder
::DlDef(..) => {}
459 // All impls were inlined above
460 decoder
::DlImpl(..) => {}
461 decoder
::DlField
=> panic
!("unimplemented field"),
467 fn build_const(cx
: &DocContext
, tcx
: &ty
::ctxt
,
468 did
: ast
::DefId
) -> clean
::Constant
{
469 use rustc
::middle
::const_eval
;
470 use syntax
::print
::pprust
;
472 let expr
= const_eval
::lookup_const_by_id(tcx
, did
, None
).unwrap_or_else(|| {
473 panic
!("expected lookup_const_by_id to succeed for {:?}", did
);
475 debug
!("converting constant expr {:?} to snippet", expr
);
476 let sn
= pprust
::expr_to_string(expr
);
477 debug
!("got snippet {}", sn
);
480 type_
: ty
::lookup_item_type(tcx
, did
).ty
.clean(cx
),
485 fn build_static(cx
: &DocContext
, tcx
: &ty
::ctxt
,
487 mutable
: bool
) -> clean
::Static
{
489 type_
: ty
::lookup_item_type(tcx
, did
).ty
.clean(cx
),
490 mutability
: if mutable {clean::Mutable}
else {clean::Immutable}
,
491 expr
: "\n\n\n".to_string(), // trigger the "[definition]" links
495 /// A trait's generics clause actually contains all of the predicates for all of
496 /// its associated types as well. We specifically move these clauses to the
497 /// associated types instead when displaying, so when we're genering the
498 /// generics for the trait itself we need to be sure to remove them.
500 /// The inverse of this filtering logic can be found in the `Clean`
501 /// implementation for `AssociatedType`
502 fn filter_non_trait_generics(trait_did
: ast
::DefId
, mut g
: clean
::Generics
)
504 g
.where_predicates
.retain(|pred
| {
506 clean
::WherePredicate
::BoundPredicate
{
508 self_type
: box clean
::Generic(ref s
),
509 trait_
: box clean
::ResolvedPath { did, .. }
,
512 } => *s
!= "Self" || did
!= trait_did
,
519 /// Supertrait bounds for a trait are also listed in the generics coming from
520 /// the metadata for a crate, so we want to separate those out and create a new
521 /// list of explicit supertrait bounds to render nicely.
522 fn separate_supertrait_bounds(mut g
: clean
::Generics
)
523 -> (clean
::Generics
, Vec
<clean
::TyParamBound
>) {
524 let mut ty_bounds
= Vec
::new();
525 g
.where_predicates
.retain(|pred
| {
527 clean
::WherePredicate
::BoundPredicate
{
528 ty
: clean
::Generic(ref s
),
530 } if *s
== "Self" => {
531 ty_bounds
.extend(bounds
.iter().cloned());