rustdoc/clean/
inline.rs

1//! Support for inlining external documentation into the current AST.
2
3use std::iter::once;
4use std::sync::Arc;
5
6use rustc_data_structures::fx::FxHashSet;
7use rustc_hir as hir;
8use rustc_hir::Mutability;
9use rustc_hir::def::{DefKind, Res};
10use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId, LocalModDefId};
11use rustc_metadata::creader::{CStore, LoadedMacro};
12use rustc_middle::ty::fast_reject::SimplifiedType;
13use rustc_middle::ty::{self, TyCtxt};
14use rustc_span::def_id::LOCAL_CRATE;
15use rustc_span::hygiene::MacroKind;
16use rustc_span::symbol::{Symbol, sym};
17use thin_vec::{ThinVec, thin_vec};
18use tracing::{debug, trace};
19
20use super::{Item, extract_cfg_from_attrs};
21use crate::clean::{
22    self, Attributes, ImplKind, ItemId, Type, clean_bound_vars, clean_generics, clean_impl_item,
23    clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_poly_fn_sig,
24    clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, clean_ty_generics,
25    clean_variant_def, utils,
26};
27use crate::core::DocContext;
28use crate::formats::item_type::ItemType;
29
30/// Attempt to inline a definition into this AST.
31///
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.
35///
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.
39///
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.
42pub(crate) fn try_inline(
43    cx: &mut DocContext<'_>,
44    res: Res,
45    name: Symbol,
46    attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
47    visited: &mut DefIdSet,
48) -> Option<Vec<clean::Item>> {
49    let did = res.opt_def_id()?;
50    if did.is_local() {
51        return None;
52    }
53    let mut ret = Vec::new();
54
55    debug!("attrs={attrs:?}");
56
57    let attrs_without_docs = attrs.map(|(attrs, def_id)| {
58        (attrs.iter().filter(|a| a.doc_str().is_none()).cloned().collect::<Vec<_>>(), def_id)
59    });
60    let attrs_without_docs =
61        attrs_without_docs.as_ref().map(|(attrs, def_id)| (&attrs[..], *def_id));
62
63    let import_def_id = attrs.and_then(|(_, def_id)| def_id);
64
65    let kind = match res {
66        Res::Def(DefKind::Trait, did) => {
67            record_extern_fqn(cx, did, ItemType::Trait);
68            cx.with_param_env(did, |cx| {
69                build_impls(cx, did, attrs_without_docs, &mut ret);
70                clean::TraitItem(Box::new(build_external_trait(cx, did)))
71            })
72        }
73        Res::Def(DefKind::Fn, did) => {
74            record_extern_fqn(cx, did, ItemType::Function);
75            cx.with_param_env(did, |cx| {
76                clean::enter_impl_trait(cx, |cx| clean::FunctionItem(build_function(cx, did)))
77            })
78        }
79        Res::Def(DefKind::Struct, did) => {
80            record_extern_fqn(cx, did, ItemType::Struct);
81            cx.with_param_env(did, |cx| {
82                build_impls(cx, did, attrs_without_docs, &mut ret);
83                clean::StructItem(build_struct(cx, did))
84            })
85        }
86        Res::Def(DefKind::Union, did) => {
87            record_extern_fqn(cx, did, ItemType::Union);
88            cx.with_param_env(did, |cx| {
89                build_impls(cx, did, attrs_without_docs, &mut ret);
90                clean::UnionItem(build_union(cx, did))
91            })
92        }
93        Res::Def(DefKind::TyAlias, did) => {
94            record_extern_fqn(cx, did, ItemType::TypeAlias);
95            cx.with_param_env(did, |cx| {
96                build_impls(cx, did, attrs_without_docs, &mut ret);
97                clean::TypeAliasItem(build_type_alias(cx, did, &mut ret))
98            })
99        }
100        Res::Def(DefKind::Enum, did) => {
101            record_extern_fqn(cx, did, ItemType::Enum);
102            cx.with_param_env(did, |cx| {
103                build_impls(cx, did, attrs_without_docs, &mut ret);
104                clean::EnumItem(build_enum(cx, did))
105            })
106        }
107        Res::Def(DefKind::ForeignTy, did) => {
108            record_extern_fqn(cx, did, ItemType::ForeignType);
109            cx.with_param_env(did, |cx| {
110                build_impls(cx, did, attrs_without_docs, &mut ret);
111                clean::ForeignTypeItem
112            })
113        }
114        // Never inline enum variants but leave them shown as re-exports.
115        Res::Def(DefKind::Variant, _) => return None,
116        // Assume that enum variants and struct types are re-exported next to
117        // their constructors.
118        Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => return Some(Vec::new()),
119        Res::Def(DefKind::Mod, did) => {
120            record_extern_fqn(cx, did, ItemType::Module);
121            clean::ModuleItem(build_module(cx, did, visited))
122        }
123        Res::Def(DefKind::Static { .. }, did) => {
124            record_extern_fqn(cx, did, ItemType::Static);
125            cx.with_param_env(did, |cx| {
126                clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did)))
127            })
128        }
129        Res::Def(DefKind::Const, did) => {
130            record_extern_fqn(cx, did, ItemType::Constant);
131            cx.with_param_env(did, |cx| {
132                let ct = build_const_item(cx, did);
133                clean::ConstantItem(Box::new(ct))
134            })
135        }
136        Res::Def(DefKind::Macro(kind), did) => {
137            let mac = build_macro(cx, did, name, kind);
138
139            let type_kind = match kind {
140                MacroKind::Bang => ItemType::Macro,
141                MacroKind::Attr => ItemType::ProcAttribute,
142                MacroKind::Derive => ItemType::ProcDerive,
143            };
144            record_extern_fqn(cx, did, type_kind);
145            mac
146        }
147        _ => return None,
148    };
149
150    cx.inlined.insert(did.into());
151    let mut item =
152        crate::clean::generate_item_with_correct_attrs(cx, kind, did, name, import_def_id, None);
153    // The visibility needs to reflect the one from the reexport and not from the "source" DefId.
154    item.inline_stmt_id = import_def_id;
155    ret.push(item);
156    Some(ret)
157}
158
159pub(crate) fn try_inline_glob(
160    cx: &mut DocContext<'_>,
161    res: Res,
162    current_mod: LocalModDefId,
163    visited: &mut DefIdSet,
164    inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
165    import: &hir::Item<'_>,
166) -> Option<Vec<clean::Item>> {
167    let did = res.opt_def_id()?;
168    if did.is_local() {
169        return None;
170    }
171
172    match res {
173        Res::Def(DefKind::Mod, did) => {
174            // Use the set of module reexports to filter away names that are not actually
175            // reexported by the glob, e.g. because they are shadowed by something else.
176            let reexports = cx
177                .tcx
178                .module_children_local(current_mod.to_local_def_id())
179                .iter()
180                .filter(|child| !child.reexport_chain.is_empty())
181                .filter_map(|child| child.res.opt_def_id())
182                .filter(|def_id| !cx.tcx.is_doc_hidden(def_id))
183                .collect();
184            let attrs = cx.tcx.hir_attrs(import.hir_id());
185            let mut items = build_module_items(
186                cx,
187                did,
188                visited,
189                inlined_names,
190                Some(&reexports),
191                Some((attrs, Some(import.owner_id.def_id))),
192            );
193            items.retain(|item| {
194                if let Some(name) = item.name {
195                    // If an item with the same type and name already exists,
196                    // it takes priority over the inlined stuff.
197                    inlined_names.insert((item.type_(), name))
198                } else {
199                    true
200                }
201            });
202            Some(items)
203        }
204        // glob imports on things like enums aren't inlined even for local exports, so just bail
205        _ => None,
206    }
207}
208
209pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [hir::Attribute] {
210    cx.tcx.get_attrs_unchecked(did)
211}
212
213pub(crate) fn item_relative_path(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<Symbol> {
214    tcx.def_path(def_id).data.into_iter().filter_map(|elem| elem.data.get_opt_name()).collect()
215}
216
217/// Record an external fully qualified name in the external_paths cache.
218///
219/// These names are used later on by HTML rendering to generate things like
220/// source links back to the original item.
221pub(crate) fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemType) {
222    if did.is_local() {
223        if cx.cache.exact_paths.contains_key(&did) {
224            return;
225        }
226    } else if cx.cache.external_paths.contains_key(&did) {
227        return;
228    }
229
230    let crate_name = cx.tcx.crate_name(did.krate);
231
232    let relative = item_relative_path(cx.tcx, did);
233    let fqn = if let ItemType::Macro = kind {
234        // Check to see if it is a macro 2.0 or built-in macro
235        if matches!(
236            CStore::from_tcx(cx.tcx).load_macro_untracked(did, cx.tcx),
237            LoadedMacro::MacroDef { def, .. } if !def.macro_rules
238        ) {
239            once(crate_name).chain(relative).collect()
240        } else {
241            vec![crate_name, *relative.last().expect("relative was empty")]
242        }
243    } else {
244        once(crate_name).chain(relative).collect()
245    };
246
247    if did.is_local() {
248        cx.cache.exact_paths.insert(did, fqn);
249    } else {
250        cx.cache.external_paths.insert(did, (fqn, kind));
251    }
252}
253
254pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait {
255    let trait_items = cx
256        .tcx
257        .associated_items(did)
258        .in_definition_order()
259        .filter(|item| !item.is_impl_trait_in_trait())
260        .map(|item| clean_middle_assoc_item(item, cx))
261        .collect();
262
263    let predicates = cx.tcx.predicates_of(did);
264    let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
265    let generics = filter_non_trait_generics(did, generics);
266    let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
267    clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds }
268}
269
270pub(crate) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box<clean::Function> {
271    let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
272    // The generics need to be cleaned before the signature.
273    let mut generics =
274        clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
275    let bound_vars = clean_bound_vars(sig.bound_vars());
276
277    // At the time of writing early & late-bound params are stored separately in rustc,
278    // namely in `generics.params` and `bound_vars` respectively.
279    //
280    // To reestablish the original source code order of the generic parameters, we
281    // need to manually sort them by their definition span after concatenation.
282    //
283    // See also:
284    // * https://rustc-dev-guide.rust-lang.org/bound-vars-and-params.html
285    // * https://rustc-dev-guide.rust-lang.org/what-does-early-late-bound-mean.html
286    let has_early_bound_params = !generics.params.is_empty();
287    let has_late_bound_params = !bound_vars.is_empty();
288    generics.params.extend(bound_vars);
289    if has_early_bound_params && has_late_bound_params {
290        // If this ever becomes a performances bottleneck either due to the sorting
291        // or due to the query calls, consider inserting the late-bound lifetime params
292        // right after the last early-bound lifetime param followed by only sorting
293        // the slice of lifetime params.
294        generics.params.sort_by_key(|param| cx.tcx.def_ident_span(param.def_id).unwrap());
295    }
296
297    let decl = clean_poly_fn_sig(cx, Some(def_id), sig);
298
299    Box::new(clean::Function { decl, generics })
300}
301
302fn build_enum(cx: &mut DocContext<'_>, did: DefId) -> clean::Enum {
303    let predicates = cx.tcx.explicit_predicates_of(did);
304
305    clean::Enum {
306        generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
307        variants: cx.tcx.adt_def(did).variants().iter().map(|v| clean_variant_def(v, cx)).collect(),
308    }
309}
310
311fn build_struct(cx: &mut DocContext<'_>, did: DefId) -> clean::Struct {
312    let predicates = cx.tcx.explicit_predicates_of(did);
313    let variant = cx.tcx.adt_def(did).non_enum_variant();
314
315    clean::Struct {
316        ctor_kind: variant.ctor_kind(),
317        generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
318        fields: variant.fields.iter().map(|x| clean_middle_field(x, cx)).collect(),
319    }
320}
321
322fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
323    let predicates = cx.tcx.explicit_predicates_of(did);
324    let variant = cx.tcx.adt_def(did).non_enum_variant();
325
326    let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
327    let fields = variant.fields.iter().map(|x| clean_middle_field(x, cx)).collect();
328    clean::Union { generics, fields }
329}
330
331fn build_type_alias(
332    cx: &mut DocContext<'_>,
333    did: DefId,
334    ret: &mut Vec<Item>,
335) -> Box<clean::TypeAlias> {
336    let predicates = cx.tcx.explicit_predicates_of(did);
337    let ty = cx.tcx.type_of(did).instantiate_identity();
338    let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None);
339    let inner_type = clean_ty_alias_inner_type(ty, cx, ret);
340
341    Box::new(clean::TypeAlias {
342        type_,
343        generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
344        inner_type,
345        item_type: None,
346    })
347}
348
349/// Builds all inherent implementations of an ADT (struct/union/enum) or Trait item/path/reexport.
350pub(crate) fn build_impls(
351    cx: &mut DocContext<'_>,
352    did: DefId,
353    attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
354    ret: &mut Vec<clean::Item>,
355) {
356    let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls");
357    let tcx = cx.tcx;
358
359    // for each implementation of an item represented by `did`, build the clean::Item for that impl
360    for &did in tcx.inherent_impls(did).iter() {
361        cx.with_param_env(did, |cx| {
362            build_impl(cx, did, attrs, ret);
363        });
364    }
365
366    // This pretty much exists expressly for `dyn Error` traits that exist in the `alloc` crate.
367    // See also:
368    //
369    // * https://github.com/rust-lang/rust/issues/103170 — where it didn't used to get documented
370    // * https://github.com/rust-lang/rust/pull/99917 — where the feature got used
371    // * https://github.com/rust-lang/rust/issues/53487 — overall tracking issue for Error
372    if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) {
373        let type_ =
374            if tcx.is_trait(did) { SimplifiedType::Trait(did) } else { SimplifiedType::Adt(did) };
375        for &did in tcx.incoherent_impls(type_).iter() {
376            cx.with_param_env(did, |cx| {
377                build_impl(cx, did, attrs, ret);
378            });
379        }
380    }
381}
382
383pub(crate) fn merge_attrs(
384    cx: &mut DocContext<'_>,
385    old_attrs: &[hir::Attribute],
386    new_attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
387) -> (clean::Attributes, Option<Arc<clean::cfg::Cfg>>) {
388    // NOTE: If we have additional attributes (from a re-export),
389    // always insert them first. This ensure that re-export
390    // doc comments show up before the original doc comments
391    // when we render them.
392    if let Some((inner, item_id)) = new_attrs {
393        let mut both = inner.to_vec();
394        both.extend_from_slice(old_attrs);
395        (
396            if let Some(item_id) = item_id {
397                Attributes::from_hir_with_additional(old_attrs, (inner, item_id.to_def_id()))
398            } else {
399                Attributes::from_hir(&both)
400            },
401            extract_cfg_from_attrs(both.iter(), cx.tcx, &cx.cache.hidden_cfg),
402        )
403    } else {
404        (
405            Attributes::from_hir(old_attrs),
406            extract_cfg_from_attrs(old_attrs.iter(), cx.tcx, &cx.cache.hidden_cfg),
407        )
408    }
409}
410
411/// Inline an `impl`, inherent or of a trait. The `did` must be for an `impl`.
412pub(crate) fn build_impl(
413    cx: &mut DocContext<'_>,
414    did: DefId,
415    attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
416    ret: &mut Vec<clean::Item>,
417) {
418    if !cx.inlined.insert(did.into()) {
419        return;
420    }
421
422    let tcx = cx.tcx;
423    let _prof_timer = tcx.sess.prof.generic_activity("build_impl");
424
425    let associated_trait = tcx.impl_trait_ref(did).map(ty::EarlyBinder::skip_binder);
426
427    // Do not inline compiler-internal items unless we're a compiler-internal crate.
428    let is_compiler_internal = |did| {
429        tcx.lookup_stability(did)
430            .is_some_and(|stab| stab.is_unstable() && stab.feature == sym::rustc_private)
431    };
432    let document_compiler_internal = is_compiler_internal(LOCAL_CRATE.as_def_id());
433    let is_directly_public = |cx: &mut DocContext<'_>, did| {
434        cx.cache.effective_visibilities.is_directly_public(tcx, did)
435            && (document_compiler_internal || !is_compiler_internal(did))
436    };
437
438    // Only inline impl if the implemented trait is
439    // reachable in rustdoc generated documentation
440    if !did.is_local()
441        && let Some(traitref) = associated_trait
442        && !is_directly_public(cx, traitref.def_id)
443    {
444        return;
445    }
446
447    let impl_item = match did.as_local() {
448        Some(did) => match &tcx.hir_expect_item(did).kind {
449            hir::ItemKind::Impl(impl_) => Some(impl_),
450            _ => panic!("`DefID` passed to `build_impl` is not an `impl"),
451        },
452        None => None,
453    };
454
455    let for_ = match &impl_item {
456        Some(impl_) => clean_ty(impl_.self_ty, cx),
457        None => clean_middle_ty(
458            ty::Binder::dummy(tcx.type_of(did).instantiate_identity()),
459            cx,
460            Some(did),
461            None,
462        ),
463    };
464
465    // Only inline impl if the implementing type is
466    // reachable in rustdoc generated documentation
467    if !did.is_local()
468        && let Some(did) = for_.def_id(&cx.cache)
469        && !is_directly_public(cx, did)
470    {
471        return;
472    }
473
474    let document_hidden = cx.render_options.document_hidden;
475    let predicates = tcx.explicit_predicates_of(did);
476    let (trait_items, generics) = match impl_item {
477        Some(impl_) => (
478            impl_
479                .items
480                .iter()
481                .map(|item| tcx.hir_impl_item(item.id))
482                .filter(|item| {
483                    // Filter out impl items whose corresponding trait item has `doc(hidden)`
484                    // not to document such impl items.
485                    // For inherent impls, we don't do any filtering, because that's already done in strip_hidden.rs.
486
487                    // When `--document-hidden-items` is passed, we don't
488                    // do any filtering, too.
489                    if document_hidden {
490                        return true;
491                    }
492                    if let Some(associated_trait) = associated_trait {
493                        let assoc_kind = match item.kind {
494                            hir::ImplItemKind::Const(..) => ty::AssocKind::Const,
495                            hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn,
496                            hir::ImplItemKind::Type(..) => ty::AssocKind::Type,
497                        };
498                        let trait_item = tcx
499                            .associated_items(associated_trait.def_id)
500                            .find_by_name_and_kind(
501                                tcx,
502                                item.ident,
503                                assoc_kind,
504                                associated_trait.def_id,
505                            )
506                            .unwrap(); // SAFETY: For all impl items there exists trait item that has the same name.
507                        !tcx.is_doc_hidden(trait_item.def_id)
508                    } else {
509                        true
510                    }
511                })
512                .map(|item| clean_impl_item(item, cx))
513                .collect::<Vec<_>>(),
514            clean_generics(impl_.generics, cx),
515        ),
516        None => (
517            tcx.associated_items(did)
518                .in_definition_order()
519                .filter(|item| !item.is_impl_trait_in_trait())
520                .filter(|item| {
521                    // If this is a trait impl, filter out associated items whose corresponding item
522                    // in the associated trait is marked `doc(hidden)`.
523                    // If this is an inherent impl, filter out private associated items.
524                    if let Some(associated_trait) = associated_trait {
525                        let trait_item = tcx
526                            .associated_items(associated_trait.def_id)
527                            .find_by_name_and_kind(
528                                tcx,
529                                item.ident(tcx),
530                                item.kind,
531                                associated_trait.def_id,
532                            )
533                            .unwrap(); // corresponding associated item has to exist
534                        document_hidden || !tcx.is_doc_hidden(trait_item.def_id)
535                    } else {
536                        item.visibility(tcx).is_public()
537                    }
538                })
539                .map(|item| clean_middle_assoc_item(item, cx))
540                .collect::<Vec<_>>(),
541            clean::enter_impl_trait(cx, |cx| {
542                clean_ty_generics(cx, tcx.generics_of(did), predicates)
543            }),
544        ),
545    };
546    let polarity = tcx.impl_polarity(did);
547    let trait_ = associated_trait
548        .map(|t| clean_trait_ref_with_constraints(cx, ty::Binder::dummy(t), ThinVec::new()));
549    if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() {
550        super::build_deref_target_impls(cx, &trait_items, ret);
551    }
552
553    // Return if the trait itself or any types of the generic parameters are doc(hidden).
554    let mut stack: Vec<&Type> = vec![&for_];
555
556    if let Some(did) = trait_.as_ref().map(|t| t.def_id())
557        && !document_hidden
558        && tcx.is_doc_hidden(did)
559    {
560        return;
561    }
562
563    if let Some(generics) = trait_.as_ref().and_then(|t| t.generics()) {
564        stack.extend(generics);
565    }
566
567    while let Some(ty) = stack.pop() {
568        if let Some(did) = ty.def_id(&cx.cache)
569            && !document_hidden
570            && tcx.is_doc_hidden(did)
571        {
572            return;
573        }
574        if let Some(generics) = ty.generics() {
575            stack.extend(generics);
576        }
577    }
578
579    if let Some(did) = trait_.as_ref().map(|t| t.def_id()) {
580        cx.with_param_env(did, |cx| {
581            record_extern_trait(cx, did);
582        });
583    }
584
585    let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs);
586    trace!("merged_attrs={merged_attrs:?}");
587
588    trace!(
589        "build_impl: impl {:?} for {:?}",
590        trait_.as_ref().map(|t| t.def_id()),
591        for_.def_id(&cx.cache)
592    );
593    ret.push(clean::Item::from_def_id_and_attrs_and_parts(
594        did,
595        None,
596        clean::ImplItem(Box::new(clean::Impl {
597            safety: hir::Safety::Safe,
598            generics,
599            trait_,
600            for_,
601            items: trait_items,
602            polarity,
603            kind: if utils::has_doc_flag(tcx, did, sym::fake_variadic) {
604                ImplKind::FakeVariadic
605            } else {
606                ImplKind::Normal
607            },
608        })),
609        merged_attrs,
610        cfg,
611    ));
612}
613
614fn build_module(cx: &mut DocContext<'_>, did: DefId, visited: &mut DefIdSet) -> clean::Module {
615    let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None, None);
616
617    let span = clean::Span::new(cx.tcx.def_span(did));
618    clean::Module { items, span }
619}
620
621fn build_module_items(
622    cx: &mut DocContext<'_>,
623    did: DefId,
624    visited: &mut DefIdSet,
625    inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
626    allowed_def_ids: Option<&DefIdSet>,
627    attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
628) -> Vec<clean::Item> {
629    let mut items = Vec::new();
630
631    // If we're re-exporting a re-export it may actually re-export something in
632    // two namespaces, so the target may be listed twice. Make sure we only
633    // visit each node at most once.
634    for item in cx.tcx.module_children(did).iter() {
635        if item.vis.is_public() {
636            let res = item.res.expect_non_local();
637            if let Some(def_id) = res.opt_def_id()
638                && let Some(allowed_def_ids) = allowed_def_ids
639                && !allowed_def_ids.contains(&def_id)
640            {
641                continue;
642            }
643            if let Some(def_id) = res.mod_def_id() {
644                // If we're inlining a glob import, it's possible to have
645                // two distinct modules with the same name. We don't want to
646                // inline it, or mark any of its contents as visited.
647                if did == def_id
648                    || inlined_names.contains(&(ItemType::Module, item.ident.name))
649                    || !visited.insert(def_id)
650                {
651                    continue;
652                }
653            }
654            if let Res::PrimTy(p) = res {
655                // Primitive types can't be inlined so generate an import instead.
656                let prim_ty = clean::PrimitiveType::from(p);
657                items.push(clean::Item {
658                    name: None,
659                    // We can use the item's `DefId` directly since the only information ever used
660                    // from it is `DefId.krate`.
661                    item_id: ItemId::DefId(did),
662                    inner: Box::new(clean::ItemInner {
663                        attrs: Default::default(),
664                        stability: None,
665                        kind: clean::ImportItem(clean::Import::new_simple(
666                            item.ident.name,
667                            clean::ImportSource {
668                                path: clean::Path {
669                                    res,
670                                    segments: thin_vec![clean::PathSegment {
671                                        name: prim_ty.as_sym(),
672                                        args: clean::GenericArgs::AngleBracketed {
673                                            args: Default::default(),
674                                            constraints: ThinVec::new(),
675                                        },
676                                    }],
677                                },
678                                did: None,
679                            },
680                            true,
681                        )),
682                    }),
683                    cfg: None,
684                    inline_stmt_id: None,
685                });
686            } else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) {
687                items.extend(i)
688            }
689        }
690    }
691
692    items
693}
694
695pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String {
696    if let Some(did) = did.as_local() {
697        let hir_id = tcx.local_def_id_to_hir_id(did);
698        rustc_hir_pretty::id_to_string(&tcx, hir_id)
699    } else {
700        tcx.rendered_const(did).clone()
701    }
702}
703
704fn build_const_item(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant {
705    let mut generics =
706        clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
707    clean::simplify::move_bounds_to_generic_parameters(&mut generics);
708    let ty = clean_middle_ty(
709        ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()),
710        cx,
711        None,
712        None,
713    );
714    clean::Constant { generics, type_: ty, kind: clean::ConstantKind::Extern { def_id } }
715}
716
717fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::Static {
718    clean::Static {
719        type_: Box::new(clean_middle_ty(
720            ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()),
721            cx,
722            Some(did),
723            None,
724        )),
725        mutability: if mutable { Mutability::Mut } else { Mutability::Not },
726        expr: None,
727    }
728}
729
730fn build_macro(
731    cx: &mut DocContext<'_>,
732    def_id: DefId,
733    name: Symbol,
734    macro_kind: MacroKind,
735) -> clean::ItemKind {
736    match CStore::from_tcx(cx.tcx).load_macro_untracked(def_id, cx.tcx) {
737        LoadedMacro::MacroDef { def, .. } => match macro_kind {
738            MacroKind::Bang => clean::MacroItem(clean::Macro {
739                source: utils::display_macro_source(cx, name, &def),
740                macro_rules: def.macro_rules,
741            }),
742            MacroKind::Derive | MacroKind::Attr => {
743                clean::ProcMacroItem(clean::ProcMacro { kind: macro_kind, helpers: Vec::new() })
744            }
745        },
746        LoadedMacro::ProcMacro(ext) => clean::ProcMacroItem(clean::ProcMacro {
747            kind: ext.macro_kind(),
748            helpers: ext.helper_attrs,
749        }),
750    }
751}
752
753/// A trait's generics clause actually contains all of the predicates for all of
754/// its associated types as well. We specifically move these clauses to the
755/// associated types instead when displaying, so when we're generating the
756/// generics for the trait itself we need to be sure to remove them.
757/// We also need to remove the implied "recursive" Self: Trait bound.
758///
759/// The inverse of this filtering logic can be found in the `Clean`
760/// implementation for `AssociatedType`
761fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics {
762    for pred in &mut g.where_predicates {
763        if let clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref mut bounds, .. } =
764            *pred
765        {
766            bounds.retain(|bound| match bound {
767                clean::GenericBound::TraitBound(clean::PolyTrait { trait_, .. }, _) => {
768                    trait_.def_id() != trait_did
769                }
770                _ => true,
771            });
772        }
773    }
774
775    g.where_predicates.retain(|pred| match pred {
776        clean::WherePredicate::BoundPredicate {
777            ty:
778                clean::QPath(box clean::QPathData {
779                    self_type: clean::Generic(_),
780                    trait_: Some(trait_),
781                    ..
782                }),
783            bounds,
784            ..
785        } => !bounds.is_empty() && trait_.def_id() != trait_did,
786        _ => true,
787    });
788    g
789}
790
791/// Supertrait bounds for a trait are also listed in the generics coming from
792/// the metadata for a crate, so we want to separate those out and create a new
793/// list of explicit supertrait bounds to render nicely.
794fn separate_supertrait_bounds(
795    mut g: clean::Generics,
796) -> (clean::Generics, Vec<clean::GenericBound>) {
797    let mut ty_bounds = Vec::new();
798    g.where_predicates.retain(|pred| match *pred {
799        clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref bounds, .. } => {
800            ty_bounds.extend(bounds.iter().cloned());
801            false
802        }
803        _ => true,
804    });
805    (g, ty_bounds)
806}
807
808pub(crate) fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) {
809    if did.is_local() {
810        return;
811    }
812
813    {
814        if cx.external_traits.contains_key(&did) || cx.active_extern_traits.contains(&did) {
815            return;
816        }
817    }
818
819    {
820        cx.active_extern_traits.insert(did);
821    }
822
823    debug!("record_extern_trait: {did:?}");
824    let trait_ = build_external_trait(cx, did);
825
826    cx.external_traits.insert(did, trait_);
827    cx.active_extern_traits.remove(&did);
828}