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