]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/format.rs
Rollup merge of #92340 - camelid:search-index-cleanup, r=GuillaumeGomez
[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::{
25     self, types::ExternalLocation, utils::find_nearest_parent_module, ExternalCrate, ItemId,
26     PrimitiveType,
27 };
28 use crate::formats::item_type::ItemType;
29 use crate::html::escape::Escape;
30 use crate::html::render::Context;
31
32 use super::url_parts_builder::UrlPartsBuilder;
33
34 crate trait Print {
35     fn print(self, buffer: &mut Buffer);
36 }
37
38 impl<F> Print for F
39 where
40     F: FnOnce(&mut Buffer),
41 {
42     fn print(self, buffer: &mut Buffer) {
43         (self)(buffer)
44     }
45 }
46
47 impl Print for String {
48     fn print(self, buffer: &mut Buffer) {
49         buffer.write_str(&self);
50     }
51 }
52
53 impl Print for &'_ str {
54     fn print(self, buffer: &mut Buffer) {
55         buffer.write_str(self);
56     }
57 }
58
59 #[derive(Debug, Clone)]
60 crate struct Buffer {
61     for_html: bool,
62     buffer: String,
63 }
64
65 impl Buffer {
66     crate fn empty_from(v: &Buffer) -> Buffer {
67         Buffer { for_html: v.for_html, buffer: String::new() }
68     }
69
70     crate fn html() -> Buffer {
71         Buffer { for_html: true, buffer: String::new() }
72     }
73
74     crate fn new() -> Buffer {
75         Buffer { for_html: false, buffer: String::new() }
76     }
77
78     crate fn is_empty(&self) -> bool {
79         self.buffer.is_empty()
80     }
81
82     crate fn into_inner(self) -> String {
83         self.buffer
84     }
85
86     crate fn insert_str(&mut self, idx: usize, s: &str) {
87         self.buffer.insert_str(idx, s);
88     }
89
90     crate fn push_str(&mut self, s: &str) {
91         self.buffer.push_str(s);
92     }
93
94     crate fn push_buffer(&mut self, other: Buffer) {
95         self.buffer.push_str(&other.buffer);
96     }
97
98     // Intended for consumption by write! and writeln! (std::fmt) but without
99     // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
100     // import).
101     crate fn write_str(&mut self, s: &str) {
102         self.buffer.push_str(s);
103     }
104
105     // Intended for consumption by write! and writeln! (std::fmt) but without
106     // the fmt::Result return type imposed by fmt::Write (and avoiding the trait
107     // import).
108     crate fn write_fmt(&mut self, v: fmt::Arguments<'_>) {
109         use fmt::Write;
110         self.buffer.write_fmt(v).unwrap();
111     }
112
113     crate fn to_display<T: Print>(mut self, t: T) -> String {
114         t.print(&mut self);
115         self.into_inner()
116     }
117
118     crate fn is_for_html(&self) -> bool {
119         self.for_html
120     }
121
122     crate fn reserve(&mut self, additional: usize) {
123         self.buffer.reserve(additional)
124     }
125 }
126
127 fn comma_sep<T: fmt::Display>(items: impl Iterator<Item = T>) -> impl fmt::Display {
128     display_fn(move |f| {
129         for (i, item) in items.enumerate() {
130             if i != 0 {
131                 write!(f, ", ")?;
132             }
133             fmt::Display::fmt(&item, f)?;
134         }
135         Ok(())
136     })
137 }
138
139 crate fn print_generic_bounds<'a, 'tcx: 'a>(
140     bounds: &'a [clean::GenericBound],
141     cx: &'a Context<'tcx>,
142 ) -> impl fmt::Display + 'a + Captures<'tcx> {
143     display_fn(move |f| {
144         let mut bounds_dup = FxHashSet::default();
145
146         for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(b.clone())).enumerate() {
147             if i > 0 {
148                 f.write_str(" + ")?;
149             }
150             fmt::Display::fmt(&bound.print(cx), f)?;
151         }
152         Ok(())
153     })
154 }
155
156 impl clean::GenericParamDef {
157     crate fn print<'a, 'tcx: 'a>(
158         &'a self,
159         cx: &'a Context<'tcx>,
160     ) -> impl fmt::Display + 'a + Captures<'tcx> {
161         display_fn(move |f| match &self.kind {
162             clean::GenericParamDefKind::Lifetime { outlives } => {
163                 write!(f, "{}", self.name)?;
164
165                 if !outlives.is_empty() {
166                     f.write_str(": ")?;
167                     for (i, lt) in outlives.iter().enumerate() {
168                         if i != 0 {
169                             f.write_str(" + ")?;
170                         }
171                         write!(f, "{}", lt.print())?;
172                     }
173                 }
174
175                 Ok(())
176             }
177             clean::GenericParamDefKind::Type { bounds, default, .. } => {
178                 f.write_str(self.name.as_str())?;
179
180                 if !bounds.is_empty() {
181                     if f.alternate() {
182                         write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
183                     } else {
184                         write!(f, ":&nbsp;{}", print_generic_bounds(bounds, cx))?;
185                     }
186                 }
187
188                 if let Some(ref ty) = default {
189                     if f.alternate() {
190                         write!(f, " = {:#}", ty.print(cx))?;
191                     } else {
192                         write!(f, "&nbsp;=&nbsp;{}", ty.print(cx))?;
193                     }
194                 }
195
196                 Ok(())
197             }
198             clean::GenericParamDefKind::Const { ty, default, .. } => {
199                 if f.alternate() {
200                     write!(f, "const {}: {:#}", self.name, ty.print(cx))?;
201                 } else {
202                     write!(f, "const {}:&nbsp;{}", self.name, ty.print(cx))?;
203                 }
204
205                 if let Some(default) = default {
206                     if f.alternate() {
207                         write!(f, " = {:#}", default)?;
208                     } else {
209                         write!(f, "&nbsp;=&nbsp;{}", default)?;
210                     }
211                 }
212
213                 Ok(())
214             }
215         })
216     }
217 }
218
219 impl clean::Generics {
220     crate fn print<'a, 'tcx: 'a>(
221         &'a self,
222         cx: &'a Context<'tcx>,
223     ) -> impl fmt::Display + 'a + Captures<'tcx> {
224         display_fn(move |f| {
225             let mut real_params =
226                 self.params.iter().filter(|p| !p.is_synthetic_type_param()).peekable();
227             if real_params.peek().is_none() {
228                 return Ok(());
229             }
230
231             if f.alternate() {
232                 write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx))))
233             } else {
234                 write!(f, "&lt;{}&gt;", comma_sep(real_params.map(|g| g.print(cx))))
235             }
236         })
237     }
238 }
239
240 /// * The Generics from which to emit a where-clause.
241 /// * The number of spaces to indent each line with.
242 /// * Whether the where-clause needs to add a comma and newline after the last bound.
243 crate fn print_where_clause<'a, 'tcx: 'a>(
244     gens: &'a clean::Generics,
245     cx: &'a Context<'tcx>,
246     indent: usize,
247     end_newline: bool,
248 ) -> impl fmt::Display + 'a + Captures<'tcx> {
249     display_fn(move |f| {
250         if gens.where_predicates.is_empty() {
251             return Ok(());
252         }
253         let mut clause = String::new();
254         if f.alternate() {
255             clause.push_str(" where");
256         } else {
257             if end_newline {
258                 clause.push_str(" <span class=\"where fmt-newline\">where");
259             } else {
260                 clause.push_str(" <span class=\"where\">where");
261             }
262         }
263         for (i, pred) in gens.where_predicates.iter().enumerate() {
264             if f.alternate() {
265                 clause.push(' ');
266             } else {
267                 clause.push_str("<br>");
268             }
269
270             match pred {
271                 clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
272                     let bounds = bounds;
273                     let for_prefix = match bound_params.len() {
274                         0 => String::new(),
275                         _ if f.alternate() => {
276                             format!(
277                                 "for&lt;{:#}&gt; ",
278                                 comma_sep(bound_params.iter().map(|lt| lt.print()))
279                             )
280                         }
281                         _ => format!(
282                             "for&lt;{}&gt; ",
283                             comma_sep(bound_params.iter().map(|lt| lt.print()))
284                         ),
285                     };
286
287                     if f.alternate() {
288                         clause.push_str(&format!(
289                             "{}{:#}: {:#}",
290                             for_prefix,
291                             ty.print(cx),
292                             print_generic_bounds(bounds, cx)
293                         ));
294                     } else {
295                         clause.push_str(&format!(
296                             "{}{}: {}",
297                             for_prefix,
298                             ty.print(cx),
299                             print_generic_bounds(bounds, cx)
300                         ));
301                     }
302                 }
303                 clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
304                     clause.push_str(&format!(
305                         "{}: {}",
306                         lifetime.print(),
307                         bounds
308                             .iter()
309                             .map(|b| b.print(cx).to_string())
310                             .collect::<Vec<_>>()
311                             .join(" + ")
312                     ));
313                 }
314                 clean::WherePredicate::EqPredicate { lhs, rhs } => {
315                     if f.alternate() {
316                         clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),));
317                     } else {
318                         clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),));
319                     }
320                 }
321             }
322
323             if i < gens.where_predicates.len() - 1 || end_newline {
324                 clause.push(',');
325             }
326         }
327
328         if end_newline {
329             // add a space so stripping <br> tags and breaking spaces still renders properly
330             if f.alternate() {
331                 clause.push(' ');
332             } else {
333                 clause.push_str("&nbsp;");
334             }
335         }
336
337         if !f.alternate() {
338             clause.push_str("</span>");
339             let padding = "&nbsp;".repeat(indent + 4);
340             clause = clause.replace("<br>", &format!("<br>{}", padding));
341             clause.insert_str(0, &"&nbsp;".repeat(indent.saturating_sub(1)));
342             if !end_newline {
343                 clause.insert_str(0, "<br>");
344             }
345         }
346         write!(f, "{}", clause)
347     })
348 }
349
350 impl clean::Lifetime {
351     crate fn print(&self) -> impl fmt::Display + '_ {
352         self.0.as_str()
353     }
354 }
355
356 impl clean::Constant {
357     crate fn print(&self, tcx: TyCtxt<'_>) -> impl fmt::Display + '_ {
358         let expr = self.expr(tcx);
359         display_fn(
360             move |f| {
361                 if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) }
362             },
363         )
364     }
365 }
366
367 impl clean::PolyTrait {
368     fn print<'a, 'tcx: 'a>(
369         &'a self,
370         cx: &'a Context<'tcx>,
371     ) -> impl fmt::Display + 'a + Captures<'tcx> {
372         display_fn(move |f| {
373             if !self.generic_params.is_empty() {
374                 if f.alternate() {
375                     write!(
376                         f,
377                         "for<{:#}> ",
378                         comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
379                     )?;
380                 } else {
381                     write!(
382                         f,
383                         "for&lt;{}&gt; ",
384                         comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
385                     )?;
386                 }
387             }
388             if f.alternate() {
389                 write!(f, "{:#}", self.trait_.print(cx))
390             } else {
391                 write!(f, "{}", self.trait_.print(cx))
392             }
393         })
394     }
395 }
396
397 impl clean::GenericBound {
398     crate fn print<'a, 'tcx: 'a>(
399         &'a self,
400         cx: &'a Context<'tcx>,
401     ) -> impl fmt::Display + 'a + Captures<'tcx> {
402         display_fn(move |f| match self {
403             clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
404             clean::GenericBound::TraitBound(ty, modifier) => {
405                 let modifier_str = match modifier {
406                     hir::TraitBoundModifier::None => "",
407                     hir::TraitBoundModifier::Maybe => "?",
408                     hir::TraitBoundModifier::MaybeConst => "~const",
409                 };
410                 if f.alternate() {
411                     write!(f, "{}{:#}", modifier_str, ty.print(cx))
412                 } else {
413                     write!(f, "{}{}", modifier_str, ty.print(cx))
414                 }
415             }
416         })
417     }
418 }
419
420 impl clean::GenericArgs {
421     fn print<'a, 'tcx: 'a>(
422         &'a self,
423         cx: &'a Context<'tcx>,
424     ) -> impl fmt::Display + 'a + Captures<'tcx> {
425         display_fn(move |f| {
426             match self {
427                 clean::GenericArgs::AngleBracketed { args, bindings } => {
428                     if !args.is_empty() || !bindings.is_empty() {
429                         if f.alternate() {
430                             f.write_str("<")?;
431                         } else {
432                             f.write_str("&lt;")?;
433                         }
434                         let mut comma = false;
435                         for arg in args {
436                             if comma {
437                                 f.write_str(", ")?;
438                             }
439                             comma = true;
440                             if f.alternate() {
441                                 write!(f, "{:#}", arg.print(cx))?;
442                             } else {
443                                 write!(f, "{}", arg.print(cx))?;
444                             }
445                         }
446                         for binding in bindings {
447                             if comma {
448                                 f.write_str(", ")?;
449                             }
450                             comma = true;
451                             if f.alternate() {
452                                 write!(f, "{:#}", binding.print(cx))?;
453                             } else {
454                                 write!(f, "{}", binding.print(cx))?;
455                             }
456                         }
457                         if f.alternate() {
458                             f.write_str(">")?;
459                         } else {
460                             f.write_str("&gt;")?;
461                         }
462                     }
463                 }
464                 clean::GenericArgs::Parenthesized { inputs, output } => {
465                     f.write_str("(")?;
466                     let mut comma = false;
467                     for ty in inputs {
468                         if comma {
469                             f.write_str(", ")?;
470                         }
471                         comma = true;
472                         if f.alternate() {
473                             write!(f, "{:#}", ty.print(cx))?;
474                         } else {
475                             write!(f, "{}", ty.print(cx))?;
476                         }
477                     }
478                     f.write_str(")")?;
479                     if let Some(ref ty) = *output {
480                         if f.alternate() {
481                             write!(f, " -> {:#}", ty.print(cx))?;
482                         } else {
483                             write!(f, " -&gt; {}", ty.print(cx))?;
484                         }
485                     }
486                 }
487             }
488             Ok(())
489         })
490     }
491 }
492
493 // Possible errors when computing href link source for a `DefId`
494 crate enum HrefError {
495     /// This item is known to rustdoc, but from a crate that does not have documentation generated.
496     ///
497     /// This can only happen for non-local items.
498     DocumentationNotBuilt,
499     /// This can only happen for non-local items when `--document-private-items` is not passed.
500     Private,
501     // Not in external cache, href link should be in same page
502     NotInExternalCache,
503 }
504
505 crate fn href_with_root_path(
506     did: DefId,
507     cx: &Context<'_>,
508     root_path: Option<&str>,
509 ) -> Result<(String, ItemType, Vec<String>), HrefError> {
510     let tcx = cx.tcx();
511     let def_kind = tcx.def_kind(did);
512     let did = match def_kind {
513         DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => {
514             // documented on their parent's page
515             tcx.parent(did).unwrap()
516         }
517         _ => did,
518     };
519     let cache = cx.cache();
520     let relative_to = &cx.current;
521     fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
522         if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
523     }
524
525     if !did.is_local()
526         && !cache.access_levels.is_public(did)
527         && !cache.document_private
528         && !cache.primitive_locations.values().any(|&id| id == did)
529     {
530         return Err(HrefError::Private);
531     }
532
533     let mut is_remote = false;
534     let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
535         Some(&(ref fqp, shortty)) => (fqp, shortty, {
536             let module_fqp = to_module_fqp(shortty, fqp);
537             debug!(?fqp, ?shortty, ?module_fqp);
538             href_relative_parts(module_fqp, relative_to)
539         }),
540         None => {
541             if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
542                 let module_fqp = to_module_fqp(shortty, fqp);
543                 (
544                     fqp,
545                     shortty,
546                     match cache.extern_locations[&did.krate] {
547                         ExternalLocation::Remote(ref s) => {
548                             is_remote = true;
549                             let s = s.trim_end_matches('/');
550                             let mut builder = UrlPartsBuilder::singleton(s);
551                             builder.extend(module_fqp.iter().map(String::as_str));
552                             builder
553                         }
554                         ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
555                         ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
556                     },
557                 )
558             } else {
559                 return Err(HrefError::NotInExternalCache);
560             }
561         }
562     };
563     if !is_remote {
564         if let Some(root_path) = root_path {
565             let root = root_path.trim_end_matches('/');
566             url_parts.push_front(root);
567         }
568     }
569     debug!(?url_parts);
570     let last = &fqp.last().unwrap()[..];
571     match shortty {
572         ItemType::Module => {
573             url_parts.push("index.html");
574         }
575         _ => {
576             let filename = format!("{}.{}.html", shortty.as_str(), last);
577             url_parts.push(&filename);
578         }
579     }
580     Ok((url_parts.finish(), shortty, fqp.to_vec()))
581 }
582
583 crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
584     href_with_root_path(did, cx, None)
585 }
586
587 /// Both paths should only be modules.
588 /// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
589 /// both need `../iter/trait.Iterator.html` to get at the iterator trait.
590 crate fn href_relative_parts(fqp: &[String], relative_to_fqp: &[String]) -> UrlPartsBuilder {
591     for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
592         // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
593         if f != r {
594             let dissimilar_part_count = relative_to_fqp.len() - i;
595             let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str);
596             return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect();
597         }
598     }
599     // e.g. linking to std::sync::atomic from std::sync
600     if relative_to_fqp.len() < fqp.len() {
601         fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect()
602     // e.g. linking to std::sync from std::sync::atomic
603     } else if fqp.len() < relative_to_fqp.len() {
604         let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
605         iter::repeat("..").take(dissimilar_part_count).collect()
606     // linking to the same module
607     } else {
608         UrlPartsBuilder::new()
609     }
610 }
611
612 /// Used to render a [`clean::Path`].
613 fn resolved_path<'cx>(
614     w: &mut fmt::Formatter<'_>,
615     did: DefId,
616     path: &clean::Path,
617     print_all: bool,
618     use_absolute: bool,
619     cx: &'cx Context<'_>,
620 ) -> fmt::Result {
621     let last = path.segments.last().unwrap();
622
623     if print_all {
624         for seg in &path.segments[..path.segments.len() - 1] {
625             write!(w, "{}::", seg.name)?;
626         }
627     }
628     if w.alternate() {
629         write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
630     } else {
631         let path = if use_absolute {
632             if let Ok((_, _, fqp)) = href(did, cx) {
633                 format!(
634                     "{}::{}",
635                     fqp[..fqp.len() - 1].join("::"),
636                     anchor(did, fqp.last().unwrap(), cx)
637                 )
638             } else {
639                 last.name.to_string()
640             }
641         } else {
642             anchor(did, last.name.as_str(), cx).to_string()
643         };
644         write!(w, "{}{}", path, last.args.print(cx))?;
645     }
646     Ok(())
647 }
648
649 fn primitive_link(
650     f: &mut fmt::Formatter<'_>,
651     prim: clean::PrimitiveType,
652     name: &str,
653     cx: &Context<'_>,
654 ) -> fmt::Result {
655     let m = &cx.cache();
656     let mut needs_termination = false;
657     if !f.alternate() {
658         match m.primitive_locations.get(&prim) {
659             Some(&def_id) if def_id.is_local() => {
660                 let len = cx.current.len();
661                 let len = if len == 0 { 0 } else { len - 1 };
662                 write!(
663                     f,
664                     "<a class=\"primitive\" href=\"{}primitive.{}.html\">",
665                     "../".repeat(len),
666                     prim.as_sym()
667                 )?;
668                 needs_termination = true;
669             }
670             Some(&def_id) => {
671                 let cname_sym;
672                 let loc = match m.extern_locations[&def_id.krate] {
673                     ExternalLocation::Remote(ref s) => {
674                         cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
675                         Some(vec![s.trim_end_matches('/'), cname_sym.as_str()])
676                     }
677                     ExternalLocation::Local => {
678                         cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
679                         Some(if cx.current.first().map(|x| &x[..]) == Some(cname_sym.as_str()) {
680                             iter::repeat("..").take(cx.current.len() - 1).collect()
681                         } else {
682                             let cname = iter::once(cname_sym.as_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();
1405                 if let hir::def::Res::PrimTy(p) = self.path.res {
1406                     primitive_link(f, PrimitiveType::from(p), name.as_str(), 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 }