= many
.iter()
.filter_map(|t| match t {
clean::Generic(name) => Some(*name),
_ => None,
})
.collect();
let is_generic = generic_names.len() == many.len();
if is_generic {
primitive_link(
f,
PrimitiveType::Tuple,
&format!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
cx,
)
} else {
write!(f, "(")?;
for (i, item) in many.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
// Carry `f.alternate()` into this display w/o branching manually.
fmt::Display::fmt(&item.print(cx), f)?;
}
write!(f, ")")
}
}
}
}
clean::Slice(ref t) => match **t {
clean::Generic(name) => {
primitive_link(f, PrimitiveType::Slice, &format!("[{name}]"), cx)
}
_ => {
write!(f, "[")?;
fmt::Display::fmt(&t.print(cx), f)?;
write!(f, "]")
}
},
clean::Array(ref t, ref n) => match **t {
clean::Generic(name) if !f.alternate() => primitive_link(
f,
PrimitiveType::Array,
&format!("[{name}; {n}]", n = Escape(n)),
cx,
),
_ => {
write!(f, "[")?;
fmt::Display::fmt(&t.print(cx), f)?;
if f.alternate() {
write!(f, "; {n}")?;
} else {
write!(f, "; ")?;
primitive_link(f, PrimitiveType::Array, &format!("{n}", n = Escape(n)), cx)?;
}
write!(f, "]")
}
},
clean::RawPointer(m, ref t) => {
let m = match m {
hir::Mutability::Mut => "mut",
hir::Mutability::Not => "const",
};
if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() {
let text = if f.alternate() {
format!("*{} {:#}", m, t.print(cx))
} else {
format!("*{} {}", m, t.print(cx))
};
primitive_link(f, clean::PrimitiveType::RawPointer, &text, cx)
} else {
primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), cx)?;
fmt::Display::fmt(&t.print(cx), f)
}
}
clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
let lt = match l {
Some(l) => format!("{} ", l.print()),
_ => String::new(),
};
let m = mutability.print_with_space();
let amp = if f.alternate() { "&" } else { "&" };
match **ty {
clean::DynTrait(ref bounds, ref trait_lt)
if bounds.len() > 1 || trait_lt.is_some() =>
{
write!(f, "{}{}{}(", amp, lt, m)?;
fmt_type(ty, f, use_absolute, cx)?;
write!(f, ")")
}
clean::Generic(name) => {
primitive_link(f, PrimitiveType::Reference, &format!("{amp}{lt}{m}{name}"), cx)
}
_ => {
write!(f, "{}{}{}", amp, lt, m)?;
fmt_type(ty, f, use_absolute, cx)
}
}
}
clean::ImplTrait(ref bounds) => {
if f.alternate() {
write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
} else {
write!(f, "impl {}", print_generic_bounds(bounds, cx))
}
}
clean::QPath(box clean::QPathData {
ref assoc,
ref self_type,
ref trait_,
should_show_cast,
}) => {
if f.alternate() {
if should_show_cast {
write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
} else {
write!(f, "{:#}::", self_type.print(cx))?
}
} else {
if should_show_cast {
write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))?
} else {
write!(f, "{}::", self_type.print(cx))?
}
};
// It's pretty unsightly to look at `::C` in output, and
// we've got hyperlinking on our side, so try to avoid longer
// notation as much as possible by making `C` a hyperlink to trait
// `B` to disambiguate.
//
// FIXME: this is still a lossy conversion and there should probably
// be a better way of representing this in general? Most of
// the ugliness comes from inlining across crates where
// everything comes in as a fully resolved QPath (hard to
// look at).
match href(trait_.def_id(), cx) {
Ok((ref url, _, ref path)) if !f.alternate() => {
write!(
f,
"{name}{args}",
url = url,
shortty = ItemType::AssocType,
name = assoc.name,
path = join_with_double_colon(path),
args = assoc.args.print(cx),
)?;
}
_ => write!(f, "{}{:#}", assoc.name, assoc.args.print(cx))?,
}
Ok(())
}
}
}
impl clean::Type {
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'b + Captures<'tcx> {
display_fn(move |f| fmt_type(self, f, false, cx))
}
}
impl clean::Path {
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'b + Captures<'tcx> {
display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx))
}
}
impl clean::Impl {
pub(crate) fn print<'a, 'tcx: 'a>(
&'a self,
use_absolute: bool,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
if f.alternate() {
write!(f, "impl{:#} ", self.generics.print(cx))?;
} else {
write!(f, "impl{} ", self.generics.print(cx))?;
}
if let Some(ref ty) = self.trait_ {
match self.polarity {
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {}
ty::ImplPolarity::Negative => write!(f, "!")?,
}
fmt::Display::fmt(&ty.print(cx), f)?;
write!(f, " for ")?;
}
if let clean::Type::Tuple(types) = &self.for_ &&
let [clean::Type::Generic(name)] = &types[..] &&
(self.kind.is_fake_variadic() || self.kind.is_auto())
{
// Hardcoded anchor library/core/src/primitive_docs.rs
// Link should match `# Trait implementations`
primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
} else if let clean::BareFunction(bare_fn) = &self.for_ &&
let [clean::Argument { type_: clean::Type::Generic(name), .. }] = &bare_fn.decl.inputs.values[..] &&
(self.kind.is_fake_variadic() || self.kind.is_auto())
{
// Hardcoded anchor library/core/src/primitive_docs.rs
// Link should match `# Trait implementations`
let hrtb = bare_fn.print_hrtb_with_space(cx);
let unsafety = bare_fn.unsafety.print_with_space();
let abi = print_abi_with_space(bare_fn.abi);
if f.alternate() {
write!(
f,
"{hrtb:#}{unsafety}{abi:#}",
)?;
} else {
write!(
f,
"{hrtb}{unsafety}{abi}",
)?;
}
let ellipsis = if bare_fn.decl.c_variadic {
", ..."
} else {
""
};
primitive_link_fragment(f, PrimitiveType::Tuple, &format!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
// Write output.
if let clean::FnRetTy::Return(ty) = &bare_fn.decl.output {
write!(f, " -> ")?;
fmt_type(ty, f, use_absolute, cx)?;
}
} else if let Some(ty) = self.kind.as_blanket_ty() {
fmt_type(ty, f, use_absolute, cx)?;
} else {
fmt_type(&self.for_, f, use_absolute, cx)?;
}
fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, Ending::Newline), f)?;
Ok(())
})
}
}
impl clean::Arguments {
pub(crate) fn print<'a, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
for (i, input) in self.values.iter().enumerate() {
write!(f, "{}: ", input.name)?;
if f.alternate() {
write!(f, "{:#}", input.type_.print(cx))?;
} else {
write!(f, "{}", input.type_.print(cx))?;
}
if i + 1 < self.values.len() {
write!(f, ", ")?;
}
}
Ok(())
})
}
}
impl clean::FnRetTy {
pub(crate) fn print<'a, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self {
clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()),
clean::Return(ty) if f.alternate() => {
write!(f, " -> {:#}", ty.print(cx))
}
clean::Return(ty) => write!(f, " -> {}", ty.print(cx)),
clean::DefaultReturn => Ok(()),
})
}
}
impl clean::BareFunctionDecl {
fn print_hrtb_with_space<'a, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
if !self.generic_params.is_empty() {
write!(
f,
"for<{}> ",
comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
)
} else {
Ok(())
}
})
}
}
// Implements Write but only counts the bytes "written".
struct WriteCounter(usize);
impl std::fmt::Write for WriteCounter {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0 += s.len();
Ok(())
}
}
// Implements Display by emitting the given number of spaces.
struct Indent(usize);
impl fmt::Display for Indent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(0..self.0).for_each(|_| {
f.write_char(' ').unwrap();
});
Ok(())
}
}
impl clean::FnDecl {
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'b + Captures<'tcx> {
display_fn(move |f| {
let ellipsis = if self.c_variadic { ", ..." } else { "" };
if f.alternate() {
write!(
f,
"({args:#}{ellipsis}){arrow:#}",
args = self.inputs.print(cx),
ellipsis = ellipsis,
arrow = self.output.print(cx)
)
} else {
write!(
f,
"({args}{ellipsis}){arrow}",
args = self.inputs.print(cx),
ellipsis = ellipsis,
arrow = self.output.print(cx)
)
}
})
}
/// * `header_len`: The length of the function header and name. In other words, the number of
/// characters in the function declaration up to but not including the parentheses.
/// This is expected to go into a ``/`code-header` block, so indentation and newlines
/// are preserved.
/// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
/// necessary.
pub(crate) fn full_print<'a, 'tcx: 'a>(
&'a self,
header_len: usize,
indent: usize,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
// First, generate the text form of the declaration, with no line wrapping, and count the bytes.
let mut counter = WriteCounter(0);
write!(&mut counter, "{:#}", display_fn(|f| { self.inner_full_print(None, f, cx) }))
.unwrap();
// If the text form was over 80 characters wide, we will line-wrap our output.
let line_wrapping_indent =
if header_len + counter.0 > 80 { Some(indent) } else { None };
// Generate the final output. This happens to accept `{:#}` formatting to get textual
// output but in practice it is only formatted with `{}` to get HTML output.
self.inner_full_print(line_wrapping_indent, f, cx)
})
}
fn inner_full_print(
&self,
// For None, the declaration will not be line-wrapped. For Some(n),
// the declaration will be line-wrapped, with an indent of n spaces.
line_wrapping_indent: Option,
f: &mut fmt::Formatter<'_>,
cx: &Context<'_>,
) -> fmt::Result {
let amp = if f.alternate() { "&" } else { "&" };
write!(f, "(")?;
if let Some(n) = line_wrapping_indent {
write!(f, "\n{}", Indent(n + 4))?;
}
for (i, input) in self.inputs.values.iter().enumerate() {
if i > 0 {
match line_wrapping_indent {
None => write!(f, ", ")?,
Some(n) => write!(f, ",\n{}", Indent(n + 4))?,
};
}
if let Some(selfty) = input.to_self() {
match selfty {
clean::SelfValue => {
write!(f, "self")?;
}
clean::SelfBorrowed(Some(ref lt), mtbl) => {
write!(f, "{}{} {}self", amp, lt.print(), mtbl.print_with_space())?;
}
clean::SelfBorrowed(None, mtbl) => {
write!(f, "{}{}self", amp, mtbl.print_with_space())?;
}
clean::SelfExplicit(ref typ) => {
write!(f, "self: ")?;
fmt::Display::fmt(&typ.print(cx), f)?;
}
}
} else {
if input.is_const {
write!(f, "const ")?;
}
write!(f, "{}: ", input.name)?;
fmt::Display::fmt(&input.type_.print(cx), f)?;
}
}
if self.c_variadic {
match line_wrapping_indent {
None => write!(f, ", ...")?,
Some(n) => write!(f, "\n{}...", Indent(n + 4))?,
};
}
match line_wrapping_indent {
None => write!(f, ")")?,
Some(n) => write!(f, "\n{})", Indent(n))?,
};
fmt::Display::fmt(&self.output.print(cx), f)?;
Ok(())
}
}
pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
visibility: Option>,
item_did: ItemId,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
use std::fmt::Write as _;
let to_print: Cow<'static, str> = match visibility {
None => "".into(),
Some(ty::Visibility::Public) => "pub ".into(),
Some(ty::Visibility::Restricted(vis_did)) => {
// FIXME(camelid): This may not work correctly if `item_did` is a module.
// However, rustdoc currently never displays a module's
// visibility, so it shouldn't matter.
let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id());
if vis_did.is_crate_root() {
"pub(crate) ".into()
} else if parent_module == Some(vis_did) {
// `pub(in foo)` where `foo` is the parent module
// is the same as no visibility modifier
"".into()
} else if parent_module.and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
== Some(vis_did)
{
"pub(super) ".into()
} else {
let path = cx.tcx().def_path(vis_did);
debug!("path={:?}", path);
// modified from `resolved_path()` to work with `DefPathData`
let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
let anchor = anchor(vis_did, last_name, cx);
let mut s = "pub(in ".to_owned();
for seg in &path.data[..path.data.len() - 1] {
let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap());
}
let _ = write!(s, "{}) ", anchor);
s.into()
}
}
};
display_fn(move |f| write!(f, "{}", to_print))
}
/// This function is the same as print_with_space, except that it renders no links.
/// It's used for macros' rendered source view, which is syntax highlighted and cannot have
/// any HTML in it.
pub(crate) fn visibility_to_src_with_space<'a, 'tcx: 'a>(
visibility: Option>,
tcx: TyCtxt<'tcx>,
item_did: DefId,
) -> impl fmt::Display + 'a + Captures<'tcx> {
let to_print: Cow<'static, str> = match visibility {
None => "".into(),
Some(ty::Visibility::Public) => "pub ".into(),
Some(ty::Visibility::Restricted(vis_did)) => {
// FIXME(camelid): This may not work correctly if `item_did` is a module.
// However, rustdoc currently never displays a module's
// visibility, so it shouldn't matter.
let parent_module = find_nearest_parent_module(tcx, item_did);
if vis_did.is_crate_root() {
"pub(crate) ".into()
} else if parent_module == Some(vis_did) {
// `pub(in foo)` where `foo` is the parent module
// is the same as no visibility modifier
"".into()
} else if parent_module.and_then(|parent| find_nearest_parent_module(tcx, parent))
== Some(vis_did)
{
"pub(super) ".into()
} else {
format!("pub(in {}) ", tcx.def_path_str(vis_did)).into()
}
}
};
display_fn(move |f| f.write_str(&to_print))
}
pub(crate) trait PrintWithSpace {
fn print_with_space(&self) -> &str;
}
impl PrintWithSpace for hir::Unsafety {
fn print_with_space(&self) -> &str {
match self {
hir::Unsafety::Unsafe => "unsafe ",
hir::Unsafety::Normal => "",
}
}
}
impl PrintWithSpace for hir::IsAsync {
fn print_with_space(&self) -> &str {
match self {
hir::IsAsync::Async => "async ",
hir::IsAsync::NotAsync => "",
}
}
}
impl PrintWithSpace for hir::Mutability {
fn print_with_space(&self) -> &str {
match self {
hir::Mutability::Not => "",
hir::Mutability::Mut => "mut ",
}
}
}
pub(crate) fn print_constness_with_space(
c: &hir::Constness,
s: Option,
) -> &'static str {
match (c, s) {
// const stable or when feature(staged_api) is not set
(
hir::Constness::Const,
Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }),
)
| (hir::Constness::Const, None) => "const ",
// const unstable or not const
_ => "",
}
}
impl clean::Import {
pub(crate) fn print<'a, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self.kind {
clean::ImportKind::Simple(name) => {
if name == self.source.path.last() {
write!(f, "use {};", self.source.print(cx))
} else {
write!(f, "use {} as {};", self.source.print(cx), name)
}
}
clean::ImportKind::Glob => {
if self.source.path.segments.is_empty() {
write!(f, "use *;")
} else {
write!(f, "use {}::*;", self.source.print(cx))
}
}
})
}
}
impl clean::ImportSource {
pub(crate) fn print<'a, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self.did {
Some(did) => resolved_path(f, did, &self.path, true, false, cx),
_ => {
for seg in &self.path.segments[..self.path.segments.len() - 1] {
write!(f, "{}::", seg.name)?;
}
let name = self.path.last();
if let hir::def::Res::PrimTy(p) = self.path.res {
primitive_link(f, PrimitiveType::from(p), name.as_str(), cx)?;
} else {
write!(f, "{}", name)?;
}
Ok(())
}
})
}
}
impl clean::TypeBinding {
pub(crate) fn print<'a, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
f.write_str(self.assoc.name.as_str())?;
if f.alternate() {
write!(f, "{:#}", self.assoc.args.print(cx))?;
} else {
write!(f, "{}", self.assoc.args.print(cx))?;
}
match self.kind {
clean::TypeBindingKind::Equality { ref term } => {
if f.alternate() {
write!(f, " = {:#}", term.print(cx))?;
} else {
write!(f, " = {}", term.print(cx))?;
}
}
clean::TypeBindingKind::Constraint { ref bounds } => {
if !bounds.is_empty() {
if f.alternate() {
write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
} else {
write!(f, ": {}", print_generic_bounds(bounds, cx))?;
}
}
}
}
Ok(())
})
}
}
pub(crate) fn print_abi_with_space(abi: Abi) -> impl fmt::Display {
display_fn(move |f| {
let quot = if f.alternate() { "\"" } else { """ };
match abi {
Abi::Rust => Ok(()),
abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
}
})
}
pub(crate) fn print_default_space<'a>(v: bool) -> &'a str {
if v { "default " } else { "" }
}
impl clean::GenericArg {
pub(crate) fn print<'a, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self {
clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(<.print(), f),
clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
clean::GenericArg::Infer => fmt::Display::fmt("_", f),
})
}
}
impl clean::types::Term {
pub(crate) fn print<'a, 'tcx: 'a>(
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| match self {
clean::types::Term::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
clean::types::Term::Constant(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
})
}
}
pub(crate) fn display_fn(
f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
) -> impl fmt::Display {
struct WithFormatter(Cell