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