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_data_structures::fx::FxHashSet;
17use rustc_hir as hir;
18use rustc_hir::def::{DefKind, MacroKinds};
19use rustc_hir::def_id::{DefId, LOCAL_CRATE};
20use rustc_hir::{ConstStability, StabilityLevel, StableSince};
21use rustc_metadata::creader::CStore;
22use rustc_middle::ty::{self, TyCtxt, TypingMode};
23use rustc_span::symbol::kw;
24use rustc_span::{Ident, Symbol};
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)]
312pub(crate) enum HrefError {
313 DocumentationNotBuilt,
332 Private,
334 NotInExternalCache,
336 UnnamableItem,
338}
339
340pub(crate) struct HrefInfo {
342 pub(crate) url: String,
344 pub(crate) kind: ItemType,
346 pub(crate) rust_path: Vec<Symbol>,
348}
349
350fn generate_macro_def_id_path(
353 def_id: DefId,
354 cx: &Context<'_>,
355 root_path: Option<&str>,
356) -> Result<HrefInfo, HrefError> {
357 let tcx = cx.tcx();
358 let crate_name = tcx.crate_name(def_id.krate);
359 let cache = cx.cache();
360
361 let cstore = CStore::from_tcx(tcx);
362 if !cstore.has_crate_data(def_id.krate) {
364 debug!("No data for crate {crate_name}");
365 return Err(HrefError::NotInExternalCache);
366 }
367 let DefKind::Macro(kinds) = tcx.def_kind(def_id) else {
368 unreachable!();
369 };
370 let item_type = if kinds == MacroKinds::DERIVE {
371 ItemType::ProcDerive
372 } else if kinds == MacroKinds::ATTR {
373 ItemType::ProcAttribute
374 } else {
375 ItemType::Macro
376 };
377 let path = clean::inline::get_item_path(tcx, def_id, item_type);
378 let [module_path @ .., last] = path.as_slice() else {
381 debug!("macro path is empty!");
382 return Err(HrefError::NotInExternalCache);
383 };
384 if module_path.is_empty() {
385 debug!("macro path too short: missing crate prefix (got 1 element, need at least 2)");
386 return Err(HrefError::NotInExternalCache);
387 }
388
389 let url = match cache.extern_locations[&def_id.krate] {
390 ExternalLocation::Remote { ref url, is_absolute } => {
391 let mut prefix = remote_url_prefix(url, is_absolute, cx.current.len());
392 prefix.extend(module_path.iter().copied());
393 prefix.push_fmt(format_args!("{}.{last}.html", item_type.as_str()));
394 prefix.finish()
395 }
396 ExternalLocation::Local => {
397 format!(
399 "{root_path}{path}/{item_type}.{last}.html",
400 root_path = root_path.unwrap_or(""),
401 path = fmt::from_fn(|f| module_path.iter().joined("/", f)),
402 item_type = item_type.as_str(),
403 )
404 }
405 ExternalLocation::Unknown => {
406 debug!("crate {crate_name} not in cache when linkifying macros");
407 return Err(HrefError::NotInExternalCache);
408 }
409 };
410 Ok(HrefInfo { url, kind: item_type, rust_path: path })
411}
412
413fn generate_item_def_id_path(
414 mut def_id: DefId,
415 original_def_id: DefId,
416 cx: &Context<'_>,
417 root_path: Option<&str>,
418) -> Result<HrefInfo, HrefError> {
419 use rustc_middle::traits::ObligationCause;
420 use rustc_trait_selection::infer::TyCtxtInferExt;
421 use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
422
423 let tcx = cx.tcx();
424 let crate_name = tcx.crate_name(def_id.krate);
425 let mut prim = None;
426
427 if def_id != original_def_id && matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) {
430 let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
431 let ty = tcx.type_of(def_id);
432 let ty = infcx
433 .at(&ObligationCause::dummy(), tcx.param_env(def_id))
434 .query_normalize(ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip()))
435 .map(|resolved| infcx.resolve_vars_if_possible(resolved.value).skip_binder())
436 .unwrap_or(ty.skip_binder());
437 if let Some(new_def_id) = ty.ty_adt_def().map(|adt| adt.did()) {
438 def_id = new_def_id;
439 } else {
440 prim = PrimitiveType::from_ty(ty);
441 }
442 }
443
444 let mut fqp = vec![crate_name];
445 let shortty = if let Some(prim) = prim {
446 fqp.push(prim.as_sym());
447 ItemType::Primitive
448 } else {
449 fqp.append(&mut clean::inline::item_relative_path(tcx, def_id));
450 ItemType::from_def_id(def_id, tcx)
451 };
452 let module_fqp = to_module_fqp(shortty, &fqp);
453
454 let (parts, is_absolute) = url_parts(cx.cache(), def_id, module_fqp, &cx.current)?;
455 let mut url = make_href(root_path, shortty, parts, &fqp, is_absolute);
456
457 if def_id != original_def_id {
458 let kind = ItemType::from_def_id(original_def_id, tcx);
459 url = format!("{url}#{kind}.{}", tcx.item_name(original_def_id))
460 };
461 Ok(HrefInfo { url, kind: shortty, rust_path: fqp })
462}
463
464fn is_unnamable(tcx: TyCtxt<'_>, did: DefId) -> bool {
466 let mut cur_did = did;
467 while let Some(parent) = tcx.opt_parent(cur_did) {
468 match tcx.def_kind(parent) {
469 DefKind::Mod | DefKind::ForeignMod => cur_did = parent,
471 DefKind::Impl { .. } => return false,
477 _ => return true,
479 }
480 }
481 return false;
482}
483
484fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
485 if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
486}
487
488fn remote_url_prefix(url: &str, is_absolute: bool, depth: usize) -> UrlPartsBuilder {
489 let url = url.trim_end_matches('/');
490 if is_absolute {
491 UrlPartsBuilder::singleton(url)
492 } else {
493 let extra = depth.saturating_sub(1);
494 let mut b: UrlPartsBuilder = iter::repeat_n("..", extra).collect();
495 b.push(url);
496 b
497 }
498}
499
500fn url_parts(
501 cache: &Cache,
502 def_id: DefId,
503 module_fqp: &[Symbol],
504 relative_to: &[Symbol],
505) -> Result<(UrlPartsBuilder, bool), HrefError> {
506 match cache.extern_locations[&def_id.krate] {
507 ExternalLocation::Remote { ref url, is_absolute } => {
508 let mut builder = remote_url_prefix(url, is_absolute, relative_to.len());
509 builder.extend(module_fqp.iter().copied());
510 Ok((builder, is_absolute))
511 }
512 ExternalLocation::Local => Ok((href_relative_parts(module_fqp, relative_to), false)),
513 ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt),
514 }
515}
516
517fn make_href(
518 root_path: Option<&str>,
519 shortty: ItemType,
520 mut url_parts: UrlPartsBuilder,
521 fqp: &[Symbol],
522 is_absolute: bool,
523) -> String {
524 if !is_absolute && let Some(root_path) = root_path {
526 let root = root_path.trim_end_matches('/');
527 url_parts.push_front(root);
528 }
529 debug!(?url_parts);
530 match shortty {
531 ItemType::Module => {
532 url_parts.push("index.html");
533 }
534 _ => {
535 let last = fqp.last().unwrap();
536 url_parts.push_fmt(format_args!("{shortty}.{last}.html"));
537 }
538 }
539 url_parts.finish()
540}
541
542pub(crate) fn href_with_root_path(
543 original_did: DefId,
544 cx: &Context<'_>,
545 root_path: Option<&str>,
546) -> Result<HrefInfo, HrefError> {
547 let tcx = cx.tcx();
548 let def_kind = tcx.def_kind(original_did);
549 let did = match def_kind {
550 DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::Variant => {
551 tcx.parent(original_did)
553 }
554 DefKind::Ctor(..) => return href_with_root_path(tcx.parent(original_did), cx, root_path),
557 DefKind::ExternCrate => {
558 if let Some(local_did) = original_did.as_local() {
560 tcx.extern_mod_stmt_cnum(local_did).unwrap_or(LOCAL_CRATE).as_def_id()
561 } else {
562 original_did
563 }
564 }
565 _ => original_did,
566 };
567 if is_unnamable(cx.tcx(), did) {
568 return Err(HrefError::UnnamableItem);
569 }
570 let cache = cx.cache();
571 let relative_to = &cx.current;
572
573 if !original_did.is_local() {
574 if root_path.is_some() {
577 if tcx.is_doc_hidden(original_did) {
578 return Err(HrefError::Private);
579 }
580 } else if !cache.effective_visibilities.is_directly_public(tcx, did)
581 && !cache.document_private
582 && !cache.primitive_locations.values().any(|&id| id == did)
583 {
584 return Err(HrefError::Private);
585 }
586 }
587
588 let (fqp, shortty, url_parts, is_absolute) = match cache.paths.get(&did) {
589 Some(&(ref fqp, shortty)) => (
590 fqp,
591 shortty,
592 {
593 let module_fqp = to_module_fqp(shortty, fqp.as_slice());
594 debug!(?fqp, ?shortty, ?module_fqp);
595 href_relative_parts(module_fqp, relative_to)
596 },
597 false,
598 ),
599 None => {
600 let def_id_to_get = if root_path.is_some() { original_did } else { did };
604 if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&def_id_to_get) {
605 let module_fqp = to_module_fqp(shortty, fqp);
606 let (parts, is_absolute) = url_parts(cache, did, module_fqp, relative_to)?;
607 (fqp, shortty, parts, is_absolute)
608 } else if matches!(def_kind, DefKind::Macro(_)) {
609 return generate_macro_def_id_path(did, cx, root_path);
610 } else if did.is_local() {
611 return Err(HrefError::Private);
612 } else {
613 return generate_item_def_id_path(did, original_did, cx, root_path);
614 }
615 }
616 };
617 Ok(HrefInfo {
618 url: make_href(root_path, shortty, url_parts, fqp, is_absolute),
619 kind: shortty,
620 rust_path: fqp.clone(),
621 })
622}
623
624pub(crate) fn href(did: DefId, cx: &Context<'_>) -> Result<HrefInfo, HrefError> {
625 href_with_root_path(did, cx, None)
626}
627
628pub(crate) fn href_relative_parts(fqp: &[Symbol], relative_to_fqp: &[Symbol]) -> UrlPartsBuilder {
632 for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
633 if f != r {
635 let dissimilar_part_count = relative_to_fqp.len() - i;
636 let fqp_module = &fqp[i..];
637 return iter::repeat_n("..", dissimilar_part_count)
638 .chain(fqp_module.iter().map(|s| s.as_str()))
639 .collect();
640 }
641 }
642 match relative_to_fqp.len().cmp(&fqp.len()) {
643 Ordering::Less => {
644 fqp[relative_to_fqp.len()..fqp.len()].iter().copied().collect()
646 }
647 Ordering::Greater => {
648 let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
650 iter::repeat_n("..", dissimilar_part_count).collect()
651 }
652 Ordering::Equal => {
653 UrlPartsBuilder::new()
655 }
656 }
657}
658
659pub(crate) fn link_tooltip(
660 did: DefId,
661 fragment: &Option<UrlFragment>,
662 cx: &Context<'_>,
663) -> impl fmt::Display {
664 fmt::from_fn(move |f| {
665 let cache = cx.cache();
666 let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did))
667 else {
668 return Ok(());
669 };
670 let fqp = if *shortty == ItemType::Primitive {
671 slice::from_ref(fqp.last().unwrap())
673 } else {
674 fqp
675 };
676 if let &Some(UrlFragment::Item(id)) = fragment {
677 write!(f, "{} ", cx.tcx().def_descr(id))?;
678 for component in fqp {
679 write!(f, "{component}::")?;
680 }
681 write!(f, "{}", cx.tcx().item_name(id))?;
682 } else if !fqp.is_empty() {
683 write!(f, "{shortty} ")?;
684 write!(f, "{}", join_path_syms(fqp))?;
685 }
686 Ok(())
687 })
688}
689
690fn resolved_path(
692 w: &mut fmt::Formatter<'_>,
693 did: DefId,
694 path: &clean::Path,
695 print_all: bool,
696 use_absolute: bool,
697 cx: &Context<'_>,
698) -> fmt::Result {
699 let last = path.segments.last().unwrap();
700
701 if print_all {
702 for seg in &path.segments[..path.segments.len() - 1] {
703 write!(w, "{}::", if seg.name == kw::PathRoot { "" } else { seg.name.as_str() })?;
704 }
705 }
706 if w.alternate() {
707 write!(w, "{}{:#}", last.name, print_generic_args(&last.args, cx))?;
708 } else {
709 let path = fmt::from_fn(|f| {
710 if use_absolute {
711 if let Ok(HrefInfo { rust_path, .. }) = href(did, cx) {
712 write!(
713 f,
714 "{path}::{anchor}",
715 path = join_path_syms(&rust_path[..rust_path.len() - 1]),
716 anchor = print_anchor(did, *rust_path.last().unwrap(), cx)
717 )
718 } else {
719 write!(f, "{}", last.name)
720 }
721 } else {
722 write!(f, "{}", print_anchor(did, last.name, cx))
723 }
724 });
725 write!(w, "{path}{args}", args = print_generic_args(&last.args, cx))?;
726 }
727 Ok(())
728}
729
730fn primitive_link(
731 f: &mut fmt::Formatter<'_>,
732 prim: clean::PrimitiveType,
733 name: fmt::Arguments<'_>,
734 cx: &Context<'_>,
735) -> fmt::Result {
736 primitive_link_fragment(f, prim, name, "", cx)
737}
738
739fn primitive_link_fragment(
740 f: &mut fmt::Formatter<'_>,
741 prim: clean::PrimitiveType,
742 name: fmt::Arguments<'_>,
743 fragment: &str,
744 cx: &Context<'_>,
745) -> fmt::Result {
746 let m = &cx.cache();
747 let mut needs_termination = false;
748 if !f.alternate() {
749 match m.primitive_locations.get(&prim) {
750 Some(&def_id) if def_id.is_local() => {
751 let len = cx.current.len();
752 let path = fmt::from_fn(|f| {
753 if len == 0 {
754 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
755 write!(f, "{cname_sym}/")?;
756 } else {
757 for _ in 0..(len - 1) {
758 f.write_str("../")?;
759 }
760 }
761 Ok(())
762 });
763 write!(
764 f,
765 "<a class=\"primitive\" href=\"{path}primitive.{}.html{fragment}\">",
766 prim.as_sym()
767 )?;
768 needs_termination = true;
769 }
770 Some(&def_id) => {
771 let loc = match m.extern_locations[&def_id.krate] {
772 ExternalLocation::Remote { ref url, is_absolute } => {
773 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
774 let mut builder = remote_url_prefix(url, is_absolute, cx.current.len());
775 builder.push(cname_sym.as_str());
776 Some(builder)
777 }
778 ExternalLocation::Local => {
779 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
780 Some(if cx.current.first() == Some(&cname_sym) {
781 iter::repeat_n("..", cx.current.len() - 1).collect()
782 } else {
783 iter::repeat_n("..", cx.current.len())
784 .chain(iter::once(cname_sym.as_str()))
785 .collect()
786 })
787 }
788 ExternalLocation::Unknown => None,
789 };
790 if let Some(mut loc) = loc {
791 loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
792 write!(f, "<a class=\"primitive\" href=\"{}{fragment}\">", loc.finish())?;
793 needs_termination = true;
794 }
795 }
796 None => {}
797 }
798 }
799 Display::fmt(&name, f)?;
800 if needs_termination {
801 write!(f, "</a>")?;
802 }
803 Ok(())
804}
805
806fn print_tybounds(
807 bounds: &[clean::PolyTrait],
808 lt: &Option<clean::Lifetime>,
809 cx: &Context<'_>,
810) -> impl Display {
811 fmt::from_fn(move |f| {
812 bounds.iter().map(|bound| print_poly_trait(bound, cx)).joined(" + ", f)?;
813 if let Some(lt) = lt {
814 write!(f, " + {}", print_lifetime(lt))?;
817 }
818 Ok(())
819 })
820}
821
822fn print_higher_ranked_params_with_space(
823 params: &[clean::GenericParamDef],
824 cx: &Context<'_>,
825 keyword: &'static str,
826) -> impl Display {
827 fmt::from_fn(move |f| {
828 if !params.is_empty() {
829 f.write_str(keyword)?;
830 Wrapped::with_angle_brackets()
831 .wrap_fn(|f| {
832 params.iter().map(|lt| print_generic_param_def(lt, cx)).joined(", ", f)
833 })
834 .fmt(f)?;
835 f.write_char(' ')?;
836 }
837 Ok(())
838 })
839}
840
841pub(crate) fn fragment(did: DefId, tcx: TyCtxt<'_>) -> impl Display {
842 fmt::from_fn(move |f| {
843 let def_kind = tcx.def_kind(did);
844 match def_kind {
845 DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::Variant => {
846 let item_type = ItemType::from_def_id(did, tcx);
847 write!(f, "#{}.{}", item_type.as_str(), tcx.item_name(did))
848 }
849 DefKind::Field => {
850 let parent_def_id = tcx.parent(did);
851 f.write_char('#')?;
852 if tcx.def_kind(parent_def_id) == DefKind::Variant {
853 write!(f, "variant.{}.field", tcx.item_name(parent_def_id).as_str())?;
854 } else {
855 f.write_str("structfield")?;
856 };
857 write!(f, ".{}", tcx.item_name(did))
858 }
859 _ => Ok(()),
860 }
861 })
862}
863
864pub(crate) fn print_anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display {
865 fmt::from_fn(move |f| {
866 if let Ok(HrefInfo { url, kind, rust_path }) = href(did, cx) {
867 write!(
868 f,
869 r#"<a class="{kind}" href="{url}{anchor}" title="{kind} {path}">{text}</a>"#,
870 anchor = fragment(did, cx.tcx()),
871 path = join_path_syms(rust_path),
872 text = EscapeBodyText(text.as_str()),
873 )
874 } else {
875 f.write_str(text.as_str())
876 }
877 })
878}
879
880fn fmt_type(
881 t: &clean::Type,
882 f: &mut fmt::Formatter<'_>,
883 use_absolute: bool,
884 cx: &Context<'_>,
885) -> fmt::Result {
886 trace!("fmt_type(t = {t:?})");
887
888 match t {
889 clean::Generic(name) => f.write_str(name.as_str()),
890 clean::SelfTy => f.write_str("Self"),
891 clean::Type::Path { path } => {
892 let did = path.def_id();
894 resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx)
895 }
896 clean::DynTrait(bounds, lt) => {
897 f.write_str("dyn ")?;
898 print_tybounds(bounds, lt, cx).fmt(f)
899 }
900 clean::Infer => write!(f, "_"),
901 clean::Primitive(clean::PrimitiveType::Never) => {
902 primitive_link(f, PrimitiveType::Never, format_args!("!"), cx)
903 }
904 &clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx),
905 clean::BareFunction(decl) => {
906 print_higher_ranked_params_with_space(&decl.generic_params, cx, "for").fmt(f)?;
907 decl.safety.print_with_space().fmt(f)?;
908 print_abi_with_space(decl.abi).fmt(f)?;
909 if f.alternate() {
910 f.write_str("fn")?;
911 } else {
912 primitive_link(f, PrimitiveType::Fn, format_args!("fn"), cx)?;
913 }
914 print_fn_decl(&decl.decl, cx).fmt(f)
915 }
916 clean::UnsafeBinder(binder) => {
917 print_higher_ranked_params_with_space(&binder.generic_params, cx, "unsafe").fmt(f)?;
918 print_type(&binder.ty, cx).fmt(f)
919 }
920 clean::Tuple(typs) => match &typs[..] {
921 &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx),
922 [one] => {
923 if let clean::Generic(name) = one {
924 primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx)
925 } else {
926 write!(f, "(")?;
927 print_type(one, cx).fmt(f)?;
928 write!(f, ",)")
929 }
930 }
931 many => {
932 let generic_names: Vec<Symbol> = many
933 .iter()
934 .filter_map(|t| match t {
935 clean::Generic(name) => Some(*name),
936 _ => None,
937 })
938 .collect();
939 let is_generic = generic_names.len() == many.len();
940 if is_generic {
941 primitive_link(
942 f,
943 PrimitiveType::Tuple,
944 format_args!(
945 "{}",
946 Wrapped::with_parens()
947 .wrap_fn(|f| generic_names.iter().joined(", ", f))
948 ),
949 cx,
950 )
951 } else {
952 Wrapped::with_parens()
953 .wrap_fn(|f| many.iter().map(|item| print_type(item, cx)).joined(", ", f))
954 .fmt(f)
955 }
956 }
957 },
958 clean::Slice(clean::Generic(name)) => {
959 primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx)
960 }
961 clean::Slice(t) => Wrapped::with_square_brackets().wrap(print_type(t, cx)).fmt(f),
962 clean::Type::Pat(t, pat) => {
963 fmt::Display::fmt(&print_type(t, cx), f)?;
964 write!(f, " is {pat}")
965 }
966 clean::Type::FieldOf(t, field) => {
967 write!(f, "field_of!(")?;
968 fmt::Display::fmt(&print_type(t, cx), f)?;
969 write!(f, ", {field})")
970 }
971 clean::Array(clean::Generic(name), n) if !f.alternate() => primitive_link(
972 f,
973 PrimitiveType::Array,
974 format_args!("[{name}; {n}]", n = Escape(n)),
975 cx,
976 ),
977 clean::Array(t, n) => Wrapped::with_square_brackets()
978 .wrap(fmt::from_fn(|f| {
979 print_type(t, cx).fmt(f)?;
980 f.write_str("; ")?;
981 if f.alternate() {
982 f.write_str(n)
983 } else {
984 primitive_link(f, PrimitiveType::Array, format_args!("{n}", n = Escape(n)), cx)
985 }
986 }))
987 .fmt(f),
988 clean::RawPointer(m, t) => {
989 let m = m.ptr_str();
990
991 if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() {
992 primitive_link(
993 f,
994 clean::PrimitiveType::RawPointer,
995 format_args!("*{m} {ty}", ty = WithOpts::from(f).display(print_type(t, cx))),
996 cx,
997 )
998 } else {
999 primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?;
1000 print_type(t, cx).fmt(f)
1001 }
1002 }
1003 clean::BorrowedRef { lifetime: l, mutability, type_: ty } => {
1004 let lt = fmt::from_fn(|f| match l {
1005 Some(l) => write!(f, "{} ", print_lifetime(l)),
1006 _ => Ok(()),
1007 });
1008 let m = mutability.print_with_space();
1009 let amp = if f.alternate() { "&" } else { "&" };
1010
1011 if let clean::Generic(name) = **ty {
1012 return primitive_link(
1013 f,
1014 PrimitiveType::Reference,
1015 format_args!("{amp}{lt}{m}{name}"),
1016 cx,
1017 );
1018 }
1019
1020 write!(f, "{amp}{lt}{m}")?;
1021
1022 let needs_parens = match **ty {
1023 clean::DynTrait(ref bounds, ref trait_lt)
1024 if bounds.len() > 1 || trait_lt.is_some() =>
1025 {
1026 true
1027 }
1028 clean::ImplTrait(ref bounds) if bounds.len() > 1 => true,
1029 _ => false,
1030 };
1031 Wrapped::with_parens()
1032 .when(needs_parens)
1033 .wrap_fn(|f| fmt_type(ty, f, use_absolute, cx))
1034 .fmt(f)
1035 }
1036 clean::ImplTrait(bounds) => {
1037 f.write_str("impl ")?;
1038 print_generic_bounds(bounds, cx).fmt(f)
1039 }
1040 clean::QPath(qpath) => print_qpath_data(qpath, cx).fmt(f),
1041 }
1042}
1043
1044pub(crate) fn print_type(type_: &clean::Type, cx: &Context<'_>) -> impl Display {
1045 fmt::from_fn(move |f| fmt_type(type_, f, false, cx))
1046}
1047
1048pub(crate) fn print_path(path: &clean::Path, cx: &Context<'_>) -> impl Display {
1049 fmt::from_fn(move |f| resolved_path(f, path.def_id(), path, false, false, cx))
1050}
1051
1052fn print_qpath_data(qpath_data: &clean::QPathData, cx: &Context<'_>) -> impl Display {
1053 let clean::QPathData { ref assoc, ref self_type, should_fully_qualify, ref trait_ } =
1054 *qpath_data;
1055
1056 fmt::from_fn(move |f| {
1057 if let Some(trait_) = trait_
1061 && should_fully_qualify
1062 {
1063 let opts = WithOpts::from(f);
1064 Wrapped::with_angle_brackets()
1065 .wrap(format_args!(
1066 "{} as {}",
1067 opts.display(print_type(self_type, cx)),
1068 opts.display(print_path(trait_, cx))
1069 ))
1070 .fmt(f)?
1071 } else {
1072 print_type(self_type, cx).fmt(f)?;
1073 }
1074 f.write_str("::")?;
1075 if !f.alternate() {
1086 let parent_href = match trait_ {
1099 Some(trait_) => href(trait_.def_id(), cx).ok(),
1100 None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()),
1101 };
1102 let tcx = cx.tcx();
1103 let assoc_type_is_hidden = !cx.cache().document_hidden
1104 && trait_.as_ref().is_some_and(|trait_| {
1105 let trait_did = trait_.def_id();
1106 tcx.associated_items(trait_did)
1107 .find_by_ident_and_kind(
1108 tcx,
1109 Ident::with_dummy_span(assoc.name),
1110 ty::AssocTag::Type,
1111 trait_did,
1112 )
1113 .is_some_and(|assoc_item| tcx.is_doc_hidden(assoc_item.def_id))
1114 });
1115
1116 if let Some(HrefInfo { url, rust_path, .. }) = parent_href
1117 && !assoc_type_is_hidden
1118 {
1119 write!(
1120 f,
1121 "<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
1122 title=\"type {path}::{name}\">{name}</a>",
1123 shortty = ItemType::AssocType,
1124 name = assoc.name,
1125 path = join_path_syms(rust_path),
1126 )
1127 } else {
1128 write!(f, "{}", assoc.name)
1129 }
1130 } else {
1131 write!(f, "{}", assoc.name)
1132 }?;
1133
1134 print_generic_args(&assoc.args, cx).fmt(f)
1135 })
1136}
1137
1138pub(crate) fn print_impl(
1139 impl_: &clean::Impl,
1140 use_absolute: bool,
1141 cx: &Context<'_>,
1142) -> impl Display {
1143 fmt::from_fn(move |f| {
1144 f.write_str("impl")?;
1145 print_generics(&impl_.generics, cx).fmt(f)?;
1146 f.write_str(" ")?;
1147
1148 if let Some(ref ty) = impl_.trait_ {
1149 if impl_.is_negative_trait_impl() {
1150 f.write_char('!')?;
1151 }
1152 if impl_.kind.is_fake_variadic()
1153 && let Some(generics) = ty.generics()
1154 && let Ok(inner_type) = generics.exactly_one()
1155 {
1156 let last = ty.last();
1157 if f.alternate() {
1158 write!(f, "{last}")?;
1159 } else {
1160 write!(f, "{}", print_anchor(ty.def_id(), last, cx))?;
1161 };
1162 Wrapped::with_angle_brackets()
1163 .wrap_fn(|f| impl_.print_type(inner_type, f, use_absolute, cx))
1164 .fmt(f)?;
1165 } else {
1166 print_path(ty, cx).fmt(f)?;
1167 }
1168 f.write_str(" for ")?;
1169 }
1170
1171 if let Some(ty) = impl_.kind.as_blanket_ty() {
1172 fmt_type(ty, f, use_absolute, cx)?;
1173 } else {
1174 impl_.print_type(&impl_.for_, f, use_absolute, cx)?;
1175 }
1176
1177 print_where_clause(&impl_.generics, cx, 0, Ending::Newline).maybe_display().fmt(f)
1178 })
1179}
1180
1181impl clean::Impl {
1182 fn print_type(
1183 &self,
1184 type_: &clean::Type,
1185 f: &mut fmt::Formatter<'_>,
1186 use_absolute: bool,
1187 cx: &Context<'_>,
1188 ) -> Result<(), fmt::Error> {
1189 if let clean::Type::Tuple(types) = type_
1190 && let [clean::Type::Generic(name)] = &types[..]
1191 && (self.kind.is_fake_variadic() || self.kind.is_auto())
1192 {
1193 primitive_link_fragment(
1196 f,
1197 PrimitiveType::Tuple,
1198 format_args!("({name}₁, {name}₂, …, {name}ₙ)"),
1199 "#trait-implementations-1",
1200 cx,
1201 )?;
1202 } else if let clean::Type::Array(ty, len) = type_
1203 && let clean::Type::Generic(name) = &**ty
1204 && &len[..] == "1"
1205 && (self.kind.is_fake_variadic() || self.kind.is_auto())
1206 {
1207 primitive_link(f, PrimitiveType::Array, format_args!("[{name}; N]"), cx)?;
1208 } else if let clean::BareFunction(bare_fn) = &type_
1209 && let [clean::Parameter { type_: clean::Type::Generic(name), .. }] =
1210 &bare_fn.decl.inputs[..]
1211 && (self.kind.is_fake_variadic() || self.kind.is_auto())
1212 {
1213 print_higher_ranked_params_with_space(&bare_fn.generic_params, cx, "for").fmt(f)?;
1217 bare_fn.safety.print_with_space().fmt(f)?;
1218 print_abi_with_space(bare_fn.abi).fmt(f)?;
1219 let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" };
1220 primitive_link_fragment(
1221 f,
1222 PrimitiveType::Tuple,
1223 format_args!("fn({name}₁, {name}₂, …, {name}ₙ{ellipsis})"),
1224 "#trait-implementations-1",
1225 cx,
1226 )?;
1227 if !bare_fn.decl.output.is_unit() {
1229 write!(f, " -> ")?;
1230 fmt_type(&bare_fn.decl.output, f, use_absolute, cx)?;
1231 }
1232 } else if let clean::Type::Path { path } = type_
1233 && let Some(generics) = path.generics()
1234 && let Ok(ty) = generics.exactly_one()
1235 && self.kind.is_fake_variadic()
1236 {
1237 print_anchor(path.def_id(), path.last(), cx).fmt(f)?;
1238 Wrapped::with_angle_brackets()
1239 .wrap_fn(|f| self.print_type(ty, f, use_absolute, cx))
1240 .fmt(f)?;
1241 } else {
1242 fmt_type(type_, f, use_absolute, cx)?;
1243 }
1244 Ok(())
1245 }
1246}
1247
1248pub(crate) fn print_params(params: &[clean::Parameter], cx: &Context<'_>) -> impl Display {
1249 fmt::from_fn(move |f| {
1250 params
1251 .iter()
1252 .map(|param| {
1253 fmt::from_fn(|f| {
1254 if let Some(name) = param.name {
1255 write!(f, "{name}: ")?;
1256 }
1257 print_type(¶m.type_, cx).fmt(f)
1258 })
1259 })
1260 .joined(", ", f)
1261 })
1262}
1263
1264struct WriteCounter(usize);
1266
1267impl std::fmt::Write for WriteCounter {
1268 fn write_str(&mut self, s: &str) -> fmt::Result {
1269 self.0 += s.len();
1270 Ok(())
1271 }
1272}
1273
1274#[derive(Clone, Copy)]
1276struct Indent(usize);
1277
1278impl Display for Indent {
1279 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1280 for _ in 0..self.0 {
1281 f.write_char(' ')?;
1282 }
1283 Ok(())
1284 }
1285}
1286
1287fn print_parameter(parameter: &clean::Parameter, cx: &Context<'_>) -> impl fmt::Display {
1288 fmt::from_fn(move |f| {
1289 if let Some(self_ty) = parameter.to_receiver() {
1290 match self_ty {
1291 clean::SelfTy => f.write_str("self"),
1292 clean::BorrowedRef { lifetime, mutability, type_: clean::SelfTy } => {
1293 f.write_str(if f.alternate() { "&" } else { "&" })?;
1294 if let Some(lt) = lifetime {
1295 write!(f, "{lt} ", lt = print_lifetime(lt))?;
1296 }
1297 write!(f, "{mutability}self", mutability = mutability.print_with_space())
1298 }
1299 _ => {
1300 f.write_str("self: ")?;
1301 print_type(self_ty, cx).fmt(f)
1302 }
1303 }
1304 } else {
1305 if parameter.is_const {
1306 write!(f, "const ")?;
1307 }
1308 if let Some(name) = parameter.name {
1309 write!(f, "{name}: ")?;
1310 }
1311 print_type(¶meter.type_, cx).fmt(f)
1312 }
1313 })
1314}
1315
1316fn print_fn_decl(fn_decl: &clean::FnDecl, cx: &Context<'_>) -> impl Display {
1317 fmt::from_fn(move |f| {
1318 let ellipsis = if fn_decl.c_variadic { ", ..." } else { "" };
1319 Wrapped::with_parens()
1320 .wrap_fn(|f| {
1321 print_params(&fn_decl.inputs, cx).fmt(f)?;
1322 f.write_str(ellipsis)
1323 })
1324 .fmt(f)?;
1325 fn_decl.print_output(cx).fmt(f)
1326 })
1327}
1328
1329pub(crate) fn full_print_fn_decl(
1336 fn_decl: &clean::FnDecl,
1337 header_len: usize,
1338 indent: usize,
1339 cx: &Context<'_>,
1340) -> impl Display {
1341 fmt::from_fn(move |f| {
1342 let mut counter = WriteCounter(0);
1344 write!(&mut counter, "{:#}", fmt::from_fn(|f| { fn_decl.inner_full_print(None, f, cx) }))?;
1345 let line_wrapping_indent = if header_len + counter.0 > 80 { Some(indent) } else { None };
1347 fn_decl.inner_full_print(line_wrapping_indent, f, cx)
1350 })
1351}
1352
1353impl clean::FnDecl {
1354 fn inner_full_print(
1355 &self,
1356 line_wrapping_indent: Option<usize>,
1359 f: &mut fmt::Formatter<'_>,
1360 cx: &Context<'_>,
1361 ) -> fmt::Result {
1362 Wrapped::with_parens()
1363 .wrap_fn(|f| {
1364 if !self.inputs.is_empty() {
1365 let line_wrapping_indent = line_wrapping_indent.map(|n| Indent(n + 4));
1366
1367 if let Some(indent) = line_wrapping_indent {
1368 write!(f, "\n{indent}")?;
1369 }
1370
1371 let sep = fmt::from_fn(|f| {
1372 if let Some(indent) = line_wrapping_indent {
1373 write!(f, ",\n{indent}")
1374 } else {
1375 f.write_str(", ")
1376 }
1377 });
1378
1379 self.inputs.iter().map(|param| print_parameter(param, cx)).joined(sep, f)?;
1380
1381 if line_wrapping_indent.is_some() {
1382 writeln!(f, ",")?
1383 }
1384
1385 if self.c_variadic {
1386 match line_wrapping_indent {
1387 None => write!(f, ", ...")?,
1388 Some(indent) => writeln!(f, "{indent}...")?,
1389 };
1390 }
1391 }
1392
1393 if let Some(n) = line_wrapping_indent {
1394 write!(f, "{}", Indent(n))?
1395 }
1396
1397 Ok(())
1398 })
1399 .fmt(f)?;
1400
1401 self.print_output(cx).fmt(f)
1402 }
1403
1404 fn print_output(&self, cx: &Context<'_>) -> impl Display {
1405 fmt::from_fn(move |f| {
1406 if self.output.is_unit() {
1407 return Ok(());
1408 }
1409
1410 f.write_str(if f.alternate() { " -> " } else { " -> " })?;
1411 print_type(&self.output, cx).fmt(f)
1412 })
1413 }
1414}
1415
1416pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) -> impl Display {
1417 fmt::from_fn(move |f| {
1418 let Some(vis) = item.visibility(cx.tcx()) else {
1419 return Ok(());
1420 };
1421
1422 match vis {
1423 ty::Visibility::Public => f.write_str("pub ")?,
1424 ty::Visibility::Restricted(vis_did) => {
1425 let parent_module =
1429 find_nearest_parent_module(cx.tcx(), item.item_id.expect_def_id());
1430
1431 if vis_did.is_crate_root() {
1432 f.write_str("pub(crate) ")?;
1433 } else if parent_module == Some(vis_did) {
1434 } else if parent_module
1437 .and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
1438 == Some(vis_did)
1439 {
1440 f.write_str("pub(super) ")?;
1441 } else {
1442 let path = cx.tcx().def_path(vis_did);
1443 debug!("path={path:?}");
1444 let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
1446 let anchor = print_anchor(vis_did, last_name, cx);
1447
1448 f.write_str("pub(in ")?;
1449 for seg in &path.data[..path.data.len() - 1] {
1450 write!(f, "{}::", seg.data.get_opt_name().unwrap())?;
1451 }
1452 write!(f, "{anchor}) ")?;
1453 }
1454 }
1455 }
1456 Ok(())
1457 })
1458}
1459
1460pub(crate) trait PrintWithSpace {
1461 fn print_with_space(&self) -> &str;
1462}
1463
1464impl PrintWithSpace for hir::Safety {
1465 fn print_with_space(&self) -> &str {
1466 self.prefix_str()
1467 }
1468}
1469
1470impl PrintWithSpace for hir::HeaderSafety {
1471 fn print_with_space(&self) -> &str {
1472 match self {
1473 hir::HeaderSafety::SafeTargetFeatures => "",
1474 hir::HeaderSafety::Normal(safety) => safety.print_with_space(),
1475 }
1476 }
1477}
1478
1479impl PrintWithSpace for hir::IsAsync {
1480 fn print_with_space(&self) -> &str {
1481 match self {
1482 hir::IsAsync::Async(_) => "async ",
1483 hir::IsAsync::NotAsync => "",
1484 }
1485 }
1486}
1487
1488impl PrintWithSpace for hir::Mutability {
1489 fn print_with_space(&self) -> &str {
1490 match self {
1491 hir::Mutability::Not => "",
1492 hir::Mutability::Mut => "mut ",
1493 }
1494 }
1495}
1496
1497pub(crate) fn print_constness_with_space(
1498 c: &hir::Constness,
1499 overall_stab: Option<StableSince>,
1500 const_stab: Option<ConstStability>,
1501) -> &'static str {
1502 match *c {
1503 hir::Constness::Const { always } => match (overall_stab, const_stab) {
1504 (_, Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }))
1506 | (_, None)
1508 | (None, Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => {
1510 if always {
1511 ""
1514 } else {
1515 "const "
1516 }
1517 }
1518 (Some(_), Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => "",
1520 },
1521 hir::Constness::NotConst => "",
1523 }
1524}
1525
1526pub(crate) fn print_import(import: &clean::Import, cx: &Context<'_>) -> impl Display {
1527 fmt::from_fn(move |f| match import.kind {
1528 clean::ImportKind::Simple(name) => {
1529 if name == import.source.path.last() {
1530 write!(f, "use {};", print_import_source(&import.source, cx))
1531 } else {
1532 write!(
1533 f,
1534 "use {source} as {name};",
1535 source = print_import_source(&import.source, cx)
1536 )
1537 }
1538 }
1539 clean::ImportKind::Glob => {
1540 if import.source.path.segments.is_empty() {
1541 write!(f, "use *;")
1542 } else {
1543 write!(f, "use {}::*;", print_import_source(&import.source, cx))
1544 }
1545 }
1546 })
1547}
1548
1549fn print_import_source(import_source: &clean::ImportSource, cx: &Context<'_>) -> impl Display {
1550 fmt::from_fn(move |f| match import_source.did {
1551 Some(did) => resolved_path(f, did, &import_source.path, true, false, cx),
1552 _ => {
1553 for seg in &import_source.path.segments[..import_source.path.segments.len() - 1] {
1554 write!(f, "{}::", seg.name)?;
1555 }
1556 let name = import_source.path.last();
1557 if let hir::def::Res::PrimTy(p) = import_source.path.res {
1558 primitive_link(f, PrimitiveType::from(p), format_args!("{name}"), cx)?;
1559 } else {
1560 f.write_str(name.as_str())?;
1561 }
1562 Ok(())
1563 }
1564 })
1565}
1566
1567fn print_assoc_item_constraint(
1568 assoc_item_constraint: &clean::AssocItemConstraint,
1569 cx: &Context<'_>,
1570) -> impl Display {
1571 fmt::from_fn(move |f| {
1572 f.write_str(assoc_item_constraint.assoc.name.as_str())?;
1573 print_generic_args(&assoc_item_constraint.assoc.args, cx).fmt(f)?;
1574 match assoc_item_constraint.kind {
1575 clean::AssocItemConstraintKind::Equality { ref term } => {
1576 f.write_str(" = ")?;
1577 print_term(term, cx).fmt(f)?;
1578 }
1579 clean::AssocItemConstraintKind::Bound { ref bounds } => {
1580 if !bounds.is_empty() {
1581 f.write_str(": ")?;
1582 print_generic_bounds(bounds, cx).fmt(f)?;
1583 }
1584 }
1585 }
1586 Ok(())
1587 })
1588}
1589
1590pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display {
1591 fmt::from_fn(move |f| {
1592 let quot = if f.alternate() { "\"" } else { """ };
1593 match abi {
1594 ExternAbi::Rust => Ok(()),
1595 abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
1596 }
1597 })
1598}
1599
1600fn print_generic_arg(generic_arg: &clean::GenericArg, cx: &Context<'_>) -> impl Display {
1601 fmt::from_fn(move |f| match generic_arg {
1602 clean::GenericArg::Lifetime(lt) => f.write_str(print_lifetime(lt)),
1603 clean::GenericArg::Type(ty) => print_type(ty, cx).fmt(f),
1604 clean::GenericArg::Const(ct) => print_constant_kind(ct, cx.tcx()).fmt(f),
1605 clean::GenericArg::Infer => f.write_char('_'),
1606 })
1607}
1608
1609fn print_term(term: &clean::Term, cx: &Context<'_>) -> impl Display {
1610 fmt::from_fn(move |f| match term {
1611 clean::Term::Type(ty) => print_type(ty, cx).fmt(f),
1612 clean::Term::Constant(ct) => print_constant_kind(ct, cx.tcx()).fmt(f),
1613 })
1614}