::P`).
if f.alternate() {
if let Some(trait_) = trait_
&& should_fully_qualify
{
write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
} else {
write!(f, "{:#}::", self_type.print(cx))?
}
} else {
if let Some(trait_) = trait_
&& should_fully_qualify
{
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).
if !f.alternate() {
// FIXME(inherent_associated_types): We always link to the very first associated
// type (in respect to source order) that bears the given name (`assoc.name`) and that is
// affiliated with the computed `DefId`. This is obviously incorrect when we have
// multiple impl blocks. Ideally, we would thread the `DefId` of the assoc ty itself
// through here and map it to the corresponding HTML ID that was generated by
// `render::Context::derive_id` when the impl blocks were rendered.
// There is no such mapping unfortunately.
// As a hack, we could badly imitate `derive_id` here by keeping *count* when looking
// for the assoc ty `DefId` in `tcx.associated_items(self_ty_did).in_definition_order()`
// considering privacy, `doc(hidden)`, etc.
// I don't feel like that right now :cold_sweat:.
let parent_href = match trait_ {
Some(trait_) => href(trait_.def_id(), cx).ok(),
None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()),
};
if let Some((url, _, path)) = parent_href {
write!(
f,
"{name}",
shortty = ItemType::AssocType,
name = assoc.name,
path = join_path_syms(path),
)
} else {
write!(f, "{}", assoc.name)
}
} else {
write!(f, "{}", assoc.name)
}?;
assoc.args.print(cx).fmt(f)
})
}
}
impl clean::Impl {
pub(crate) fn print(&self, use_absolute: bool, cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| {
f.write_str("impl")?;
self.generics.print(cx).fmt(f)?;
f.write_str(" ")?;
if let Some(ref ty) = self.trait_ {
if self.is_negative_trait_impl() {
write!(f, "!")?;
}
if self.kind.is_fake_variadic()
&& let Some(generics) = ty.generics()
&& let Ok(inner_type) = generics.exactly_one()
{
let last = ty.last();
if f.alternate() {
write!(f, "{last}<")?;
self.print_type(inner_type, f, use_absolute, cx)?;
write!(f, ">")?;
} else {
write!(f, "{}<", print_anchor(ty.def_id(), last, cx))?;
self.print_type(inner_type, f, use_absolute, cx)?;
write!(f, ">")?;
}
} else {
ty.print(cx).fmt(f)?;
}
write!(f, " for ")?;
}
if let Some(ty) = self.kind.as_blanket_ty() {
fmt_type(ty, f, use_absolute, cx)?;
} else {
self.print_type(&self.for_, f, use_absolute, cx)?;
}
print_where_clause(&self.generics, cx, 0, Ending::Newline).maybe_display().fmt(f)
})
}
fn print_type(
&self,
type_: &clean::Type,
f: &mut fmt::Formatter<'_>,
use_absolute: bool,
cx: &Context<'_>,
) -> Result<(), fmt::Error> {
if let clean::Type::Tuple(types) = type_
&& 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_args!("({name}₁, {name}₂, …, {name}ₙ)"),
"#trait-implementations-1",
cx,
)?;
} else if let clean::Type::Array(ty, len) = type_
&& let clean::Type::Generic(name) = &**ty
&& &len[..] == "1"
&& (self.kind.is_fake_variadic() || self.kind.is_auto())
{
primitive_link(f, PrimitiveType::Array, format_args!("[{name}; N]"), cx)?;
} else if let clean::BareFunction(bare_fn) = &type_
&& let [clean::Parameter { type_: clean::Type::Generic(name), .. }] =
&bare_fn.decl.inputs[..]
&& (self.kind.is_fake_variadic() || self.kind.is_auto())
{
// Hardcoded anchor library/core/src/primitive_docs.rs
// Link should match `# Trait implementations`
print_higher_ranked_params_with_space(&bare_fn.generic_params, cx, "for").fmt(f)?;
bare_fn.safety.print_with_space().fmt(f)?;
print_abi_with_space(bare_fn.abi).fmt(f)?;
let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" };
primitive_link_fragment(
f,
PrimitiveType::Tuple,
format_args!("fn({name}₁, {name}₂, …, {name}ₙ{ellipsis})"),
"#trait-implementations-1",
cx,
)?;
// Write output.
if !bare_fn.decl.output.is_unit() {
write!(f, " -> ")?;
fmt_type(&bare_fn.decl.output, f, use_absolute, cx)?;
}
} else if let clean::Type::Path { path } = type_
&& let Some(generics) = path.generics()
&& let Ok(ty) = generics.exactly_one()
&& self.kind.is_fake_variadic()
{
let wrapper = print_anchor(path.def_id(), path.last(), cx);
if f.alternate() {
write!(f, "{wrapper:#}<")?;
} else {
write!(f, "{wrapper}<")?;
}
self.print_type(ty, f, use_absolute, cx)?;
if f.alternate() {
write!(f, ">")?;
} else {
write!(f, ">")?;
}
} else {
fmt_type(type_, f, use_absolute, cx)?;
}
Ok(())
}
}
pub(crate) fn print_params(params: &[clean::Parameter], cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| {
params
.iter()
.map(|param| {
fmt::from_fn(|f| {
if let Some(name) = param.name {
write!(f, "{name}: ")?;
}
param.type_.print(cx).fmt(f)
})
})
.joined(", ", f)
})
}
// 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.
#[derive(Clone, Copy)]
struct Indent(usize);
impl Display for Indent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(0..self.0).for_each(|_| {
f.write_char(' ').unwrap();
});
Ok(())
}
}
impl clean::Parameter {
fn print(&self, cx: &Context<'_>) -> impl fmt::Display {
fmt::from_fn(move |f| {
if let Some(self_ty) = self.to_receiver() {
match self_ty {
clean::SelfTy => f.write_str("self"),
clean::BorrowedRef { lifetime, mutability, type_: box clean::SelfTy } => {
f.write_str(if f.alternate() { "&" } else { "&" })?;
if let Some(lt) = lifetime {
write!(f, "{lt} ", lt = lt.print())?;
}
write!(f, "{mutability}self", mutability = mutability.print_with_space())
}
_ => {
f.write_str("self: ")?;
self_ty.print(cx).fmt(f)
}
}
} else {
if self.is_const {
write!(f, "const ")?;
}
if let Some(name) = self.name {
write!(f, "{name}: ")?;
}
self.type_.print(cx).fmt(f)
}
})
}
}
impl clean::FnDecl {
pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| {
let ellipsis = if self.c_variadic { ", ..." } else { "" };
if f.alternate() {
write!(
f,
"({params:#}{ellipsis}){arrow:#}",
params = print_params(&self.inputs, cx),
ellipsis = ellipsis,
arrow = self.print_output(cx)
)
} else {
write!(
f,
"({params}{ellipsis}){arrow}",
params = print_params(&self.inputs, cx),
ellipsis = ellipsis,
arrow = self.print_output(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(
&self,
header_len: usize,
indent: usize,
cx: &Context<'_>,
) -> impl Display {
fmt::from_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, "{:#}", fmt::from_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 {
f.write_char('(')?;
if !self.inputs.is_empty() {
let line_wrapping_indent = line_wrapping_indent.map(|n| Indent(n + 4));
if let Some(indent) = line_wrapping_indent {
write!(f, "\n{indent}")?;
}
let sep = fmt::from_fn(|f| {
if let Some(indent) = line_wrapping_indent {
write!(f, ",\n{indent}")
} else {
f.write_str(", ")
}
});
self.inputs.iter().map(|param| param.print(cx)).joined(sep, f)?;
if line_wrapping_indent.is_some() {
writeln!(f, ",")?
}
if self.c_variadic {
match line_wrapping_indent {
None => write!(f, ", ...")?,
Some(indent) => writeln!(f, "{indent}...")?,
};
}
}
if let Some(n) = line_wrapping_indent {
write!(f, "{}", Indent(n))?
}
f.write_char(')')?;
self.print_output(cx).fmt(f)
}
fn print_output(&self, cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| match &self.output {
clean::Tuple(tys) if tys.is_empty() => Ok(()),
ty if f.alternate() => {
write!(f, " -> {:#}", ty.print(cx))
}
ty => write!(f, " -> {}", ty.print(cx)),
})
}
}
pub(crate) fn visibility_print_with_space(item: &clean::Item, cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| {
if item.is_doc_hidden() {
f.write_str("#[doc(hidden)] ")?;
}
match item.visibility(cx.tcx()) {
None => {}
Some(ty::Visibility::Public) => f.write_str("pub ")?,
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.item_id.expect_def_id());
if vis_did.is_crate_root() {
f.write_str("pub(crate) ")?;
} else if parent_module == Some(vis_did) {
// `pub(in foo)` where `foo` is the parent module
// is the same as no visibility modifier; do nothing
} else if parent_module
.and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
== Some(vis_did)
{
f.write_str("pub(super) ")?;
} 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 = print_anchor(vis_did, last_name, cx);
f.write_str("pub(in ")?;
for seg in &path.data[..path.data.len() - 1] {
write!(f, "{}::", seg.data.get_opt_name().unwrap())?;
}
write!(f, "{anchor}) ")?;
}
}
}
Ok(())
})
}
pub(crate) trait PrintWithSpace {
fn print_with_space(&self) -> &str;
}
impl PrintWithSpace for hir::Safety {
fn print_with_space(&self) -> &str {
self.prefix_str()
}
}
impl PrintWithSpace for hir::HeaderSafety {
fn print_with_space(&self) -> &str {
match self {
hir::HeaderSafety::SafeTargetFeatures => "",
hir::HeaderSafety::Normal(safety) => safety.print_with_space(),
}
}
}
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,
overall_stab: Option,
const_stab: Option,
) -> &'static str {
match c {
hir::Constness::Const => match (overall_stab, const_stab) {
// const stable...
(_, Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }))
// ...or when feature(staged_api) is not set...
| (_, None)
// ...or when const unstable, but overall unstable too
| (None, Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => {
"const "
}
// const unstable (and overall stable)
(Some(_), Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => "",
},
// not const
hir::Constness::NotConst => "",
}
}
impl clean::Import {
pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
fmt::from_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 {source} as {name};", source = self.source.print(cx))
}
}
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(&self, cx: &Context<'_>) -> impl Display {
fmt::from_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), format_args!("{name}"), cx)?;
} else {
f.write_str(name.as_str())?;
}
Ok(())
}
})
}
}
impl clean::AssocItemConstraint {
pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| {
f.write_str(self.assoc.name.as_str())?;
self.assoc.args.print(cx).fmt(f)?;
match self.kind {
clean::AssocItemConstraintKind::Equality { ref term } => {
f.write_str(" = ")?;
term.print(cx).fmt(f)?;
}
clean::AssocItemConstraintKind::Bound { ref bounds } => {
if !bounds.is_empty() {
f.write_str(": ")?;
print_generic_bounds(bounds, cx).fmt(f)?;
}
}
}
Ok(())
})
}
}
pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display {
fmt::from_fn(move |f| {
let quot = if f.alternate() { "\"" } else { """ };
match abi {
ExternAbi::Rust => Ok(()),
abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
}
})
}
pub(crate) fn print_default_space(v: bool) -> &'static str {
if v { "default " } else { "" }
}
impl clean::GenericArg {
pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| match self {
clean::GenericArg::Lifetime(lt) => lt.print().fmt(f),
clean::GenericArg::Type(ty) => ty.print(cx).fmt(f),
clean::GenericArg::Const(ct) => ct.print(cx.tcx()).fmt(f),
clean::GenericArg::Infer => Display::fmt("_", f),
})
}
}
impl clean::Term {
pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
fmt::from_fn(move |f| match self {
clean::Term::Type(ty) => ty.print(cx).fmt(f),
clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f),
})
}
}