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