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