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