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