1use std::cmp::Ordering;
10use std::fmt::{self, Display, Write};
11use std::{iter, slice};
12
13use itertools::{Either, Itertools};
14use rustc_abi::ExternAbi;
15use rustc_ast::join_path_syms;
16use rustc_attr_ir::{ConstStability, StabilityLevel, StableSince};
17use rustc_data_structures::fx::FxHashSet;
18use rustc_hir as hir;
19use rustc_hir::def::{DefKind, MacroKinds};
20use rustc_hir::def_id::{DefId, LOCAL_CRATE, ModId};
21use rustc_metadata::creader::CStore;
22use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode};
23use rustc_span::symbol::kw;
24use rustc_span::{Ident, Symbol, sym};
25use tracing::{debug, trace};
26
27use super::url_parts_builder::UrlPartsBuilder;
28use crate::clean::types::ExternalLocation;
29use crate::clean::utils::find_nearest_parent_module;
30use crate::clean::{self, ExternalCrate, PrimitiveType, WherePredicate};
31use crate::display::{Joined as _, MaybeDisplay as _, WithOpts, Wrapped};
32use crate::formats::cache::Cache;
33use crate::formats::item_type::ItemType;
34use crate::html::escape::{Escape, EscapeBodyText};
35use crate::html::render::Context;
36use crate::passes::collect_intra_doc_links::UrlFragment;
37
38pub(crate) fn print_generic_bounds(
39 bounds: &[clean::GenericBound],
40 cx: &Context<'_>,
41) -> impl Display {
42 fmt::from_fn(move |f| {
43 let mut bounds_dup = FxHashSet::default();
44
45 bounds
46 .iter()
47 .filter(move |b| bounds_dup.insert(*b))
48 .map(|bound| print_generic_bound(bound, cx))
49 .joined(" + ", f)
50 })
51}
52
53pub(crate) fn print_generic_param_def(
54 generic_param: &clean::GenericParamDef,
55 cx: &Context<'_>,
56) -> impl Display {
57 fmt::from_fn(move |f| match &generic_param.kind {
58 clean::GenericParamDefKind::Lifetime { outlives } => {
59 write!(f, "{}", generic_param.name)?;
60
61 if !outlives.is_empty() {
62 f.write_str(": ")?;
63 outlives.iter().map(|lt| print_lifetime(lt)).joined(" + ", f)?;
64 }
65
66 Ok(())
67 }
68 clean::GenericParamDefKind::Type { bounds, default, .. } => {
69 f.write_str(generic_param.name.as_str())?;
70
71 if !bounds.is_empty() {
72 f.write_str(": ")?;
73 print_generic_bounds(bounds, cx).fmt(f)?;
74 }
75
76 if let Some(ty) = default {
77 f.write_str(" = ")?;
78 print_type(ty, cx).fmt(f)?;
79 }
80
81 Ok(())
82 }
83 clean::GenericParamDefKind::Const { ty, default, .. } => {
84 write!(f, "const {}: ", generic_param.name)?;
85 print_type(ty, cx).fmt(f)?;
86
87 if let Some(default) = default {
88 f.write_str(" = ")?;
89 if f.alternate() {
90 write!(f, "{default}")?;
91 } else {
92 write!(f, "{}", Escape(default))?;
93 }
94 }
95
96 Ok(())
97 }
98 })
99}
100
101pub(crate) fn print_generics(generics: &clean::Generics, cx: &Context<'_>) -> impl Display {
102 let mut real_params = generics.params.iter().filter(|p| !p.is_synthetic_param()).peekable();
103 if real_params.peek().is_none() {
104 None
105 } else {
106 Some(Wrapped::with_angle_brackets().wrap_fn(move |f| {
107 real_params.clone().map(|g| print_generic_param_def(g, cx)).joined(", ", f)
108 }))
109 }
110 .maybe_display()
111}
112
113#[derive(Clone, Copy, PartialEq, Eq)]
114pub(crate) enum Ending {
115 Newline,
116 NoNewline,
117}
118
119fn print_where_predicate(predicate: &clean::WherePredicate, cx: &Context<'_>) -> impl Display {
120 fmt::from_fn(move |f| {
121 match predicate {
122 clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
123 print_higher_ranked_params_with_space(bound_params, cx, "for").fmt(f)?;
124 print_type(ty, cx).fmt(f)?;
125 f.write_str(":")?;
126 if !bounds.is_empty() {
127 f.write_str(" ")?;
128 print_generic_bounds(bounds, cx).fmt(f)?;
129 }
130 Ok(())
131 }
132 clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
133 write!(f, "{}:", print_lifetime(lifetime))?;
136 if !bounds.is_empty() {
137 write!(f, " {}", print_generic_bounds(bounds, cx))?;
138 }
139 Ok(())
140 }
141 clean::WherePredicate::ProjectionPredicate { lhs, rhs } => {
142 let opts = WithOpts::from(f);
143 write!(
144 f,
145 "{} == {}",
146 opts.display(print_qpath_data(lhs, cx)),
147 opts.display(print_term(rhs, cx)),
148 )
149 }
150 }
151 })
152}
153
154pub(crate) fn print_where_clause(
158 gens: &clean::Generics,
159 cx: &Context<'_>,
160 indent: usize,
161 ending: Ending,
162) -> Option<impl Display> {
163 if gens.where_predicates.is_empty() {
164 return None;
165 }
166
167 fn where_preds(
168 predicates: &[WherePredicate],
169 cx: &Context<'_>,
170 sep: impl Display,
171 ) -> impl Display {
172 fmt::from_fn(move |f| {
173 predicates.iter().map(|predicate| print_where_predicate(predicate, cx)).joined(&sep, f)
174 })
175 }
176
177 let spaces = |n: usize| crate::display::repeat(' ', n);
178
179 Some(fmt::from_fn(move |f| {
180 if f.alternate() {
181 write!(f, " where {:#}", where_preds(&gens.where_predicates, cx, ", "))?;
182 if ending == Ending::Newline {
183 f.write_char(',')?;
184 }
185 return Ok(());
186 }
187
188 const WHERE_INDENT: usize = 3;
189
190 let padding = {
191 let padding_amount = if ending == Ending::Newline {
192 indent + 4
193 } else if indent == 0 {
194 4
195 } else {
196 indent + WHERE_INDENT + "where ".len()
197 };
198 spaces(padding_amount)
199 };
200
201 let br_with_padding = format_args!("\n{padding}");
202 let sep = format_args!(",{br_with_padding}");
203 let where_preds = where_preds(&gens.where_predicates, cx, sep);
204
205 if ending == Ending::Newline {
206 write!(
207 f,
208 "{indent}<div class=\"where\">where{br_with_padding}{where_preds},</div>",
209 indent = spaces(indent.saturating_sub(1)),
210 )
211 } else if indent == 0 {
212 write!(f, "\n<span class=\"where\">where{br_with_padding}{where_preds}</span>")
213 } else {
214 write!(
215 f,
216 "\n{indent}<span class=\"where\">where {where_preds}</span>",
217 indent = spaces(indent + WHERE_INDENT),
218 )
219 }
220 }))
221}
222
223#[inline]
224pub(crate) fn print_lifetime(lt: &clean::Lifetime) -> &str {
225 lt.0.as_str()
226}
227
228pub(crate) fn print_constant_kind(
229 constant_kind: &clean::ConstantKind,
230 tcx: TyCtxt<'_>,
231) -> impl Display {
232 let expr = constant_kind.expr(tcx);
233 fmt::from_fn(
234 move |f| {
235 if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) }
236 },
237 )
238}
239
240fn print_poly_trait(poly_trait: &clean::PolyTrait, cx: &Context<'_>) -> impl Display {
241 fmt::from_fn(move |f| {
242 print_higher_ranked_params_with_space(&poly_trait.generic_params, cx, "for").fmt(f)?;
243 print_path(&poly_trait.trait_, cx).fmt(f)
244 })
245}
246
247pub(crate) fn print_generic_bound(
248 generic_bound: &clean::GenericBound,
249 cx: &Context<'_>,
250) -> impl Display {
251 fmt::from_fn(move |f| match generic_bound {
252 clean::GenericBound::Outlives(lt) => f.write_str(print_lifetime(lt)),
253 clean::GenericBound::TraitBound(ty, modifiers) => {
254 let hir::TraitBoundModifiers { polarity, constness: _ } = modifiers;
256 f.write_str(match polarity {
257 hir::BoundPolarity::Positive => "",
258 hir::BoundPolarity::Maybe(_) => "?",
259 hir::BoundPolarity::Negative(_) => "!",
260 })?;
261 print_poly_trait(ty, cx).fmt(f)
262 }
263 clean::GenericBound::Use(args) => {
264 f.write_str("use")?;
265 Wrapped::with_angle_brackets()
266 .wrap_fn(|f| args.iter().map(|arg| arg.name()).joined(", ", f))
267 .fmt(f)
268 }
269 })
270}
271
272fn print_generic_args(generic_args: &clean::GenericArgs, cx: &Context<'_>) -> impl Display {
273 fmt::from_fn(move |f| {
274 match generic_args {
275 clean::GenericArgs::AngleBracketed { args, constraints } => {
276 if !args.is_empty() || !constraints.is_empty() {
277 Wrapped::with_angle_brackets()
278 .wrap_fn(|f| {
279 [Either::Left(args), Either::Right(constraints)]
280 .into_iter()
281 .flat_map(Either::factor_into_iter)
282 .map(|either| {
283 either.map_either(
284 |arg| print_generic_arg(arg, cx),
285 |constraint| print_assoc_item_constraint(constraint, cx),
286 )
287 })
288 .joined(", ", f)
289 })
290 .fmt(f)?;
291 }
292 }
293 clean::GenericArgs::Parenthesized { inputs, output } => {
294 Wrapped::with_parens()
295 .wrap_fn(|f| inputs.iter().map(|ty| print_type(ty, cx)).joined(", ", f))
296 .fmt(f)?;
297 if let Some(ref ty) = *output {
298 f.write_str(if f.alternate() { " -> " } else { " -> " })?;
299 print_type(ty, cx).fmt(f)?;
300 }
301 }
302 clean::GenericArgs::ReturnTypeNotation => {
303 f.write_str("(..)")?;
304 }
305 }
306 Ok(())
307 })
308}
309
310#[derive(PartialEq, Eq, Debug)]
312pub(crate) enum HrefError {
313 DocumentationNotBuilt,
332 Private,
334 NotInExternalCache,
336 UnnamableItem,
338}
339
340#[derive(Debug)]
342pub(crate) struct HrefInfo {
343 pub(crate) url: String,
345 pub(crate) kind: ItemType,
347 pub(crate) rust_path: Vec<Symbol>,
349}
350
351fn generate_macro_def_id_path(
354 def_id: DefId,
355 cx: &Context<'_>,
356 jump_to_def_path_depth: Option<usize>,
357) -> Result<HrefInfo, HrefError> {
358 let tcx = cx.tcx();
359 let crate_name = tcx.crate_name(def_id.krate);
360 let cache = cx.cache();
361
362 let cstore = CStore::from_tcx(tcx);
363 if !cstore.has_crate_data(def_id.krate) {
365 debug!("No data for crate {crate_name}");
366 return Err(HrefError::NotInExternalCache);
367 }
368 let DefKind::Macro(kinds) = tcx.def_kind(def_id) else {
369 unreachable!();
370 };
371 let item_type = if kinds == MacroKinds::DERIVE {
372 ItemType::ProcDerive
373 } else if kinds == MacroKinds::ATTR {
374 ItemType::ProcAttribute
375 } else {
376 ItemType::Macro
377 };
378 let path = clean::inline::get_item_path(tcx, def_id, item_type);
379 let [module_path @ .., last] = path.as_slice() else {
382 debug!("macro path is empty!");
383 return Err(HrefError::NotInExternalCache);
384 };
385 if module_path.is_empty() {
386 debug!("macro path too short: missing crate prefix (got 1 element, need at least 2)");
387 return Err(HrefError::NotInExternalCache);
388 }
389
390 let url = match cache.extern_locations[&def_id.krate] {
391 ExternalLocation::Remote { ref url, is_absolute } => {
392 let depth = jump_to_def_path_depth.unwrap_or(cx.current.len());
393 let mut prefix = remote_url_prefix(url, is_absolute, depth);
394 prefix.extend(module_path.iter().copied());
395 prefix.push_fmt(format_args!("{}.{last}.html", item_type.as_str()));
396 prefix.finish()
397 }
398 ExternalLocation::Local => {
399 format!(
400 "{prefix}{path}/{item_type}.{last}.html",
401 prefix = "../".repeat(jump_to_def_path_depth.unwrap_or(0)),
402 path = fmt::from_fn(|f| module_path.iter().joined("/", f)),
403 item_type = item_type.as_str(),
404 )
405 }
406 ExternalLocation::Unknown => {
407 debug!("crate {crate_name} not in cache when linkifying macros");
408 return Err(HrefError::NotInExternalCache);
409 }
410 };
411 Ok(HrefInfo { url, kind: item_type, rust_path: path })
412}
413
414fn impl_self_ty(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Ty<'_> {
416 use rustc_middle::traits::ObligationCause;
417 use rustc_middle::ty;
418 use rustc_trait_selection::infer::TyCtxtInferExt;
419 use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
420
421 let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
422 let ty = tcx.type_of(impl_def_id);
423 infcx
424 .at(&ObligationCause::dummy(), tcx.param_env(impl_def_id))
425 .query_normalize(ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip()))
426 .map(|resolved| infcx.deeply_resolve_ignoring_regions(resolved.value).skip_binder())
427 .unwrap_or(ty.skip_binder())
428}
429
430fn transitive_reexport_path(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Vec<Symbol>> {
431 transitive_reexport_path_inner(tcx, def_id, &mut Vec::new())
432}
433
434fn transitive_reexport_path_inner(
436 tcx: TyCtxt<'_>,
437 def_id: DefId,
438 callers: &mut Vec<DefId>,
439) -> Option<Vec<Symbol>> {
440 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
441
442 if let Some(cnum) = def_id.as_crate_root() {
443 return Some(vec![tcx.crate_name(cnum)]);
444 }
445
446 let visible_parent_map = tcx.visible_parent_map(());
447 let mut cur_def_key = tcx.def_key(def_id);
448
449 if let DefPathData::Ctor = cur_def_key.disambiguated_data.data {
451 let parent = DefId {
452 krate: def_id.krate,
453 index: cur_def_key
454 .parent
455 .expect("`DefPathData::Ctor` / `VariantData` missing a parent"),
456 };
457
458 cur_def_key = tcx.def_key(parent);
459 }
460
461 let visible_parent = visible_parent_map.get(&def_id).cloned()?;
462 if tcx.is_doc_hidden(visible_parent) {
464 return None;
465 }
466
467 let actual_parent = tcx.opt_parent(def_id);
468 let mut data = cur_def_key.disambiguated_data.data;
469 match data {
470 DefPathData::TypeNs(ref mut name) if Some(visible_parent) != actual_parent => {
471 let reexport = tcx
474 .module_children(ModId::new_unchecked(visible_parent))
475 .iter()
476 .filter(|child| child.res.opt_def_id() == Some(def_id))
477 .find(|child| child.vis.is_public() && child.ident.name != kw::Underscore)
478 .map(|child| child.ident.name);
479
480 if let Some(new_name) = reexport {
481 *name = new_name;
482 } else {
483 return None;
485 }
486 }
487 DefPathData::CrateRoot => {
489 data = DefPathData::TypeNs(tcx.crate_name(def_id.krate));
490 }
491 _ => {}
492 }
493
494 if callers.contains(&visible_parent) {
495 return None;
496 }
497 callers.push(visible_parent);
498 let mut path = transitive_reexport_path_inner(tcx, visible_parent, callers)?;
499 callers.pop();
500 path.push(DisambiguatedDefPathData { data, disambiguator: 0 }.as_sym(false));
501 Some(path)
502}
503
504fn generate_item_def_id_path(
505 mut def_id: DefId,
506 original_def_id: DefId,
507 cx: &Context<'_>,
508 jump_to_def_path_depth: Option<usize>,
509) -> Result<HrefInfo, HrefError> {
510 let tcx = cx.tcx();
511 let crate_name = tcx.crate_name(def_id.krate);
512 let mut prim = None;
513 let mut maybe_have_impl_not_in_def_crate = false;
514
515 if def_id != original_def_id
518 && let DefKind::Impl { of_trait } = tcx.def_kind(def_id)
519 {
520 let ty = impl_self_ty(tcx, def_id);
521 if let ty::Dynamic(traits, _) = ty.kind()
525 && let Some(trait_def_id) =
526 traits.iter().find_map(|trait_| match trait_.skip_binder() {
527 ty::ExistentialPredicate::Trait(t) => Some(t.def_id),
528 ty::ExistentialPredicate::Projection(p) => Some(p.trait_ref(tcx).def_id),
529 ty::ExistentialPredicate::AutoTrait(_) => None,
530 })
531 {
532 def_id = trait_def_id;
533 } else if let Some(new_def_id) = ty.ty_adt_def().map(|adt| adt.did()) {
534 if cx.cache().paths.contains_key(&new_def_id)
537 && let Ok(mut info) =
538 href_with_jump_to_def_path_depth(new_def_id, cx, jump_to_def_path_depth, None)
539 {
540 let kind = ItemType::from_def_id(original_def_id, tcx);
541 info.url = format!("{}#{kind}.{}", info.url, tcx.item_name(original_def_id));
544 return Ok(info);
545 }
546 def_id = new_def_id;
547 maybe_have_impl_not_in_def_crate = !of_trait
548 && !original_def_id.is_local()
549 && !def_id.is_local()
550 && def_id.krate != original_def_id.krate;
551 } else {
552 if !of_trait && crate_name != sym::core {
558 if ![sym::alloc, sym::std].contains(&crate_name) {
559 return Err(HrefError::UnnamableItem);
562 }
563 maybe_have_impl_not_in_def_crate = true;
564 }
565 prim = PrimitiveType::from_ty(ty);
566 }
567 }
568
569 let (shortty, fqp) = if let Some(prim) = prim {
570 let crate_name = if maybe_have_impl_not_in_def_crate { sym::std } else { crate_name };
571 (ItemType::Primitive, vec![crate_name, prim.as_sym()])
572 } else {
573 (
574 ItemType::from_def_id(def_id, tcx),
575 if maybe_have_impl_not_in_def_crate
576 && let Some(fqp) = transitive_reexport_path(tcx, def_id)
582 {
583 fqp
584 } else {
585 let mut fqp = vec![crate_name];
586 fqp.append(&mut clean::inline::item_relative_path(tcx, def_id));
587 fqp
588 },
589 )
590 };
591 let module_fqp = to_module_fqp(shortty, &fqp);
592
593 let (parts, is_remote) =
594 url_parts(cx.cache(), def_id, module_fqp, &cx.current, jump_to_def_path_depth)?;
595 let mut url = make_href(jump_to_def_path_depth, shortty, parts, &fqp, is_remote);
596
597 if def_id != original_def_id {
598 let kind = ItemType::from_def_id(original_def_id, tcx);
599 url = format!("{url}#{kind}.{}", tcx.item_name(original_def_id))
600 };
601 Ok(HrefInfo { url, kind: shortty, rust_path: fqp })
602}
603
604fn is_unnamable(tcx: TyCtxt<'_>, did: DefId) -> bool {
606 let mut cur_did = did;
607 while let Some(parent) = tcx.opt_parent(cur_did) {
608 match tcx.def_kind(parent) {
609 DefKind::Mod | DefKind::ForeignMod => cur_did = parent,
611 DefKind::Impl { .. } => return false,
617 _ => return true,
619 }
620 }
621 return false;
622}
623
624fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
625 if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
626}
627
628fn remote_url_prefix(url: &str, is_absolute: bool, depth: usize) -> UrlPartsBuilder {
629 let url = url.trim_end_matches('/');
630 if is_absolute {
631 UrlPartsBuilder::singleton(url)
632 } else {
633 let extra = depth.saturating_sub(1);
634 let mut b: UrlPartsBuilder = iter::repeat_n("..", extra).collect();
635 b.push(url);
636 b
637 }
638}
639
640fn url_parts(
653 cache: &Cache,
654 def_id: DefId,
655 module_fqp: &[Symbol],
656 relative_to: &[Symbol],
657 jump_to_def_path_depth: Option<usize>,
658) -> Result<(UrlPartsBuilder, bool), HrefError> {
659 let location = if def_id.krate == LOCAL_CRATE {
660 &ExternalLocation::Local
661 } else {
662 &cache.extern_locations[&def_id.krate]
663 };
664 match *location {
665 ExternalLocation::Remote { ref url, is_absolute } => {
666 let depth = jump_to_def_path_depth.unwrap_or(relative_to.len());
667 let mut builder = remote_url_prefix(url, is_absolute, depth);
668 builder.extend(module_fqp.iter().copied());
669 Ok((builder, true))
672 }
673 ExternalLocation::Local => Ok((href_relative_parts(module_fqp, relative_to), false)),
674 ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt),
675 }
676}
677
678fn make_href(
679 jump_to_def_path_depth: Option<usize>,
680 shortty: ItemType,
681 mut url_parts: UrlPartsBuilder,
682 fqp: &[Symbol],
683 is_remote: bool,
684) -> String {
685 if !is_remote && let Some(depth) = jump_to_def_path_depth {
688 url_parts.push_front(&iter::repeat_n("..", depth).join("/"));
689 }
690 debug!(?url_parts);
691 match shortty {
692 ItemType::Module => {
693 url_parts.push("index.html");
694 }
695 _ => {
696 let last = fqp.last().unwrap();
697 url_parts.push_fmt(format_args!("{shortty}.{last}.html"));
698 }
699 }
700 url_parts.finish()
701}
702
703fn ty_inherits_doc_hidden<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
704 match ty.kind() {
705 ty::Dynamic(traits, _) => traits
709 .iter()
710 .find_map(|trait_| match trait_.skip_binder() {
711 ty::ExistentialPredicate::Trait(t) => Some(inherits_doc_hidden(tcx, t.def_id)),
712 ty::ExistentialPredicate::Projection(p) => {
713 Some(inherits_doc_hidden(tcx, p.trait_ref(tcx).def_id))
714 }
715 ty::ExistentialPredicate::AutoTrait(_) => None,
716 })
717 .unwrap_or(false),
718 ty::Adt(adt, _) => inherits_doc_hidden(tcx, adt.did()),
719 ty::Foreign(def_id) => inherits_doc_hidden(tcx, *def_id),
720 ty::Ref(_, ty, _) => ty_inherits_doc_hidden(tcx, *ty),
721 _ => false,
724 }
725}
726
727fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool {
728 loop {
729 if tcx.is_doc_hidden(def_id) {
730 return true;
731 } else if def_id.is_crate_root() {
732 return false;
733 } else if let DefKind::Impl { of_trait } = tcx.def_kind(def_id) {
734 return ty_inherits_doc_hidden(tcx, impl_self_ty(tcx, def_id))
738 || (of_trait && inherits_doc_hidden(tcx, tcx.impl_trait_id(def_id)));
739 }
740 def_id = tcx.parent(def_id);
741 }
742}
743
744pub(crate) fn href_with_jump_to_def_path_depth(
747 original_did: DefId,
748 cx: &Context<'_>,
749 jump_to_def_path_depth: Option<usize>,
750 preferred_name: Option<&str>,
751) -> Result<HrefInfo, HrefError> {
752 let tcx = cx.tcx();
753 let def_kind = tcx.def_kind(original_did);
754 let did = match def_kind {
755 DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => {
756 tcx.parent(original_did)
758 }
759 DefKind::Ctor(..) => {
762 return href_with_jump_to_def_path_depth(
763 tcx.parent(original_did),
764 cx,
765 jump_to_def_path_depth,
766 preferred_name,
767 );
768 }
769 DefKind::ExternCrate => {
770 if let Some(local_did) = original_did.as_local() {
772 tcx.extern_mod_stmt_cnum(local_did).unwrap_or(LOCAL_CRATE).as_def_id()
773 } else {
774 original_did
775 }
776 }
777 _ => original_did,
778 };
779 if is_unnamable(tcx, did) {
780 return Err(HrefError::UnnamableItem);
781 }
782 let cache = cx.cache();
783 let relative_to = &cx.current;
784
785 if !original_did.is_local() {
786 if jump_to_def_path_depth.is_some() {
789 if inherits_doc_hidden(tcx, original_did) {
790 return Err(HrefError::Private);
791 }
792 } else if !cache.effective_visibilities.is_directly_public(tcx, did)
793 && !cache.document_private
794 && !cache.primitive_locations.values().any(|&id| id == did)
795 {
796 return Err(HrefError::Private);
797 }
798 }
799
800 let (fqp, shortty, url_parts, is_remote) = match cache.paths.get(&did) {
801 Some(info) => (
802 info.get_preferred_path(preferred_name),
803 info.ty,
804 {
805 let module_fqp = to_module_fqp(info.ty, info.parts.as_slice());
806 debug!(?info.parts, ?info.ty, ?module_fqp);
807 href_relative_parts(module_fqp, relative_to)
808 },
809 false,
810 ),
811 None => {
812 if (jump_to_def_path_depth.is_none() || original_did == did)
819 && let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did)
820 {
821 let module_fqp = to_module_fqp(shortty, fqp);
822 let (parts, is_remote) =
823 url_parts(cache, did, module_fqp, relative_to, jump_to_def_path_depth)?;
824 (fqp.as_slice(), shortty, parts, is_remote)
825 } else if matches!(def_kind, DefKind::Macro(_)) {
826 return generate_macro_def_id_path(did, cx, jump_to_def_path_depth);
827 } else if did.is_local() {
828 return Err(HrefError::Private);
829 } else {
830 return generate_item_def_id_path(did, original_did, cx, jump_to_def_path_depth);
831 }
832 }
833 };
834 Ok(HrefInfo {
835 url: make_href(jump_to_def_path_depth, shortty, url_parts, fqp, is_remote),
836 kind: shortty,
837 rust_path: fqp.to_vec(),
838 })
839}
840
841pub(crate) fn href(did: DefId, cx: &Context<'_>) -> Result<HrefInfo, HrefError> {
842 href_with_jump_to_def_path_depth(did, cx, None, None)
843}
844
845pub(crate) fn href_with_path_check(
846 did: DefId,
847 cx: &Context<'_>,
848 text: &str,
849) -> Result<HrefInfo, HrefError> {
850 href_with_jump_to_def_path_depth(did, cx, None, Some(text))
851}
852
853pub(crate) fn href_relative_parts(fqp: &[Symbol], relative_to_fqp: &[Symbol]) -> UrlPartsBuilder {
857 for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
858 if f != r {
860 let dissimilar_part_count = relative_to_fqp.len() - i;
861 let fqp_module = &fqp[i..];
862 return iter::repeat_n("..", dissimilar_part_count)
863 .chain(fqp_module.iter().map(|s| s.as_str()))
864 .collect();
865 }
866 }
867 match relative_to_fqp.len().cmp(&fqp.len()) {
868 Ordering::Less => {
869 fqp[relative_to_fqp.len()..fqp.len()].iter().copied().collect()
871 }
872 Ordering::Greater => {
873 let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
875 iter::repeat_n("..", dissimilar_part_count).collect()
876 }
877 Ordering::Equal => {
878 UrlPartsBuilder::new()
880 }
881 }
882}
883
884pub(crate) fn link_tooltip(
885 did: DefId,
886 fragment: &Option<UrlFragment>,
887 cx: &Context<'_>,
888 preferred_name: Option<&str>,
889) -> impl fmt::Display {
890 fmt::from_fn(move |f| {
891 let cache = cx.cache();
892 let Some((fqp, shortty)) = cache
893 .paths
894 .get(&did)
895 .map(|info| (info.get_preferred_path(preferred_name), info.ty))
896 .or_else(|| {
897 cache.external_paths.get(&did).map(|(fqp, shortty)| (fqp.as_slice(), *shortty))
898 })
899 else {
900 return Ok(());
901 };
902 let fqp = if shortty == ItemType::Primitive {
903 slice::from_ref(fqp.last().unwrap())
905 } else {
906 fqp
907 };
908 if let &Some(UrlFragment::Item(id)) = fragment {
909 let tcx = cx.tcx();
910 write!(f, "{} ", tcx.def_descr(id))?;
911 for component in fqp {
912 write!(f, "{component}::")?;
913 }
914 if shortty == ItemType::Enum && tcx.def_kind(id) == DefKind::Field {
915 write!(f, "{}::", tcx.item_name(tcx.parent(id)))?;
916 }
917 write!(f, "{}", tcx.item_name(id))?;
918 } else if !fqp.is_empty() {
919 write!(f, "{shortty} ")?;
920 write!(f, "{}", join_path_syms(fqp))?;
921 }
922 Ok(())
923 })
924}
925
926fn resolved_path(
928 w: &mut fmt::Formatter<'_>,
929 did: DefId,
930 path: &clean::Path,
931 print_all: bool,
932 use_absolute: bool,
933 cx: &Context<'_>,
934) -> fmt::Result {
935 let last = path.segments.last().unwrap();
936
937 if print_all {
938 for seg in &path.segments[..path.segments.len() - 1] {
939 write!(w, "{}::", if seg.name == kw::PathRoot { "" } else { seg.name.as_str() })?;
940 }
941 }
942 if w.alternate() {
943 write!(w, "{}{:#}", last.name, print_generic_args(&last.args, cx))?;
944 } else {
945 let path = fmt::from_fn(|f| {
946 if use_absolute {
947 if let Ok(HrefInfo { rust_path, .. }) = href(did, cx) {
948 write!(
949 f,
950 "{path}::{anchor}",
951 path = join_path_syms(&rust_path[..rust_path.len() - 1]),
952 anchor = print_anchor(did, *rust_path.last().unwrap(), cx)
953 )
954 } else {
955 write!(f, "{}", last.name)
956 }
957 } else {
958 write!(f, "{}", print_anchor(did, last.name, cx))
959 }
960 });
961 write!(w, "{path}{args}", args = print_generic_args(&last.args, cx))?;
962 }
963 Ok(())
964}
965
966fn primitive_link(
967 f: &mut fmt::Formatter<'_>,
968 prim: clean::PrimitiveType,
969 name: fmt::Arguments<'_>,
970 cx: &Context<'_>,
971) -> fmt::Result {
972 primitive_link_fragment(f, prim, name, "", cx)
973}
974
975fn primitive_link_fragment(
976 f: &mut fmt::Formatter<'_>,
977 prim: clean::PrimitiveType,
978 name: fmt::Arguments<'_>,
979 fragment: &str,
980 cx: &Context<'_>,
981) -> fmt::Result {
982 let m = &cx.cache();
983 let mut needs_termination = false;
984 if !f.alternate() {
985 match m.primitive_locations.get(&prim) {
986 Some(&def_id) if def_id.is_local() => {
987 let len = cx.current.len();
988 let path = fmt::from_fn(|f| {
989 if len == 0 {
990 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
991 write!(f, "{cname_sym}/")?;
992 } else {
993 for _ in 0..(len - 1) {
994 f.write_str("../")?;
995 }
996 }
997 Ok(())
998 });
999 write!(
1000 f,
1001 "<a class=\"primitive\" href=\"{path}primitive.{}.html{fragment}\">",
1002 prim.as_sym()
1003 )?;
1004 needs_termination = true;
1005 }
1006 Some(&def_id) => {
1007 let loc = match m.extern_locations[&def_id.krate] {
1008 ExternalLocation::Remote { ref url, is_absolute } => {
1009 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
1010 let mut builder = remote_url_prefix(url, is_absolute, cx.current.len());
1011 builder.push(cname_sym.as_str());
1012 Some(builder)
1013 }
1014 ExternalLocation::Local => {
1015 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
1016 Some(if cx.current.first() == Some(&cname_sym) {
1017 iter::repeat_n("..", cx.current.len() - 1).collect()
1018 } else {
1019 iter::repeat_n("..", cx.current.len())
1020 .chain(iter::once(cname_sym.as_str()))
1021 .collect()
1022 })
1023 }
1024 ExternalLocation::Unknown => None,
1025 };
1026 if let Some(mut loc) = loc {
1027 loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
1028 write!(f, "<a class=\"primitive\" href=\"{}{fragment}\">", loc.finish())?;
1029 needs_termination = true;
1030 }
1031 }
1032 None => {}
1033 }
1034 }
1035 Display::fmt(&name, f)?;
1036 if needs_termination {
1037 write!(f, "</a>")?;
1038 }
1039 Ok(())
1040}
1041
1042fn print_tybounds(
1043 bounds: &[clean::PolyTrait],
1044 lt: &Option<clean::Lifetime>,
1045 cx: &Context<'_>,
1046) -> impl Display {
1047 fmt::from_fn(move |f| {
1048 bounds.iter().map(|bound| print_poly_trait(bound, cx)).joined(" + ", f)?;
1049 if let Some(lt) = lt {
1050 write!(f, " + {}", print_lifetime(lt))?;
1053 }
1054 Ok(())
1055 })
1056}
1057
1058fn print_higher_ranked_params_with_space(
1059 params: &[clean::GenericParamDef],
1060 cx: &Context<'_>,
1061 keyword: &'static str,
1062) -> impl Display {
1063 fmt::from_fn(move |f| {
1064 if !params.is_empty() {
1065 f.write_str(keyword)?;
1066 Wrapped::with_angle_brackets()
1067 .wrap_fn(|f| {
1068 params.iter().map(|lt| print_generic_param_def(lt, cx)).joined(", ", f)
1069 })
1070 .fmt(f)?;
1071 f.write_char(' ')?;
1072 }
1073 Ok(())
1074 })
1075}
1076
1077pub(crate) fn fragment(did: DefId, tcx: TyCtxt<'_>) -> impl Display {
1078 fmt::from_fn(move |f| {
1079 let def_kind = tcx.def_kind(did);
1080 match def_kind {
1081 DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => {
1082 let item_type = ItemType::from_def_id(did, tcx);
1083 write!(f, "#{}.{}", item_type.as_str(), tcx.item_name(did))
1084 }
1085 DefKind::Field => {
1086 let parent_def_id = tcx.parent(did);
1087 f.write_char('#')?;
1088 if tcx.def_kind(parent_def_id) == DefKind::Variant {
1089 write!(f, "variant.{}.field", tcx.item_name(parent_def_id).as_str())?;
1090 } else {
1091 f.write_str("structfield")?;
1092 };
1093 write!(f, ".{}", tcx.item_name(did))
1094 }
1095 _ => Ok(()),
1096 }
1097 })
1098}
1099
1100pub(crate) fn print_anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display {
1101 fmt::from_fn(move |f| {
1102 if let Ok(HrefInfo { url, kind, rust_path }) = href(did, cx) {
1103 write!(
1104 f,
1105 r#"<a class="{kind}" href="{url}{anchor}" title="{kind} {path}">{text}</a>"#,
1106 anchor = fragment(did, cx.tcx()),
1107 path = join_path_syms(rust_path),
1108 text = EscapeBodyText(text.as_str()),
1109 )
1110 } else {
1111 f.write_str(text.as_str())
1112 }
1113 })
1114}
1115
1116fn fmt_type(
1117 t: &clean::Type,
1118 f: &mut fmt::Formatter<'_>,
1119 use_absolute: bool,
1120 cx: &Context<'_>,
1121) -> fmt::Result {
1122 trace!("fmt_type(t = {t:?})");
1123
1124 match t {
1125 clean::Generic(name) => f.write_str(name.as_str()),
1126 clean::SelfTy => f.write_str("Self"),
1127 clean::Type::Path { path } => {
1128 let did = path.def_id();
1130 resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx)
1131 }
1132 clean::DynTrait(bounds, lt) => {
1133 f.write_str("dyn ")?;
1134 print_tybounds(bounds, lt, cx).fmt(f)
1135 }
1136 clean::Infer => write!(f, "_"),
1137 clean::Primitive(clean::PrimitiveType::Never) => {
1138 primitive_link(f, PrimitiveType::Never, format_args!("!"), cx)
1139 }
1140 &clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx),
1141 clean::BareFunction(decl) => {
1142 print_higher_ranked_params_with_space(&decl.generic_params, cx, "for").fmt(f)?;
1143 decl.safety.print_with_space().fmt(f)?;
1144 print_abi_with_space(decl.abi).fmt(f)?;
1145 if f.alternate() {
1146 f.write_str("fn")?;
1147 } else {
1148 primitive_link(f, PrimitiveType::Fn, format_args!("fn"), cx)?;
1149 }
1150 print_fn_decl(&decl.decl, cx).fmt(f)
1151 }
1152 clean::UnsafeBinder(binder) => {
1153 print_higher_ranked_params_with_space(&binder.generic_params, cx, "unsafe").fmt(f)?;
1154 print_type(&binder.ty, cx).fmt(f)
1155 }
1156 clean::Tuple(typs) => match &typs[..] {
1157 &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx),
1158 [one] => {
1159 if let clean::Generic(name) = one {
1160 primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx)
1161 } else {
1162 write!(f, "(")?;
1163 print_type(one, cx).fmt(f)?;
1164 write!(f, ",)")
1165 }
1166 }
1167 many => {
1168 let generic_names: Vec<Symbol> = many
1169 .iter()
1170 .filter_map(|t| match t {
1171 clean::Generic(name) => Some(*name),
1172 _ => None,
1173 })
1174 .collect();
1175 let is_generic = generic_names.len() == many.len();
1176 if is_generic {
1177 primitive_link(
1178 f,
1179 PrimitiveType::Tuple,
1180 format_args!(
1181 "{}",
1182 Wrapped::with_parens()
1183 .wrap_fn(|f| generic_names.iter().joined(", ", f))
1184 ),
1185 cx,
1186 )
1187 } else {
1188 Wrapped::with_parens()
1189 .wrap_fn(|f| many.iter().map(|item| print_type(item, cx)).joined(", ", f))
1190 .fmt(f)
1191 }
1192 }
1193 },
1194 clean::Slice(clean::Generic(name)) => {
1195 primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx)
1196 }
1197 clean::Slice(t) => Wrapped::with_square_brackets().wrap(print_type(t, cx)).fmt(f),
1198 clean::Type::Pat(t, pat) => {
1199 fmt::Display::fmt(&print_type(t, cx), f)?;
1200 write!(f, " is {pat}")
1201 }
1202 clean::Type::FieldOf(t, field) => {
1203 write!(f, "field_of!(")?;
1204 fmt::Display::fmt(&print_type(t, cx), f)?;
1205 write!(f, ", {field})")
1206 }
1207 clean::Array(clean::Generic(name), n) if !f.alternate() => primitive_link(
1208 f,
1209 PrimitiveType::Array,
1210 format_args!("[{name}; {n}]", n = Escape(n)),
1211 cx,
1212 ),
1213 clean::Array(t, n) => Wrapped::with_square_brackets()
1214 .wrap(fmt::from_fn(|f| {
1215 print_type(t, cx).fmt(f)?;
1216 f.write_str("; ")?;
1217 if f.alternate() {
1218 f.write_str(n)
1219 } else {
1220 primitive_link(f, PrimitiveType::Array, format_args!("{n}", n = Escape(n)), cx)
1221 }
1222 }))
1223 .fmt(f),
1224 clean::RawPointer(m, t) => {
1225 let m = m.ptr_str();
1226
1227 if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() {
1228 primitive_link(
1229 f,
1230 clean::PrimitiveType::RawPointer,
1231 format_args!("*{m} {ty}", ty = WithOpts::from(f).display(print_type(t, cx))),
1232 cx,
1233 )
1234 } else {
1235 primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?;
1236 print_type(t, cx).fmt(f)
1237 }
1238 }
1239 clean::BorrowedRef { lifetime: l, mutability, type_: ty } => {
1240 let lt = fmt::from_fn(|f| match l {
1241 Some(l) => write!(f, "{} ", print_lifetime(l)),
1242 _ => Ok(()),
1243 });
1244 let m = mutability.print_with_space();
1245 let amp = if f.alternate() { "&" } else { "&" };
1246
1247 if let clean::Generic(name) = **ty {
1248 return primitive_link(
1249 f,
1250 PrimitiveType::Reference,
1251 format_args!("{amp}{lt}{m}{name}"),
1252 cx,
1253 );
1254 }
1255
1256 write!(f, "{amp}{lt}{m}")?;
1257
1258 let needs_parens = match **ty {
1259 clean::DynTrait(ref bounds, ref trait_lt)
1260 if bounds.len() > 1 || trait_lt.is_some() =>
1261 {
1262 true
1263 }
1264 clean::ImplTrait(ref bounds) if bounds.len() > 1 => true,
1265 _ => false,
1266 };
1267 Wrapped::with_parens()
1268 .when(needs_parens)
1269 .wrap_fn(|f| fmt_type(ty, f, use_absolute, cx))
1270 .fmt(f)
1271 }
1272 clean::ImplTrait(bounds) => {
1273 f.write_str("impl ")?;
1274 print_generic_bounds(bounds, cx).fmt(f)
1275 }
1276 clean::QPath(qpath) => print_qpath_data(qpath, cx).fmt(f),
1277 }
1278}
1279
1280pub(crate) fn print_type(type_: &clean::Type, cx: &Context<'_>) -> impl Display {
1281 fmt::from_fn(move |f| fmt_type(type_, f, false, cx))
1282}
1283
1284pub(crate) fn print_path(path: &clean::Path, cx: &Context<'_>) -> impl Display {
1285 fmt::from_fn(move |f| resolved_path(f, path.def_id(), path, false, false, cx))
1286}
1287
1288fn print_qpath_data(qpath_data: &clean::QPathData, cx: &Context<'_>) -> impl Display {
1289 let clean::QPathData { ref assoc, ref self_type, should_fully_qualify, ref trait_ } =
1290 *qpath_data;
1291
1292 fmt::from_fn(move |f| {
1293 if let Some(trait_) = trait_
1297 && should_fully_qualify
1298 {
1299 let opts = WithOpts::from(f);
1300 Wrapped::with_angle_brackets()
1301 .wrap(format_args!(
1302 "{} as {}",
1303 opts.display(print_type(self_type, cx)),
1304 opts.display(print_path(trait_, cx))
1305 ))
1306 .fmt(f)?
1307 } else {
1308 print_type(self_type, cx).fmt(f)?;
1309 }
1310 f.write_str("::")?;
1311 if !f.alternate() {
1322 let parent_href = match trait_ {
1335 Some(trait_) => href(trait_.def_id(), cx).ok(),
1336 None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()),
1337 };
1338 let tcx = cx.tcx();
1339 let assoc_type_is_hidden = !cx.cache().document_hidden
1340 && trait_.as_ref().is_some_and(|trait_| {
1341 let trait_did = trait_.def_id();
1342 tcx.associated_items(trait_did)
1343 .find_by_ident_and_kind(
1344 tcx,
1345 Ident::with_dummy_span(assoc.name),
1346 ty::AssocTag::Type,
1347 trait_did,
1348 )
1349 .is_some_and(|assoc_item| tcx.is_doc_hidden(assoc_item.def_id))
1350 });
1351
1352 if let Some(HrefInfo { url, rust_path, .. }) = parent_href
1353 && !assoc_type_is_hidden
1354 {
1355 write!(
1356 f,
1357 "<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
1358 title=\"type {path}::{name}\">{name}</a>",
1359 shortty = ItemType::AssocType,
1360 name = assoc.name,
1361 path = join_path_syms(rust_path),
1362 )
1363 } else {
1364 write!(f, "{}", assoc.name)
1365 }
1366 } else {
1367 write!(f, "{}", assoc.name)
1368 }?;
1369
1370 print_generic_args(&assoc.args, cx).fmt(f)
1371 })
1372}
1373
1374pub(crate) fn print_impl(
1375 impl_: &clean::Impl,
1376 use_absolute: bool,
1377 cx: &Context<'_>,
1378) -> impl Display {
1379 fmt::from_fn(move |f| {
1380 f.write_str("impl")?;
1381 print_generics(&impl_.generics, cx).fmt(f)?;
1382 f.write_str(" ")?;
1383
1384 if let Some(ref ty) = impl_.trait_ {
1385 if impl_.is_negative_trait_impl() {
1386 f.write_char('!')?;
1387 }
1388 if impl_.kind.is_fake_variadic()
1389 && let Some(generics) = ty.generics()
1390 && let Ok(inner_type) = generics.exactly_one()
1391 {
1392 let last = ty.last();
1393 if f.alternate() {
1394 write!(f, "{last}")?;
1395 } else {
1396 write!(f, "{}", print_anchor(ty.def_id(), last, cx))?;
1397 };
1398 Wrapped::with_angle_brackets()
1399 .wrap_fn(|f| impl_.print_type(inner_type, f, use_absolute, cx))
1400 .fmt(f)?;
1401 } else {
1402 print_path(ty, cx).fmt(f)?;
1403 }
1404 f.write_str(" for ")?;
1405 }
1406
1407 if let Some(ty) = impl_.kind.as_blanket_ty() {
1408 fmt_type(ty, f, use_absolute, cx)?;
1409 } else {
1410 impl_.print_type(&impl_.for_, f, use_absolute, cx)?;
1411 }
1412
1413 print_where_clause(&impl_.generics, cx, 0, Ending::Newline).maybe_display().fmt(f)
1414 })
1415}
1416
1417impl clean::Impl {
1418 fn print_type(
1419 &self,
1420 type_: &clean::Type,
1421 f: &mut fmt::Formatter<'_>,
1422 use_absolute: bool,
1423 cx: &Context<'_>,
1424 ) -> Result<(), fmt::Error> {
1425 if let clean::Type::Tuple(types) = type_
1426 && let [clean::Type::Generic(name)] = &types[..]
1427 && (self.kind.is_fake_variadic() || self.kind.is_auto())
1428 {
1429 primitive_link_fragment(
1432 f,
1433 PrimitiveType::Tuple,
1434 format_args!("({name}₁, {name}₂, …, {name}ₙ)"),
1435 "#trait-implementations-1",
1436 cx,
1437 )?;
1438 } else if let clean::Type::Array(ty, len) = type_
1439 && let clean::Type::Generic(name) = &**ty
1440 && &len[..] == "1"
1441 && (self.kind.is_fake_variadic() || self.kind.is_auto())
1442 {
1443 primitive_link(f, PrimitiveType::Array, format_args!("[{name}; N]"), cx)?;
1444 } else if let clean::BareFunction(bare_fn) = &type_
1445 && let [clean::Parameter { type_: clean::Type::Generic(name), .. }] =
1446 &bare_fn.decl.inputs[..]
1447 && (self.kind.is_fake_variadic() || self.kind.is_auto())
1448 {
1449 print_higher_ranked_params_with_space(&bare_fn.generic_params, cx, "for").fmt(f)?;
1453 bare_fn.safety.print_with_space().fmt(f)?;
1454 print_abi_with_space(bare_fn.abi).fmt(f)?;
1455 let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" };
1456 primitive_link_fragment(
1457 f,
1458 PrimitiveType::Tuple,
1459 format_args!("fn({name}₁, {name}₂, …, {name}ₙ{ellipsis})"),
1460 "#trait-implementations-1",
1461 cx,
1462 )?;
1463 if !bare_fn.decl.output.is_unit() {
1465 write!(f, " -> ")?;
1466 fmt_type(&bare_fn.decl.output, f, use_absolute, cx)?;
1467 }
1468 } else if let clean::Type::Path { path } = type_
1469 && let Some(generics) = path.generics()
1470 && let Ok(ty) = generics.exactly_one()
1471 && self.kind.is_fake_variadic()
1472 {
1473 print_anchor(path.def_id(), path.last(), cx).fmt(f)?;
1474 Wrapped::with_angle_brackets()
1475 .wrap_fn(|f| self.print_type(ty, f, use_absolute, cx))
1476 .fmt(f)?;
1477 } else {
1478 fmt_type(type_, f, use_absolute, cx)?;
1479 }
1480 Ok(())
1481 }
1482}
1483
1484pub(crate) fn print_params(params: &[clean::Parameter], cx: &Context<'_>) -> impl Display {
1485 fmt::from_fn(move |f| {
1486 params
1487 .iter()
1488 .map(|param| {
1489 fmt::from_fn(|f| {
1490 if param.is_splat {
1491 write!(f, "…: ")?;
1492 } else if let Some(name) = param.name {
1493 write!(f, "{name}: ")?;
1494 }
1495 print_type(¶m.type_, cx).fmt(f)
1496 })
1497 })
1498 .joined(", ", f)
1499 })
1500}
1501
1502struct WriteCounter(usize);
1504
1505impl std::fmt::Write for WriteCounter {
1506 fn write_str(&mut self, s: &str) -> fmt::Result {
1507 self.0 += s.len();
1508 Ok(())
1509 }
1510}
1511
1512#[derive(Clone, Copy)]
1514struct Indent(usize);
1515
1516impl Display for Indent {
1517 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1518 for _ in 0..self.0 {
1519 f.write_char(' ')?;
1520 }
1521 Ok(())
1522 }
1523}
1524
1525fn print_parameter(parameter: &clean::Parameter, cx: &Context<'_>) -> impl fmt::Display {
1526 fmt::from_fn(move |f| {
1527 if let Some(self_ty) = parameter.to_receiver() {
1528 match self_ty {
1529 clean::SelfTy => f.write_str("self"),
1530 clean::BorrowedRef { lifetime, mutability, type_: clean::SelfTy } => {
1531 f.write_str(if f.alternate() { "&" } else { "&" })?;
1532 if let Some(lt) = lifetime {
1533 write!(f, "{lt} ", lt = print_lifetime(lt))?;
1534 }
1535 write!(f, "{mutability}self", mutability = mutability.print_with_space())
1536 }
1537 _ => {
1538 f.write_str("self: ")?;
1539 print_type(self_ty, cx).fmt(f)
1540 }
1541 }
1542 } else {
1543 if parameter.is_const {
1544 write!(f, "const ")?;
1545 }
1546 if parameter.is_splat {
1547 write!(f, "…: ")?;
1548 } else if let Some(name) = parameter.name {
1549 write!(f, "{name}: ")?;
1550 }
1551 print_type(¶meter.type_, cx).fmt(f)
1552 }
1553 })
1554}
1555
1556fn print_fn_decl(fn_decl: &clean::FnDecl, cx: &Context<'_>) -> impl Display {
1557 fmt::from_fn(move |f| {
1558 let ellipsis = if fn_decl.c_variadic { ", ..." } else { "" };
1559 Wrapped::with_parens()
1560 .wrap_fn(|f| {
1561 print_params(&fn_decl.inputs, cx).fmt(f)?;
1562 f.write_str(ellipsis)
1563 })
1564 .fmt(f)?;
1565 fn_decl.print_output(cx).fmt(f)
1566 })
1567}
1568
1569pub(crate) fn full_print_fn_decl(
1576 fn_decl: &clean::FnDecl,
1577 header_len: usize,
1578 indent: usize,
1579 cx: &Context<'_>,
1580) -> impl Display {
1581 fmt::from_fn(move |f| {
1582 let mut counter = WriteCounter(0);
1584 write!(&mut counter, "{:#}", fmt::from_fn(|f| { fn_decl.inner_full_print(None, f, cx) }))?;
1585 let line_wrapping_indent = if header_len + counter.0 > 80 { Some(indent) } else { None };
1587 fn_decl.inner_full_print(line_wrapping_indent, f, cx)
1590 })
1591}
1592
1593impl clean::FnDecl {
1594 fn inner_full_print(
1595 &self,
1596 line_wrapping_indent: Option<usize>,
1599 f: &mut fmt::Formatter<'_>,
1600 cx: &Context<'_>,
1601 ) -> fmt::Result {
1602 Wrapped::with_parens()
1603 .wrap_fn(|f| {
1604 if !self.inputs.is_empty() {
1605 let line_wrapping_indent = line_wrapping_indent.map(|n| Indent(n + 4));
1606
1607 if let Some(indent) = line_wrapping_indent {
1608 write!(f, "\n{indent}")?;
1609 }
1610
1611 let sep = fmt::from_fn(|f| {
1612 if let Some(indent) = line_wrapping_indent {
1613 write!(f, ",\n{indent}")
1614 } else {
1615 f.write_str(", ")
1616 }
1617 });
1618
1619 self.inputs.iter().map(|param| print_parameter(param, cx)).joined(sep, f)?;
1620
1621 if line_wrapping_indent.is_some() {
1622 writeln!(f, ",")?
1623 }
1624
1625 if self.c_variadic {
1626 match line_wrapping_indent {
1627 None => write!(f, ", ...")?,
1628 Some(indent) => writeln!(f, "{indent}...")?,
1629 };
1630 }
1631 }
1632
1633 if let Some(n) = line_wrapping_indent {
1634 write!(f, "{}", Indent(n))?
1635 }
1636
1637 Ok(())
1638 })
1639 .fmt(f)?;
1640
1641 self.print_output(cx).fmt(f)
1642 }
1643
1644 fn print_output(&self, cx: &Context<'_>) -> impl Display {
1645 fmt::from_fn(move |f| {
1646 if self.output.is_unit() {
1647 return Ok(());
1648 }
1649
1650 f.write_str(if f.alternate() { " -> " } else { " -> " })?;
1651 print_type(&self.output, cx).fmt(f)
1652 })
1653 }
1654}
1655
1656pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) -> impl Display {
1657 fmt::from_fn(move |f| {
1658 let Some(vis) = item.visibility(cx.tcx()) else {
1659 return Ok(());
1660 };
1661
1662 match vis {
1663 ty::Visibility::Public => f.write_str("pub ")?,
1664 ty::Visibility::Restricted(vis_mod_id) => {
1665 let parent_module =
1669 find_nearest_parent_module(cx.tcx(), item.item_id.expect_def_id());
1670
1671 if vis_mod_id.is_crate_root() {
1672 f.write_str("pub(crate) ")?;
1673 } else if parent_module == Some(vis_mod_id) {
1674 } else if parent_module
1677 .and_then(|parent| find_nearest_parent_module(cx.tcx(), parent.to_def_id()))
1678 == Some(vis_mod_id)
1679 {
1680 f.write_str("pub(super) ")?;
1681 } else {
1682 let path = cx.tcx().def_path(vis_mod_id.to_def_id());
1683 debug!("path={path:?}");
1684 let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
1686 let anchor = print_anchor(vis_mod_id.to_def_id(), last_name, cx);
1687
1688 f.write_str("pub(in ")?;
1689 for seg in &path.data[..path.data.len() - 1] {
1690 write!(f, "{}::", seg.data.get_opt_name().unwrap())?;
1691 }
1692 write!(f, "{anchor}) ")?;
1693 }
1694 }
1695 }
1696 Ok(())
1697 })
1698}
1699
1700pub(crate) trait PrintWithSpace {
1701 fn print_with_space(&self) -> &str;
1702}
1703
1704impl PrintWithSpace for hir::Safety {
1705 fn print_with_space(&self) -> &str {
1706 self.prefix_str()
1707 }
1708}
1709
1710impl PrintWithSpace for hir::HeaderSafety {
1711 fn print_with_space(&self) -> &str {
1712 match self {
1713 hir::HeaderSafety::SafeTargetFeatures => "",
1714 hir::HeaderSafety::Normal(safety) => safety.print_with_space(),
1715 }
1716 }
1717}
1718
1719impl PrintWithSpace for hir::IsAsync {
1720 fn print_with_space(&self) -> &str {
1721 match self {
1722 hir::IsAsync::Async(_) => "async ",
1723 hir::IsAsync::NotAsync => "",
1724 }
1725 }
1726}
1727
1728impl PrintWithSpace for hir::Mutability {
1729 fn print_with_space(&self) -> &str {
1730 match self {
1731 hir::Mutability::Not => "",
1732 hir::Mutability::Mut => "mut ",
1733 }
1734 }
1735}
1736
1737pub(crate) fn print_constness_with_space(
1738 c: &hir::Constness,
1739 overall_stab: Option<StableSince>,
1740 const_stab: Option<ConstStability>,
1741) -> &'static str {
1742 match *c {
1743 hir::Constness::Const { always } => match (overall_stab, const_stab) {
1744 (_, Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }))
1746 | (_, None)
1748 | (None, Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => {
1750 if always {
1751 ""
1754 } else {
1755 "const "
1756 }
1757 }
1758 (Some(_), Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => "",
1760 },
1761 hir::Constness::NotConst => "",
1763 }
1764}
1765
1766pub(crate) fn print_import(import: &clean::Import, cx: &Context<'_>) -> impl Display {
1767 fmt::from_fn(move |f| match import.kind {
1768 clean::ImportKind::Simple(name) => {
1769 if name == import.source.path.last() {
1770 write!(f, "use {};", print_import_source(&import.source, cx))
1771 } else {
1772 write!(
1773 f,
1774 "use {source} as {name};",
1775 source = print_import_source(&import.source, cx)
1776 )
1777 }
1778 }
1779 clean::ImportKind::Glob => {
1780 if import.source.path.segments.is_empty() {
1781 write!(f, "use *;")
1782 } else {
1783 write!(f, "use {}::*;", print_import_source(&import.source, cx))
1784 }
1785 }
1786 })
1787}
1788
1789fn print_import_source(import_source: &clean::ImportSource, cx: &Context<'_>) -> impl Display {
1790 fmt::from_fn(move |f| match import_source.did {
1791 Some(did) => resolved_path(f, did, &import_source.path, true, false, cx),
1792 _ => {
1793 for seg in &import_source.path.segments[..import_source.path.segments.len() - 1] {
1794 write!(f, "{}::", seg.name)?;
1795 }
1796 let name = import_source.path.last();
1797 if let hir::def::Res::PrimTy(p) = import_source.path.res {
1798 primitive_link(f, PrimitiveType::from(p), format_args!("{name}"), cx)?;
1799 } else {
1800 f.write_str(name.as_str())?;
1801 }
1802 Ok(())
1803 }
1804 })
1805}
1806
1807fn print_assoc_item_constraint(
1808 assoc_item_constraint: &clean::AssocItemConstraint,
1809 cx: &Context<'_>,
1810) -> impl Display {
1811 fmt::from_fn(move |f| {
1812 f.write_str(assoc_item_constraint.assoc.name.as_str())?;
1813 print_generic_args(&assoc_item_constraint.assoc.args, cx).fmt(f)?;
1814 match assoc_item_constraint.kind {
1815 clean::AssocItemConstraintKind::Equality { ref term } => {
1816 f.write_str(" = ")?;
1817 print_term(term, cx).fmt(f)?;
1818 }
1819 clean::AssocItemConstraintKind::Bound { ref bounds } => {
1820 if !bounds.is_empty() {
1821 f.write_str(": ")?;
1822 print_generic_bounds(bounds, cx).fmt(f)?;
1823 }
1824 }
1825 }
1826 Ok(())
1827 })
1828}
1829
1830pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display {
1831 fmt::from_fn(move |f| {
1832 let quot = if f.alternate() { "\"" } else { """ };
1833 match abi {
1834 ExternAbi::Rust => Ok(()),
1835 abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
1836 }
1837 })
1838}
1839
1840fn print_generic_arg(generic_arg: &clean::GenericArg, cx: &Context<'_>) -> impl Display {
1841 fmt::from_fn(move |f| match generic_arg {
1842 clean::GenericArg::Lifetime(lt) => f.write_str(print_lifetime(lt)),
1843 clean::GenericArg::Type(ty) => print_type(ty, cx).fmt(f),
1844 clean::GenericArg::Const(ct) => print_constant_kind(ct, cx.tcx()).fmt(f),
1845 clean::GenericArg::Infer => f.write_char('_'),
1846 })
1847}
1848
1849fn print_term(term: &clean::Term, cx: &Context<'_>) -> impl Display {
1850 fmt::from_fn(move |f| match term {
1851 clean::Term::Type(ty) => print_type(ty, cx).fmt(f),
1852 clean::Term::Constant(ct) => print_constant_kind(ct, cx.tcx()).fmt(f),
1853 })
1854}