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