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