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