]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/clean/inline.rs
Rename VariantKind -> Variant
[rust.git] / src / librustdoc / clean / inline.rs
1 //! Support for inlining external documentation into the current AST.
2
3 use std::iter::once;
4
5 use rustc_ast as ast;
6 use rustc_data_structures::fx::FxHashSet;
7 use rustc_hir as hir;
8 use rustc_hir::def::{CtorKind, DefKind, Res};
9 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
10 use rustc_hir::Mutability;
11 use rustc_metadata::creader::LoadedMacro;
12 use rustc_middle::ty;
13 use rustc_mir::const_eval::is_min_const_fn;
14 use rustc_span::hygiene::MacroKind;
15 use rustc_span::symbol::{kw, sym, Symbol};
16 use rustc_span::Span;
17
18 use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind};
19 use crate::core::DocContext;
20 use crate::doctree;
21
22 use super::Clean;
23
24 type Attrs<'hir> = rustc_middle::ty::Attributes<'hir>;
25
26 /// Attempt to inline a definition into this AST.
27 ///
28 /// This function will fetch the definition specified, and if it is
29 /// from another crate it will attempt to inline the documentation
30 /// from the other crate into this crate.
31 ///
32 /// This is primarily used for `pub use` statements which are, in general,
33 /// implementation details. Inlining the documentation should help provide a
34 /// better experience when reading the documentation in this use case.
35 ///
36 /// The returned value is `None` if the definition could not be inlined,
37 /// and `Some` of a vector of items if it was successfully expanded.
38 ///
39 /// `parent_module` refers to the parent of the *re-export*, not the original item.
40 crate fn try_inline(
41     cx: &DocContext<'_>,
42     parent_module: DefId,
43     res: Res,
44     name: Symbol,
45     attrs: Option<Attrs<'_>>,
46     visited: &mut FxHashSet<DefId>,
47 ) -> Option<Vec<clean::Item>> {
48     let did = res.opt_def_id()?;
49     if did.is_local() {
50         return None;
51     }
52     let mut ret = Vec::new();
53
54     debug!("attrs={:?}", attrs);
55     let attrs_clone = attrs;
56
57     let kind = match res {
58         Res::Def(DefKind::Trait, did) => {
59             record_extern_fqn(cx, did, clean::TypeKind::Trait);
60             ret.extend(build_impls(cx, Some(parent_module), did, attrs));
61             clean::TraitItem(build_external_trait(cx, did))
62         }
63         Res::Def(DefKind::Fn, did) => {
64             record_extern_fqn(cx, did, clean::TypeKind::Function);
65             clean::FunctionItem(build_external_function(cx, did))
66         }
67         Res::Def(DefKind::Struct, did) => {
68             record_extern_fqn(cx, did, clean::TypeKind::Struct);
69             ret.extend(build_impls(cx, Some(parent_module), did, attrs));
70             clean::StructItem(build_struct(cx, did))
71         }
72         Res::Def(DefKind::Union, did) => {
73             record_extern_fqn(cx, did, clean::TypeKind::Union);
74             ret.extend(build_impls(cx, Some(parent_module), did, attrs));
75             clean::UnionItem(build_union(cx, did))
76         }
77         Res::Def(DefKind::TyAlias, did) => {
78             record_extern_fqn(cx, did, clean::TypeKind::Typedef);
79             ret.extend(build_impls(cx, Some(parent_module), did, attrs));
80             clean::TypedefItem(build_type_alias(cx, did), false)
81         }
82         Res::Def(DefKind::Enum, did) => {
83             record_extern_fqn(cx, did, clean::TypeKind::Enum);
84             ret.extend(build_impls(cx, Some(parent_module), did, attrs));
85             clean::EnumItem(build_enum(cx, did))
86         }
87         Res::Def(DefKind::ForeignTy, did) => {
88             record_extern_fqn(cx, did, clean::TypeKind::Foreign);
89             ret.extend(build_impls(cx, Some(parent_module), did, attrs));
90             clean::ForeignTypeItem
91         }
92         // Never inline enum variants but leave them shown as re-exports.
93         Res::Def(DefKind::Variant, _) => return None,
94         // Assume that enum variants and struct types are re-exported next to
95         // their constructors.
96         Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => return Some(Vec::new()),
97         Res::Def(DefKind::Mod, did) => {
98             record_extern_fqn(cx, did, clean::TypeKind::Module);
99             clean::ModuleItem(build_module(cx, did, visited))
100         }
101         Res::Def(DefKind::Static, did) => {
102             record_extern_fqn(cx, did, clean::TypeKind::Static);
103             clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did)))
104         }
105         Res::Def(DefKind::Const, did) => {
106             record_extern_fqn(cx, did, clean::TypeKind::Const);
107             clean::ConstantItem(build_const(cx, did))
108         }
109         Res::Def(DefKind::Macro(kind), did) => {
110             let mac = build_macro(cx, did, name);
111
112             let type_kind = match kind {
113                 MacroKind::Bang => TypeKind::Macro,
114                 MacroKind::Attr => TypeKind::Attr,
115                 MacroKind::Derive => TypeKind::Derive,
116             };
117             record_extern_fqn(cx, did, type_kind);
118             mac
119         }
120         _ => return None,
121     };
122
123     let target_attrs = load_attrs(cx, did);
124     let attrs = box merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone);
125
126     cx.renderinfo.borrow_mut().inlined.insert(did);
127     let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name), kind, cx);
128     ret.push(clean::Item { attrs, ..what_rustc_thinks });
129     Some(ret)
130 }
131
132 crate fn try_inline_glob(
133     cx: &DocContext<'_>,
134     res: Res,
135     visited: &mut FxHashSet<DefId>,
136 ) -> Option<Vec<clean::Item>> {
137     if res == Res::Err {
138         return None;
139     }
140     let did = res.def_id();
141     if did.is_local() {
142         return None;
143     }
144
145     match res {
146         Res::Def(DefKind::Mod, did) => {
147             let m = build_module(cx, did, visited);
148             Some(m.items)
149         }
150         // glob imports on things like enums aren't inlined even for local exports, so just bail
151         _ => None,
152     }
153 }
154
155 crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
156     cx.tcx.get_attrs(did)
157 }
158
159 /// Record an external fully qualified name in the external_paths cache.
160 ///
161 /// These names are used later on by HTML rendering to generate things like
162 /// source links back to the original item.
163 crate fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKind) {
164     let crate_name = cx.tcx.crate_name(did.krate).to_string();
165
166     let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
167         // extern blocks have an empty name
168         let s = elem.data.to_string();
169         if !s.is_empty() { Some(s) } else { None }
170     });
171     let fqn = if let clean::TypeKind::Macro = kind {
172         // Check to see if it is a macro 2.0 or built-in macro
173         if matches!(
174             cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())),
175             LoadedMacro::MacroDef(def, _)
176                 if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def)
177                     if !ast_def.macro_rules)
178         ) {
179             once(crate_name).chain(relative).collect()
180         } else {
181             vec![crate_name, relative.last().expect("relative was empty")]
182         }
183     } else {
184         once(crate_name).chain(relative).collect()
185     };
186
187     if did.is_local() {
188         cx.renderinfo.borrow_mut().exact_paths.insert(did, fqn);
189     } else {
190         cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
191     }
192 }
193
194 crate fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait {
195     let trait_items =
196         cx.tcx.associated_items(did).in_definition_order().map(|item| item.clean(cx)).collect();
197
198     let predicates = cx.tcx.predicates_of(did);
199     let generics = (cx.tcx.generics_of(did), predicates).clean(cx);
200     let generics = filter_non_trait_generics(did, generics);
201     let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
202     let is_spotlight = load_attrs(cx, did).clean(cx).has_doc_flag(sym::spotlight);
203     let is_auto = cx.tcx.trait_is_auto(did);
204     clean::Trait {
205         unsafety: cx.tcx.trait_def(did).unsafety,
206         generics,
207         items: trait_items,
208         bounds: supertrait_bounds,
209         is_spotlight,
210         is_auto,
211     }
212 }
213
214 fn build_external_function(cx: &DocContext<'_>, did: DefId) -> clean::Function {
215     let sig = cx.tcx.fn_sig(did);
216
217     let constness =
218         if is_min_const_fn(cx.tcx, did) { hir::Constness::Const } else { hir::Constness::NotConst };
219     let asyncness = cx.tcx.asyncness(did);
220     let predicates = cx.tcx.predicates_of(did);
221     let (generics, decl) = clean::enter_impl_trait(cx, || {
222         ((cx.tcx.generics_of(did), predicates).clean(cx), (did, sig).clean(cx))
223     });
224     let (all_types, ret_types) = clean::get_all_types(&generics, &decl, cx);
225     clean::Function {
226         decl,
227         generics,
228         header: hir::FnHeader { unsafety: sig.unsafety(), abi: sig.abi(), constness, asyncness },
229         all_types,
230         ret_types,
231     }
232 }
233
234 fn build_enum(cx: &DocContext<'_>, did: DefId) -> clean::Enum {
235     let predicates = cx.tcx.explicit_predicates_of(did);
236
237     clean::Enum {
238         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
239         variants_stripped: false,
240         variants: cx.tcx.adt_def(did).variants.clean(cx),
241     }
242 }
243
244 fn build_struct(cx: &DocContext<'_>, did: DefId) -> clean::Struct {
245     let predicates = cx.tcx.explicit_predicates_of(did);
246     let variant = cx.tcx.adt_def(did).non_enum_variant();
247
248     clean::Struct {
249         struct_type: match variant.ctor_kind {
250             CtorKind::Fictive => doctree::Plain,
251             CtorKind::Fn => doctree::Tuple,
252             CtorKind::Const => doctree::Unit,
253         },
254         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
255         fields: variant.fields.clean(cx),
256         fields_stripped: false,
257     }
258 }
259
260 fn build_union(cx: &DocContext<'_>, did: DefId) -> clean::Union {
261     let predicates = cx.tcx.explicit_predicates_of(did);
262     let variant = cx.tcx.adt_def(did).non_enum_variant();
263
264     clean::Union {
265         struct_type: doctree::Plain,
266         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
267         fields: variant.fields.clean(cx),
268         fields_stripped: false,
269     }
270 }
271
272 fn build_type_alias(cx: &DocContext<'_>, did: DefId) -> clean::Typedef {
273     let predicates = cx.tcx.explicit_predicates_of(did);
274     let type_ = cx.tcx.type_of(did).clean(cx);
275
276     clean::Typedef {
277         type_,
278         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
279         item_type: None,
280     }
281 }
282
283 /// Builds all inherent implementations of an ADT (struct/union/enum) or Trait item/path/reexport.
284 crate fn build_impls(
285     cx: &DocContext<'_>,
286     parent_module: Option<DefId>,
287     did: DefId,
288     attrs: Option<Attrs<'_>>,
289 ) -> Vec<clean::Item> {
290     let tcx = cx.tcx;
291     let mut impls = Vec::new();
292
293     // for each implementation of an item represented by `did`, build the clean::Item for that impl
294     for &did in tcx.inherent_impls(did).iter() {
295         build_impl(cx, parent_module, did, attrs, &mut impls);
296     }
297
298     impls
299 }
300
301 /// `parent_module` refers to the parent of the re-export, not the original item
302 fn merge_attrs(
303     cx: &DocContext<'_>,
304     parent_module: Option<DefId>,
305     old_attrs: Attrs<'_>,
306     new_attrs: Option<Attrs<'_>>,
307 ) -> clean::Attributes {
308     // NOTE: If we have additional attributes (from a re-export),
309     // always insert them first. This ensure that re-export
310     // doc comments show up before the original doc comments
311     // when we render them.
312     if let Some(inner) = new_attrs {
313         if let Some(new_id) = parent_module {
314             let diag = cx.sess().diagnostic();
315             Attributes::from_ast(diag, old_attrs, Some((inner, new_id)))
316         } else {
317             let mut both = inner.to_vec();
318             both.extend_from_slice(old_attrs);
319             both.clean(cx)
320         }
321     } else {
322         old_attrs.clean(cx)
323     }
324 }
325
326 /// Builds a specific implementation of a type. The `did` could be a type method or trait method.
327 crate fn build_impl(
328     cx: &DocContext<'_>,
329     parent_module: impl Into<Option<DefId>>,
330     did: DefId,
331     attrs: Option<Attrs<'_>>,
332     ret: &mut Vec<clean::Item>,
333 ) {
334     if !cx.renderinfo.borrow_mut().inlined.insert(did) {
335         return;
336     }
337
338     let tcx = cx.tcx;
339     let associated_trait = tcx.impl_trait_ref(did);
340
341     // Only inline impl if the implemented trait is
342     // reachable in rustdoc generated documentation
343     if !did.is_local() {
344         if let Some(traitref) = associated_trait {
345             let did = traitref.def_id;
346             if !cx.renderinfo.borrow().access_levels.is_public(did) {
347                 return;
348             }
349
350             if let Some(stab) = tcx.lookup_stability(did) {
351                 if stab.level.is_unstable() && stab.feature == sym::rustc_private {
352                     return;
353                 }
354             }
355         }
356     }
357
358     let impl_item = match did.as_local() {
359         Some(did) => {
360             let hir_id = tcx.hir().local_def_id_to_hir_id(did);
361             match &tcx.hir().expect_item(hir_id).kind {
362                 hir::ItemKind::Impl(impl_) => Some(impl_),
363                 _ => panic!("`DefID` passed to `build_impl` is not an `impl"),
364             }
365         }
366         None => None,
367     };
368
369     let for_ = match &impl_item {
370         Some(impl_) => impl_.self_ty.clean(cx),
371         None => tcx.type_of(did).clean(cx),
372     };
373
374     // Only inline impl if the implementing type is
375     // reachable in rustdoc generated documentation
376     if !did.is_local() {
377         if let Some(did) = for_.def_id() {
378             if !cx.renderinfo.borrow().access_levels.is_public(did) {
379                 return;
380             }
381
382             if let Some(stab) = tcx.lookup_stability(did) {
383                 if stab.level.is_unstable() && stab.feature == sym::rustc_private {
384                     return;
385                 }
386             }
387         }
388     }
389
390     let predicates = tcx.explicit_predicates_of(did);
391     let (trait_items, generics) = match impl_item {
392         Some(impl_) => (
393             impl_
394                 .items
395                 .iter()
396                 .map(|item| tcx.hir().impl_item(item.id).clean(cx))
397                 .collect::<Vec<_>>(),
398             impl_.generics.clean(cx),
399         ),
400         None => (
401             tcx.associated_items(did)
402                 .in_definition_order()
403                 .filter_map(|item| {
404                     if associated_trait.is_some() || item.vis == ty::Visibility::Public {
405                         Some(item.clean(cx))
406                     } else {
407                         None
408                     }
409                 })
410                 .collect::<Vec<_>>(),
411             clean::enter_impl_trait(cx, || (tcx.generics_of(did), predicates).clean(cx)),
412         ),
413     };
414     let polarity = tcx.impl_polarity(did);
415     let trait_ = associated_trait.clean(cx).map(|bound| match bound {
416         clean::GenericBound::TraitBound(polyt, _) => polyt.trait_,
417         clean::GenericBound::Outlives(..) => unreachable!(),
418     });
419     if trait_.def_id() == tcx.lang_items().deref_trait() {
420         super::build_deref_target_impls(cx, &trait_items, ret);
421     }
422     if let Some(trait_did) = trait_.def_id() {
423         record_extern_trait(cx, trait_did);
424     }
425
426     let provided = trait_
427         .def_id()
428         .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.ident.name).collect())
429         .unwrap_or_default();
430
431     debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
432
433     let mut item = clean::Item::from_def_id_and_parts(
434         did,
435         None,
436         clean::ImplItem(clean::Impl {
437             unsafety: hir::Unsafety::Normal,
438             generics,
439             provided_trait_methods: provided,
440             trait_,
441             for_,
442             items: trait_items,
443             negative_polarity: polarity.clean(cx),
444             synthetic: false,
445             blanket_impl: None,
446         }),
447         cx,
448     );
449     item.attrs = box merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
450     debug!("merged_attrs={:?}", item.attrs);
451     ret.push(item);
452 }
453
454 fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet<DefId>) -> clean::Module {
455     let mut items = Vec::new();
456
457     // If we're re-exporting a re-export it may actually re-export something in
458     // two namespaces, so the target may be listed twice. Make sure we only
459     // visit each node at most once.
460     for &item in cx.tcx.item_children(did).iter() {
461         if item.vis == ty::Visibility::Public {
462             if let Some(def_id) = item.res.mod_def_id() {
463                 if did == def_id || !visited.insert(def_id) {
464                     continue;
465                 }
466             }
467             if let Res::PrimTy(p) = item.res {
468                 // Primitive types can't be inlined so generate an import instead.
469                 items.push(clean::Item {
470                     name: None,
471                     attrs: box clean::Attributes::default(),
472                     source: clean::Span::dummy(),
473                     def_id: DefId::local(CRATE_DEF_INDEX),
474                     visibility: clean::Public,
475                     kind: box clean::ImportItem(clean::Import::new_simple(
476                         item.ident.name,
477                         clean::ImportSource {
478                             path: clean::Path {
479                                 global: false,
480                                 res: item.res,
481                                 segments: vec![clean::PathSegment {
482                                     name: clean::PrimitiveType::from(p).as_sym(),
483                                     args: clean::GenericArgs::AngleBracketed {
484                                         args: Vec::new(),
485                                         bindings: Vec::new(),
486                                     },
487                                 }],
488                             },
489                             did: None,
490                         },
491                         true,
492                     )),
493                 });
494             } else if let Some(i) = try_inline(cx, did, item.res, item.ident.name, None, visited) {
495                 items.extend(i)
496             }
497         }
498     }
499
500     clean::Module { items, is_crate: false }
501 }
502
503 crate fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String {
504     if let Some(did) = did.as_local() {
505         let hir_id = cx.tcx.hir().local_def_id_to_hir_id(did);
506         rustc_hir_pretty::id_to_string(&cx.tcx.hir(), hir_id)
507     } else {
508         cx.tcx.rendered_const(did)
509     }
510 }
511
512 fn build_const(cx: &DocContext<'_>, did: DefId) -> clean::Constant {
513     clean::Constant {
514         type_: cx.tcx.type_of(did).clean(cx),
515         expr: print_inlined_const(cx, did),
516         value: clean::utils::print_evaluated_const(cx, did),
517         is_literal: did.as_local().map_or(false, |did| {
518             clean::utils::is_literal_expr(cx, cx.tcx.hir().local_def_id_to_hir_id(did))
519         }),
520     }
521 }
522
523 fn build_static(cx: &DocContext<'_>, did: DefId, mutable: bool) -> clean::Static {
524     clean::Static {
525         type_: cx.tcx.type_of(did).clean(cx),
526         mutability: if mutable { Mutability::Mut } else { Mutability::Not },
527         expr: "\n\n\n".to_string(), // trigger the "[definition]" links
528     }
529 }
530
531 fn build_macro(cx: &DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemKind {
532     let imported_from = cx.tcx.original_crate_name(did.krate);
533     match cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())) {
534         LoadedMacro::MacroDef(def, _) => {
535             let matchers: Vec<Span> = if let ast::ItemKind::MacroDef(ref def) = def.kind {
536                 let tts: Vec<_> = def.body.inner_tokens().into_trees().collect();
537                 tts.chunks(4).map(|arm| arm[0].span()).collect()
538             } else {
539                 unreachable!()
540             };
541
542             let source = format!(
543                 "macro_rules! {} {{\n{}}}",
544                 name.clean(cx),
545                 matchers
546                     .iter()
547                     .map(|span| { format!("    {} => {{ ... }};\n", span.to_src(cx)) })
548                     .collect::<String>()
549             );
550
551             clean::MacroItem(clean::Macro { source, imported_from: Some(imported_from) })
552         }
553         LoadedMacro::ProcMacro(ext) => clean::ProcMacroItem(clean::ProcMacro {
554             kind: ext.macro_kind(),
555             helpers: ext.helper_attrs,
556         }),
557     }
558 }
559
560 /// A trait's generics clause actually contains all of the predicates for all of
561 /// its associated types as well. We specifically move these clauses to the
562 /// associated types instead when displaying, so when we're generating the
563 /// generics for the trait itself we need to be sure to remove them.
564 /// We also need to remove the implied "recursive" Self: Trait bound.
565 ///
566 /// The inverse of this filtering logic can be found in the `Clean`
567 /// implementation for `AssociatedType`
568 fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics {
569     for pred in &mut g.where_predicates {
570         match *pred {
571             clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref mut bounds }
572                 if *s == kw::SelfUpper =>
573             {
574                 bounds.retain(|bound| match *bound {
575                     clean::GenericBound::TraitBound(
576                         clean::PolyTrait { trait_: clean::ResolvedPath { did, .. }, .. },
577                         _,
578                     ) => did != trait_did,
579                     _ => true,
580                 });
581             }
582             _ => {}
583         }
584     }
585
586     g.where_predicates.retain(|pred| match *pred {
587         clean::WherePredicate::BoundPredicate {
588             ty:
589                 clean::QPath {
590                     self_type: box clean::Generic(ref s),
591                     trait_: box clean::ResolvedPath { did, .. },
592                     name: ref _name,
593                 },
594             ref bounds,
595         } => !(bounds.is_empty() || *s == kw::SelfUpper && did == trait_did),
596         _ => true,
597     });
598     g
599 }
600
601 /// Supertrait bounds for a trait are also listed in the generics coming from
602 /// the metadata for a crate, so we want to separate those out and create a new
603 /// list of explicit supertrait bounds to render nicely.
604 fn separate_supertrait_bounds(
605     mut g: clean::Generics,
606 ) -> (clean::Generics, Vec<clean::GenericBound>) {
607     let mut ty_bounds = Vec::new();
608     g.where_predicates.retain(|pred| match *pred {
609         clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds }
610             if *s == kw::SelfUpper =>
611         {
612             ty_bounds.extend(bounds.iter().cloned());
613             false
614         }
615         _ => true,
616     });
617     (g, ty_bounds)
618 }
619
620 crate fn record_extern_trait(cx: &DocContext<'_>, did: DefId) {
621     if did.is_local() {
622         return;
623     }
624
625     {
626         if cx.external_traits.borrow().contains_key(&did)
627             || cx.active_extern_traits.borrow().contains(&did)
628         {
629             return;
630         }
631     }
632
633     {
634         cx.active_extern_traits.borrow_mut().insert(did);
635     }
636
637     debug!("record_extern_trait: {:?}", did);
638     let trait_ = build_external_trait(cx, did);
639
640     cx.external_traits.borrow_mut().insert(did, trait_);
641     cx.active_extern_traits.borrow_mut().remove(&did);
642 }