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_summary, render_stability_since_raw, write_srclink, AssocItemLink, Context, }; use crate::clean::{self, GetDefId}; use crate::formats::item_type::ItemType; use crate::formats::{AssocItemRender, Impl}; 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,
            "");
        render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx);
        w.write_str("");
        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, "