use clean::AttributesExt; use std::cmp::Ordering; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; use rustc_middle::middle::stability; use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; use super::{ collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_strs, notable_traits_decl, render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl, render_stability_since_raw, write_srclink, AssocItemLink, Context, }; use crate::clean::{self, GetDefId}; use crate::formats::item_type::ItemType; use crate::formats::{AssocItemRender, Impl, RenderMode}; use crate::html::escape::Escape; use crate::html::format::{print_abi_with_space, print_where_clause, Buffer, PrintWithSpace}; use crate::html::highlight; use crate::html::layout::Page; use crate::html::markdown::MarkdownSummaryLine; pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer, page: &Page<'_>) { debug_assert!(!item.is_stripped()); // Write the breadcrumb trail header for the top buf.write_str("
{}extern crate {} as {};",
myitem.visibility.print_with_space(myitem.def_id, cx),
anchor(myitem.def_id.expect_real(), &*src.as_str(), cx),
myitem.name.as_ref().unwrap(),
),
None => write!(
w,
" |
");
render_attributes_in_pre(w, it, "");
write!(
w,
"{vis}{constness}{asyncness}{unsafety}{abi}fn \
{name}{generics}{decl}{notable_traits}{where_clause}",
vis = it.visibility.print_with_space(it.def_id, cx),
constness = f.header.constness.print_with_space(),
asyncness = f.header.asyncness.print_with_space(),
unsafety = f.header.unsafety.print_with_space(),
abi = print_abi_with_space(f.header.abi),
name = it.name.as_ref().unwrap(),
generics = f.generics.print(cx),
where_clause = print_where_clause(&f.generics, cx, 0, true),
decl = f.decl.full_print(header_len, 0, f.header.asyncness, cx),
notable_traits = notable_traits_decl(&f.decl, cx),
);
document(w, cx, it, None)
}
fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
let bounds = bounds(&t.bounds, false, cx);
let types = t.items.iter().filter(|m| m.is_associated_type()).collect::");
render_attributes_in_pre(w, it, "");
write!(
w,
"{}{}{}trait {}{}{}",
it.visibility.print_with_space(it.def_id, cx),
t.unsafety.print_with_space(),
if t.is_auto { "auto " } else { "" },
it.name.as_ref().unwrap(),
t.generics.print(cx),
bounds
);
if !t.generics.where_predicates.is_empty() {
write!(w, "{}", print_where_clause(&t.generics, cx, 0, true));
} else {
w.write_str(" ");
}
if t.items.is_empty() {
w.write_str("{ }");
} else {
// FIXME: we should be using a derived_id for the Anchors here
w.write_str("{\n");
let mut toggle = false;
// If there are too many associated types, hide _everything_
if should_hide_fields(types.len()) {
toggle = true;
toggle_open(w, "associated items");
}
for t in &types {
render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
w.write_str(";\n");
}
// If there are too many associated constants, hide everything after them
// We also do this if the types + consts is large because otherwise we could
// render a bunch of types and _then_ a bunch of consts just because both were
// _just_ under the limit
if !toggle && should_hide_fields(types.len() + consts.len()) {
toggle = true;
toggle_open(w, "associated constants and methods");
}
if !types.is_empty() && !consts.is_empty() {
w.write_str("\n");
}
for t in &consts {
render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
w.write_str(";\n");
}
if !toggle && should_hide_fields(required.len() + provided.len()) {
toggle = true;
toggle_open(w, "methods");
}
if !consts.is_empty() && !required.is_empty() {
w.write_str("\n");
}
for (pos, m) in required.iter().enumerate() {
render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
w.write_str(";\n");
if pos < required.len() - 1 {
w.write_str("");
}
}
if !required.is_empty() && !provided.is_empty() {
w.write_str("\n");
}
for (pos, m) in provided.iter().enumerate() {
render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
match *m.kind {
clean::MethodItem(ref inner, _)
if !inner.generics.where_predicates.is_empty() =>
{
w.write_str(",\n { ... }\n");
}
_ => {
w.write_str(" { ... }\n");
}
}
if pos < provided.len() - 1 {
w.write_str("");
}
}
if toggle {
toggle_close(w);
}
w.write_str("}");
}
w.write_str("")
});
// Trait documentation
document(w, cx, it, None);
fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
write!(
w,
"", id);
render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx);
w.write_str("");
render_stability_since(w, m, t, cx.tcx());
write_srclink(cx, m, w);
w.write_str("");
render_attributes_in_pre(w, it, "");
write!(
w,
"trait {}{}{} = {};",
it.name.as_ref().unwrap(),
t.generics.print(cx),
print_where_clause(&t.generics, cx, 0, true),
bounds(&t.bounds, true, cx)
);
document(w, cx, it, None);
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
}
fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
w.write_str("");
render_attributes_in_pre(w, it, "");
write!(
w,
"type {}{}{where_clause} = impl {bounds};",
it.name.as_ref().unwrap(),
t.generics.print(cx),
where_clause = print_where_clause(&t.generics, cx, 0, true),
bounds = bounds(&t.bounds, false, cx),
);
document(w, cx, it, None);
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
}
fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
w.write_str("");
render_attributes_in_pre(w, it, "");
write!(
w,
"type {}{}{where_clause} = {type_};",
it.name.as_ref().unwrap(),
t.generics.print(cx),
where_clause = print_where_clause(&t.generics, cx, 0, true),
type_ = t.type_.print(cx),
);
document(w, cx, it, None);
let def_id = it.def_id.expect_real();
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
}
fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
wrap_into_docblock(w, |w| {
w.write_str("");
render_attributes_in_pre(w, it, "");
render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
w.write_str("")
});
document(w, cx, it, None);
let mut fields = s
.fields
.iter()
.filter_map(|f| match *f.kind {
clean::StructFieldItem(ref ty) => Some((f, ty)),
_ => None,
})
.peekable();
if fields.peek().is_some() {
write!(
w,
"{name}: {ty}\
",
id = id,
name = name,
shortty = ItemType::StructField,
ty = ty.print(cx),
);
if let Some(stability_class) = field.stability_class(cx.tcx()) {
write!(w, "", stab = stability_class);
}
document(w, cx, field, Some(it));
}
}
let def_id = it.def_id.expect_real();
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
document_type_layout(w, cx, def_id);
}
fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
wrap_into_docblock(w, |w| {
w.write_str("");
render_attributes_in_pre(w, it, "");
write!(
w,
"{}enum {}{}{}",
it.visibility.print_with_space(it.def_id, cx),
it.name.as_ref().unwrap(),
e.generics.print(cx),
print_where_clause(&e.generics, cx, 0, true),
);
if e.variants.is_empty() && !e.variants_stripped {
w.write_str(" {}");
} else {
w.write_str(" {\n");
let toggle = should_hide_fields(e.variants.len());
if toggle {
toggle_open(w, "variants");
}
for v in &e.variants {
w.write_str(" ");
let name = v.name.as_ref().unwrap();
match *v.kind {
clean::VariantItem(ref var) => match var {
clean::Variant::CLike => write!(w, "{}", name),
clean::Variant::Tuple(ref tys) => {
write!(w, "{}(", name);
for (i, ty) in tys.iter().enumerate() {
if i > 0 {
w.write_str(", ")
}
write!(w, "{}", ty.print(cx));
}
w.write_str(")");
}
clean::Variant::Struct(ref s) => {
render_struct(w, v, None, s.struct_type, &s.fields, " ", false, cx);
}
},
_ => unreachable!(),
}
w.write_str(",\n");
}
if e.variants_stripped {
w.write_str(" // some variants omitted\n");
}
if toggle {
toggle_close(w);
}
w.write_str("}");
}
w.write_str("")
});
document(w, cx, it, None);
if !e.variants.is_empty() {
write!(
w,
"{name}",
id = id,
name = variant.name.as_ref().unwrap()
);
if let clean::VariantItem(clean::Variant::Tuple(ref tys)) = *variant.kind {
w.write_str("(");
for (i, ty) in tys.iter().enumerate() {
if i > 0 {
w.write_str(", ");
}
write!(w, "{}", ty.print(cx));
}
w.write_str(")");
}
w.write_str("{f}: {t}\
",
id = id,
f = field.name.as_ref().unwrap(),
t = ty.print(cx)
);
document(w, cx, field, Some(variant));
}
}
w.write_str("");
write!(w, "{}!() {{ /* proc-macro */ }}", name);
w.push_str("");
}
MacroKind::Attr => {
w.push_str("");
write!(w, "#[{}]", name);
w.push_str("");
}
MacroKind::Derive => {
w.push_str("");
write!(w, "#[derive({})]", name);
if !m.helpers.is_empty() {
w.push_str("\n{\n");
w.push_str(" // Attributes available to this derive:\n");
for attr in &m.helpers {
writeln!(w, " #[{}]", attr);
}
w.push_str("}\n");
}
w.push_str("");
}
}
document(w, cx, it, None)
}
fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
document(w, cx, it, None);
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
}
fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) {
w.write_str("");
render_attributes_in_code(w, it);
write!(
w,
"{vis}const {name}: {typ}",
vis = it.visibility.print_with_space(it.def_id, cx),
name = it.name.as_ref().unwrap(),
typ = c.type_.print(cx),
);
let value = c.value(cx.tcx());
let is_literal = c.is_literal(cx.tcx());
let expr = c.expr(cx.tcx());
if value.is_some() || is_literal {
write!(w, " = {expr};", expr = Escape(&expr));
} else {
w.write_str(";");
}
if !is_literal {
if let Some(value) = &value {
let value_lowercase = value.to_lowercase();
let expr_lowercase = expr.to_lowercase();
if value_lowercase != expr_lowercase
&& value_lowercase.trim_end_matches("i32") != expr_lowercase
{
write!(w, " // {value}", value = Escape(value));
}
}
}
w.write_str("");
document(w, cx, it, None)
}
fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
wrap_into_docblock(w, |w| {
w.write_str("");
render_attributes_in_code(w, it);
render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
w.write_str("")
});
document(w, cx, it, None);
let mut fields = s
.fields
.iter()
.filter_map(|f| match *f.kind {
clean::StructFieldItem(ref ty) => Some((f, ty)),
_ => None,
})
.peekable();
if let CtorKind::Fictive = s.struct_type {
if fields.peek().is_some() {
write!(
w,
"{name}: {ty}\
",
item_type = ItemType::StructField,
id = id,
name = field.name.as_ref().unwrap(),
ty = ty.print(cx)
);
document(w, cx, field, Some(it));
}
}
}
let def_id = it.def_id.expect_real();
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
document_type_layout(w, cx, def_id);
}
fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
w.write_str("");
render_attributes_in_code(w, it);
write!(
w,
"{vis}static {mutability}{name}: {typ}",
vis = it.visibility.print_with_space(it.def_id, cx),
mutability = s.mutability.print_with_space(),
name = it.name.as_ref().unwrap(),
typ = s.type_.print(cx)
);
document(w, cx, it, None)
}
fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
w.write_str("extern {\n");
render_attributes_in_code(w, it);
write!(
w,
" {}type {};\n}}",
it.visibility.print_with_space(it.def_id, cx),
it.name.as_ref().unwrap(),
);
document(w, cx, it, None);
render_assoc_items(w, cx, it, it.def_id.expect_real(), AssocItemRender::All)
}
fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
document(w, cx, it, None)
}
/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
crate fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering {
/// Takes a non-numeric and a numeric part from the given &str.
fn take_parts<'a>(s: &mut &'a str) -> (&'a str, &'a str) {
let i = s.find(|c: char| c.is_ascii_digit());
let (a, b) = s.split_at(i.unwrap_or(s.len()));
let i = b.find(|c: char| !c.is_ascii_digit());
let (b, c) = b.split_at(i.unwrap_or(b.len()));
*s = c;
(a, b)
}
while !lhs.is_empty() || !rhs.is_empty() {
let (la, lb) = take_parts(&mut lhs);
let (ra, rb) = take_parts(&mut rhs);
// First process the non-numeric part.
match la.cmp(ra) {
Ordering::Equal => (),
x => return x,
}
// Then process the numeric part, if both sides have one (and they fit in a u64).
if let (Ok(ln), Ok(rn)) = (lb.parse::Struct { .. } syntax; cannot be \
matched against without a wildcard ..; and \
struct update syntax will not work.",
);
} else if item.is_enum() {
w.write_str(
"Non-exhaustive enums could have additional variants added in future. \
Therefore, when matching against variants of non-exhaustive enums, an \
extra wildcard arm must be added to account for any future variants.",
);
} else if item.is_variant() {
w.write_str(
"Non-exhaustive enum variants could have additional fields added in future. \
Therefore, non-exhaustive enum variants cannot be constructed in external \
crates and cannot be matched against.",
);
} else {
w.write_str(
"This type will require a wildcard arm in any match statements or constructors.",
);
}
w.write_str("Note: Most layout information is \
completely unstable and may be different between compiler versions and platforms. \
The only exception is types with certain repr(...) attributes. \
Please see the Rust Reference’s \
“Type Layout” \
chapter for details on type layout guarantees.
Size: (unsized)
"); } else { let bytes = ty_layout.layout.size.bytes(); writeln!( w, "Size: {size} byte{pl}
", size = bytes, pl = if bytes == 1 { "" } else { "s" }, ); } } // This kind of layout error can occur with valid code, e.g. if you try to // get the layout of a generic type such as `VecNote: Unable to compute type layout, \ possibly due to this type having generic parameters. \ Layout can only be computed for concrete, fully-instantiated types.
" ); } // This kind of error probably can't happen with valid code, but we don't // want to panic and prevent the docs from building, so we just let the // user know that we couldn't compute the layout. Err(LayoutError::SizeOverflow(_)) => { writeln!( w, "Note: Encountered an error during type layout; \ the type was too big.
" ); } } writeln!(w, "