]>
git.proxmox.com Git - rustc.git/blob - src/librustdoc/clean/inline.rs
1 //! Support for inlining external documentation into the current AST.
6 use syntax
::symbol
::sym
;
7 use syntax_pos
::hygiene
::MacroKind
;
11 use rustc
::hir
::def
::{Res, DefKind, CtorKind}
;
12 use rustc
::hir
::def_id
::DefId
;
13 use rustc_metadata
::creader
::LoadedMacro
;
15 use rustc
::util
::nodemap
::FxHashSet
;
17 use crate::core
::DocContext
;
28 type Attrs
<'hir
> = rustc
::ty
::Attributes
<'hir
>;
30 /// Attempt to inline a definition into this AST.
32 /// This function will fetch the definition specified, and if it is
33 /// from another crate it will attempt to inline the documentation
34 /// from the 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 definition could not be inlined,
41 /// and `Some` of a vector of items if it was successfully expanded.
46 attrs
: Option
<Attrs
<'_
>>,
47 visited
: &mut FxHashSet
<DefId
>
48 ) -> Option
<Vec
<clean
::Item
>> {
49 let did
= if let Some(did
) = res
.opt_def_id() {
54 if did
.is_local() { return None }
55 let mut ret
= Vec
::new();
57 let attrs_clone
= attrs
.clone();
59 let inner
= match res
{
60 Res
::Def(DefKind
::Trait
, did
) => {
61 record_extern_fqn(cx
, did
, clean
::TypeKind
::Trait
);
62 ret
.extend(build_impls(cx
, did
, attrs
));
63 clean
::TraitItem(build_external_trait(cx
, did
))
65 Res
::Def(DefKind
::Fn
, did
) => {
66 record_extern_fqn(cx
, did
, clean
::TypeKind
::Function
);
67 clean
::FunctionItem(build_external_function(cx
, did
))
69 Res
::Def(DefKind
::Struct
, did
) => {
70 record_extern_fqn(cx
, did
, clean
::TypeKind
::Struct
);
71 ret
.extend(build_impls(cx
, did
, attrs
));
72 clean
::StructItem(build_struct(cx
, did
))
74 Res
::Def(DefKind
::Union
, did
) => {
75 record_extern_fqn(cx
, did
, clean
::TypeKind
::Union
);
76 ret
.extend(build_impls(cx
, did
, attrs
));
77 clean
::UnionItem(build_union(cx
, did
))
79 Res
::Def(DefKind
::TyAlias
, did
) => {
80 record_extern_fqn(cx
, did
, clean
::TypeKind
::Typedef
);
81 ret
.extend(build_impls(cx
, did
, attrs
));
82 clean
::TypedefItem(build_type_alias(cx
, did
), false)
84 Res
::Def(DefKind
::Enum
, did
) => {
85 record_extern_fqn(cx
, did
, clean
::TypeKind
::Enum
);
86 ret
.extend(build_impls(cx
, did
, attrs
));
87 clean
::EnumItem(build_enum(cx
, did
))
89 Res
::Def(DefKind
::ForeignTy
, did
) => {
90 record_extern_fqn(cx
, did
, clean
::TypeKind
::Foreign
);
91 ret
.extend(build_impls(cx
, did
, attrs
));
92 clean
::ForeignTypeItem
94 // Never inline enum variants but leave them shown as re-exports.
95 Res
::Def(DefKind
::Variant
, _
) => return None
,
96 // Assume that enum variants and struct types are re-exported next to
97 // their constructors.
98 Res
::Def(DefKind
::Ctor(..), _
) | Res
::SelfCtor(..) => return Some(Vec
::new()),
99 Res
::Def(DefKind
::Mod
, did
) => {
100 record_extern_fqn(cx
, did
, clean
::TypeKind
::Module
);
101 clean
::ModuleItem(build_module(cx
, did
, visited
))
103 Res
::Def(DefKind
::Static
, did
) => {
104 record_extern_fqn(cx
, did
, clean
::TypeKind
::Static
);
105 clean
::StaticItem(build_static(cx
, did
, cx
.tcx
.is_mutable_static(did
)))
107 Res
::Def(DefKind
::Const
, did
) => {
108 record_extern_fqn(cx
, did
, clean
::TypeKind
::Const
);
109 clean
::ConstantItem(build_const(cx
, did
))
111 Res
::Def(DefKind
::Macro(kind
), did
) => {
112 let mac
= build_macro(cx
, did
, name
);
114 let type_kind
= match kind
{
115 MacroKind
::Bang
=> TypeKind
::Macro
,
116 MacroKind
::Attr
=> TypeKind
::Attr
,
117 MacroKind
::Derive
=> TypeKind
::Derive
119 record_extern_fqn(cx
, did
, type_kind
);
125 let target_attrs
= load_attrs(cx
, did
);
126 let attrs
= merge_attrs(cx
, target_attrs
, attrs_clone
);
128 cx
.renderinfo
.borrow_mut().inlined
.insert(did
);
129 ret
.push(clean
::Item
{
130 source
: cx
.tcx
.def_span(did
).clean(cx
),
131 name
: Some(name
.clean(cx
)),
134 visibility
: clean
::Public
,
135 stability
: cx
.tcx
.lookup_stability(did
).clean(cx
),
136 deprecation
: cx
.tcx
.lookup_deprecation(did
).clean(cx
),
142 pub fn try_inline_glob(cx
: &DocContext
<'_
>, res
: Res
, visited
: &mut FxHashSet
<DefId
>)
143 -> Option
<Vec
<clean
::Item
>>
145 if res
== Res
::Err { return None }
146 let did
= res
.def_id();
147 if did
.is_local() { return None }
150 Res
::Def(DefKind
::Mod
, did
) => {
151 let m
= build_module(cx
, did
, visited
);
154 // glob imports on things like enums aren't inlined even for local exports, so just bail
159 pub fn load_attrs
<'hir
>(cx
: &DocContext
<'hir
>, did
: DefId
) -> Attrs
<'hir
> {
160 cx
.tcx
.get_attrs(did
)
163 /// Record an external fully qualified name in the external_paths cache.
165 /// These names are used later on by HTML rendering to generate things like
166 /// source links back to the original item.
167 pub fn record_extern_fqn(cx
: &DocContext
<'_
>, did
: DefId
, kind
: clean
::TypeKind
) {
168 let crate_name
= cx
.tcx
.crate_name(did
.krate
).to_string();
170 let relative
= cx
.tcx
.def_path(did
).data
.into_iter().filter_map(|elem
| {
171 // extern blocks have an empty name
172 let s
= elem
.data
.to_string();
179 let fqn
= if let clean
::TypeKind
::Macro
= kind
{
180 vec
![crate_name
, relative
.last().expect("relative was empty")]
182 once(crate_name
).chain(relative
).collect()
186 cx
.renderinfo
.borrow_mut().exact_paths
.insert(did
, fqn
);
188 cx
.renderinfo
.borrow_mut().external_paths
.insert(did
, (fqn
, kind
));
192 pub fn build_external_trait(cx
: &DocContext
<'_
>, did
: DefId
) -> clean
::Trait
{
193 let auto_trait
= cx
.tcx
.trait_def(did
).has_auto_impl
;
194 let trait_items
= cx
.tcx
.associated_items(did
).map(|item
| item
.clean(cx
)).collect();
195 let predicates
= cx
.tcx
.predicates_of(did
);
196 let generics
= (cx
.tcx
.generics_of(did
), predicates
).clean(cx
);
197 let generics
= filter_non_trait_generics(did
, generics
);
198 let (generics
, supertrait_bounds
) = separate_supertrait_bounds(generics
);
199 let is_spotlight
= load_attrs(cx
, did
).clean(cx
).has_doc_flag(sym
::spotlight
);
200 let is_auto
= cx
.tcx
.trait_is_auto(did
);
203 unsafety
: cx
.tcx
.trait_def(did
).unsafety
,
206 bounds
: supertrait_bounds
,
212 fn build_external_function(cx
: &DocContext
<'_
>, did
: DefId
) -> clean
::Function
{
213 let sig
= cx
.tcx
.fn_sig(did
);
215 let constness
= if cx
.tcx
.is_min_const_fn(did
) {
216 hir
::Constness
::Const
218 hir
::Constness
::NotConst
220 let asyncness
= cx
.tcx
.asyncness(did
);
221 let predicates
= cx
.tcx
.predicates_of(did
);
222 let (generics
, decl
) = clean
::enter_impl_trait(cx
, || {
223 ((cx
.tcx
.generics_of(did
), predicates
).clean(cx
), (did
, sig
).clean(cx
))
225 let (all_types
, ret_types
) = clean
::get_all_types(&generics
, &decl
, cx
);
229 header
: hir
::FnHeader
{
230 unsafety
: sig
.unsafety(),
240 fn build_enum(cx
: &DocContext
<'_
>, did
: DefId
) -> clean
::Enum
{
241 let predicates
= cx
.tcx
.explicit_predicates_of(did
);
244 generics
: (cx
.tcx
.generics_of(did
), predicates
).clean(cx
),
245 variants_stripped
: false,
246 variants
: cx
.tcx
.adt_def(did
).variants
.clean(cx
),
250 fn build_struct(cx
: &DocContext
<'_
>, did
: DefId
) -> clean
::Struct
{
251 let predicates
= cx
.tcx
.explicit_predicates_of(did
);
252 let variant
= cx
.tcx
.adt_def(did
).non_enum_variant();
255 struct_type
: match variant
.ctor_kind
{
256 CtorKind
::Fictive
=> doctree
::Plain
,
257 CtorKind
::Fn
=> doctree
::Tuple
,
258 CtorKind
::Const
=> doctree
::Unit
,
260 generics
: (cx
.tcx
.generics_of(did
), predicates
).clean(cx
),
261 fields
: variant
.fields
.clean(cx
),
262 fields_stripped
: false,
266 fn build_union(cx
: &DocContext
<'_
>, did
: DefId
) -> clean
::Union
{
267 let predicates
= cx
.tcx
.explicit_predicates_of(did
);
268 let variant
= cx
.tcx
.adt_def(did
).non_enum_variant();
271 struct_type
: doctree
::Plain
,
272 generics
: (cx
.tcx
.generics_of(did
), predicates
).clean(cx
),
273 fields
: variant
.fields
.clean(cx
),
274 fields_stripped
: false,
278 fn build_type_alias(cx
: &DocContext
<'_
>, did
: DefId
) -> clean
::Typedef
{
279 let predicates
= cx
.tcx
.explicit_predicates_of(did
);
282 type_
: cx
.tcx
.type_of(did
).clean(cx
),
283 generics
: (cx
.tcx
.generics_of(did
), predicates
).clean(cx
),
287 pub fn build_impls(cx
: &DocContext
<'_
>, did
: DefId
, attrs
: Option
<Attrs
<'_
>>) -> Vec
<clean
::Item
> {
289 let mut impls
= Vec
::new();
291 for &did
in tcx
.inherent_impls(did
).iter() {
292 build_impl(cx
, did
, attrs
.clone(), &mut impls
);
298 fn merge_attrs(cx
: &DocContext
<'_
>, attrs
: Attrs
<'_
>, other_attrs
: Option
<Attrs
<'_
>>
299 ) -> clean
::Attributes
{
300 let mut merged_attrs
: Vec
<ast
::Attribute
> = Vec
::with_capacity(attrs
.len());
301 // If we have additional attributes (from a re-export),
302 // always insert them first. This ensure that re-export
303 // doc comments show up before the original doc comments
304 // when we render them.
305 if let Some(a
) = other_attrs
{
306 merged_attrs
.extend(a
.iter().cloned());
308 merged_attrs
.extend(attrs
.to_vec());
309 merged_attrs
.clean(cx
)
312 pub fn build_impl(cx
: &DocContext
<'_
>, did
: DefId
, attrs
: Option
<Attrs
<'_
>>,
313 ret
: &mut Vec
<clean
::Item
>
315 if !cx
.renderinfo
.borrow_mut().inlined
.insert(did
) {
319 let attrs
= merge_attrs(cx
, load_attrs(cx
, did
), attrs
);
323 let associated_trait
= tcx
.impl_trait_ref(did
);
325 // Only inline impl if the implemented trait is
326 // reachable in rustdoc generated documentation
328 if let Some(traitref
) = associated_trait
{
329 if !cx
.renderinfo
.borrow().access_levels
.is_public(traitref
.def_id
) {
335 let for_
= if let Some(hir_id
) = tcx
.hir().as_local_hir_id(did
) {
336 match tcx
.hir().expect_item(hir_id
).kind
{
337 hir
::ItemKind
::Impl(.., ref t
, _
) => {
340 _
=> panic
!("did given to build_impl was not an impl"),
343 tcx
.type_of(did
).clean(cx
)
346 // Only inline impl if the implementing type is
347 // reachable in rustdoc generated documentation
349 if let Some(did
) = for_
.def_id() {
350 if !cx
.renderinfo
.borrow().access_levels
.is_public(did
) {
356 let predicates
= tcx
.explicit_predicates_of(did
);
357 let (trait_items
, generics
) = if let Some(hir_id
) = tcx
.hir().as_local_hir_id(did
) {
358 match tcx
.hir().expect_item(hir_id
).kind
{
359 hir
::ItemKind
::Impl(.., ref gen
, _
, _
, ref item_ids
) => {
362 .map(|ii
| tcx
.hir().impl_item(ii
.id
).clean(cx
))
363 .collect
::<Vec
<_
>>(),
367 _
=> panic
!("did given to build_impl was not an impl"),
371 tcx
.associated_items(did
).filter_map(|item
| {
372 if associated_trait
.is_some() || item
.vis
== ty
::Visibility
::Public
{
377 }).collect
::<Vec
<_
>>(),
378 clean
::enter_impl_trait(cx
, || {
379 (tcx
.generics_of(did
), predicates
).clean(cx
)
383 let polarity
= tcx
.impl_polarity(did
);
384 let trait_
= associated_trait
.clean(cx
).map(|bound
| {
386 clean
::GenericBound
::TraitBound(polyt
, _
) => polyt
.trait_
,
387 clean
::GenericBound
::Outlives(..) => unreachable
!(),
390 if trait_
.def_id() == tcx
.lang_items().deref_trait() {
391 super::build_deref_target_impls(cx
, &trait_items
, ret
);
393 if let Some(trait_did
) = trait_
.def_id() {
394 record_extern_trait(cx
, trait_did
);
397 let provided
= trait_
.def_id().map(|did
| {
398 tcx
.provided_trait_methods(did
)
400 .map(|meth
| meth
.ident
.to_string())
402 }).unwrap_or_default();
404 debug
!("build_impl: impl {:?} for {:?}", trait_
.def_id(), for_
.def_id());
406 ret
.push(clean
::Item
{
407 inner
: clean
::ImplItem(clean
::Impl
{
408 unsafety
: hir
::Unsafety
::Normal
,
410 provided_trait_methods
: provided
,
414 polarity
: Some(polarity
.clean(cx
)),
418 source
: tcx
.def_span(did
).clean(cx
),
421 visibility
: clean
::Inherited
,
422 stability
: tcx
.lookup_stability(did
).clean(cx
),
423 deprecation
: tcx
.lookup_deprecation(did
).clean(cx
),
431 visited
: &mut FxHashSet
<DefId
>
433 let mut items
= Vec
::new();
434 fill_in(cx
, did
, &mut items
, visited
);
435 return clean
::Module
{
440 fn fill_in(cx
: &DocContext
<'_
>, did
: DefId
, items
: &mut Vec
<clean
::Item
>,
441 visited
: &mut FxHashSet
<DefId
>) {
442 // If we're re-exporting a re-export it may actually re-export something in
443 // two namespaces, so the target may be listed twice. Make sure we only
444 // visit each node at most once.
445 for &item
in cx
.tcx
.item_children(did
).iter() {
446 let def_id
= item
.res
.def_id();
447 if item
.vis
== ty
::Visibility
::Public
{
448 if did
== def_id
|| !visited
.insert(def_id
) { continue }
449 if let Some(i
) = try_inline(cx
, item
.res
, item
.ident
.name
, None
, visited
) {
457 pub fn print_inlined_const(cx
: &DocContext
<'_
>, did
: DefId
) -> String
{
458 if let Some(node_id
) = cx
.tcx
.hir().as_local_hir_id(did
) {
459 cx
.tcx
.hir().hir_to_pretty_string(node_id
)
461 cx
.tcx
.rendered_const(did
)
465 fn build_const(cx
: &DocContext
<'_
>, did
: DefId
) -> clean
::Constant
{
467 type_
: cx
.tcx
.type_of(did
).clean(cx
),
468 expr
: print_inlined_const(cx
, did
)
472 fn build_static(cx
: &DocContext
<'_
>, did
: DefId
, mutable
: bool
) -> clean
::Static
{
474 type_
: cx
.tcx
.type_of(did
).clean(cx
),
475 mutability
: if mutable {clean::Mutable}
else {clean::Immutable}
,
476 expr
: "\n\n\n".to_string(), // trigger the "[definition]" links
480 fn build_macro(cx
: &DocContext
<'_
>, did
: DefId
, name
: ast
::Name
) -> clean
::ItemEnum
{
481 let imported_from
= cx
.tcx
.original_crate_name(did
.krate
);
482 match cx
.enter_resolver(|r
| r
.cstore().load_macro_untracked(did
, cx
.sess())) {
483 LoadedMacro
::MacroDef(def
, _
) => {
484 let matchers
: hir
::HirVec
<Span
> = if let ast
::ItemKind
::MacroDef(ref def
) = def
.kind
{
485 let tts
: Vec
<_
> = def
.body
.inner_tokens().into_trees().collect();
486 tts
.chunks(4).map(|arm
| arm
[0].span()).collect()
491 let source
= format
!("macro_rules! {} {{\n{}}}",
493 matchers
.iter().map(|span
| {
494 format
!(" {} => {{ ... }};\n", span
.to_src(cx
))
495 }).collect
::<String
>());
497 clean
::MacroItem(clean
::Macro
{
499 imported_from
: Some(imported_from
).clean(cx
),
502 LoadedMacro
::ProcMacro(ext
) => {
503 clean
::ProcMacroItem(clean
::ProcMacro
{
504 kind
: ext
.macro_kind(),
505 helpers
: ext
.helper_attrs
.clean(cx
),
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 generating the
514 /// generics for the trait itself we need to be sure to remove them.
515 /// We also need to remove the implied "recursive" Self: Trait bound.
517 /// The inverse of this filtering logic can be found in the `Clean`
518 /// implementation for `AssociatedType`
519 fn filter_non_trait_generics(trait_did
: DefId
, mut g
: clean
::Generics
) -> clean
::Generics
{
520 for pred
in &mut g
.where_predicates
{
522 clean
::WherePredicate
::BoundPredicate
{
523 ty
: clean
::Generic(ref s
),
525 } if *s
== "Self" => {
526 bounds
.retain(|bound
| {
528 clean
::GenericBound
::TraitBound(clean
::PolyTrait
{
529 trait_
: clean
::ResolvedPath { did, .. }
,
531 }, _
) => did
!= trait_did
,
540 g
.where_predicates
.retain(|pred
| {
542 clean
::WherePredicate
::BoundPredicate
{
544 self_type
: box clean
::Generic(ref s
),
545 trait_
: box clean
::ResolvedPath { did, .. }
,
548 } => !(*s
== "Self" && did
== trait_did
) && !bounds
.is_empty(),
555 /// Supertrait bounds for a trait are also listed in the generics coming from
556 /// the metadata for a crate, so we want to separate those out and create a new
557 /// list of explicit supertrait bounds to render nicely.
558 fn separate_supertrait_bounds(mut g
: clean
::Generics
)
559 -> (clean
::Generics
, Vec
<clean
::GenericBound
>) {
560 let mut ty_bounds
= Vec
::new();
561 g
.where_predicates
.retain(|pred
| {
563 clean
::WherePredicate
::BoundPredicate
{
564 ty
: clean
::Generic(ref s
),
566 } if *s
== "Self" => {
567 ty_bounds
.extend(bounds
.iter().cloned());
576 pub fn record_extern_trait(cx
: &DocContext
<'_
>, did
: DefId
) {
582 if cx
.external_traits
.borrow().contains_key(&did
) ||
583 cx
.active_extern_traits
.borrow().contains(&did
)
589 cx
.active_extern_traits
.borrow_mut().insert(did
);
591 debug
!("record_extern_trait: {:?}", did
);
592 let trait_
= build_external_trait(cx
, did
);
594 cx
.external_traits
.borrow_mut().insert(did
, trait_
);
595 cx
.active_extern_traits
.borrow_mut().remove(&did
);