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.
16 use syntax
::ext
::base
::MacroKind
;
20 use rustc
::hir
::def
::{Def, CtorKind}
;
21 use rustc
::hir
::def_id
::DefId
;
22 use rustc
::middle
::cstore
::LoadedMacro
;
24 use rustc
::util
::nodemap
::FxHashSet
;
26 use core
::{DocContext, DocAccessLevels}
;
28 use clean
::{self, GetDefId, ToSource, get_auto_traits_with_def_id}
;
32 /// Attempt to inline a definition into this AST.
34 /// This function will fetch the definition specified, and if it is
35 /// from another crate it will attempt to inline the documentation
36 /// from the other crate into this crate.
38 /// This is primarily used for `pub use` statements which are, in general,
39 /// implementation details. Inlining the documentation should help provide a
40 /// better experience when reading the documentation in this use case.
42 /// The returned value is `None` if the definition could not be inlined,
43 /// and `Some` of a vector of items if it was successfully expanded.
44 pub fn try_inline(cx
: &DocContext
, def
: Def
, name
: ast
::Name
, visited
: &mut FxHashSet
<DefId
>)
45 -> Option
<Vec
<clean
::Item
>> {
46 if def
== Def
::Err { return None }
47 let did
= def
.def_id();
48 if did
.is_local() { return None }
49 let mut ret
= Vec
::new();
50 let inner
= match def
{
52 record_extern_fqn(cx
, did
, clean
::TypeKind
::Trait
);
53 ret
.extend(build_impls(cx
, did
, false));
54 clean
::TraitItem(build_external_trait(cx
, did
))
57 record_extern_fqn(cx
, did
, clean
::TypeKind
::Function
);
58 clean
::FunctionItem(build_external_function(cx
, did
))
61 record_extern_fqn(cx
, did
, clean
::TypeKind
::Struct
);
62 ret
.extend(build_impls(cx
, did
, true));
63 clean
::StructItem(build_struct(cx
, did
))
66 record_extern_fqn(cx
, did
, clean
::TypeKind
::Union
);
67 ret
.extend(build_impls(cx
, did
, true));
68 clean
::UnionItem(build_union(cx
, did
))
70 Def
::TyAlias(did
) => {
71 record_extern_fqn(cx
, did
, clean
::TypeKind
::Typedef
);
72 ret
.extend(build_impls(cx
, did
, false));
73 clean
::TypedefItem(build_type_alias(cx
, did
), false)
76 record_extern_fqn(cx
, did
, clean
::TypeKind
::Enum
);
77 ret
.extend(build_impls(cx
, did
, true));
78 clean
::EnumItem(build_enum(cx
, did
))
80 Def
::TyForeign(did
) => {
81 record_extern_fqn(cx
, did
, clean
::TypeKind
::Foreign
);
82 ret
.extend(build_impls(cx
, did
, false));
83 clean
::ForeignTypeItem
85 // Never inline enum variants but leave them shown as re-exports.
86 Def
::Variant(..) => return None
,
87 // Assume that enum variants and struct types are re-exported next to
88 // their constructors.
89 Def
::VariantCtor(..) |
90 Def
::StructCtor(..) => return Some(Vec
::new()),
92 record_extern_fqn(cx
, did
, clean
::TypeKind
::Module
);
93 clean
::ModuleItem(build_module(cx
, did
, visited
))
95 Def
::Static(did
, mtbl
) => {
96 record_extern_fqn(cx
, did
, clean
::TypeKind
::Static
);
97 clean
::StaticItem(build_static(cx
, did
, mtbl
))
100 record_extern_fqn(cx
, did
, clean
::TypeKind
::Const
);
101 clean
::ConstantItem(build_const(cx
, did
))
103 // FIXME(misdreavus): if attributes/derives come down here we should probably document them
105 Def
::Macro(did
, MacroKind
::Bang
) => {
106 record_extern_fqn(cx
, did
, clean
::TypeKind
::Macro
);
107 if let Some(mac
) = build_macro(cx
, did
, name
) {
108 clean
::MacroItem(mac
)
115 cx
.renderinfo
.borrow_mut().inlined
.insert(did
);
116 ret
.push(clean
::Item
{
117 source
: cx
.tcx
.def_span(did
).clean(cx
),
118 name
: Some(name
.clean(cx
)),
119 attrs
: load_attrs(cx
, did
),
121 visibility
: Some(clean
::Public
),
122 stability
: cx
.tcx
.lookup_stability(did
).clean(cx
),
123 deprecation
: cx
.tcx
.lookup_deprecation(did
).clean(cx
),
129 pub fn try_inline_glob(cx
: &DocContext
, def
: Def
, visited
: &mut FxHashSet
<DefId
>)
130 -> Option
<Vec
<clean
::Item
>>
132 if def
== Def
::Err { return None }
133 let did
= def
.def_id();
134 if did
.is_local() { return None }
138 let m
= build_module(cx
, did
, visited
);
141 // glob imports on things like enums aren't inlined even for local exports, so just bail
146 pub fn load_attrs(cx
: &DocContext
, did
: DefId
) -> clean
::Attributes
{
147 cx
.tcx
.get_attrs(did
).clean(cx
)
150 /// Record an external fully qualified name in the external_paths cache.
152 /// These names are used later on by HTML rendering to generate things like
153 /// source links back to the original item.
154 pub fn record_extern_fqn(cx
: &DocContext
, did
: DefId
, kind
: clean
::TypeKind
) {
156 debug
!("record_extern_fqn(did={:?}, kind+{:?}): def_id is local, aborting", did
, kind
);
160 let crate_name
= cx
.tcx
.crate_name(did
.krate
).to_string();
161 let relative
= cx
.tcx
.def_path(did
).data
.into_iter().filter_map(|elem
| {
162 // extern blocks have an empty name
163 let s
= elem
.data
.to_string();
170 let fqn
= if let clean
::TypeKind
::Macro
= kind
{
171 vec
![crate_name
, relative
.last().unwrap()]
173 once(crate_name
).chain(relative
).collect()
175 cx
.renderinfo
.borrow_mut().external_paths
.insert(did
, (fqn
, kind
));
178 pub fn build_external_trait(cx
: &DocContext
, did
: DefId
) -> clean
::Trait
{
179 let auto_trait
= cx
.tcx
.trait_def(did
).has_auto_impl
;
180 let trait_items
= cx
.tcx
.associated_items(did
).map(|item
| item
.clean(cx
)).collect();
181 let predicates
= cx
.tcx
.predicates_of(did
);
182 let generics
= (cx
.tcx
.generics_of(did
), &predicates
).clean(cx
);
183 let generics
= filter_non_trait_generics(did
, generics
);
184 let (generics
, supertrait_bounds
) = separate_supertrait_bounds(generics
);
185 let is_spotlight
= load_attrs(cx
, did
).has_doc_flag("spotlight");
186 let is_auto
= cx
.tcx
.trait_is_auto(did
);
189 unsafety
: cx
.tcx
.trait_def(did
).unsafety
,
192 bounds
: supertrait_bounds
,
198 fn build_external_function(cx
: &DocContext
, did
: DefId
) -> clean
::Function
{
199 let sig
= cx
.tcx
.fn_sig(did
);
201 let constness
= if cx
.tcx
.is_const_fn(did
) {
202 hir
::Constness
::Const
204 hir
::Constness
::NotConst
207 let predicates
= cx
.tcx
.predicates_of(did
);
209 decl
: (did
, sig
).clean(cx
),
210 generics
: (cx
.tcx
.generics_of(did
), &predicates
).clean(cx
),
211 header
: hir
::FnHeader
{
212 unsafety
: sig
.unsafety(),
215 asyncness
: hir
::IsAsync
::NotAsync
,
220 fn build_enum(cx
: &DocContext
, did
: DefId
) -> clean
::Enum
{
221 let predicates
= cx
.tcx
.predicates_of(did
);
224 generics
: (cx
.tcx
.generics_of(did
), &predicates
).clean(cx
),
225 variants_stripped
: false,
226 variants
: cx
.tcx
.adt_def(did
).variants
.clean(cx
),
230 fn build_struct(cx
: &DocContext
, did
: DefId
) -> clean
::Struct
{
231 let predicates
= cx
.tcx
.predicates_of(did
);
232 let variant
= cx
.tcx
.adt_def(did
).non_enum_variant();
235 struct_type
: match variant
.ctor_kind
{
236 CtorKind
::Fictive
=> doctree
::Plain
,
237 CtorKind
::Fn
=> doctree
::Tuple
,
238 CtorKind
::Const
=> doctree
::Unit
,
240 generics
: (cx
.tcx
.generics_of(did
), &predicates
).clean(cx
),
241 fields
: variant
.fields
.clean(cx
),
242 fields_stripped
: false,
246 fn build_union(cx
: &DocContext
, did
: DefId
) -> clean
::Union
{
247 let predicates
= cx
.tcx
.predicates_of(did
);
248 let variant
= cx
.tcx
.adt_def(did
).non_enum_variant();
251 struct_type
: doctree
::Plain
,
252 generics
: (cx
.tcx
.generics_of(did
), &predicates
).clean(cx
),
253 fields
: variant
.fields
.clean(cx
),
254 fields_stripped
: false,
258 fn build_type_alias(cx
: &DocContext
, did
: DefId
) -> clean
::Typedef
{
259 let predicates
= cx
.tcx
.predicates_of(did
);
262 type_
: cx
.tcx
.type_of(did
).clean(cx
),
263 generics
: (cx
.tcx
.generics_of(did
), &predicates
).clean(cx
),
267 pub fn build_impls(cx
: &DocContext
, did
: DefId
, auto_traits
: bool
) -> Vec
<clean
::Item
> {
269 let mut impls
= Vec
::new();
271 for &did
in tcx
.inherent_impls(did
).iter() {
272 build_impl(cx
, did
, &mut impls
);
276 let auto_impls
= get_auto_traits_with_def_id(cx
, did
);
277 let mut renderinfo
= cx
.renderinfo
.borrow_mut();
278 let new_impls
: Vec
<clean
::Item
> = auto_impls
.into_iter()
279 .filter(|i
| renderinfo
.inlined
.insert(i
.def_id
)).collect();
281 impls
.extend(new_impls
);
284 // If this is the first time we've inlined something from another crate, then
285 // we inline *all* impls from all the crates into this crate. Note that there's
286 // currently no way for us to filter this based on type, and we likely need
287 // many impls for a variety of reasons.
289 // Primarily, the impls will be used to populate the documentation for this
290 // type being inlined, but impls can also be used when generating
291 // documentation for primitives (no way to find those specifically).
292 if cx
.populated_all_crate_impls
.get() {
296 cx
.populated_all_crate_impls
.set(true);
298 for &cnum
in tcx
.crates().iter() {
299 for did
in tcx
.all_trait_implementations(cnum
).iter() {
300 build_impl(cx
, *did
, &mut impls
);
304 // Also try to inline primitive impls from other crates.
305 let lang_items
= tcx
.lang_items();
306 let primitive_impls
= [
307 lang_items
.isize_impl(),
308 lang_items
.i8_impl(),
309 lang_items
.i16_impl(),
310 lang_items
.i32_impl(),
311 lang_items
.i64_impl(),
312 lang_items
.i128_impl(),
313 lang_items
.usize_impl(),
314 lang_items
.u8_impl(),
315 lang_items
.u16_impl(),
316 lang_items
.u32_impl(),
317 lang_items
.u64_impl(),
318 lang_items
.u128_impl(),
319 lang_items
.f32_impl(),
320 lang_items
.f64_impl(),
321 lang_items
.f32_runtime_impl(),
322 lang_items
.f64_runtime_impl(),
323 lang_items
.char_impl(),
324 lang_items
.str_impl(),
325 lang_items
.slice_impl(),
326 lang_items
.slice_u8_impl(),
327 lang_items
.str_alloc_impl(),
328 lang_items
.slice_alloc_impl(),
329 lang_items
.slice_u8_alloc_impl(),
330 lang_items
.const_ptr_impl(),
331 lang_items
.mut_ptr_impl(),
334 for def_id
in primitive_impls
.iter().filter_map(|&def_id
| def_id
) {
335 if !def_id
.is_local() {
336 build_impl(cx
, def_id
, &mut impls
);
338 let auto_impls
= get_auto_traits_with_def_id(cx
, def_id
);
339 let mut renderinfo
= cx
.renderinfo
.borrow_mut();
341 let new_impls
: Vec
<clean
::Item
> = auto_impls
.into_iter()
342 .filter(|i
| renderinfo
.inlined
.insert(i
.def_id
)).collect();
344 impls
.extend(new_impls
);
351 pub fn build_impl(cx
: &DocContext
, did
: DefId
, ret
: &mut Vec
<clean
::Item
>) {
352 if !cx
.renderinfo
.borrow_mut().inlined
.insert(did
) {
356 let attrs
= load_attrs(cx
, did
);
358 let associated_trait
= tcx
.impl_trait_ref(did
);
360 // Only inline impl if the implemented trait is
361 // reachable in rustdoc generated documentation
362 if let Some(traitref
) = associated_trait
{
363 if !cx
.access_levels
.borrow().is_doc_reachable(traitref
.def_id
) {
368 let for_
= tcx
.type_of(did
).clean(cx
);
370 // Only inline impl if the implementing type is
371 // reachable in rustdoc generated documentation
372 if let Some(did
) = for_
.def_id() {
373 if !cx
.access_levels
.borrow().is_doc_reachable(did
) {
378 let predicates
= tcx
.predicates_of(did
);
379 let trait_items
= tcx
.associated_items(did
).filter_map(|item
| {
380 if associated_trait
.is_some() || item
.vis
== ty
::Visibility
::Public
{
385 }).collect
::<Vec
<_
>>();
386 let polarity
= tcx
.impl_polarity(did
);
387 let trait_
= associated_trait
.clean(cx
).map(|bound
| {
389 clean
::GenericBound
::TraitBound(polyt
, _
) => polyt
.trait_
,
390 clean
::GenericBound
::Outlives(..) => unreachable
!(),
393 if trait_
.def_id() == tcx
.lang_items().deref_trait() {
394 super::build_deref_target_impls(cx
, &trait_items
, ret
);
396 if let Some(trait_did
) = trait_
.def_id() {
397 record_extern_trait(cx
, trait_did
);
400 let provided
= trait_
.def_id().map(|did
| {
401 tcx
.provided_trait_methods(did
)
403 .map(|meth
| meth
.ident
.to_string())
405 }).unwrap_or(FxHashSet());
407 ret
.push(clean
::Item
{
408 inner
: clean
::ImplItem(clean
::Impl
{
409 unsafety
: hir
::Unsafety
::Normal
,
410 generics
: (tcx
.generics_of(did
), &predicates
).clean(cx
),
411 provided_trait_methods
: provided
,
415 polarity
: Some(polarity
.clean(cx
)),
419 source
: tcx
.def_span(did
).clean(cx
),
422 visibility
: Some(clean
::Inherited
),
423 stability
: tcx
.lookup_stability(did
).clean(cx
),
424 deprecation
: tcx
.lookup_deprecation(did
).clean(cx
),
429 fn build_module(cx
: &DocContext
, did
: DefId
, visited
: &mut FxHashSet
<DefId
>) -> clean
::Module
{
430 let mut items
= Vec
::new();
431 fill_in(cx
, did
, &mut items
, visited
);
432 return clean
::Module
{
437 fn fill_in(cx
: &DocContext
, did
: DefId
, items
: &mut Vec
<clean
::Item
>,
438 visited
: &mut FxHashSet
<DefId
>) {
439 // If we're re-exporting a re-export it may actually re-export something in
440 // two namespaces, so the target may be listed twice. Make sure we only
441 // visit each node at most once.
442 for &item
in cx
.tcx
.item_children(did
).iter() {
443 let def_id
= item
.def
.def_id();
444 if item
.vis
== ty
::Visibility
::Public
{
445 if did
== def_id
|| !visited
.insert(def_id
) { continue }
446 if let Some(i
) = try_inline(cx
, item
.def
, item
.ident
.name
, visited
) {
454 pub fn print_inlined_const(cx
: &DocContext
, did
: DefId
) -> String
{
455 cx
.tcx
.rendered_const(did
)
458 fn build_const(cx
: &DocContext
, did
: DefId
) -> clean
::Constant
{
460 type_
: cx
.tcx
.type_of(did
).clean(cx
),
461 expr
: print_inlined_const(cx
, did
)
465 fn build_static(cx
: &DocContext
, did
: DefId
, mutable
: bool
) -> clean
::Static
{
467 type_
: cx
.tcx
.type_of(did
).clean(cx
),
468 mutability
: if mutable {clean::Mutable}
else {clean::Immutable}
,
469 expr
: "\n\n\n".to_string(), // trigger the "[definition]" links
473 fn build_macro(cx
: &DocContext
, did
: DefId
, name
: ast
::Name
) -> Option
<clean
::Macro
> {
474 let imported_from
= cx
.tcx
.original_crate_name(did
.krate
);
475 let def
= match cx
.cstore
.load_macro_untracked(did
, cx
.sess()) {
476 LoadedMacro
::MacroDef(macro_def
) => macro_def
,
477 // FIXME(jseyfried): document proc macro re-exports
478 LoadedMacro
::ProcMacro(..) => return None
,
481 let matchers
: hir
::HirVec
<Span
> = if let ast
::ItemKind
::MacroDef(ref def
) = def
.node
{
482 let tts
: Vec
<_
> = def
.stream().into_trees().collect();
483 tts
.chunks(4).map(|arm
| arm
[0].span()).collect()
488 let source
= format
!("macro_rules! {} {{\n{}}}",
490 matchers
.iter().map(|span
| {
491 format
!(" {} => {{ ... }};\n", span
.to_src(cx
))
492 }).collect
::<String
>());
496 imported_from
: Some(imported_from
).clean(cx
),
500 /// A trait's generics clause actually contains all of the predicates for all of
501 /// its associated types as well. We specifically move these clauses to the
502 /// associated types instead when displaying, so when we're generating the
503 /// generics for the trait itself we need to be sure to remove them.
504 /// We also need to remove the implied "recursive" Self: Trait bound.
506 /// The inverse of this filtering logic can be found in the `Clean`
507 /// implementation for `AssociatedType`
508 fn filter_non_trait_generics(trait_did
: DefId
, mut g
: clean
::Generics
) -> clean
::Generics
{
509 for pred
in &mut g
.where_predicates
{
511 clean
::WherePredicate
::BoundPredicate
{
512 ty
: clean
::Generic(ref s
),
514 } if *s
== "Self" => {
515 bounds
.retain(|bound
| {
517 clean
::GenericBound
::TraitBound(clean
::PolyTrait
{
518 trait_
: clean
::ResolvedPath { did, .. }
,
520 }, _
) => did
!= trait_did
,
529 g
.where_predicates
.retain(|pred
| {
531 clean
::WherePredicate
::BoundPredicate
{
533 self_type
: box clean
::Generic(ref s
),
534 trait_
: box clean
::ResolvedPath { did, .. }
,
537 } => !(*s
== "Self" && did
== trait_did
) && !bounds
.is_empty(),
544 /// Supertrait bounds for a trait are also listed in the generics coming from
545 /// the metadata for a crate, so we want to separate those out and create a new
546 /// list of explicit supertrait bounds to render nicely.
547 fn separate_supertrait_bounds(mut g
: clean
::Generics
)
548 -> (clean
::Generics
, Vec
<clean
::GenericBound
>) {
549 let mut ty_bounds
= Vec
::new();
550 g
.where_predicates
.retain(|pred
| {
552 clean
::WherePredicate
::BoundPredicate
{
553 ty
: clean
::Generic(ref s
),
555 } if *s
== "Self" => {
556 ty_bounds
.extend(bounds
.iter().cloned());
565 pub fn record_extern_trait(cx
: &DocContext
, did
: DefId
) {
566 if cx
.external_traits
.borrow().contains_key(&did
) ||
567 cx
.active_extern_traits
.borrow().contains(&did
)
572 cx
.active_extern_traits
.borrow_mut().push(did
);
574 let trait_
= build_external_trait(cx
, did
);
576 cx
.external_traits
.borrow_mut().insert(did
, trait_
);
577 cx
.active_extern_traits
.borrow_mut().remove_item(&did
);