1 //! HTML formatting module
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.
12 use rustc_data_structures
::captures
::Captures
;
13 use rustc_data_structures
::fx
::FxHashSet
;
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
;
21 self, utils
::find_nearest_parent_module
, ExternalCrate
, FakeDefId
, GetDefId
, PrimitiveType
,
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
;
29 fn print(self, buffer
: &mut Buffer
);
34 F
: FnOnce(&mut Buffer
),
36 fn print(self, buffer
: &mut Buffer
) {
41 impl Print
for String
{
42 fn print(self, buffer
: &mut Buffer
) {
43 buffer
.write_str(&self);
47 impl Print
for &'_
str {
48 fn print(self, buffer
: &mut Buffer
) {
49 buffer
.write_str(self);
53 #[derive(Debug, Clone)]
60 crate fn empty_from(v
: &Buffer
) -> Buffer
{
61 Buffer { for_html: v.for_html, buffer: String::new() }
64 crate fn html() -> Buffer
{
65 Buffer { for_html: true, buffer: String::new() }
68 crate fn new() -> Buffer
{
69 Buffer { for_html: false, buffer: String::new() }
72 crate fn is_empty(&self) -> bool
{
73 self.buffer
.is_empty()
76 crate fn into_inner(self) -> String
{
80 crate fn insert_str(&mut self, idx
: usize, s
: &str) {
81 self.buffer
.insert_str(idx
, s
);
84 crate fn push_str(&mut self, s
: &str) {
85 self.buffer
.push_str(s
);
88 crate fn push_buffer(&mut self, other
: Buffer
) {
89 self.buffer
.push_str(&other
.buffer
);
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
95 crate fn write_str(&mut self, s
: &str) {
96 self.buffer
.push_str(s
);
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
102 crate fn write_fmt(&mut self, v
: fmt
::Arguments
<'_
>) {
104 self.buffer
.write_fmt(v
).unwrap();
107 crate fn to_display
<T
: Print
>(mut self, t
: T
) -> String
{
112 crate fn is_for_html(&self) -> bool
{
116 crate fn reserve(&mut self, additional
: usize) {
117 self.buffer
.reserve(additional
)
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() {
127 fmt
::Display
::fmt(&item
, f
)?
;
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();
141 bounds
.iter().filter(|b
| bounds_dup
.insert(b
.print(cx
).to_string())).enumerate()
146 fmt
::Display
::fmt(&bound
.print(cx
), f
)?
;
152 impl clean
::GenericParamDef
{
153 crate fn print
<'a
, 'tcx
: 'a
>(
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())?
;
162 if !bounds
.is_empty() {
164 write
!(f
, ": {:#}", print_generic_bounds(bounds
, cx
))?
;
166 write
!(f
, ": {}", print_generic_bounds(bounds
, cx
))?
;
170 if let Some(ref ty
) = default {
172 write
!(f
, " = {:#}", ty
.print(cx
))?
;
174 write
!(f
, " = {}", ty
.print(cx
))?
;
180 clean
::GenericParamDefKind
::Const { ref ty, ref default, .. }
=> {
182 write
!(f
, "const {}: {:#}", self.name
, ty
.print(cx
))?
;
184 write
!(f
, "const {}: {}", self.name
, ty
.print(cx
))?
;
187 if let Some(default) = default {
189 write
!(f
, " = {:#}", default)?
;
191 write
!(f
, " = {}", default)?
;
201 impl clean
::Generics
{
202 crate fn print
<'a
, 'tcx
: 'a
>(
204 cx
: &'a Context
<'tcx
>,
205 ) -> impl fmt
::Display
+ 'a
+ Captures
<'tcx
> {
206 display_fn(move |f
| {
208 self.params
.iter().filter(|p
| !p
.is_synthetic_type_param()).collect
::<Vec
<_
>>();
209 if real_params
.is_empty() {
213 write
!(f
, "<{:#}>", comma_sep(real_params
.iter().map(|g
| g
.print(cx
))))
215 write
!(f
, "<{}>", comma_sep(real_params
.iter().map(|g
| g
.print(cx
))))
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
>,
229 ) -> impl fmt
::Display
+ 'a
+ Captures
<'tcx
> {
230 display_fn(move |f
| {
231 if gens
.where_predicates
.is_empty() {
234 let mut clause
= String
::new();
236 clause
.push_str(" where");
239 clause
.push_str(" <span class=\"where fmt-newline\">where");
241 clause
.push_str(" <span class=\"where\">where");
244 for (i
, pred
) in gens
.where_predicates
.iter().enumerate() {
248 clause
.push_str("<br>");
252 clean
::WherePredicate
::BoundPredicate { ty, bounds }
=> {
255 clause
.push_str(&format
!(
258 print_generic_bounds(bounds
, cx
)
261 clause
.push_str(&format
!(
264 print_generic_bounds(bounds
, cx
)
268 clean
::WherePredicate
::RegionPredicate { lifetime, bounds }
=> {
269 clause
.push_str(&format
!(
274 .map(|b
| b
.print(cx
).to_string())
279 clean
::WherePredicate
::EqPredicate { lhs, rhs }
=> {
281 clause
.push_str(&format
!("{:#} == {:#}", lhs
.print(cx
), rhs
.print(cx
),));
283 clause
.push_str(&format
!("{} == {}", lhs
.print(cx
), rhs
.print(cx
),));
288 if i
< gens
.where_predicates
.len() - 1 || end_newline
{
294 // add a space so stripping <br> tags and breaking spaces still renders properly
298 clause
.push_str(" ");
303 clause
.push_str("</span>");
304 let padding
= " ".repeat(indent
+ 4);
305 clause
= clause
.replace("<br>", &format
!("<br>{}", padding
));
306 clause
.insert_str(0, &" ".repeat(indent
.saturating_sub(1)));
308 clause
.insert_str(0, "<br>");
311 write
!(f
, "{}", clause
)
315 impl clean
::Lifetime
{
316 crate fn print(&self) -> impl fmt
::Display
+ '_
{
321 impl clean
::Constant
{
322 crate fn print(&self, tcx
: TyCtxt
<'_
>) -> impl fmt
::Display
+ '_
{
323 let expr
= self.expr(tcx
);
326 if f
.alternate() { f.write_str(&expr) }
else { write!(f, "{}
", Escape(&expr)) }
332 impl clean::PolyTrait {
333 fn print<'a, 'tcx: 'a>(
335 cx: &'a Context<'tcx>,
336 ) -> impl fmt::Display + 'a + Captures<'tcx> {
337 display_fn(move |f| {
338 if !self.generic_params.is_empty() {
343 comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
349 comma_sep(self.generic_params.iter().map(|g| g.print(cx)))
354 write!(f, "{:#}
", self.trait_.print(cx))
356 write!(f, "{}
", self.trait_.print(cx))
362 impl clean::GenericBound {
363 crate fn print<'a, 'tcx: 'a>(
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",
376 write!(f, "{}{:#}
", modifier_str, ty.print(cx))
378 write!(f, "{}{}
", modifier_str, ty.print(cx))
385 impl clean::GenericArgs {
386 fn print<'a, 'tcx: 'a>(
388 cx: &'a Context<'tcx>,
389 ) -> impl fmt::Display + 'a + Captures<'tcx> {
390 display_fn(move |f| {
392 clean::GenericArgs::AngleBracketed { args, bindings } => {
393 if !args.is_empty() || !bindings.is_empty() {
397 f.write_str("<
;")?;
399 let mut comma = false;
406 write!(f, "{:#}
", arg.print(cx))?;
408 write!(f, "{}
", arg.print(cx))?;
411 for binding in bindings {
417 write!(f, "{:#}
", binding.print(cx))?;
419 write!(f, "{}
", binding.print(cx))?;
425 f.write_str(">
;")?;
429 clean::GenericArgs::Parenthesized { inputs, output } => {
431 let mut comma = false;
438 write!(f, "{:#}
", ty.print(cx))?;
440 write!(f, "{}
", ty.print(cx))?;
444 if let Some(ref ty) = *output {
446 write!(f, " -> {:#}
", ty.print(cx))?;
448 write!(f, " ->
; {}
", ty.print(cx))?;
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] }
465 if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
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)
475 let &(ref fqp, shortty) = cache.external_paths.get(&did)?;
476 let module_fqp = to_module_fqp(shortty, fqp);
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));
487 ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
488 ExternalLocation::Unknown => return None,
493 let last = &fqp.last().unwrap()[..];
496 ItemType::Module => {
497 url_parts.push("index
.html
");
500 filename = format!("{}
.{}
.html
", shortty.as_str(), last);
501 url_parts.push(&filename);
504 Some((url_parts.join("/"), shortty, fqp.to_vec()))
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)
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();
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
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<'_>,
540 cx: &'cx Context<'_>,
542 let last = path.segments.last().unwrap();
545 for seg in &path.segments[..path.segments.len() - 1] {
546 write!(w, "{}
::", seg.name)?;
550 write!(w, "{}{:#}
", &last.name, last.args.print(cx))?;
552 let path = if use_absolute {
553 if let Some((_, _, fqp)) = href(did, cx) {
556 fqp[..fqp.len() - 1].join("::"),
557 anchor(did, fqp.last().unwrap(), cx)
560 last.name.to_string()
563 anchor(did, &*last.name.as_str(), cx).to_string()
565 write!(w, "{}{}
", path, last.args.print(cx))?;
571 f: &mut fmt::Formatter<'_>,
572 prim: clean::PrimitiveType,
577 let mut needs_termination = false;
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 };
585 "<a class
=\"primitive
\" href
=\"{}primitive
.{}
.html
\">",
589 needs_termination = true;
593 let loc = match m.extern_locations[&def_id.krate] {
594 ExternalLocation::Remote(ref s) => {
596 ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()).as_str();
597 Some(vec![s.trim_end_matches('/'), &cname_str[..]])
599 ExternalLocation::Local => {
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()
605 let cname = iter::once(&cname_str[..]);
606 iter::repeat("..").take(cx.current.len()).chain(cname).collect()
609 ExternalLocation::Unknown => None,
611 if let Some(loc) = loc {
614 "<a class
=\"primitive
\" href
=\"{}
/primitive
.{}
.html
\">",
618 needs_termination = true;
624 write!(f, "{}
", name)?;
625 if needs_termination {
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 {
640 fmt::Display::fmt(¶m.print(cx), f)?;
648 crate fn anchor<'a, 'cx: 'a>(
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 {
658 r#"<a class
="{}" href
="{}" title
="{} {}">{}
</a
>"#,
666 write!(f, "{}
", text)
673 f: &mut fmt::Formatter<'_>,
675 cx: &'cx Context<'_>,
677 debug!("fmt_type(t
= {:?}
)", 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 ")?;
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)
689 clean::Infer => write!(f, "_
"),
690 clean::Primitive(prim) => primitive_link(f, prim, &*prim.as_sym().as_str(), cx),
691 clean::BareFunction(ref decl) => {
696 decl.print_hrtb_with_space(cx),
697 decl.unsafety.print_with_space(),
698 print_abi_with_space(decl.abi),
705 decl.print_hrtb_with_space(cx),
706 decl.unsafety.print_with_space(),
707 print_abi_with_space(decl.abi)
709 primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
710 write!(f, "{}
", decl.decl.print(cx))
713 clean::Tuple(ref typs) => {
715 &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
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)
723 primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
724 for (i, item) in many.iter().enumerate() {
728 fmt::Display::fmt(&item.print(cx), f)?;
730 primitive_link(f, PrimitiveType::Tuple, ")", cx)
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)
739 clean::Array(ref t, ref n) => {
740 primitive_link(f, PrimitiveType::Array, "[", cx)?;
741 fmt::Display::fmt(&t.print(cx), f)?;
743 primitive_link(f, PrimitiveType::Array, &format!("; {}
]", n), cx)
745 primitive_link(f, PrimitiveType::Array, &format!("; {}
]", Escape(n)), cx)
748 clean::Never => primitive_link(f, PrimitiveType::Never, "!", cx),
749 clean::RawPointer(m, ref t) => {
751 hir::Mutability::Mut => "mut",
752 hir::Mutability::Not => "const",
755 clean::Generic(_) | clean::ResolvedPath { is_generic: true, .. } => {
759 clean::PrimitiveType::RawPointer,
760 &format!("*{} {:#}
", m, t.print(cx)),
766 clean::PrimitiveType::RawPointer,
767 &format!("*{} {}
", m, t.print(cx)),
773 primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{}
", m), cx)?;
774 fmt::Display::fmt(&t.print(cx), f)
778 clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
780 Some(l) => format!("{}
", l.print()),
783 let m = mutability.print_with_space();
784 let amp = if f.alternate() { "&".to_string() } else { "&".to_string() };
786 clean::Slice(ref bt) => {
787 // `BorrowedRef{ ... Slice(T) }` is `&[T]`
789 clean::Generic(_) => {
793 PrimitiveType::Slice,
794 &format!("{}{}{}
[{:#}
]", amp, lt, m, bt.print(cx)),
800 PrimitiveType::Slice,
801 &format!("{}{}{}
[{}
]", amp, lt, m, bt.print(cx)),
809 PrimitiveType::Slice,
810 &format!("{}{}{}
[", amp, lt, m),
814 write!(f, "{:#}
", bt.print(cx))?;
816 write!(f, "{}
", bt.print(cx))?;
818 primitive_link(f, PrimitiveType::Slice, "]", cx)
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)?;
827 clean::Generic(..) => {
830 PrimitiveType::Reference,
831 &format!("{}{}{}
", amp, lt, m),
834 fmt_type(&ty, f, use_absolute, cx)
837 write!(f, "{}{}{}
", amp, lt, m)?;
838 fmt_type(&ty, f, use_absolute, cx)
842 clean::ImplTrait(ref bounds) => {
844 write!(f, "impl {:#}
", print_generic_bounds(bounds, cx))
846 write!(f, "impl {}
", print_generic_bounds(bounds, cx))
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()
854 .zip(trait_.def_id())
855 .map_or(!self_type.is_self_type(), |(id, trait_)| id != trait_)
860 if should_show_cast {
861 write!(f, "<{:#}
as {:#}
>::", self_type.print(cx), trait_.print(cx))?
863 write!(f, "{:#}
::", self_type.print(cx))?
866 if should_show_cast {
867 write!(f, "<
;{}
as {}
>
;::", self_type.print(cx), trait_.print(cx))?
869 write!(f, "{}
::", self_type.print(cx))?
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.
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
883 box clean::ResolvedPath { did, ref param_names, .. } => {
884 match href(did.into(), cx) {
885 Some((ref url, _, ref path)) if !f.alternate() => {
888 "<a class
=\"type\" href
=\"{url}
#{shortty}.{name}\" \
889 title=\"type {path}::{name}\">{name}</a>",
891 shortty = ItemType::AssocType,
893 path = path.join("::")
896 _ => write!(f, "{}", name)?,
899 // FIXME: `param_names` are not rendered, and this seems bad?
903 _ => write!(f, "{}", name),
910 crate fn print<'b, 'a: 'b, 'tcx: 'a>(
912 cx: &'a Context<'tcx>,
913 ) -> impl fmt::Display + 'b + Captures<'tcx> {
914 display_fn(move |f| fmt_type(self, f, false, cx))
919 crate fn print<'a, 'tcx: 'a>(
922 cx: &'a Context<'tcx>,
923 ) -> impl fmt::Display + 'a + Captures<'tcx> {
924 display_fn(move |f| {
926 write!(f, "impl{:#} ", self.generics.print(cx))?;
928 write!(f, "impl{} ", self.generics.print(cx))?;
931 if let Some(ref ty) = self.trait_ {
932 if self.negative_polarity {
935 fmt::Display::fmt(&ty.print(cx), f)?;
939 if let Some(ref ty) = self.blanket_impl {
940 fmt_type(ty, f, use_absolute, cx)?;
942 fmt_type(&self.for_, f, use_absolute, cx)?;
945 fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, true), f)?;
951 impl clean::Arguments {
952 crate fn print<'a, 'tcx: 'a>(
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)?;
962 write!(f, "{:#}", input.type_.print(cx))?;
964 write!(f, "{}", input.type_.print(cx))?;
966 if i + 1 < self.values.len() {
975 impl clean::FnRetTy {
976 crate fn print<'a, 'tcx: 'a>(
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))
985 clean::Return(ty) => write!(f, " -> {}", ty.print(cx)),
986 clean::DefaultReturn => Ok(()),
991 impl clean::BareFunctionDecl {
992 fn print_hrtb_with_space<'a, 'tcx: 'a>(
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))))
1006 impl clean::FnDecl {
1007 crate fn print<'b, 'a: 'b, 'tcx: 'a>(
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 { "" };
1016 "({args:#}{ellipsis}){arrow:#}",
1017 args = self.inputs.print(cx),
1018 ellipsis = ellipsis,
1019 arrow = self.output.print(cx)
1024 "({args}{ellipsis}){arrow}",
1025 args = self.inputs.print(cx),
1026 ellipsis = ellipsis,
1027 arrow = self.output.print(cx)
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
1038 /// * `asyncness`: Whether the function is async or not.
1039 crate fn full_print<'a, 'tcx: 'a>(
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))
1049 fn inner_full_print(
1053 asyncness: hir::IsAsync,
1054 f: &mut fmt::Formatter<'_>,
1057 let amp = if f.alternate() { "&" } else { "&" };
1058 let mut args = String::new();
1059 let mut args_plain = String::new();
1060 for (i, input) in self.inputs.values.iter().enumerate() {
1062 args.push_str("<br>");
1065 if let Some(selfty) = input.to_self() {
1067 clean::SelfValue => {
1068 args.push_str("self");
1069 args_plain.push_str("self");
1071 clean::SelfBorrowed(Some(ref lt), mtbl) => {
1072 args.push_str(&format!(
1076 mtbl.print_with_space()
1078 args_plain.push_str(&format!(
1081 mtbl.print_with_space()
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()));
1088 clean::SelfExplicit(ref typ) => {
1090 args.push_str(&format!("self: {:#}", typ.print(cx)));
1092 args.push_str(&format!("self: {}", typ.print(cx)));
1094 args_plain.push_str(&format!("self: {:#}", typ.print(cx)));
1099 args.push_str(" <br>");
1100 args_plain.push(' ');
1102 if !input.name.is_empty() {
1103 args.push_str(&format!("{}: ", input.name));
1104 args_plain.push_str(&format!("{}: ", input.name));
1108 args.push_str(&format!("{:#}", input.type_.print(cx)));
1110 args.push_str(&input.type_.print(cx).to_string());
1112 args_plain.push_str(&format!("{:#}", input.type_.print(cx)));
1114 if i + 1 < self.inputs.values.len() {
1116 args_plain.push(',');
1120 let mut args_plain = format!("({})", args_plain);
1122 if self.c_variadic {
1123 args.push_str(",<br> ...");
1124 args_plain.push_str(", ...");
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)) }
1133 arrow_plain
= format
!("{:#}", self.output
.print(cx
));
1134 if f
.alternate() { arrow_plain.clone() }
else { format!("{}
", self.output.print(cx)) }
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
>{}
", " 
;".repeat(indent + 4));
1140 let close_pad = format!("<br
>{}
", " 
;".repeat(indent));
1142 "({args}{close}
){arrow}
",
1143 args = args.replace("<br
>", &full_pad),
1148 format!("({args}
){arrow}
", args = args.replace("<br
>", ""), arrow = arrow)
1152 write!(f, "{}
", output.replace("<br
>", "\n"))
1154 write!(f, "{}
", output)
1159 impl clean::Visibility {
1160 crate fn print_with_space<'a, 'tcx: 'a>(
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());
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
1180 } else if parent_module
1181 .map(|parent| find_nearest_parent_module(cx.tcx(), parent))
1185 "pub(super) ".to_owned()
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();
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()));
1197 s.push_str(&format!("{}
) ", anchor));
1202 display_fn(move |f| f.write_str(&to_print))
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
1208 crate fn to_src_with_space<'a, 'tcx: 'a>(
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);
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
1228 } else if parent_module
1229 .map(|parent| find_nearest_parent_module(tcx, parent))
1233 "pub(super) ".to_owned()
1235 format!("pub(in {}
) ", tcx.def_path_str(vis_did))
1239 display_fn(move |f| f.write_str(&to_print))
1243 crate trait PrintWithSpace {
1244 fn print_with_space(&self) -> &str;
1247 impl PrintWithSpace for hir::Unsafety {
1248 fn print_with_space(&self) -> &str {
1250 hir::Unsafety::Unsafe => "unsafe ",
1251 hir::Unsafety::Normal => "",
1256 impl PrintWithSpace for hir::Constness {
1257 fn print_with_space(&self) -> &str {
1259 hir::Constness::Const => "const ",
1260 hir::Constness::NotConst => "",
1265 impl PrintWithSpace for hir::IsAsync {
1266 fn print_with_space(&self) -> &str {
1268 hir::IsAsync::Async => "async
",
1269 hir::IsAsync::NotAsync => "",
1274 impl PrintWithSpace for hir::Mutability {
1275 fn print_with_space(&self) -> &str {
1277 hir::Mutability::Not => "",
1278 hir::Mutability::Mut => "mut ",
1283 impl clean::Import {
1284 crate fn print<'a, 'tcx: 'a>(
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))
1293 write!(f, "use {}
as {}
;", self.source.print(cx), name)
1296 clean::ImportKind::Glob => {
1297 if self.source.path.segments.is_empty() {
1300 write!(f, "use {}
::*;", self.source.print(cx))
1307 impl clean::ImportSource {
1308 crate fn print<'a, 'tcx: 'a>(
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),
1315 for seg in &self.path.segments[..self.path.segments.len() - 1] {
1316 write!(f, "{}
::", seg.name)?;
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)?;
1322 write!(f, "{}
", name)?;
1330 impl clean::TypeBinding {
1331 crate fn print<'a, 'tcx: 'a>(
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())?;
1338 clean::TypeBindingKind::Equality { ref ty } => {
1340 write!(f, " = {:#}
", ty.print(cx))?;
1342 write!(f, " = {}
", ty.print(cx))?;
1345 clean::TypeBindingKind::Constraint { ref bounds } => {
1346 if !bounds.is_empty() {
1348 write!(f, ": {:#}
", print_generic_bounds(bounds, cx))?;
1350 write!(f, ": 
;{}
", print_generic_bounds(bounds, cx))?;
1360 crate fn print_abi_with_space(abi: Abi) -> impl fmt::Display {
1361 display_fn(move |f| {
1362 let quot = if f.alternate() { "\"" } else { """ };
1364 Abi::Rust => Ok(()),
1365 abi => write!(f, "extern {0}{1}{0}
", quot, abi.name()),
1370 crate fn print_default_space<'a>(v: bool) -> &'a str {
1371 if v { "default " } else { "" }
1374 impl clean::GenericArg {
1375 crate fn print<'a, 'tcx: 'a>(
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(<.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),
1387 crate fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Display {
1388 struct WithFormatter<F>(Cell<Option<F>>);
1390 impl<F> fmt::Display for WithFormatter<F>
1392 F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
1394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1395 (self.0.take()).unwrap()(f)
1399 WithFormatter(Cell::new(Some(f)))