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