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
;
19 use rustc
::middle
::cstore
;
20 use rustc
::hir
::def
::Def
;
21 use rustc
::hir
::def_id
::DefId
;
22 use rustc
::hir
::print
as pprust
;
23 use rustc
::ty
::{self, TyCtxt}
;
26 use rustc_const_eval
::lookup_const_by_id
;
28 use core
::{DocContext, DocAccessLevels}
;
30 use clean
::{self, GetDefId}
;
34 /// Attempt to inline the definition of a local node id into this AST.
36 /// This function will fetch the definition of the id specified, and if it is
37 /// from another crate it will attempt to inline the documentation from the
38 /// other crate into this crate.
40 /// This is primarily used for `pub use` statements which are, in general,
41 /// implementation details. Inlining the documentation should help provide a
42 /// better experience when reading the documentation in this use case.
44 /// The returned value is `None` if the `id` could not be inlined, and `Some`
45 /// of a vector of items if it was successfully expanded.
46 pub fn try_inline(cx
: &DocContext
, id
: ast
::NodeId
, into
: Option
<ast
::Name
>)
47 -> Option
<Vec
<clean
::Item
>> {
48 let tcx
= match cx
.tcx_opt() {
52 let def
= match tcx
.expect_def_or_none(id
) {
56 let did
= def
.def_id();
57 if did
.is_local() { return None }
58 try_inline_def(cx
, tcx
, def
).map(|vec
| {
59 vec
.into_iter().map(|mut item
| {
61 Some(into
) if item
.name
.is_some() => {
62 item
.name
= Some(into
.clean(cx
));
71 fn try_inline_def
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
72 def
: Def
) -> Option
<Vec
<clean
::Item
>> {
73 let mut ret
= Vec
::new();
74 let did
= def
.def_id();
75 let inner
= match def
{
77 record_extern_fqn(cx
, did
, clean
::TypeTrait
);
78 ret
.extend(build_impls(cx
, tcx
, did
));
79 clean
::TraitItem(build_external_trait(cx
, tcx
, did
))
82 record_extern_fqn(cx
, did
, clean
::TypeFunction
);
83 clean
::FunctionItem(build_external_function(cx
, tcx
, did
))
86 // If this is a struct constructor, we skip it
87 if tcx
.sess
.cstore
.tuple_struct_definition_if_ctor(did
).is_none() => {
88 record_extern_fqn(cx
, did
, clean
::TypeStruct
);
89 ret
.extend(build_impls(cx
, tcx
, did
));
90 clean
::StructItem(build_struct(cx
, tcx
, did
))
92 Def
::TyAlias(did
) => {
93 record_extern_fqn(cx
, did
, clean
::TypeTypedef
);
94 ret
.extend(build_impls(cx
, tcx
, did
));
95 build_type(cx
, tcx
, did
)
98 record_extern_fqn(cx
, did
, clean
::TypeEnum
);
99 ret
.extend(build_impls(cx
, tcx
, did
));
100 build_type(cx
, tcx
, did
)
102 // Assume that the enum type is reexported next to the variant, and
103 // variants don't show up in documentation specially.
104 Def
::Variant(..) => return Some(Vec
::new()),
106 record_extern_fqn(cx
, did
, clean
::TypeModule
);
107 clean
::ModuleItem(build_module(cx
, tcx
, did
))
109 Def
::Static(did
, mtbl
) => {
110 record_extern_fqn(cx
, did
, clean
::TypeStatic
);
111 clean
::StaticItem(build_static(cx
, tcx
, did
, mtbl
))
113 Def
::Const(did
) | Def
::AssociatedConst(did
) => {
114 record_extern_fqn(cx
, did
, clean
::TypeConst
);
115 clean
::ConstantItem(build_const(cx
, tcx
, did
))
119 cx
.renderinfo
.borrow_mut().inlined
.insert(did
);
120 ret
.push(clean
::Item
{
121 source
: clean
::Span
::empty(),
122 name
: Some(tcx
.item_name(did
).to_string()),
123 attrs
: load_attrs(cx
, tcx
, did
),
125 visibility
: Some(clean
::Public
),
126 stability
: tcx
.lookup_stability(did
).clean(cx
),
127 deprecation
: tcx
.lookup_deprecation(did
).clean(cx
),
133 pub fn load_attrs
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
134 did
: DefId
) -> Vec
<clean
::Attribute
> {
135 tcx
.get_attrs(did
).iter().map(|a
| a
.clean(cx
)).collect()
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
: DefId
, kind
: clean
::TypeKind
) {
143 if let Some(tcx
) = cx
.tcx_opt() {
144 let crate_name
= tcx
.sess
.cstore
.crate_name(did
.krate
).to_string();
145 let relative
= tcx
.def_path(did
).data
.into_iter().filter_map(|elem
| {
146 // extern blocks have an empty name
147 let s
= elem
.data
.to_string();
154 let fqn
= once(crate_name
).chain(relative
).collect();
155 cx
.renderinfo
.borrow_mut().external_paths
.insert(did
, (fqn
, kind
));
159 pub fn build_external_trait
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
160 did
: DefId
) -> clean
::Trait
{
161 let def
= tcx
.lookup_trait_def(did
);
162 let trait_items
= tcx
.trait_items(did
).clean(cx
);
163 let predicates
= tcx
.lookup_predicates(did
);
164 let generics
= (&def
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
);
165 let generics
= filter_non_trait_generics(did
, generics
);
166 let (generics
, supertrait_bounds
) = separate_supertrait_bounds(generics
);
168 unsafety
: def
.unsafety
,
171 bounds
: supertrait_bounds
,
175 fn build_external_function
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
176 did
: DefId
) -> clean
::Function
{
177 let t
= tcx
.lookup_item_type(did
);
178 let (decl
, style
, abi
) = match t
.ty
.sty
{
179 ty
::TyFnDef(_
, _
, ref f
) => ((did
, &f
.sig
).clean(cx
), f
.unsafety
, f
.abi
),
180 _
=> panic
!("bad function"),
183 let constness
= if tcx
.sess
.cstore
.is_const_fn(did
) {
184 hir
::Constness
::Const
186 hir
::Constness
::NotConst
189 let predicates
= tcx
.lookup_predicates(did
);
192 generics
: (&t
.generics
, &predicates
, subst
::FnSpace
).clean(cx
),
194 constness
: constness
,
199 fn build_struct
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
200 did
: DefId
) -> clean
::Struct
{
201 let t
= tcx
.lookup_item_type(did
);
202 let predicates
= tcx
.lookup_predicates(did
);
203 let variant
= tcx
.lookup_adt_def(did
).struct_variant();
206 struct_type
: match &variant
.fields
[..] {
207 &[] => doctree
::Unit
,
208 &[_
] if variant
.kind
== ty
::VariantKind
::Tuple
=> doctree
::Newtype
,
209 &[..] if variant
.kind
== ty
::VariantKind
::Tuple
=> doctree
::Tuple
,
212 generics
: (&t
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
),
213 fields
: variant
.fields
.clean(cx
),
214 fields_stripped
: false,
218 fn build_type
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
219 did
: DefId
) -> clean
::ItemEnum
{
220 let t
= tcx
.lookup_item_type(did
);
221 let predicates
= tcx
.lookup_predicates(did
);
223 ty
::TyEnum(edef
, _
) if !tcx
.sess
.cstore
.is_typedef(did
) => {
224 return clean
::EnumItem(clean
::Enum
{
225 generics
: (&t
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
),
226 variants_stripped
: false,
227 variants
: edef
.variants
.clean(cx
),
233 clean
::TypedefItem(clean
::Typedef
{
234 type_
: t
.ty
.clean(cx
),
235 generics
: (&t
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
),
239 pub fn build_impls
<'a
, 'tcx
>(cx
: &DocContext
,
240 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
241 did
: DefId
) -> Vec
<clean
::Item
> {
242 tcx
.populate_inherent_implementations_for_type_if_necessary(did
);
243 let mut impls
= Vec
::new();
245 if let Some(i
) = tcx
.inherent_impls
.borrow().get(&did
) {
246 for &did
in i
.iter() {
247 build_impl(cx
, tcx
, did
, &mut impls
);
251 // If this is the first time we've inlined something from this crate, then
252 // we inline *all* impls from the crate into this crate. Note that there's
253 // currently no way for us to filter this based on type, and we likely need
254 // many impls for a variety of reasons.
256 // Primarily, the impls will be used to populate the documentation for this
257 // type being inlined, but impls can also be used when generating
258 // documentation for primitives (no way to find those specifically).
259 if cx
.populated_crate_impls
.borrow_mut().insert(did
.krate
) {
260 for item
in tcx
.sess
.cstore
.crate_top_level_items(did
.krate
) {
261 populate_impls(cx
, tcx
, item
.def
, &mut impls
);
264 fn populate_impls
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
265 def
: cstore
::DefLike
,
266 impls
: &mut Vec
<clean
::Item
>) {
268 cstore
::DlImpl(did
) => build_impl(cx
, tcx
, did
, impls
),
269 cstore
::DlDef(Def
::Mod(did
)) => {
270 for item
in tcx
.sess
.cstore
.item_children(did
) {
271 populate_impls(cx
, tcx
, item
.def
, impls
)
282 pub fn build_impl
<'a
, 'tcx
>(cx
: &DocContext
,
283 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
285 ret
: &mut Vec
<clean
::Item
>) {
286 if !cx
.renderinfo
.borrow_mut().inlined
.insert(did
) {
290 let attrs
= load_attrs(cx
, tcx
, did
);
291 let associated_trait
= tcx
.impl_trait_ref(did
);
293 // Only inline impl if the implemented trait is
294 // reachable in rustdoc generated documentation
295 if let Some(traitref
) = associated_trait
{
296 if !cx
.access_levels
.borrow().is_doc_reachable(traitref
.def_id
) {
301 // If this is a defaulted impl, then bail out early here
302 if tcx
.sess
.cstore
.is_default_impl(did
) {
303 return ret
.push(clean
::Item
{
304 inner
: clean
::DefaultImplItem(clean
::DefaultImpl
{
305 // FIXME: this should be decoded
306 unsafety
: hir
::Unsafety
::Normal
,
307 trait_
: match associated_trait
.as_ref().unwrap().clean(cx
) {
308 clean
::TraitBound(polyt
, _
) => polyt
.trait_
,
309 clean
::RegionBound(..) => unreachable
!(),
312 source
: clean
::Span
::empty(),
315 visibility
: Some(clean
::Inherited
),
316 stability
: tcx
.lookup_stability(did
).clean(cx
),
317 deprecation
: tcx
.lookup_deprecation(did
).clean(cx
),
322 let ty
= tcx
.lookup_item_type(did
);
323 let for_
= ty
.ty
.clean(cx
);
325 // Only inline impl if the implementing type is
326 // reachable in rustdoc generated documentation
327 if let Some(did
) = for_
.def_id() {
328 if !cx
.access_levels
.borrow().is_doc_reachable(did
) {
333 let predicates
= tcx
.lookup_predicates(did
);
334 let trait_items
= tcx
.sess
.cstore
.impl_items(did
)
337 let did
= did
.def_id();
338 let impl_item
= tcx
.impl_or_trait_item(did
);
340 ty
::ConstTraitItem(ref assoc_const
) => {
341 let did
= assoc_const
.def_id
;
342 let type_scheme
= tcx
.lookup_item_type(did
);
343 let default = if assoc_const
.has_value
{
344 Some(pprust
::expr_to_string(
345 lookup_const_by_id(tcx
, did
, None
).unwrap().0))
350 name
: Some(assoc_const
.name
.clean(cx
)),
351 inner
: clean
::AssociatedConstItem(
352 type_scheme
.ty
.clean(cx
),
355 source
: clean
::Span
::empty(),
358 stability
: tcx
.lookup_stability(did
).clean(cx
),
359 deprecation
: tcx
.lookup_deprecation(did
).clean(cx
),
363 ty
::MethodTraitItem(method
) => {
364 if method
.vis
!= ty
::Visibility
::Public
&& associated_trait
.is_none() {
367 let mut item
= method
.clean(cx
);
368 item
.inner
= match item
.inner
.clone() {
369 clean
::TyMethodItem(clean
::TyMethod
{
370 unsafety
, decl
, generics
, abi
372 let constness
= if tcx
.sess
.cstore
.is_const_fn(did
) {
373 hir
::Constness
::Const
375 hir
::Constness
::NotConst
378 clean
::MethodItem(clean
::Method
{
380 constness
: constness
,
386 _
=> panic
!("not a tymethod"),
390 ty
::TypeTraitItem(ref assoc_ty
) => {
391 let did
= assoc_ty
.def_id
;
392 let type_scheme
= ty
::TypeScheme
{
393 ty
: assoc_ty
.ty
.unwrap(),
394 generics
: ty
::Generics
::empty()
396 // Not sure the choice of ParamSpace actually matters here,
397 // because an associated type won't have generics on the LHS
398 let typedef
= (type_scheme
, ty
::GenericPredicates
::empty(),
399 subst
::ParamSpace
::TypeSpace
).clean(cx
);
401 name
: Some(assoc_ty
.name
.clean(cx
)),
402 inner
: clean
::TypedefItem(typedef
, true),
403 source
: clean
::Span
::empty(),
406 stability
: tcx
.lookup_stability(did
).clean(cx
),
407 deprecation
: tcx
.lookup_deprecation(did
).clean(cx
),
412 }).collect
::<Vec
<_
>>();
413 let polarity
= tcx
.trait_impl_polarity(did
);
414 let trait_
= associated_trait
.clean(cx
).map(|bound
| {
416 clean
::TraitBound(polyt
, _
) => polyt
.trait_
,
417 clean
::RegionBound(..) => unreachable
!(),
420 if trait_
.def_id() == cx
.deref_trait_did
.get() {
421 super::build_deref_target_impls(cx
, &trait_items
, ret
);
424 let provided
= trait_
.def_id().map(|did
| {
425 cx
.tcx().provided_trait_methods(did
)
427 .map(|meth
| meth
.name
.to_string())
429 }).unwrap_or(HashSet
::new());
431 ret
.push(clean
::Item
{
432 inner
: clean
::ImplItem(clean
::Impl
{
433 unsafety
: hir
::Unsafety
::Normal
, // FIXME: this should be decoded
434 provided_trait_methods
: provided
,
437 generics
: (&ty
.generics
, &predicates
, subst
::TypeSpace
).clean(cx
),
439 polarity
: polarity
.map(|p
| { p.clean(cx) }
),
441 source
: clean
::Span
::empty(),
444 visibility
: Some(clean
::Inherited
),
445 stability
: tcx
.lookup_stability(did
).clean(cx
),
446 deprecation
: tcx
.lookup_deprecation(did
).clean(cx
),
451 fn build_module
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
452 did
: DefId
) -> clean
::Module
{
453 let mut items
= Vec
::new();
454 fill_in(cx
, tcx
, did
, &mut items
);
455 return clean
::Module
{
460 fn fill_in
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
461 did
: DefId
, items
: &mut Vec
<clean
::Item
>) {
462 // If we're reexporting a reexport it may actually reexport something in
463 // two namespaces, so the target may be listed twice. Make sure we only
464 // visit each node at most once.
465 let mut visited
= HashSet
::new();
466 for item
in tcx
.sess
.cstore
.item_children(did
) {
468 cstore
::DlDef(Def
::ForeignMod(did
)) => {
469 fill_in(cx
, tcx
, did
, items
);
471 cstore
::DlDef(def
) if item
.vis
== ty
::Visibility
::Public
=> {
472 if !visited
.insert(def
) { continue }
473 if let Some(i
) = try_inline_def(cx
, tcx
, def
) {
477 cstore
::DlDef(..) => {}
478 // All impls were inlined above
479 cstore
::DlImpl(..) => {}
480 cstore
::DlField
=> panic
!("unimplemented field"),
486 fn build_const
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
487 did
: DefId
) -> clean
::Constant
{
488 let (expr
, ty
) = lookup_const_by_id(tcx
, did
, None
).unwrap_or_else(|| {
489 panic
!("expected lookup_const_by_id to succeed for {:?}", did
);
491 debug
!("converting constant expr {:?} to snippet", expr
);
492 let sn
= pprust
::expr_to_string(expr
);
493 debug
!("got snippet {}", sn
);
496 type_
: ty
.map(|t
| t
.clean(cx
)).unwrap_or_else(|| tcx
.lookup_item_type(did
).ty
.clean(cx
)),
501 fn build_static
<'a
, 'tcx
>(cx
: &DocContext
, tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
503 mutable
: bool
) -> clean
::Static
{
505 type_
: tcx
.lookup_item_type(did
).ty
.clean(cx
),
506 mutability
: if mutable {clean::Mutable}
else {clean::Immutable}
,
507 expr
: "\n\n\n".to_string(), // trigger the "[definition]" links
511 /// A trait's generics clause actually contains all of the predicates for all of
512 /// its associated types as well. We specifically move these clauses to the
513 /// associated types instead when displaying, so when we're genering the
514 /// generics for the trait itself we need to be sure to remove them.
516 /// The inverse of this filtering logic can be found in the `Clean`
517 /// implementation for `AssociatedType`
518 fn filter_non_trait_generics(trait_did
: DefId
, mut g
: clean
::Generics
)
520 g
.where_predicates
.retain(|pred
| {
522 clean
::WherePredicate
::BoundPredicate
{
524 self_type
: box clean
::Generic(ref s
),
525 trait_
: box clean
::ResolvedPath { did, .. }
,
528 } => *s
!= "Self" || did
!= trait_did
,
535 /// Supertrait bounds for a trait are also listed in the generics coming from
536 /// the metadata for a crate, so we want to separate those out and create a new
537 /// list of explicit supertrait bounds to render nicely.
538 fn separate_supertrait_bounds(mut g
: clean
::Generics
)
539 -> (clean
::Generics
, Vec
<clean
::TyParamBound
>) {
540 let mut ty_bounds
= Vec
::new();
541 g
.where_predicates
.retain(|pred
| {
543 clean
::WherePredicate
::BoundPredicate
{
544 ty
: clean
::Generic(ref s
),
546 } if *s
== "Self" => {
547 ty_bounds
.extend(bounds
.iter().cloned());