]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/html/format.rs
New upstream version 1.66.0+dfsg1
[rustc.git] / src / librustdoc / html / format.rs
CommitLineData
1a4d82fc
JJ
1//! HTML formatting module
2//!
85aaf69f 3//! This module contains a large number of `fmt::Display` implementations for
1a4d82fc
JJ
4//! various types in `rustdoc::clean`. These implementations all currently
5//! assume that HTML output is desired, although it may be possible to redesign
6//! them in the future to instead emit any format desired.
7
923072b8 8use std::borrow::Cow;
e1599b0c 9use std::cell::Cell;
1a4d82fc 10use std::fmt;
923072b8 11use std::iter::{self, once};
1a4d82fc 12
923072b8 13use rustc_ast as ast;
136023e0 14use rustc_attr::{ConstStability, StabilityLevel};
cdc7bbd5 15use rustc_data_structures::captures::Captures;
dfeec247
XL
16use rustc_data_structures::fx::FxHashSet;
17use rustc_hir as hir;
3c0e092e 18use rustc_hir::def::DefKind;
17df50a5 19use rustc_hir::def_id::DefId;
923072b8 20use rustc_metadata::creader::{CStore, LoadedMacro};
3c0e092e
XL
21use rustc_middle::ty;
22use rustc_middle::ty::DefIdTree;
fc512014 23use rustc_middle::ty::TyCtxt;
04454e1e 24use rustc_span::symbol::kw;
5099ac24 25use rustc_span::{sym, Symbol};
83c7162d 26use rustc_target::spec::abi::Abi;
1a4d82fc 27
923072b8
FG
28use itertools::Itertools;
29
a2a8927a
XL
30use crate::clean::{
31 self, types::ExternalLocation, utils::find_nearest_parent_module, ExternalCrate, ItemId,
32 PrimitiveType,
33};
3dfed10e 34use crate::formats::item_type::ItemType;
dfeec247 35use crate::html::escape::Escape;
cdc7bbd5 36use crate::html::render::Context;
e1599b0c 37
5099ac24 38use super::url_parts_builder::estimate_item_path_byte_length;
a2a8927a
XL
39use super::url_parts_builder::UrlPartsBuilder;
40
923072b8 41pub(crate) trait Print {
e1599b0c
XL
42 fn print(self, buffer: &mut Buffer);
43}
44
45impl<F> Print for F
dfeec247
XL
46where
47 F: FnOnce(&mut Buffer),
e1599b0c
XL
48{
49 fn print(self, buffer: &mut Buffer) {
50 (self)(buffer)
51 }
52}
53
54impl Print for String {
55 fn print(self, buffer: &mut Buffer) {
56 buffer.write_str(&self);
57 }
58}
59
60impl Print for &'_ str {
61 fn print(self, buffer: &mut Buffer) {
62 buffer.write_str(self);
63 }
64}
65
66#[derive(Debug, Clone)]
923072b8 67pub(crate) struct Buffer {
e1599b0c
XL
68 for_html: bool,
69 buffer: String,
70}
71
5099ac24
FG
72impl core::fmt::Write for Buffer {
73 #[inline]
74 fn write_str(&mut self, s: &str) -> fmt::Result {
75 self.buffer.write_str(s)
76 }
77
78 #[inline]
79 fn write_char(&mut self, c: char) -> fmt::Result {
80 self.buffer.write_char(c)
81 }
82
83 #[inline]
84 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
85 self.buffer.write_fmt(args)
86 }
87}
88
e1599b0c 89impl Buffer {
923072b8 90 pub(crate) fn empty_from(v: &Buffer) -> Buffer {
dfeec247 91 Buffer { for_html: v.for_html, buffer: String::new() }
e1599b0c
XL
92 }
93
923072b8 94 pub(crate) fn html() -> Buffer {
dfeec247 95 Buffer { for_html: true, buffer: String::new() }
e1599b0c
XL
96 }
97
923072b8 98 pub(crate) fn new() -> Buffer {
dfeec247 99 Buffer { for_html: false, buffer: String::new() }
60c5eb7d
XL
100 }
101
923072b8 102 pub(crate) fn is_empty(&self) -> bool {
3dfed10e
XL
103 self.buffer.is_empty()
104 }
105
923072b8 106 pub(crate) fn into_inner(self) -> String {
e1599b0c
XL
107 self.buffer
108 }
109
923072b8 110 pub(crate) fn insert_str(&mut self, idx: usize, s: &str) {
3dfed10e
XL
111 self.buffer.insert_str(idx, s);
112 }
113
923072b8 114 pub(crate) fn push_str(&mut self, s: &str) {
3dfed10e
XL
115 self.buffer.push_str(s);
116 }
117
923072b8 118 pub(crate) fn push_buffer(&mut self, other: Buffer) {
cdc7bbd5
XL
119 self.buffer.push_str(&other.buffer);
120 }
121
e1599b0c
XL
122 // Intended for consumption by write! and writeln! (std::fmt) but without
123 // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
124 // import).
923072b8 125 pub(crate) fn write_str(&mut self, s: &str) {
e1599b0c
XL
126 self.buffer.push_str(s);
127 }
128
129 // Intended for consumption by write! and writeln! (std::fmt) but without
130 // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
131 // import).
923072b8 132 pub(crate) fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
e1599b0c
XL
133 use fmt::Write;
134 self.buffer.write_fmt(v).unwrap();
135 }
136
923072b8 137 pub(crate) fn to_display<T: Print>(mut self, t: T) -> String {
e1599b0c
XL
138 t.print(&mut self);
139 self.into_inner()
140 }
141
923072b8 142 pub(crate) fn is_for_html(&self) -> bool {
60c5eb7d
XL
143 self.for_html
144 }
9fa01778 145
923072b8 146 pub(crate) fn reserve(&mut self, additional: usize) {
cdc7bbd5
XL
147 self.buffer.reserve(additional)
148 }
064997fb
FG
149
150 pub(crate) fn len(&self) -> usize {
151 self.buffer.len()
152 }
cc61c64b
XL
153}
154
f2b60f7d 155pub(crate) fn comma_sep<T: fmt::Display>(
5e7ed085
FG
156 items: impl Iterator<Item = T>,
157 space_after_comma: bool,
158) -> impl fmt::Display {
e1599b0c 159 display_fn(move |f| {
e74abb32 160 for (i, item) in items.enumerate() {
dfeec247 161 if i != 0 {
5e7ed085 162 write!(f, ",{}", if space_after_comma { " " } else { "" })?;
dfeec247 163 }
e74abb32 164 fmt::Display::fmt(&item, f)?;
1a4d82fc
JJ
165 }
166 Ok(())
e1599b0c 167 })
1a4d82fc
JJ
168}
169
923072b8 170pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
5869c6ff 171 bounds: &'a [clean::GenericBound],
cdc7bbd5
XL
172 cx: &'a Context<'tcx>,
173) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32 174 display_fn(move |f| {
dc9dc135 175 let mut bounds_dup = FxHashSet::default();
dc9dc135 176
a2a8927a 177 for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(b.clone())).enumerate() {
1a4d82fc 178 if i > 0 {
54a0048b 179 f.write_str(" + ")?;
1a4d82fc 180 }
cdc7bbd5 181 fmt::Display::fmt(&bound.print(cx), f)?;
1a4d82fc
JJ
182 }
183 Ok(())
e74abb32 184 })
1a4d82fc
JJ
185}
186
e74abb32 187impl clean::GenericParamDef {
923072b8 188 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
189 &'a self,
190 cx: &'a Context<'tcx>,
191 ) -> impl fmt::Display + 'a + Captures<'tcx> {
c295e0f8
XL
192 display_fn(move |f| match &self.kind {
193 clean::GenericParamDefKind::Lifetime { outlives } => {
194 write!(f, "{}", self.name)?;
195
196 if !outlives.is_empty() {
197 f.write_str(": ")?;
198 for (i, lt) in outlives.iter().enumerate() {
199 if i != 0 {
200 f.write_str(" + ")?;
201 }
202 write!(f, "{}", lt.print())?;
203 }
204 }
205
206 Ok(())
207 }
208 clean::GenericParamDefKind::Type { bounds, default, .. } => {
a2a8927a 209 f.write_str(self.name.as_str())?;
1a4d82fc 210
dfeec247
XL
211 if !bounds.is_empty() {
212 if f.alternate() {
cdc7bbd5 213 write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
dfeec247 214 } else {
cdc7bbd5 215 write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
c30ab7b3 216 }
1a4d82fc
JJ
217 }
218
dfeec247 219 if let Some(ref ty) = default {
c30ab7b3 220 if f.alternate() {
cdc7bbd5 221 write!(f, " = {:#}", ty.print(cx))?;
c30ab7b3 222 } else {
cdc7bbd5 223 write!(f, "&nbsp;=&nbsp;{}", ty.print(cx))?;
c30ab7b3 224 }
ff7c6d11 225 }
dfeec247
XL
226
227 Ok(())
228 }
c295e0f8 229 clean::GenericParamDefKind::Const { ty, default, .. } => {
dfeec247 230 if f.alternate() {
17df50a5 231 write!(f, "const {}: {:#}", self.name, ty.print(cx))?;
dfeec247 232 } else {
17df50a5 233 write!(f, "const {}:&nbsp;{}", self.name, ty.print(cx))?;
dfeec247 234 }
17df50a5
XL
235
236 if let Some(default) = default {
237 if f.alternate() {
238 write!(f, " = {:#}", default)?;
239 } else {
240 write!(f, "&nbsp;=&nbsp;{}", default)?;
241 }
242 }
243
244 Ok(())
1a4d82fc 245 }
e74abb32 246 })
ff7c6d11
XL
247 }
248}
249
e74abb32 250impl clean::Generics {
923072b8 251 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
252 &'a self,
253 cx: &'a Context<'tcx>,
254 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32 255 display_fn(move |f| {
a2a8927a
XL
256 let mut real_params =
257 self.params.iter().filter(|p| !p.is_synthetic_type_param()).peekable();
258 if real_params.peek().is_none() {
e74abb32
XL
259 return Ok(());
260 }
a2a8927a 261
e74abb32 262 if f.alternate() {
5e7ed085 263 write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx)), true))
e74abb32 264 } else {
5e7ed085 265 write!(f, "&lt;{}&gt;", comma_sep(real_params.map(|g| g.print(cx)), true))
e74abb32
XL
266 }
267 })
1a4d82fc
JJ
268 }
269}
270
064997fb
FG
271#[derive(Clone, Copy, PartialEq, Eq)]
272pub(crate) enum Ending {
273 Newline,
274 NoNewline,
275}
276
cdc7bbd5
XL
277/// * The Generics from which to emit a where-clause.
278/// * The number of spaces to indent each line with.
279/// * Whether the where-clause needs to add a comma and newline after the last bound.
923072b8 280pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
cdc7bbd5
XL
281 gens: &'a clean::Generics,
282 cx: &'a Context<'tcx>,
283 indent: usize,
064997fb 284 ending: Ending,
cdc7bbd5 285) -> impl fmt::Display + 'a + Captures<'tcx> {
04454e1e
FG
286 use fmt::Write;
287
cdc7bbd5 288 display_fn(move |f| {
5e7ed085
FG
289 let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
290 !matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
291 }).map(|pred| {
292 display_fn(move |f| {
293 if f.alternate() {
294 f.write_str(" ")?;
295 } else {
296 f.write_str("<br>")?;
297 }
298
299 match pred {
300 clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
04454e1e
FG
301 let ty_cx = ty.print(cx);
302 let generic_bounds = print_generic_bounds(bounds, cx);
5e7ed085 303
04454e1e
FG
304 if bound_params.is_empty() {
305 if f.alternate() {
306 write!(f, "{ty_cx:#}: {generic_bounds:#}")
307 } else {
308 write!(f, "{ty_cx}: {generic_bounds}")
309 }
5e7ed085 310 } else {
04454e1e
FG
311 if f.alternate() {
312 write!(
313 f,
314 "for<{:#}> {ty_cx:#}: {generic_bounds:#}",
315 comma_sep(bound_params.iter().map(|lt| lt.print()), true)
316 )
317 } else {
318 write!(
319 f,
320 "for&lt;{}&gt; {ty_cx}: {generic_bounds}",
321 comma_sep(bound_params.iter().map(|lt| lt.print()), true)
322 )
323 }
5e7ed085
FG
324 }
325 }
326 clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
04454e1e
FG
327 let mut bounds_display = String::new();
328 for bound in bounds.iter().map(|b| b.print(cx)) {
329 write!(bounds_display, "{bound} + ")?;
330 }
331 bounds_display.truncate(bounds_display.len() - " + ".len());
332 write!(f, "{}: {bounds_display}", lifetime.print())
5e7ed085 333 }
2b03887a
FG
334 // FIXME(fmease): Render bound params.
335 clean::WherePredicate::EqPredicate { lhs, rhs, bound_params: _ } => {
5e7ed085 336 if f.alternate() {
04454e1e 337 write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
5e7ed085 338 } else {
04454e1e 339 write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
5e7ed085
FG
340 }
341 }
342 }
343 })
344 }).peekable();
345
346 if where_predicates.peek().is_none() {
cdc7bbd5
XL
347 return Ok(());
348 }
5e7ed085 349
04454e1e
FG
350 let where_preds = comma_sep(where_predicates, false);
351 let clause = if f.alternate() {
064997fb 352 if ending == Ending::Newline {
f2b60f7d 353 format!(" where{where_preds},")
cdc7bbd5 354 } else {
04454e1e 355 format!(" where{where_preds}")
cc61c64b 356 }
04454e1e
FG
357 } else {
358 let mut br_with_padding = String::with_capacity(6 * indent + 28);
359 br_with_padding.push_str("<br>");
360 for _ in 0..indent + 4 {
361 br_with_padding.push_str("&nbsp;");
cc61c64b 362 }
04454e1e 363 let where_preds = where_preds.to_string().replace("<br>", &br_with_padding);
cc61c64b 364
064997fb 365 if ending == Ending::Newline {
04454e1e 366 let mut clause = "&nbsp;".repeat(indent.saturating_sub(1));
f2b60f7d 367 write!(clause, "<span class=\"where fmt-newline\">where{where_preds},</span>")?;
04454e1e
FG
368 clause
369 } else {
370 // insert a <br> tag after a single space but before multiple spaces at the start
371 if indent == 0 {
f2b60f7d 372 format!("<br><span class=\"where\">where{where_preds}</span>")
04454e1e
FG
373 } else {
374 let mut clause = br_with_padding;
f2b60f7d
FG
375 clause.truncate(clause.len() - 4 * "&nbsp;".len());
376 write!(clause, "<span class=\"where\">where{where_preds}</span>")?;
04454e1e
FG
377 clause
378 }
476ff2be 379 }
04454e1e
FG
380 };
381 write!(f, "{clause}")
cdc7bbd5 382 })
1a4d82fc
JJ
383}
384
e74abb32 385impl clean::Lifetime {
923072b8 386 pub(crate) fn print(&self) -> impl fmt::Display + '_ {
a2a8927a 387 self.0.as_str()
1a4d82fc
JJ
388 }
389}
390
e74abb32 391impl clean::Constant {
923072b8 392 pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl fmt::Display + '_ {
cdc7bbd5
XL
393 let expr = self.expr(tcx);
394 display_fn(
395 move |f| {
396 if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) }
397 },
398 )
532ac7d7
XL
399 }
400}
401
e74abb32 402impl clean::PolyTrait {
cdc7bbd5
XL
403 fn print<'a, 'tcx: 'a>(
404 &'a self,
405 cx: &'a Context<'tcx>,
406 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32
XL
407 display_fn(move |f| {
408 if !self.generic_params.is_empty() {
409 if f.alternate() {
dfeec247
XL
410 write!(
411 f,
412 "for<{:#}> ",
5e7ed085 413 comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
dfeec247 414 )?;
e74abb32 415 } else {
dfeec247
XL
416 write!(
417 f,
418 "for&lt;{}&gt; ",
5e7ed085 419 comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
dfeec247 420 )?;
e74abb32
XL
421 }
422 }
c30ab7b3 423 if f.alternate() {
cdc7bbd5 424 write!(f, "{:#}", self.trait_.print(cx))
c30ab7b3 425 } else {
cdc7bbd5 426 write!(f, "{}", self.trait_.print(cx))
c30ab7b3 427 }
e74abb32 428 })
1a4d82fc
JJ
429 }
430}
431
e74abb32 432impl clean::GenericBound {
923072b8 433 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
434 &'a self,
435 cx: &'a Context<'tcx>,
436 ) -> impl fmt::Display + 'a + Captures<'tcx> {
dfeec247
XL
437 display_fn(move |f| match self {
438 clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
439 clean::GenericBound::TraitBound(ty, modifier) => {
440 let modifier_str = match modifier {
441 hir::TraitBoundModifier::None => "",
442 hir::TraitBoundModifier::Maybe => "?",
5e7ed085
FG
443 // ~const is experimental; do not display those bounds in rustdoc
444 hir::TraitBoundModifier::MaybeConst => "",
dfeec247
XL
445 };
446 if f.alternate() {
cdc7bbd5 447 write!(f, "{}{:#}", modifier_str, ty.print(cx))
dfeec247 448 } else {
cdc7bbd5 449 write!(f, "{}{}", modifier_str, ty.print(cx))
c30ab7b3 450 }
1a4d82fc 451 }
e74abb32 452 })
1a4d82fc
JJ
453 }
454}
455
e74abb32 456impl clean::GenericArgs {
cdc7bbd5
XL
457 fn print<'a, 'tcx: 'a>(
458 &'a self,
459 cx: &'a Context<'tcx>,
460 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32 461 display_fn(move |f| {
5869c6ff
XL
462 match self {
463 clean::GenericArgs::AngleBracketed { args, bindings } => {
e74abb32
XL
464 if !args.is_empty() || !bindings.is_empty() {
465 if f.alternate() {
466 f.write_str("<")?;
467 } else {
468 f.write_str("&lt;")?;
469 }
470 let mut comma = false;
923072b8 471 for arg in args.iter() {
e74abb32
XL
472 if comma {
473 f.write_str(", ")?;
474 }
475 comma = true;
476 if f.alternate() {
cdc7bbd5 477 write!(f, "{:#}", arg.print(cx))?;
e74abb32 478 } else {
cdc7bbd5 479 write!(f, "{}", arg.print(cx))?;
e74abb32
XL
480 }
481 }
923072b8 482 for binding in bindings.iter() {
e74abb32
XL
483 if comma {
484 f.write_str(", ")?;
485 }
486 comma = true;
487 if f.alternate() {
cdc7bbd5 488 write!(f, "{:#}", binding.print(cx))?;
e74abb32 489 } else {
cdc7bbd5 490 write!(f, "{}", binding.print(cx))?;
e74abb32 491 }
1a4d82fc 492 }
c30ab7b3 493 if f.alternate() {
e74abb32 494 f.write_str(">")?;
c30ab7b3 495 } else {
e74abb32 496 f.write_str("&gt;")?;
c30ab7b3 497 }
1a4d82fc 498 }
e74abb32 499 }
5869c6ff 500 clean::GenericArgs::Parenthesized { inputs, output } => {
e74abb32
XL
501 f.write_str("(")?;
502 let mut comma = false;
923072b8 503 for ty in inputs.iter() {
1a4d82fc 504 if comma {
c30ab7b3 505 f.write_str(", ")?;
1a4d82fc
JJ
506 }
507 comma = true;
c30ab7b3 508 if f.alternate() {
cdc7bbd5 509 write!(f, "{:#}", ty.print(cx))?;
c30ab7b3 510 } else {
cdc7bbd5 511 write!(f, "{}", ty.print(cx))?;
c30ab7b3
SL
512 }
513 }
e74abb32
XL
514 f.write_str(")")?;
515 if let Some(ref ty) = *output {
516 if f.alternate() {
cdc7bbd5 517 write!(f, " -> {:#}", ty.print(cx))?;
e74abb32 518 } else {
cdc7bbd5 519 write!(f, " -&gt; {}", ty.print(cx))?;
e74abb32 520 }
c30ab7b3 521 }
1a4d82fc
JJ
522 }
523 }
e74abb32
XL
524 Ok(())
525 })
1a4d82fc
JJ
526 }
527}
528
136023e0 529// Possible errors when computing href link source for a `DefId`
923072b8
FG
530#[derive(PartialEq, Eq)]
531pub(crate) enum HrefError {
136023e0
XL
532 /// This item is known to rustdoc, but from a crate that does not have documentation generated.
533 ///
534 /// This can only happen for non-local items.
04454e1e
FG
535 ///
536 /// # Example
537 ///
538 /// Crate `a` defines a public trait and crate `b` – the target crate that depends on `a` –
539 /// implements it for a local type.
540 /// We document `b` but **not** `a` (we only _build_ the latter – with `rustc`):
541 ///
542 /// ```sh
543 /// rustc a.rs --crate-type=lib
544 /// rustdoc b.rs --crate-type=lib --extern=a=liba.rlib
545 /// ```
546 ///
547 /// Now, the associated items in the trait impl want to link to the corresponding item in the
548 /// trait declaration (see `html::render::assoc_href_attr`) but it's not available since their
549 /// *documentation (was) not built*.
136023e0
XL
550 DocumentationNotBuilt,
551 /// This can only happen for non-local items when `--document-private-items` is not passed.
552 Private,
553 // Not in external cache, href link should be in same page
554 NotInExternalCache,
555}
556
5099ac24 557// Panics if `syms` is empty.
923072b8 558pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String {
5099ac24 559 let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len()));
923072b8 560 s.push_str(syms[0].as_str());
5099ac24
FG
561 for sym in &syms[1..] {
562 s.push_str("::");
923072b8 563 s.push_str(sym.as_str());
5099ac24
FG
564 }
565 s
566}
567
923072b8
FG
568/// This function is to get the external macro path because they are not in the cache used in
569/// `href_with_root_path`.
570fn generate_macro_def_id_path(
571 def_id: DefId,
572 cx: &Context<'_>,
573 root_path: Option<&str>,
574) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
575 let tcx = cx.shared.tcx;
576 let crate_name = tcx.crate_name(def_id.krate).to_string();
577 let cache = cx.cache();
578
579 let fqp: Vec<Symbol> = tcx
580 .def_path(def_id)
581 .data
582 .into_iter()
583 .filter_map(|elem| {
584 // extern blocks (and a few others things) have an empty name.
585 match elem.data.get_opt_name() {
586 Some(s) if !s.is_empty() => Some(s),
587 _ => None,
588 }
589 })
590 .collect();
f2b60f7d 591 let mut relative = fqp.iter().map(|elem| elem.to_string());
923072b8
FG
592 let cstore = CStore::from_tcx(tcx);
593 // We need this to prevent a `panic` when this function is used from intra doc links...
594 if !cstore.has_crate_data(def_id.krate) {
595 debug!("No data for crate {}", crate_name);
596 return Err(HrefError::NotInExternalCache);
597 }
598 // Check to see if it is a macro 2.0 or built-in macro.
599 // More information in <https://rust-lang.github.io/rfcs/1584-macros.html>.
600 let is_macro_2 = match cstore.load_macro_untracked(def_id, tcx.sess) {
601 LoadedMacro::MacroDef(def, _) => {
602 // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0.
603 matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules)
604 }
605 _ => false,
606 };
607
608 let mut path = if is_macro_2 {
609 once(crate_name.clone()).chain(relative).collect()
610 } else {
f2b60f7d 611 vec![crate_name.clone(), relative.next_back().unwrap()]
923072b8
FG
612 };
613 if path.len() < 2 {
614 // The minimum we can have is the crate name followed by the macro name. If shorter, then
2b03887a 615 // it means that `relative` was empty, which is an error.
923072b8
FG
616 debug!("macro path cannot be empty!");
617 return Err(HrefError::NotInExternalCache);
618 }
619
620 if let Some(last) = path.last_mut() {
621 *last = format!("macro.{}.html", last);
622 }
623
624 let url = match cache.extern_locations[&def_id.krate] {
625 ExternalLocation::Remote(ref s) => {
626 // `ExternalLocation::Remote` always end with a `/`.
627 format!("{}{}", s, path.join("/"))
628 }
629 ExternalLocation::Local => {
630 // `root_path` always end with a `/`.
631 format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/"))
632 }
633 ExternalLocation::Unknown => {
634 debug!("crate {} not in cache when linkifying macros", crate_name);
635 return Err(HrefError::NotInExternalCache);
636 }
637 };
638 Ok((url, ItemType::Macro, fqp))
639}
640
641pub(crate) fn href_with_root_path(
94222f64
XL
642 did: DefId,
643 cx: &Context<'_>,
644 root_path: Option<&str>,
5099ac24 645) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
3c0e092e
XL
646 let tcx = cx.tcx();
647 let def_kind = tcx.def_kind(did);
648 let did = match def_kind {
649 DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => {
650 // documented on their parent's page
04454e1e 651 tcx.parent(did)
3c0e092e
XL
652 }
653 _ => did,
654 };
655 let cache = cx.cache();
cdc7bbd5 656 let relative_to = &cx.current;
5099ac24 657 fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
94222f64 658 if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
1a4d82fc 659 }
1a4d82fc 660
c295e0f8 661 if !did.is_local()
2b03887a 662 && !cache.effective_visibilities.is_directly_public(did)
c295e0f8
XL
663 && !cache.document_private
664 && !cache.primitive_locations.values().any(|&id| id == did)
665 {
136023e0 666 return Err(HrefError::Private);
a7813a04
XL
667 }
668
94222f64 669 let mut is_remote = false;
cdc7bbd5
XL
670 let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
671 Some(&(ref fqp, shortty)) => (fqp, shortty, {
5099ac24 672 let module_fqp = to_module_fqp(shortty, fqp.as_slice());
c295e0f8 673 debug!(?fqp, ?shortty, ?module_fqp);
5099ac24 674 href_relative_parts(module_fqp, relative_to).collect()
cdc7bbd5 675 }),
ff7c6d11 676 None => {
136023e0
XL
677 if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
678 let module_fqp = to_module_fqp(shortty, fqp);
679 (
680 fqp,
681 shortty,
682 match cache.extern_locations[&did.krate] {
683 ExternalLocation::Remote(ref s) => {
94222f64 684 is_remote = true;
136023e0 685 let s = s.trim_end_matches('/');
a2a8927a 686 let mut builder = UrlPartsBuilder::singleton(s);
5099ac24 687 builder.extend(module_fqp.iter().copied());
a2a8927a 688 builder
136023e0 689 }
5099ac24
FG
690 ExternalLocation::Local => {
691 href_relative_parts(module_fqp, relative_to).collect()
692 }
136023e0
XL
693 ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
694 },
695 )
923072b8
FG
696 } else if matches!(def_kind, DefKind::Macro(_)) {
697 return generate_macro_def_id_path(did, cx, root_path);
136023e0
XL
698 } else {
699 return Err(HrefError::NotInExternalCache);
700 }
9346a6ac
AL
701 }
702 };
94222f64
XL
703 if !is_remote {
704 if let Some(root_path) = root_path {
705 let root = root_path.trim_end_matches('/');
a2a8927a 706 url_parts.push_front(root);
94222f64
XL
707 }
708 }
c295e0f8 709 debug!(?url_parts);
9346a6ac
AL
710 match shortty {
711 ItemType::Module => {
cdc7bbd5 712 url_parts.push("index.html");
9346a6ac
AL
713 }
714 _ => {
5099ac24
FG
715 let prefix = shortty.as_str();
716 let last = fqp.last().unwrap();
717 url_parts.push_fmt(format_args!("{}.{}.html", prefix, last));
9346a6ac
AL
718 }
719 }
a2a8927a 720 Ok((url_parts.finish(), shortty, fqp.to_vec()))
cdc7bbd5
XL
721}
722
923072b8
FG
723pub(crate) fn href(
724 did: DefId,
725 cx: &Context<'_>,
726) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
94222f64
XL
727 href_with_root_path(did, cx, None)
728}
729
cdc7bbd5
XL
730/// Both paths should only be modules.
731/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
732/// both need `../iter/trait.Iterator.html` to get at the iterator trait.
923072b8 733pub(crate) fn href_relative_parts<'fqp>(
5099ac24
FG
734 fqp: &'fqp [Symbol],
735 relative_to_fqp: &[Symbol],
736) -> Box<dyn Iterator<Item = Symbol> + 'fqp> {
cdc7bbd5
XL
737 for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
738 // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
739 if f != r {
740 let dissimilar_part_count = relative_to_fqp.len() - i;
5099ac24 741 let fqp_module = &fqp[i..fqp.len()];
064997fb
FG
742 return Box::new(
743 iter::repeat(sym::dotdot)
744 .take(dissimilar_part_count)
745 .chain(fqp_module.iter().copied()),
746 );
cdc7bbd5
XL
747 }
748 }
749 // e.g. linking to std::sync::atomic from std::sync
750 if relative_to_fqp.len() < fqp.len() {
064997fb 751 Box::new(fqp[relative_to_fqp.len()..fqp.len()].iter().copied())
cdc7bbd5
XL
752 // e.g. linking to std::sync from std::sync::atomic
753 } else if fqp.len() < relative_to_fqp.len() {
754 let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
064997fb 755 Box::new(iter::repeat(sym::dotdot).take(dissimilar_part_count))
cdc7bbd5
XL
756 // linking to the same module
757 } else {
064997fb 758 Box::new(iter::empty())
cdc7bbd5 759 }
9346a6ac
AL
760}
761
3c0e092e
XL
762/// Used to render a [`clean::Path`].
763fn resolved_path<'cx>(
dfeec247
XL
764 w: &mut fmt::Formatter<'_>,
765 did: DefId,
766 path: &clean::Path,
767 print_all: bool,
768 use_absolute: bool,
cdc7bbd5 769 cx: &'cx Context<'_>,
dfeec247 770) -> fmt::Result {
041b39d2 771 let last = path.segments.last().unwrap();
1a4d82fc
JJ
772
773 if print_all {
ff7c6d11 774 for seg in &path.segments[..path.segments.len() - 1] {
04454e1e 775 write!(w, "{}::", if seg.name == kw::PathRoot { "" } else { seg.name.as_str() })?;
1a4d82fc
JJ
776 }
777 }
c30ab7b3 778 if w.alternate() {
cdc7bbd5 779 write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
c30ab7b3 780 } else {
7cac9316 781 let path = if use_absolute {
136023e0 782 if let Ok((_, _, fqp)) = href(did, cx) {
5869c6ff
XL
783 format!(
784 "{}::{}",
5099ac24
FG
785 join_with_double_colon(&fqp[..fqp.len() - 1]),
786 anchor(did, *fqp.last().unwrap(), cx)
5869c6ff 787 )
e1599b0c
XL
788 } else {
789 last.name.to_string()
7cac9316 790 }
32a655c1 791 } else {
5099ac24 792 anchor(did, last.name, cx).to_string()
7cac9316 793 };
cdc7bbd5 794 write!(w, "{}{}", path, last.args.print(cx))?;
c30ab7b3 795 }
1a4d82fc
JJ
796 Ok(())
797}
798
dfeec247
XL
799fn primitive_link(
800 f: &mut fmt::Formatter<'_>,
801 prim: clean::PrimitiveType,
802 name: &str,
cdc7bbd5 803 cx: &Context<'_>,
923072b8
FG
804) -> fmt::Result {
805 primitive_link_fragment(f, prim, name, "", cx)
806}
807
808fn primitive_link_fragment(
809 f: &mut fmt::Formatter<'_>,
810 prim: clean::PrimitiveType,
811 name: &str,
812 fragment: &str,
813 cx: &Context<'_>,
dfeec247 814) -> fmt::Result {
cdc7bbd5 815 let m = &cx.cache();
1a4d82fc 816 let mut needs_termination = false;
c30ab7b3
SL
817 if !f.alternate() {
818 match m.primitive_locations.get(&prim) {
476ff2be 819 Some(&def_id) if def_id.is_local() => {
cdc7bbd5 820 let len = cx.current.len();
dfeec247
XL
821 let len = if len == 0 { 0 } else { len - 1 };
822 write!(
823 f,
923072b8 824 "<a class=\"primitive\" href=\"{}primitive.{}.html{fragment}\">",
dfeec247 825 "../".repeat(len),
17df50a5 826 prim.as_sym()
dfeec247 827 )?;
3157f602 828 needs_termination = true;
1a4d82fc 829 }
476ff2be
SL
830 Some(&def_id) => {
831 let loc = match m.extern_locations[&def_id.krate] {
17df50a5 832 ExternalLocation::Remote(ref s) => {
5099ac24
FG
833 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
834 let builder: UrlPartsBuilder =
835 [s.as_str().trim_end_matches('/'), cname_sym.as_str()]
836 .into_iter()
837 .collect();
838 Some(builder)
cdc7bbd5 839 }
17df50a5 840 ExternalLocation::Local => {
5099ac24
FG
841 let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
842 Some(if cx.current.first() == Some(&cname_sym) {
843 iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect()
cdc7bbd5 844 } else {
5099ac24
FG
845 iter::repeat(sym::dotdot)
846 .take(cx.current.len())
847 .chain(iter::once(cname_sym))
848 .collect()
cdc7bbd5 849 })
c30ab7b3 850 }
17df50a5 851 ExternalLocation::Unknown => None,
c30ab7b3 852 };
5099ac24
FG
853 if let Some(mut loc) = loc {
854 loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
923072b8 855 write!(f, "<a class=\"primitive\" href=\"{}{fragment}\">", loc.finish())?;
c30ab7b3
SL
856 needs_termination = true;
857 }
858 }
859 None => {}
1a4d82fc 860 }
1a4d82fc 861 }
54a0048b 862 write!(f, "{}", name)?;
1a4d82fc 863 if needs_termination {
54a0048b 864 write!(f, "</a>")?;
1a4d82fc
JJ
865 }
866 Ok(())
867}
868
869/// Helper to render type parameters
cdc7bbd5 870fn tybounds<'a, 'tcx: 'a>(
3c0e092e 871 bounds: &'a [clean::PolyTrait],
136023e0 872 lt: &'a Option<clean::Lifetime>,
cdc7bbd5
XL
873 cx: &'a Context<'tcx>,
874) -> impl fmt::Display + 'a + Captures<'tcx> {
136023e0
XL
875 display_fn(move |f| {
876 for (i, bound) in bounds.iter().enumerate() {
877 if i > 0 {
dfeec247 878 write!(f, " + ")?;
1a4d82fc 879 }
136023e0
XL
880
881 fmt::Display::fmt(&bound.print(cx), f)?;
1a4d82fc 882 }
136023e0
XL
883
884 if let Some(lt) = lt {
885 write!(f, " + ")?;
886 fmt::Display::fmt(&lt.print(), f)?;
887 }
888 Ok(())
e1599b0c 889 })
a7813a04
XL
890}
891
923072b8 892pub(crate) fn anchor<'a, 'cx: 'a>(
cdc7bbd5 893 did: DefId,
5099ac24 894 text: Symbol,
cdc7bbd5
XL
895 cx: &'cx Context<'_>,
896) -> impl fmt::Display + 'a {
94222f64 897 let parts = href(did, cx);
e1599b0c 898 display_fn(move |f| {
136023e0 899 if let Ok((url, short_ty, fqp)) = parts {
dfeec247
XL
900 write!(
901 f,
902 r#"<a class="{}" href="{}" title="{} {}">{}</a>"#,
903 short_ty,
904 url,
905 short_ty,
5099ac24
FG
906 join_with_double_colon(&fqp),
907 text.as_str()
dfeec247 908 )
e1599b0c
XL
909 } else {
910 write!(f, "{}", text)
a7813a04 911 }
e1599b0c 912 })
a7813a04
XL
913}
914
cdc7bbd5 915fn fmt_type<'cx>(
5869c6ff
XL
916 t: &clean::Type,
917 f: &mut fmt::Formatter<'_>,
918 use_absolute: bool,
cdc7bbd5 919 cx: &'cx Context<'_>,
5869c6ff 920) -> fmt::Result {
c295e0f8 921 trace!("fmt_type(t = {:?})", t);
5869c6ff 922
32a655c1 923 match *t {
fc512014 924 clean::Generic(name) => write!(f, "{}", name),
3c0e092e 925 clean::Type::Path { ref path } => {
dc9dc135 926 // Paths like `T::Output` and `Self::Output` should be rendered with all segments.
3c0e092e 927 let did = path.def_id();
c295e0f8 928 resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx)
136023e0
XL
929 }
930 clean::DynTrait(ref bounds, ref lt) => {
931 f.write_str("dyn ")?;
932 fmt::Display::fmt(&tybounds(bounds, lt, cx), f)
32a655c1
SL
933 }
934 clean::Infer => write!(f, "_"),
c295e0f8
XL
935 clean::Primitive(clean::PrimitiveType::Never) => {
936 primitive_link(f, PrimitiveType::Never, "!", cx)
937 }
a2a8927a 938 clean::Primitive(prim) => primitive_link(f, prim, prim.as_sym().as_str(), cx),
32a655c1
SL
939 clean::BareFunction(ref decl) => {
940 if f.alternate() {
dfeec247
XL
941 write!(
942 f,
5869c6ff 943 "{:#}{}{:#}fn{:#}",
cdc7bbd5 944 decl.print_hrtb_with_space(cx),
dfeec247
XL
945 decl.unsafety.print_with_space(),
946 print_abi_with_space(decl.abi),
cdc7bbd5 947 decl.decl.print(cx),
dfeec247 948 )
32a655c1 949 } else {
dfeec247
XL
950 write!(
951 f,
5869c6ff 952 "{}{}{}",
cdc7bbd5 953 decl.print_hrtb_with_space(cx),
dfeec247
XL
954 decl.unsafety.print_with_space(),
955 print_abi_with_space(decl.abi)
956 )?;
cdc7bbd5
XL
957 primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
958 write!(f, "{}", decl.decl.print(cx))
32a655c1
SL
959 }
960 }
961 clean::Tuple(ref typs) => {
962 match &typs[..] {
cdc7bbd5 963 &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
7cac9316 964 &[ref one] => {
923072b8
FG
965 if let clean::Generic(name) = one {
966 primitive_link(f, PrimitiveType::Tuple, &format!("({name},)"), cx)
967 } else {
968 write!(f, "(")?;
969 // Carry `f.alternate()` into this display w/o branching manually.
970 fmt::Display::fmt(&one.print(cx), f)?;
971 write!(f, ",)")
972 }
c30ab7b3 973 }
7cac9316 974 many => {
923072b8
FG
975 let generic_names: Vec<Symbol> = many
976 .iter()
977 .filter_map(|t| match t {
978 clean::Generic(name) => Some(*name),
979 _ => None,
980 })
981 .collect();
982 let is_generic = generic_names.len() == many.len();
983 if is_generic {
984 primitive_link(
985 f,
986 PrimitiveType::Tuple,
987 &format!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
988 cx,
989 )
990 } else {
991 write!(f, "(")?;
992 for (i, item) in many.iter().enumerate() {
993 if i != 0 {
994 write!(f, ", ")?;
995 }
996 // Carry `f.alternate()` into this display w/o branching manually.
997 fmt::Display::fmt(&item.print(cx), f)?;
dfeec247 998 }
923072b8 999 write!(f, ")")
e74abb32 1000 }
7453a54e 1001 }
1a4d82fc 1002 }
32a655c1 1003 }
923072b8
FG
1004 clean::Slice(ref t) => match **t {
1005 clean::Generic(name) => {
1006 primitive_link(f, PrimitiveType::Slice, &format!("[{name}]"), cx)
1007 }
1008 _ => {
1009 write!(f, "[")?;
1010 fmt::Display::fmt(&t.print(cx), f)?;
1011 write!(f, "]")
1012 }
1013 },
2b03887a
FG
1014 clean::Array(ref t, ref n) => match **t {
1015 clean::Generic(name) if !f.alternate() => primitive_link(
1016 f,
1017 PrimitiveType::Array,
1018 &format!("[{name}; {n}]", n = Escape(n)),
1019 cx,
1020 ),
1021 _ => {
1022 write!(f, "[")?;
1023 fmt::Display::fmt(&t.print(cx), f)?;
1024 if f.alternate() {
1025 write!(f, "; {n}")?;
1026 } else {
1027 write!(f, "; ")?;
1028 primitive_link(f, PrimitiveType::Array, &format!("{n}", n = Escape(n)), cx)?;
1029 }
1030 write!(f, "]")
dfeec247 1031 }
2b03887a 1032 },
32a655c1 1033 clean::RawPointer(m, ref t) => {
e1599b0c 1034 let m = match m {
dfeec247
XL
1035 hir::Mutability::Mut => "mut",
1036 hir::Mutability::Not => "const",
e1599b0c 1037 };
c295e0f8
XL
1038
1039 if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() {
1040 let text = if f.alternate() {
1041 format!("*{} {:#}", m, t.print(cx))
1042 } else {
1043 format!("*{} {}", m, t.print(cx))
1044 };
1045 primitive_link(f, clean::PrimitiveType::RawPointer, &text, cx)
1046 } else {
1047 primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), cx)?;
1048 fmt::Display::fmt(&t.print(cx), f)
1a4d82fc 1049 }
32a655c1 1050 }
dfeec247 1051 clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
e74abb32
XL
1052 let lt = match l {
1053 Some(l) => format!("{} ", l.print()),
dfeec247 1054 _ => String::new(),
32a655c1 1055 };
e74abb32 1056 let m = mutability.print_with_space();
dfeec247 1057 let amp = if f.alternate() { "&".to_string() } else { "&amp;".to_string() };
32a655c1 1058 match **ty {
136023e0
XL
1059 clean::DynTrait(ref bounds, ref trait_lt)
1060 if bounds.len() > 1 || trait_lt.is_some() =>
1061 {
3b2f2976 1062 write!(f, "{}{}{}(", amp, lt, m)?;
3c0e092e 1063 fmt_type(ty, f, use_absolute, cx)?;
7cac9316
XL
1064 write!(f, ")")
1065 }
3b2f2976 1066 clean::Generic(..) => {
5869c6ff
XL
1067 primitive_link(
1068 f,
1069 PrimitiveType::Reference,
1070 &format!("{}{}{}", amp, lt, m),
cdc7bbd5 1071 cx,
5869c6ff 1072 )?;
3c0e092e 1073 fmt_type(ty, f, use_absolute, cx)
3b2f2976 1074 }
32a655c1 1075 _ => {
3b2f2976 1076 write!(f, "{}{}{}", amp, lt, m)?;
3c0e092e 1077 fmt_type(ty, f, use_absolute, cx)
1a4d82fc 1078 }
1a4d82fc 1079 }
32a655c1
SL
1080 }
1081 clean::ImplTrait(ref bounds) => {
9fa01778 1082 if f.alternate() {
cdc7bbd5 1083 write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
9fa01778 1084 } else {
cdc7bbd5 1085 write!(f, "impl {}", print_generic_bounds(bounds, cx))
9fa01778 1086 }
32a655c1 1087 }
f2b60f7d
FG
1088 clean::QPath(box clean::QPathData {
1089 ref assoc,
1090 ref self_type,
1091 ref trait_,
1092 should_show_cast,
1093 }) => {
32a655c1 1094 if f.alternate() {
7cac9316 1095 if should_show_cast {
cdc7bbd5 1096 write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
8bb4bdeb 1097 } else {
cdc7bbd5 1098 write!(f, "{:#}::", self_type.print(cx))?
8bb4bdeb 1099 }
32a655c1 1100 } else {
7cac9316 1101 if should_show_cast {
cdc7bbd5 1102 write!(f, "&lt;{} as {}&gt;::", self_type.print(cx), trait_.print(cx))?
8bb4bdeb 1103 } else {
cdc7bbd5 1104 write!(f, "{}::", self_type.print(cx))?
cc61c64b
XL
1105 }
1106 };
c295e0f8
XL
1107 // It's pretty unsightly to look at `<A as B>::C` in output, and
1108 // we've got hyperlinking on our side, so try to avoid longer
1109 // notation as much as possible by making `C` a hyperlink to trait
1110 // `B` to disambiguate.
1111 //
1112 // FIXME: this is still a lossy conversion and there should probably
1113 // be a better way of representing this in general? Most of
1114 // the ugliness comes from inlining across crates where
1115 // everything comes in as a fully resolved QPath (hard to
1116 // look at).
1117 match href(trait_.def_id(), cx) {
1118 Ok((ref url, _, ref path)) if !f.alternate() => {
1119 write!(
1120 f,
a2a8927a 1121 "<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
5e7ed085 1122 title=\"type {path}::{name}\">{name}</a>{args}",
c295e0f8
XL
1123 url = url,
1124 shortty = ItemType::AssocType,
5e7ed085 1125 name = assoc.name,
5099ac24 1126 path = join_with_double_colon(path),
5e7ed085 1127 args = assoc.args.print(cx),
c295e0f8 1128 )?;
cc61c64b 1129 }
5e7ed085 1130 _ => write!(f, "{}{:#}", assoc.name, assoc.args.print(cx))?,
1a4d82fc 1131 }
c295e0f8 1132 Ok(())
1a4d82fc 1133 }
32a655c1
SL
1134 }
1135}
1136
e74abb32 1137impl clean::Type {
923072b8 1138 pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
cdc7bbd5
XL
1139 &'a self,
1140 cx: &'a Context<'tcx>,
1141 ) -> impl fmt::Display + 'b + Captures<'tcx> {
1142 display_fn(move |f| fmt_type(self, f, false, cx))
1a4d82fc
JJ
1143 }
1144}
1145
c295e0f8 1146impl clean::Path {
923072b8 1147 pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
c295e0f8
XL
1148 &'a self,
1149 cx: &'a Context<'tcx>,
1150 ) -> impl fmt::Display + 'b + Captures<'tcx> {
1151 display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx))
1152 }
1153}
1154
e74abb32 1155impl clean::Impl {
923072b8 1156 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1157 &'a self,
1158 use_absolute: bool,
1159 cx: &'a Context<'tcx>,
1160 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32
XL
1161 display_fn(move |f| {
1162 if f.alternate() {
cdc7bbd5 1163 write!(f, "impl{:#} ", self.generics.print(cx))?;
e74abb32 1164 } else {
cdc7bbd5 1165 write!(f, "impl{} ", self.generics.print(cx))?;
e74abb32 1166 }
476ff2be 1167
e74abb32 1168 if let Some(ref ty) = self.trait_ {
3c0e092e
XL
1169 match self.polarity {
1170 ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {}
1171 ty::ImplPolarity::Negative => write!(f, "!")?,
a7813a04 1172 }
cdc7bbd5 1173 fmt::Display::fmt(&ty.print(cx), f)?;
e74abb32
XL
1174 write!(f, " for ")?;
1175 }
476ff2be 1176
923072b8
FG
1177 if let clean::Type::Tuple(types) = &self.for_ &&
1178 let [clean::Type::Generic(name)] = &types[..] &&
064997fb
FG
1179 (self.kind.is_fake_variadic() || self.kind.is_auto())
1180 {
923072b8
FG
1181 // Hardcoded anchor library/core/src/primitive_docs.rs
1182 // Link should match `# Trait implementations`
1183 primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
064997fb
FG
1184 } else if let clean::BareFunction(bare_fn) = &self.for_ &&
1185 let [clean::Argument { type_: clean::Type::Generic(name), .. }] = &bare_fn.decl.inputs.values[..] &&
1186 (self.kind.is_fake_variadic() || self.kind.is_auto())
1187 {
1188 // Hardcoded anchor library/core/src/primitive_docs.rs
1189 // Link should match `# Trait implementations`
1190
1191 let hrtb = bare_fn.print_hrtb_with_space(cx);
1192 let unsafety = bare_fn.unsafety.print_with_space();
1193 let abi = print_abi_with_space(bare_fn.abi);
1194 if f.alternate() {
1195 write!(
1196 f,
1197 "{hrtb:#}{unsafety}{abi:#}",
1198 )?;
1199 } else {
1200 write!(
1201 f,
1202 "{hrtb}{unsafety}{abi}",
1203 )?;
1204 }
1205 let ellipsis = if bare_fn.decl.c_variadic {
1206 ", ..."
1207 } else {
1208 ""
1209 };
1210 primitive_link_fragment(f, PrimitiveType::Tuple, &format!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
1211 // Write output.
1212 if let clean::FnRetTy::Return(ty) = &bare_fn.decl.output {
1213 write!(f, " -> ")?;
1214 fmt_type(ty, f, use_absolute, cx)?;
1215 }
923072b8 1216 } else if let Some(ty) = self.kind.as_blanket_ty() {
cdc7bbd5 1217 fmt_type(ty, f, use_absolute, cx)?;
e74abb32 1218 } else {
cdc7bbd5 1219 fmt_type(&self.for_, f, use_absolute, cx)?;
e74abb32 1220 }
a7813a04 1221
064997fb 1222 fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, Ending::Newline), f)?;
e74abb32
XL
1223 Ok(())
1224 })
c1a9b12d
SL
1225 }
1226}
1227
e74abb32 1228impl clean::Arguments {
923072b8 1229 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1230 &'a self,
1231 cx: &'a Context<'tcx>,
1232 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32
XL
1233 display_fn(move |f| {
1234 for (i, input) in self.values.iter().enumerate() {
1235 if !input.name.is_empty() {
1236 write!(f, "{}: ", input.name)?;
1237 }
1238 if f.alternate() {
cdc7bbd5 1239 write!(f, "{:#}", input.type_.print(cx))?;
e74abb32 1240 } else {
cdc7bbd5 1241 write!(f, "{}", input.type_.print(cx))?;
e74abb32 1242 }
dfeec247
XL
1243 if i + 1 < self.values.len() {
1244 write!(f, ", ")?;
1245 }
c30ab7b3 1246 }
e74abb32
XL
1247 Ok(())
1248 })
1a4d82fc
JJ
1249 }
1250}
1251
74b04a01 1252impl clean::FnRetTy {
923072b8 1253 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1254 &'a self,
1255 cx: &'a Context<'tcx>,
1256 ) -> impl fmt::Display + 'a + Captures<'tcx> {
dfeec247
XL
1257 display_fn(move |f| match self {
1258 clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()),
cdc7bbd5
XL
1259 clean::Return(ty) if f.alternate() => {
1260 write!(f, " -> {:#}", ty.print(cx))
1261 }
1262 clean::Return(ty) => write!(f, " -&gt; {}", ty.print(cx)),
dfeec247 1263 clean::DefaultReturn => Ok(()),
e74abb32 1264 })
1a4d82fc
JJ
1265 }
1266}
1267
e74abb32 1268impl clean::BareFunctionDecl {
cdc7bbd5
XL
1269 fn print_hrtb_with_space<'a, 'tcx: 'a>(
1270 &'a self,
1271 cx: &'a Context<'tcx>,
1272 ) -> impl fmt::Display + 'a + Captures<'tcx> {
5869c6ff
XL
1273 display_fn(move |f| {
1274 if !self.generic_params.is_empty() {
c295e0f8
XL
1275 write!(
1276 f,
1277 "for&lt;{}&gt; ",
5e7ed085 1278 comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
c295e0f8 1279 )
5869c6ff
XL
1280 } else {
1281 Ok(())
1282 }
1283 })
1a4d82fc
JJ
1284 }
1285}
1286
e74abb32 1287impl clean::FnDecl {
923072b8 1288 pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
cdc7bbd5
XL
1289 &'a self,
1290 cx: &'a Context<'tcx>,
1291 ) -> impl fmt::Display + 'b + Captures<'tcx> {
e74abb32 1292 display_fn(move |f| {
dfeec247 1293 let ellipsis = if self.c_variadic { ", ..." } else { "" };
e74abb32 1294 if f.alternate() {
dfeec247
XL
1295 write!(
1296 f,
e74abb32 1297 "({args:#}{ellipsis}){arrow:#}",
cdc7bbd5 1298 args = self.inputs.print(cx),
dfeec247 1299 ellipsis = ellipsis,
cdc7bbd5 1300 arrow = self.output.print(cx)
dfeec247 1301 )
e74abb32 1302 } else {
dfeec247
XL
1303 write!(
1304 f,
e74abb32 1305 "({args}{ellipsis}){arrow}",
cdc7bbd5 1306 args = self.inputs.print(cx),
dfeec247 1307 ellipsis = ellipsis,
cdc7bbd5 1308 arrow = self.output.print(cx)
dfeec247 1309 )
cc61c64b 1310 }
e74abb32
XL
1311 })
1312 }
e74abb32 1313
cdc7bbd5
XL
1314 /// * `header_len`: The length of the function header and name. In other words, the number of
1315 /// characters in the function declaration up to but not including the parentheses.
1316 /// <br>Used to determine line-wrapping.
1317 /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
1318 /// necessary.
923072b8 1319 pub(crate) fn full_print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1320 &'a self,
1321 header_len: usize,
1322 indent: usize,
cdc7bbd5
XL
1323 cx: &'a Context<'tcx>,
1324 ) -> impl fmt::Display + 'a + Captures<'tcx> {
f2b60f7d 1325 display_fn(move |f| self.inner_full_print(header_len, indent, f, cx))
cdc7bbd5 1326 }
cc61c64b 1327
cdc7bbd5
XL
1328 fn inner_full_print(
1329 &self,
1330 header_len: usize,
1331 indent: usize,
cdc7bbd5
XL
1332 f: &mut fmt::Formatter<'_>,
1333 cx: &Context<'_>,
1334 ) -> fmt::Result {
1335 let amp = if f.alternate() { "&" } else { "&amp;" };
5e7ed085
FG
1336 let mut args = Buffer::html();
1337 let mut args_plain = Buffer::new();
cdc7bbd5 1338 for (i, input) in self.inputs.values.iter().enumerate() {
cdc7bbd5
XL
1339 if let Some(selfty) = input.to_self() {
1340 match selfty {
1341 clean::SelfValue => {
1342 args.push_str("self");
1343 args_plain.push_str("self");
c30ab7b3 1344 }
cdc7bbd5 1345 clean::SelfBorrowed(Some(ref lt), mtbl) => {
5e7ed085
FG
1346 write!(args, "{}{} {}self", amp, lt.print(), mtbl.print_with_space());
1347 write!(args_plain, "&{} {}self", lt.print(), mtbl.print_with_space());
a7813a04 1348 }
cdc7bbd5 1349 clean::SelfBorrowed(None, mtbl) => {
5e7ed085
FG
1350 write!(args, "{}{}self", amp, mtbl.print_with_space());
1351 write!(args_plain, "&{}self", mtbl.print_with_space());
a7813a04 1352 }
cdc7bbd5
XL
1353 clean::SelfExplicit(ref typ) => {
1354 if f.alternate() {
5e7ed085 1355 write!(args, "self: {:#}", typ.print(cx));
cdc7bbd5 1356 } else {
5e7ed085 1357 write!(args, "self: {}", typ.print(cx));
cdc7bbd5 1358 }
5e7ed085 1359 write!(args_plain, "self: {:#}", typ.print(cx));
a7813a04
XL
1360 }
1361 }
cdc7bbd5
XL
1362 } else {
1363 if i > 0 {
064997fb 1364 args.push_str("<br>");
cdc7bbd5 1365 }
a2a8927a
XL
1366 if input.is_const {
1367 args.push_str("const ");
1368 args_plain.push_str("const ");
1369 }
cdc7bbd5 1370 if !input.name.is_empty() {
5e7ed085
FG
1371 write!(args, "{}: ", input.name);
1372 write!(args_plain, "{}: ", input.name);
c30ab7b3 1373 }
c30ab7b3 1374
cdc7bbd5 1375 if f.alternate() {
5e7ed085 1376 write!(args, "{:#}", input.type_.print(cx));
cdc7bbd5 1377 } else {
5e7ed085 1378 write!(args, "{}", input.type_.print(cx));
cdc7bbd5 1379 }
5e7ed085 1380 write!(args_plain, "{:#}", input.type_.print(cx));
e74abb32 1381 }
cdc7bbd5 1382 if i + 1 < self.inputs.values.len() {
5e7ed085
FG
1383 args.push_str(",");
1384 args_plain.push_str(",");
cdc7bbd5
XL
1385 }
1386 }
c30ab7b3 1387
5e7ed085
FG
1388 let mut args_plain = format!("({})", args_plain.into_inner());
1389 let mut args = args.into_inner();
cc61c64b 1390
cdc7bbd5
XL
1391 if self.c_variadic {
1392 args.push_str(",<br> ...");
1393 args_plain.push_str(", ...");
1394 }
c30ab7b3 1395
f2b60f7d
FG
1396 let arrow_plain = format!("{:#}", self.output.print(cx));
1397 let arrow =
1398 if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) };
e74abb32 1399
cdc7bbd5
XL
1400 let declaration_len = header_len + args_plain.len() + arrow_plain.len();
1401 let output = if declaration_len > 80 {
1402 let full_pad = format!("<br>{}", "&nbsp;".repeat(indent + 4));
1403 let close_pad = format!("<br>{}", "&nbsp;".repeat(indent));
1404 format!(
064997fb
FG
1405 "({pad}{args}{close}){arrow}",
1406 pad = if self.inputs.values.is_empty() { "" } else { &full_pad },
cdc7bbd5
XL
1407 args = args.replace("<br>", &full_pad),
1408 close = close_pad,
1409 arrow = arrow
1410 )
1411 } else {
064997fb 1412 format!("({args}){arrow}", args = args.replace("<br>", " "), arrow = arrow)
cdc7bbd5
XL
1413 };
1414
1415 if f.alternate() {
1416 write!(f, "{}", output.replace("<br>", "\n"))
1417 } else {
1418 write!(f, "{}", output)
1419 }
1a4d82fc
JJ
1420 }
1421}
1422
e74abb32 1423impl clean::Visibility {
923072b8 1424 pub(crate) fn print_with_space<'a, 'tcx: 'a>(
fc512014 1425 self,
136023e0 1426 item_did: ItemId,
cdc7bbd5
XL
1427 cx: &'a Context<'tcx>,
1428 ) -> impl fmt::Display + 'a + Captures<'tcx> {
923072b8
FG
1429 use std::fmt::Write as _;
1430
1431 let to_print: Cow<'static, str> = match self {
1432 clean::Public => "pub ".into(),
1433 clean::Inherited => "".into(),
fc512014
XL
1434 clean::Visibility::Restricted(vis_did) => {
1435 // FIXME(camelid): This may not work correctly if `item_did` is a module.
1436 // However, rustdoc currently never displays a module's
1437 // visibility, so it shouldn't matter.
136023e0 1438 let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id());
fc512014 1439
04454e1e 1440 if vis_did.is_crate_root() {
923072b8 1441 "pub(crate) ".into()
fc512014
XL
1442 } else if parent_module == Some(vis_did) {
1443 // `pub(in foo)` where `foo` is the parent module
1444 // is the same as no visibility modifier
923072b8 1445 "".into()
fc512014 1446 } else if parent_module
923072b8 1447 .and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
fc512014 1448 == Some(vis_did)
dfeec247 1449 {
923072b8 1450 "pub(super) ".into()
fc512014 1451 } else {
cdc7bbd5 1452 let path = cx.tcx().def_path(vis_did);
fc512014 1453 debug!("path={:?}", path);
5869c6ff
XL
1454 // modified from `resolved_path()` to work with `DefPathData`
1455 let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
5099ac24 1456 let anchor = anchor(vis_did, last_name, cx).to_string();
5869c6ff 1457
cdc7bbd5 1458 let mut s = "pub(in ".to_owned();
fc512014 1459 for seg in &path.data[..path.data.len() - 1] {
923072b8 1460 let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap());
fc512014 1461 }
923072b8
FG
1462 let _ = write!(s, "{}) ", anchor);
1463 s.into()
94b46f34 1464 }
94b46f34 1465 }
5869c6ff 1466 };
923072b8 1467 display_fn(move |f| write!(f, "{}", to_print))
1a4d82fc 1468 }
cdc7bbd5
XL
1469
1470 /// This function is the same as print_with_space, except that it renders no links.
1471 /// It's used for macros' rendered source view, which is syntax highlighted and cannot have
1472 /// any HTML in it.
923072b8 1473 pub(crate) fn to_src_with_space<'a, 'tcx: 'a>(
cdc7bbd5
XL
1474 self,
1475 tcx: TyCtxt<'tcx>,
1476 item_did: DefId,
1477 ) -> impl fmt::Display + 'a + Captures<'tcx> {
1478 let to_print = match self {
1479 clean::Public => "pub ".to_owned(),
1480 clean::Inherited => String::new(),
1481 clean::Visibility::Restricted(vis_did) => {
1482 // FIXME(camelid): This may not work correctly if `item_did` is a module.
1483 // However, rustdoc currently never displays a module's
1484 // visibility, so it shouldn't matter.
1485 let parent_module = find_nearest_parent_module(tcx, item_did);
1486
04454e1e 1487 if vis_did.is_crate_root() {
cdc7bbd5
XL
1488 "pub(crate) ".to_owned()
1489 } else if parent_module == Some(vis_did) {
1490 // `pub(in foo)` where `foo` is the parent module
1491 // is the same as no visibility modifier
1492 String::new()
923072b8 1493 } else if parent_module.and_then(|parent| find_nearest_parent_module(tcx, parent))
cdc7bbd5
XL
1494 == Some(vis_did)
1495 {
1496 "pub(super) ".to_owned()
1497 } else {
1498 format!("pub(in {}) ", tcx.def_path_str(vis_did))
1499 }
1500 }
1501 };
1502 display_fn(move |f| f.write_str(&to_print))
1503 }
1a4d82fc
JJ
1504}
1505
923072b8 1506pub(crate) trait PrintWithSpace {
e74abb32
XL
1507 fn print_with_space(&self) -> &str;
1508}
1509
1510impl PrintWithSpace for hir::Unsafety {
1511 fn print_with_space(&self) -> &str {
1512 match self {
1513 hir::Unsafety::Unsafe => "unsafe ",
dfeec247 1514 hir::Unsafety::Normal => "",
1a4d82fc
JJ
1515 }
1516 }
1517}
1518
e74abb32
XL
1519impl PrintWithSpace for hir::IsAsync {
1520 fn print_with_space(&self) -> &str {
1521 match self {
1522 hir::IsAsync::Async => "async ",
1523 hir::IsAsync::NotAsync => "",
8faf50e0
XL
1524 }
1525 }
1526}
1527
dfeec247
XL
1528impl PrintWithSpace for hir::Mutability {
1529 fn print_with_space(&self) -> &str {
1530 match self {
1531 hir::Mutability::Not => "",
1532 hir::Mutability::Mut => "mut ",
1533 }
1534 }
1535}
1536
923072b8
FG
1537pub(crate) fn print_constness_with_space(
1538 c: &hir::Constness,
1539 s: Option<ConstStability>,
1540) -> &'static str {
136023e0
XL
1541 match (c, s) {
1542 // const stable or when feature(staged_api) is not set
1543 (
1544 hir::Constness::Const,
1545 Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }),
1546 )
1547 | (hir::Constness::Const, None) => "const ",
1548 // const unstable or not const
1549 _ => "",
1550 }
1551}
1552
e74abb32 1553impl clean::Import {
923072b8 1554 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1555 &'a self,
1556 cx: &'a Context<'tcx>,
1557 ) -> impl fmt::Display + 'a + Captures<'tcx> {
29967ef6 1558 display_fn(move |f| match self.kind {
fc512014
XL
1559 clean::ImportKind::Simple(name) => {
1560 if name == self.source.path.last() {
cdc7bbd5 1561 write!(f, "use {};", self.source.print(cx))
dfeec247 1562 } else {
cdc7bbd5 1563 write!(f, "use {} as {};", self.source.print(cx), name)
1a4d82fc 1564 }
dfeec247 1565 }
29967ef6
XL
1566 clean::ImportKind::Glob => {
1567 if self.source.path.segments.is_empty() {
dfeec247
XL
1568 write!(f, "use *;")
1569 } else {
cdc7bbd5 1570 write!(f, "use {}::*;", self.source.print(cx))
041b39d2 1571 }
1a4d82fc 1572 }
e74abb32 1573 })
1a4d82fc
JJ
1574 }
1575}
1576
e74abb32 1577impl clean::ImportSource {
923072b8 1578 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1579 &'a self,
1580 cx: &'a Context<'tcx>,
1581 ) -> impl fmt::Display + 'a + Captures<'tcx> {
dfeec247 1582 display_fn(move |f| match self.did {
cdc7bbd5 1583 Some(did) => resolved_path(f, did, &self.path, true, false, cx),
dfeec247 1584 _ => {
74b04a01
XL
1585 for seg in &self.path.segments[..self.path.segments.len() - 1] {
1586 write!(f, "{}::", seg.name)?;
1587 }
a2a8927a 1588 let name = self.path.last();
74b04a01 1589 if let hir::def::Res::PrimTy(p) = self.path.res {
a2a8927a 1590 primitive_link(f, PrimitiveType::from(p), name.as_str(), cx)?;
74b04a01
XL
1591 } else {
1592 write!(f, "{}", name)?;
1a4d82fc 1593 }
dfeec247 1594 Ok(())
1a4d82fc 1595 }
e74abb32 1596 })
1a4d82fc
JJ
1597 }
1598}
1599
e74abb32 1600impl clean::TypeBinding {
923072b8 1601 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1602 &'a self,
1603 cx: &'a Context<'tcx>,
1604 ) -> impl fmt::Display + 'a + Captures<'tcx> {
e74abb32 1605 display_fn(move |f| {
5e7ed085
FG
1606 f.write_str(self.assoc.name.as_str())?;
1607 if f.alternate() {
1608 write!(f, "{:#}", self.assoc.args.print(cx))?;
1609 } else {
1610 write!(f, "{}", self.assoc.args.print(cx))?;
1611 }
e74abb32 1612 match self.kind {
5099ac24 1613 clean::TypeBindingKind::Equality { ref term } => {
dc9dc135 1614 if f.alternate() {
5099ac24 1615 write!(f, " = {:#}", term.print(cx))?;
dc9dc135 1616 } else {
5099ac24 1617 write!(f, " = {}", term.print(cx))?;
e74abb32
XL
1618 }
1619 }
1620 clean::TypeBindingKind::Constraint { ref bounds } => {
1621 if !bounds.is_empty() {
1622 if f.alternate() {
cdc7bbd5 1623 write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
e74abb32 1624 } else {
cdc7bbd5 1625 write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
e74abb32 1626 }
dc9dc135
XL
1627 }
1628 }
1629 }
e74abb32
XL
1630 Ok(())
1631 })
1a4d82fc
JJ
1632 }
1633}
1634
923072b8 1635pub(crate) fn print_abi_with_space(abi: Abi) -> impl fmt::Display {
e74abb32 1636 display_fn(move |f| {
c30ab7b3 1637 let quot = if f.alternate() { "\"" } else { "&quot;" };
e74abb32 1638 match abi {
9346a6ac 1639 Abi::Rust => Ok(()),
c30ab7b3 1640 abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
9346a6ac 1641 }
e74abb32
XL
1642 })
1643}
1644
923072b8 1645pub(crate) fn print_default_space<'a>(v: bool) -> &'a str {
dfeec247 1646 if v { "default " } else { "" }
9346a6ac 1647}
532ac7d7 1648
e74abb32 1649impl clean::GenericArg {
923072b8 1650 pub(crate) fn print<'a, 'tcx: 'a>(
cdc7bbd5
XL
1651 &'a self,
1652 cx: &'a Context<'tcx>,
1653 ) -> impl fmt::Display + 'a + Captures<'tcx> {
dfeec247
XL
1654 display_fn(move |f| match self {
1655 clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(&lt.print(), f),
cdc7bbd5
XL
1656 clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
1657 clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
94222f64 1658 clean::GenericArg::Infer => fmt::Display::fmt("_", f),
e74abb32 1659 })
532ac7d7
XL
1660 }
1661}
e1599b0c 1662
5099ac24 1663impl clean::types::Term {
923072b8 1664 pub(crate) fn print<'a, 'tcx: 'a>(
5099ac24
FG
1665 &'a self,
1666 cx: &'a Context<'tcx>,
1667 ) -> impl fmt::Display + 'a + Captures<'tcx> {
1668 match self {
1669 clean::types::Term::Type(ty) => ty.print(cx),
1670 _ => todo!(),
1671 }
1672 }
1673}
1674
923072b8
FG
1675pub(crate) fn display_fn(
1676 f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
1677) -> impl fmt::Display {
5869c6ff
XL
1678 struct WithFormatter<F>(Cell<Option<F>>);
1679
1680 impl<F> fmt::Display for WithFormatter<F>
1681 where
1682 F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
1683 {
1684 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1685 (self.0.take()).unwrap()(f)
1686 }
e1599b0c 1687 }
5869c6ff
XL
1688
1689 WithFormatter(Cell::new(Some(f)))
e1599b0c 1690}