Skip to main content

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