]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/format.rs
Merge commit '7bfc26ec8e7a454786668e7e52ffe527fc649735' into clippyup
[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_with_root_path(
488     did: DefId,
489     cx: &Context<'_>,
490     root_path: Option<&str>,
491 ) -> Result<(String, ItemType, Vec<String>), HrefError> {
492     let cache = &cx.cache();
493     let relative_to = &cx.current;
494     fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
495         if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
496     }
497
498     if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
499         return Err(HrefError::Private);
500     }
501
502     let mut is_remote = false;
503     let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
504         Some(&(ref fqp, shortty)) => (fqp, shortty, {
505             let module_fqp = to_module_fqp(shortty, fqp);
506             href_relative_parts(module_fqp, relative_to)
507         }),
508         None => {
509             if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
510                 let module_fqp = to_module_fqp(shortty, fqp);
511                 (
512                     fqp,
513                     shortty,
514                     match cache.extern_locations[&did.krate] {
515                         ExternalLocation::Remote(ref s) => {
516                             is_remote = true;
517                             let s = s.trim_end_matches('/');
518                             let mut s = vec![s];
519                             s.extend(module_fqp[..].iter().map(String::as_str));
520                             s
521                         }
522                         ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
523                         ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
524                     },
525                 )
526             } else {
527                 return Err(HrefError::NotInExternalCache);
528             }
529         }
530     };
531     if !is_remote {
532         if let Some(root_path) = root_path {
533             let root = root_path.trim_end_matches('/');
534             url_parts.insert(0, root);
535         }
536     }
537     let last = &fqp.last().unwrap()[..];
538     let filename;
539     match shortty {
540         ItemType::Module => {
541             url_parts.push("index.html");
542         }
543         _ => {
544             filename = format!("{}.{}.html", shortty.as_str(), last);
545             url_parts.push(&filename);
546         }
547     }
548     Ok((url_parts.join("/"), shortty, fqp.to_vec()))
549 }
550
551 crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
552     href_with_root_path(did, cx, None)
553 }
554
555 /// Both paths should only be modules.
556 /// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
557 /// both need `../iter/trait.Iterator.html` to get at the iterator trait.
558 crate fn href_relative_parts<'a>(fqp: &'a [String], relative_to_fqp: &'a [String]) -> Vec<&'a str> {
559     for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
560         // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
561         if f != r {
562             let dissimilar_part_count = relative_to_fqp.len() - i;
563             let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str);
564             return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect();
565         }
566     }
567     // e.g. linking to std::sync::atomic from std::sync
568     if relative_to_fqp.len() < fqp.len() {
569         fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect()
570     // e.g. linking to std::sync from std::sync::atomic
571     } else if fqp.len() < relative_to_fqp.len() {
572         let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
573         iter::repeat("..").take(dissimilar_part_count).collect()
574     // linking to the same module
575     } else {
576         Vec::new()
577     }
578 }
579
580 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
581 /// rendering function with the necessary arguments for linking to a local path.
582 fn resolved_path<'a, 'cx: 'a>(
583     w: &mut fmt::Formatter<'_>,
584     did: DefId,
585     path: &clean::Path,
586     print_all: bool,
587     use_absolute: bool,
588     cx: &'cx Context<'_>,
589 ) -> fmt::Result {
590     let last = path.segments.last().unwrap();
591
592     if print_all {
593         for seg in &path.segments[..path.segments.len() - 1] {
594             write!(w, "{}::", seg.name)?;
595         }
596     }
597     if w.alternate() {
598         write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
599     } else {
600         let path = if use_absolute {
601             if let Ok((_, _, fqp)) = href(did, cx) {
602                 format!(
603                     "{}::{}",
604                     fqp[..fqp.len() - 1].join("::"),
605                     anchor(did, fqp.last().unwrap(), cx)
606                 )
607             } else {
608                 last.name.to_string()
609             }
610         } else {
611             anchor(did, &*last.name.as_str(), cx).to_string()
612         };
613         write!(w, "{}{}", path, last.args.print(cx))?;
614     }
615     Ok(())
616 }
617
618 fn primitive_link(
619     f: &mut fmt::Formatter<'_>,
620     prim: clean::PrimitiveType,
621     name: &str,
622     cx: &Context<'_>,
623 ) -> fmt::Result {
624     let m = &cx.cache();
625     let mut needs_termination = false;
626     if !f.alternate() {
627         match m.primitive_locations.get(&prim) {
628             Some(&def_id) if def_id.is_local() => {
629                 let len = cx.current.len();
630                 let len = if len == 0 { 0 } else { len - 1 };
631                 write!(
632                     f,
633                     "<a class=\"primitive\" href=\"{}primitive.{}.html\">",
634                     "../".repeat(len),
635                     prim.as_sym()
636                 )?;
637                 needs_termination = true;
638             }
639             Some(&def_id) => {
640                 let cname_str;
641                 let loc = match m.extern_locations[&def_id.krate] {
642                     ExternalLocation::Remote(ref s) => {
643                         cname_str =
644                             ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()).as_str();
645                         Some(vec![s.trim_end_matches('/'), &cname_str[..]])
646                     }
647                     ExternalLocation::Local => {
648                         cname_str =
649                             ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()).as_str();
650                         Some(if cx.current.first().map(|x| &x[..]) == Some(&cname_str[..]) {
651                             iter::repeat("..").take(cx.current.len() - 1).collect()
652                         } else {
653                             let cname = iter::once(&cname_str[..]);
654                             iter::repeat("..").take(cx.current.len()).chain(cname).collect()
655                         })
656                     }
657                     ExternalLocation::Unknown => None,
658                 };
659                 if let Some(loc) = loc {
660                     write!(
661                         f,
662                         "<a class=\"primitive\" href=\"{}/primitive.{}.html\">",
663                         loc.join("/"),
664                         prim.as_sym()
665                     )?;
666                     needs_termination = true;
667                 }
668             }
669             None => {}
670         }
671     }
672     write!(f, "{}", name)?;
673     if needs_termination {
674         write!(f, "</a>")?;
675     }
676     Ok(())
677 }
678
679 /// Helper to render type parameters
680 fn tybounds<'a, 'tcx: 'a>(
681     bounds: &'a Vec<clean::PolyTrait>,
682     lt: &'a Option<clean::Lifetime>,
683     cx: &'a Context<'tcx>,
684 ) -> impl fmt::Display + 'a + Captures<'tcx> {
685     display_fn(move |f| {
686         for (i, bound) in bounds.iter().enumerate() {
687             if i > 0 {
688                 write!(f, " + ")?;
689             }
690
691             fmt::Display::fmt(&bound.print(cx), f)?;
692         }
693
694         if let Some(lt) = lt {
695             write!(f, " + ")?;
696             fmt::Display::fmt(&lt.print(), f)?;
697         }
698         Ok(())
699     })
700 }
701
702 crate fn anchor<'a, 'cx: 'a>(
703     did: DefId,
704     text: &'a str,
705     cx: &'cx Context<'_>,
706 ) -> impl fmt::Display + 'a {
707     let parts = href(did, cx);
708     display_fn(move |f| {
709         if let Ok((url, short_ty, fqp)) = parts {
710             write!(
711                 f,
712                 r#"<a class="{}" href="{}" title="{} {}">{}</a>"#,
713                 short_ty,
714                 url,
715                 short_ty,
716                 fqp.join("::"),
717                 text
718             )
719         } else {
720             write!(f, "{}", text)
721         }
722     })
723 }
724
725 fn fmt_type<'cx>(
726     t: &clean::Type,
727     f: &mut fmt::Formatter<'_>,
728     use_absolute: bool,
729     cx: &'cx Context<'_>,
730 ) -> fmt::Result {
731     debug!("fmt_type(t = {:?})", t);
732
733     match *t {
734         clean::Generic(name) => write!(f, "{}", name),
735         clean::ResolvedPath { did, ref path, is_generic } => {
736             // Paths like `T::Output` and `Self::Output` should be rendered with all segments.
737             resolved_path(f, did, path, is_generic, use_absolute, cx)
738         }
739         clean::DynTrait(ref bounds, ref lt) => {
740             f.write_str("dyn ")?;
741             fmt::Display::fmt(&tybounds(bounds, lt, cx), f)
742         }
743         clean::Infer => write!(f, "_"),
744         clean::Primitive(prim) => primitive_link(f, prim, &*prim.as_sym().as_str(), cx),
745         clean::BareFunction(ref decl) => {
746             if f.alternate() {
747                 write!(
748                     f,
749                     "{:#}{}{:#}fn{:#}",
750                     decl.print_hrtb_with_space(cx),
751                     decl.unsafety.print_with_space(),
752                     print_abi_with_space(decl.abi),
753                     decl.decl.print(cx),
754                 )
755             } else {
756                 write!(
757                     f,
758                     "{}{}{}",
759                     decl.print_hrtb_with_space(cx),
760                     decl.unsafety.print_with_space(),
761                     print_abi_with_space(decl.abi)
762                 )?;
763                 primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
764                 write!(f, "{}", decl.decl.print(cx))
765             }
766         }
767         clean::Tuple(ref typs) => {
768             match &typs[..] {
769                 &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
770                 &[ref one] => {
771                     primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
772                     // Carry `f.alternate()` into this display w/o branching manually.
773                     fmt::Display::fmt(&one.print(cx), f)?;
774                     primitive_link(f, PrimitiveType::Tuple, ",)", cx)
775                 }
776                 many => {
777                     primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
778                     for (i, item) in many.iter().enumerate() {
779                         if i != 0 {
780                             write!(f, ", ")?;
781                         }
782                         fmt::Display::fmt(&item.print(cx), f)?;
783                     }
784                     primitive_link(f, PrimitiveType::Tuple, ")", cx)
785                 }
786             }
787         }
788         clean::Slice(ref t) => {
789             primitive_link(f, PrimitiveType::Slice, "[", cx)?;
790             fmt::Display::fmt(&t.print(cx), f)?;
791             primitive_link(f, PrimitiveType::Slice, "]", cx)
792         }
793         clean::Array(ref t, ref n) => {
794             primitive_link(f, PrimitiveType::Array, "[", cx)?;
795             fmt::Display::fmt(&t.print(cx), f)?;
796             if f.alternate() {
797                 primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cx)
798             } else {
799                 primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cx)
800             }
801         }
802         clean::Never => primitive_link(f, PrimitiveType::Never, "!", cx),
803         clean::RawPointer(m, ref t) => {
804             let m = match m {
805                 hir::Mutability::Mut => "mut",
806                 hir::Mutability::Not => "const",
807             };
808             match **t {
809                 clean::Generic(_) | clean::ResolvedPath { is_generic: true, .. } => {
810                     if f.alternate() {
811                         primitive_link(
812                             f,
813                             clean::PrimitiveType::RawPointer,
814                             &format!("*{} {:#}", m, t.print(cx)),
815                             cx,
816                         )
817                     } else {
818                         primitive_link(
819                             f,
820                             clean::PrimitiveType::RawPointer,
821                             &format!("*{} {}", m, t.print(cx)),
822                             cx,
823                         )
824                     }
825                 }
826                 _ => {
827                     primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), cx)?;
828                     fmt::Display::fmt(&t.print(cx), f)
829                 }
830             }
831         }
832         clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
833             let lt = match l {
834                 Some(l) => format!("{} ", l.print()),
835                 _ => String::new(),
836             };
837             let m = mutability.print_with_space();
838             let amp = if f.alternate() { "&".to_string() } else { "&amp;".to_string() };
839             match **ty {
840                 clean::Slice(ref bt) => {
841                     // `BorrowedRef{ ... Slice(T) }` is `&[T]`
842                     match **bt {
843                         clean::Generic(_) => {
844                             if f.alternate() {
845                                 primitive_link(
846                                     f,
847                                     PrimitiveType::Slice,
848                                     &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cx)),
849                                     cx,
850                                 )
851                             } else {
852                                 primitive_link(
853                                     f,
854                                     PrimitiveType::Slice,
855                                     &format!("{}{}{}[{}]", amp, lt, m, bt.print(cx)),
856                                     cx,
857                                 )
858                             }
859                         }
860                         _ => {
861                             primitive_link(
862                                 f,
863                                 PrimitiveType::Slice,
864                                 &format!("{}{}{}[", amp, lt, m),
865                                 cx,
866                             )?;
867                             if f.alternate() {
868                                 write!(f, "{:#}", bt.print(cx))?;
869                             } else {
870                                 write!(f, "{}", bt.print(cx))?;
871                             }
872                             primitive_link(f, PrimitiveType::Slice, "]", cx)
873                         }
874                     }
875                 }
876                 clean::DynTrait(ref bounds, ref trait_lt)
877                     if bounds.len() > 1 || trait_lt.is_some() =>
878                 {
879                     write!(f, "{}{}{}(", amp, lt, m)?;
880                     fmt_type(&ty, f, use_absolute, cx)?;
881                     write!(f, ")")
882                 }
883                 clean::Generic(..) => {
884                     primitive_link(
885                         f,
886                         PrimitiveType::Reference,
887                         &format!("{}{}{}", amp, lt, m),
888                         cx,
889                     )?;
890                     fmt_type(&ty, f, use_absolute, cx)
891                 }
892                 _ => {
893                     write!(f, "{}{}{}", amp, lt, m)?;
894                     fmt_type(&ty, f, use_absolute, cx)
895                 }
896             }
897         }
898         clean::ImplTrait(ref bounds) => {
899             if f.alternate() {
900                 write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
901             } else {
902                 write!(f, "impl {}", print_generic_bounds(bounds, cx))
903             }
904         }
905         clean::QPath { ref name, ref self_type, ref trait_, ref self_def_id } => {
906             let should_show_cast = match *trait_ {
907                 box clean::ResolvedPath { ref path, .. } => {
908                     !path.segments.is_empty()
909                         && self_def_id
910                             .zip(trait_.def_id())
911                             .map_or(!self_type.is_self_type(), |(id, trait_)| id != trait_)
912                 }
913                 _ => true,
914             };
915             if f.alternate() {
916                 if should_show_cast {
917                     write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
918                 } else {
919                     write!(f, "{:#}::", self_type.print(cx))?
920                 }
921             } else {
922                 if should_show_cast {
923                     write!(f, "&lt;{} as {}&gt;::", self_type.print(cx), trait_.print(cx))?
924                 } else {
925                     write!(f, "{}::", self_type.print(cx))?
926                 }
927             };
928             match *trait_ {
929                 // It's pretty unsightly to look at `<A as B>::C` in output, and
930                 // we've got hyperlinking on our side, so try to avoid longer
931                 // notation as much as possible by making `C` a hyperlink to trait
932                 // `B` to disambiguate.
933                 //
934                 // FIXME: this is still a lossy conversion and there should probably
935                 //        be a better way of representing this in general? Most of
936                 //        the ugliness comes from inlining across crates where
937                 //        everything comes in as a fully resolved QPath (hard to
938                 //        look at).
939                 box clean::ResolvedPath { did, .. } => {
940                     match href(did, cx) {
941                         Ok((ref url, _, ref path)) if !f.alternate() => {
942                             write!(
943                                 f,
944                                 "<a class=\"type\" href=\"{url}#{shortty}.{name}\" \
945                                     title=\"type {path}::{name}\">{name}</a>",
946                                 url = url,
947                                 shortty = ItemType::AssocType,
948                                 name = name,
949                                 path = path.join("::")
950                             )?;
951                         }
952                         _ => write!(f, "{}", name)?,
953                     }
954                     Ok(())
955                 }
956                 _ => write!(f, "{}", name),
957             }
958         }
959     }
960 }
961
962 impl clean::Type {
963     crate fn print<'b, 'a: 'b, 'tcx: 'a>(
964         &'a self,
965         cx: &'a Context<'tcx>,
966     ) -> impl fmt::Display + 'b + Captures<'tcx> {
967         display_fn(move |f| fmt_type(self, f, false, cx))
968     }
969 }
970
971 impl clean::Impl {
972     crate fn print<'a, 'tcx: 'a>(
973         &'a self,
974         use_absolute: bool,
975         cx: &'a Context<'tcx>,
976     ) -> impl fmt::Display + 'a + Captures<'tcx> {
977         display_fn(move |f| {
978             if f.alternate() {
979                 write!(f, "impl{:#} ", self.generics.print(cx))?;
980             } else {
981                 write!(f, "impl{} ", self.generics.print(cx))?;
982             }
983
984             if let Some(ref ty) = self.trait_ {
985                 if self.negative_polarity {
986                     write!(f, "!")?;
987                 }
988                 fmt::Display::fmt(&ty.print(cx), f)?;
989                 write!(f, " for ")?;
990             }
991
992             if let Some(ref ty) = self.blanket_impl {
993                 fmt_type(ty, f, use_absolute, cx)?;
994             } else {
995                 fmt_type(&self.for_, f, use_absolute, cx)?;
996             }
997
998             fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, true), f)?;
999             Ok(())
1000         })
1001     }
1002 }
1003
1004 impl clean::Arguments {
1005     crate fn print<'a, 'tcx: 'a>(
1006         &'a self,
1007         cx: &'a Context<'tcx>,
1008     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1009         display_fn(move |f| {
1010             for (i, input) in self.values.iter().enumerate() {
1011                 if !input.name.is_empty() {
1012                     write!(f, "{}: ", input.name)?;
1013                 }
1014                 if f.alternate() {
1015                     write!(f, "{:#}", input.type_.print(cx))?;
1016                 } else {
1017                     write!(f, "{}", input.type_.print(cx))?;
1018                 }
1019                 if i + 1 < self.values.len() {
1020                     write!(f, ", ")?;
1021                 }
1022             }
1023             Ok(())
1024         })
1025     }
1026 }
1027
1028 impl clean::FnRetTy {
1029     crate fn print<'a, 'tcx: 'a>(
1030         &'a self,
1031         cx: &'a Context<'tcx>,
1032     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1033         display_fn(move |f| match self {
1034             clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()),
1035             clean::Return(ty) if f.alternate() => {
1036                 write!(f, " -> {:#}", ty.print(cx))
1037             }
1038             clean::Return(ty) => write!(f, " -&gt; {}", ty.print(cx)),
1039             clean::DefaultReturn => Ok(()),
1040         })
1041     }
1042 }
1043
1044 impl clean::BareFunctionDecl {
1045     fn print_hrtb_with_space<'a, 'tcx: 'a>(
1046         &'a self,
1047         cx: &'a Context<'tcx>,
1048     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1049         display_fn(move |f| {
1050             if !self.generic_params.is_empty() {
1051                 write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cx))))
1052             } else {
1053                 Ok(())
1054             }
1055         })
1056     }
1057 }
1058
1059 impl clean::FnDecl {
1060     crate fn print<'b, 'a: 'b, 'tcx: 'a>(
1061         &'a self,
1062         cx: &'a Context<'tcx>,
1063     ) -> impl fmt::Display + 'b + Captures<'tcx> {
1064         display_fn(move |f| {
1065             let ellipsis = if self.c_variadic { ", ..." } else { "" };
1066             if f.alternate() {
1067                 write!(
1068                     f,
1069                     "({args:#}{ellipsis}){arrow:#}",
1070                     args = self.inputs.print(cx),
1071                     ellipsis = ellipsis,
1072                     arrow = self.output.print(cx)
1073                 )
1074             } else {
1075                 write!(
1076                     f,
1077                     "({args}{ellipsis}){arrow}",
1078                     args = self.inputs.print(cx),
1079                     ellipsis = ellipsis,
1080                     arrow = self.output.print(cx)
1081                 )
1082             }
1083         })
1084     }
1085
1086     /// * `header_len`: The length of the function header and name. In other words, the number of
1087     ///   characters in the function declaration up to but not including the parentheses.
1088     ///   <br>Used to determine line-wrapping.
1089     /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
1090     ///   necessary.
1091     /// * `asyncness`: Whether the function is async or not.
1092     crate fn full_print<'a, 'tcx: 'a>(
1093         &'a self,
1094         header_len: usize,
1095         indent: usize,
1096         asyncness: hir::IsAsync,
1097         cx: &'a Context<'tcx>,
1098     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1099         display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx))
1100     }
1101
1102     fn inner_full_print(
1103         &self,
1104         header_len: usize,
1105         indent: usize,
1106         asyncness: hir::IsAsync,
1107         f: &mut fmt::Formatter<'_>,
1108         cx: &Context<'_>,
1109     ) -> fmt::Result {
1110         let amp = if f.alternate() { "&" } else { "&amp;" };
1111         let mut args = String::new();
1112         let mut args_plain = String::new();
1113         for (i, input) in self.inputs.values.iter().enumerate() {
1114             if i == 0 {
1115                 args.push_str("<br>");
1116             }
1117
1118             if let Some(selfty) = input.to_self() {
1119                 match selfty {
1120                     clean::SelfValue => {
1121                         args.push_str("self");
1122                         args_plain.push_str("self");
1123                     }
1124                     clean::SelfBorrowed(Some(ref lt), mtbl) => {
1125                         args.push_str(&format!(
1126                             "{}{} {}self",
1127                             amp,
1128                             lt.print(),
1129                             mtbl.print_with_space()
1130                         ));
1131                         args_plain.push_str(&format!(
1132                             "&{} {}self",
1133                             lt.print(),
1134                             mtbl.print_with_space()
1135                         ));
1136                     }
1137                     clean::SelfBorrowed(None, mtbl) => {
1138                         args.push_str(&format!("{}{}self", amp, mtbl.print_with_space()));
1139                         args_plain.push_str(&format!("&{}self", mtbl.print_with_space()));
1140                     }
1141                     clean::SelfExplicit(ref typ) => {
1142                         if f.alternate() {
1143                             args.push_str(&format!("self: {:#}", typ.print(cx)));
1144                         } else {
1145                             args.push_str(&format!("self: {}", typ.print(cx)));
1146                         }
1147                         args_plain.push_str(&format!("self: {:#}", typ.print(cx)));
1148                     }
1149                 }
1150             } else {
1151                 if i > 0 {
1152                     args.push_str(" <br>");
1153                     args_plain.push(' ');
1154                 }
1155                 if !input.name.is_empty() {
1156                     args.push_str(&format!("{}: ", input.name));
1157                     args_plain.push_str(&format!("{}: ", input.name));
1158                 }
1159
1160                 if f.alternate() {
1161                     args.push_str(&format!("{:#}", input.type_.print(cx)));
1162                 } else {
1163                     args.push_str(&input.type_.print(cx).to_string());
1164                 }
1165                 args_plain.push_str(&format!("{:#}", input.type_.print(cx)));
1166             }
1167             if i + 1 < self.inputs.values.len() {
1168                 args.push(',');
1169                 args_plain.push(',');
1170             }
1171         }
1172
1173         let mut args_plain = format!("({})", args_plain);
1174
1175         if self.c_variadic {
1176             args.push_str(",<br> ...");
1177             args_plain.push_str(", ...");
1178         }
1179
1180         let arrow_plain;
1181         let arrow = if let hir::IsAsync::Async = asyncness {
1182             let output = self.sugared_async_return_type();
1183             arrow_plain = format!("{:#}", output.print(cx));
1184             if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) }
1185         } else {
1186             arrow_plain = format!("{:#}", self.output.print(cx));
1187             if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) }
1188         };
1189
1190         let declaration_len = header_len + args_plain.len() + arrow_plain.len();
1191         let output = if declaration_len > 80 {
1192             let full_pad = format!("<br>{}", "&nbsp;".repeat(indent + 4));
1193             let close_pad = format!("<br>{}", "&nbsp;".repeat(indent));
1194             format!(
1195                 "({args}{close}){arrow}",
1196                 args = args.replace("<br>", &full_pad),
1197                 close = close_pad,
1198                 arrow = arrow
1199             )
1200         } else {
1201             format!("({args}){arrow}", args = args.replace("<br>", ""), arrow = arrow)
1202         };
1203
1204         if f.alternate() {
1205             write!(f, "{}", output.replace("<br>", "\n"))
1206         } else {
1207             write!(f, "{}", output)
1208         }
1209     }
1210 }
1211
1212 impl clean::Visibility {
1213     crate fn print_with_space<'a, 'tcx: 'a>(
1214         self,
1215         item_did: ItemId,
1216         cx: &'a Context<'tcx>,
1217     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1218         let to_print = match self {
1219             clean::Public => "pub ".to_owned(),
1220             clean::Inherited => String::new(),
1221             clean::Visibility::Restricted(vis_did) => {
1222                 // FIXME(camelid): This may not work correctly if `item_did` is a module.
1223                 //                 However, rustdoc currently never displays a module's
1224                 //                 visibility, so it shouldn't matter.
1225                 let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id());
1226
1227                 if vis_did.index == CRATE_DEF_INDEX {
1228                     "pub(crate) ".to_owned()
1229                 } else if parent_module == Some(vis_did) {
1230                     // `pub(in foo)` where `foo` is the parent module
1231                     // is the same as no visibility modifier
1232                     String::new()
1233                 } else if parent_module
1234                     .map(|parent| find_nearest_parent_module(cx.tcx(), parent))
1235                     .flatten()
1236                     == Some(vis_did)
1237                 {
1238                     "pub(super) ".to_owned()
1239                 } else {
1240                     let path = cx.tcx().def_path(vis_did);
1241                     debug!("path={:?}", path);
1242                     // modified from `resolved_path()` to work with `DefPathData`
1243                     let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
1244                     let anchor = anchor(vis_did, &last_name.as_str(), cx).to_string();
1245
1246                     let mut s = "pub(in ".to_owned();
1247                     for seg in &path.data[..path.data.len() - 1] {
1248                         s.push_str(&format!("{}::", seg.data.get_opt_name().unwrap()));
1249                     }
1250                     s.push_str(&format!("{}) ", anchor));
1251                     s
1252                 }
1253             }
1254         };
1255         display_fn(move |f| f.write_str(&to_print))
1256     }
1257
1258     /// This function is the same as print_with_space, except that it renders no links.
1259     /// It's used for macros' rendered source view, which is syntax highlighted and cannot have
1260     /// any HTML in it.
1261     crate fn to_src_with_space<'a, 'tcx: 'a>(
1262         self,
1263         tcx: TyCtxt<'tcx>,
1264         item_did: DefId,
1265     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1266         let to_print = match self {
1267             clean::Public => "pub ".to_owned(),
1268             clean::Inherited => String::new(),
1269             clean::Visibility::Restricted(vis_did) => {
1270                 // FIXME(camelid): This may not work correctly if `item_did` is a module.
1271                 //                 However, rustdoc currently never displays a module's
1272                 //                 visibility, so it shouldn't matter.
1273                 let parent_module = find_nearest_parent_module(tcx, item_did);
1274
1275                 if vis_did.index == CRATE_DEF_INDEX {
1276                     "pub(crate) ".to_owned()
1277                 } else if parent_module == Some(vis_did) {
1278                     // `pub(in foo)` where `foo` is the parent module
1279                     // is the same as no visibility modifier
1280                     String::new()
1281                 } else if parent_module
1282                     .map(|parent| find_nearest_parent_module(tcx, parent))
1283                     .flatten()
1284                     == Some(vis_did)
1285                 {
1286                     "pub(super) ".to_owned()
1287                 } else {
1288                     format!("pub(in {}) ", tcx.def_path_str(vis_did))
1289                 }
1290             }
1291         };
1292         display_fn(move |f| f.write_str(&to_print))
1293     }
1294 }
1295
1296 crate trait PrintWithSpace {
1297     fn print_with_space(&self) -> &str;
1298 }
1299
1300 impl PrintWithSpace for hir::Unsafety {
1301     fn print_with_space(&self) -> &str {
1302         match self {
1303             hir::Unsafety::Unsafe => "unsafe ",
1304             hir::Unsafety::Normal => "",
1305         }
1306     }
1307 }
1308
1309 impl PrintWithSpace for hir::IsAsync {
1310     fn print_with_space(&self) -> &str {
1311         match self {
1312             hir::IsAsync::Async => "async ",
1313             hir::IsAsync::NotAsync => "",
1314         }
1315     }
1316 }
1317
1318 impl PrintWithSpace for hir::Mutability {
1319     fn print_with_space(&self) -> &str {
1320         match self {
1321             hir::Mutability::Not => "",
1322             hir::Mutability::Mut => "mut ",
1323         }
1324     }
1325 }
1326
1327 crate fn print_constness_with_space(
1328     c: &hir::Constness,
1329     s: Option<&ConstStability>,
1330 ) -> &'static str {
1331     match (c, s) {
1332         // const stable or when feature(staged_api) is not set
1333         (
1334             hir::Constness::Const,
1335             Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }),
1336         )
1337         | (hir::Constness::Const, None) => "const ",
1338         // const unstable or not const
1339         _ => "",
1340     }
1341 }
1342
1343 impl clean::Import {
1344     crate fn print<'a, 'tcx: 'a>(
1345         &'a self,
1346         cx: &'a Context<'tcx>,
1347     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1348         display_fn(move |f| match self.kind {
1349             clean::ImportKind::Simple(name) => {
1350                 if name == self.source.path.last() {
1351                     write!(f, "use {};", self.source.print(cx))
1352                 } else {
1353                     write!(f, "use {} as {};", self.source.print(cx), name)
1354                 }
1355             }
1356             clean::ImportKind::Glob => {
1357                 if self.source.path.segments.is_empty() {
1358                     write!(f, "use *;")
1359                 } else {
1360                     write!(f, "use {}::*;", self.source.print(cx))
1361                 }
1362             }
1363         })
1364     }
1365 }
1366
1367 impl clean::ImportSource {
1368     crate fn print<'a, 'tcx: 'a>(
1369         &'a self,
1370         cx: &'a Context<'tcx>,
1371     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1372         display_fn(move |f| match self.did {
1373             Some(did) => resolved_path(f, did, &self.path, true, false, cx),
1374             _ => {
1375                 for seg in &self.path.segments[..self.path.segments.len() - 1] {
1376                     write!(f, "{}::", seg.name)?;
1377                 }
1378                 let name = self.path.last_name();
1379                 if let hir::def::Res::PrimTy(p) = self.path.res {
1380                     primitive_link(f, PrimitiveType::from(p), &*name, cx)?;
1381                 } else {
1382                     write!(f, "{}", name)?;
1383                 }
1384                 Ok(())
1385             }
1386         })
1387     }
1388 }
1389
1390 impl clean::TypeBinding {
1391     crate fn print<'a, 'tcx: 'a>(
1392         &'a self,
1393         cx: &'a Context<'tcx>,
1394     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1395         display_fn(move |f| {
1396             f.write_str(&*self.name.as_str())?;
1397             match self.kind {
1398                 clean::TypeBindingKind::Equality { ref ty } => {
1399                     if f.alternate() {
1400                         write!(f, " = {:#}", ty.print(cx))?;
1401                     } else {
1402                         write!(f, " = {}", ty.print(cx))?;
1403                     }
1404                 }
1405                 clean::TypeBindingKind::Constraint { ref bounds } => {
1406                     if !bounds.is_empty() {
1407                         if f.alternate() {
1408                             write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
1409                         } else {
1410                             write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
1411                         }
1412                     }
1413                 }
1414             }
1415             Ok(())
1416         })
1417     }
1418 }
1419
1420 crate fn print_abi_with_space(abi: Abi) -> impl fmt::Display {
1421     display_fn(move |f| {
1422         let quot = if f.alternate() { "\"" } else { "&quot;" };
1423         match abi {
1424             Abi::Rust => Ok(()),
1425             abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
1426         }
1427     })
1428 }
1429
1430 crate fn print_default_space<'a>(v: bool) -> &'a str {
1431     if v { "default " } else { "" }
1432 }
1433
1434 impl clean::GenericArg {
1435     crate fn print<'a, 'tcx: 'a>(
1436         &'a self,
1437         cx: &'a Context<'tcx>,
1438     ) -> impl fmt::Display + 'a + Captures<'tcx> {
1439         display_fn(move |f| match self {
1440             clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(&lt.print(), f),
1441             clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
1442             clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
1443             clean::GenericArg::Infer => fmt::Display::fmt("_", f),
1444         })
1445     }
1446 }
1447
1448 crate fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Display {
1449     struct WithFormatter<F>(Cell<Option<F>>);
1450
1451     impl<F> fmt::Display for WithFormatter<F>
1452     where
1453         F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
1454     {
1455         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1456             (self.0.take()).unwrap()(f)
1457         }
1458     }
1459
1460     WithFormatter(Cell::new(Some(f)))
1461 }