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