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