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