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