use std::string::ToString;
use rustc_ast_pretty::pprust;
-use rustc_attr::{Deprecation, StabilityLevel};
+use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
-use crate::clean::{self, FakeDefId, GetDefId, RenderedLink, SelfTy};
+use crate::clean::{self, GetDefId, ItemId, RenderedLink, SelfTy};
use crate::docfs::PathError;
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
- href, print_abi_with_space, print_default_space, print_generic_bounds, print_where_clause,
- Buffer, PrintWithSpace,
+ href, print_abi_with_space, print_constness_with_space, print_default_space,
+ print_generic_bounds, print_where_clause, Buffer, HrefError, PrintWithSpace,
};
use crate::html::markdown::{Markdown, MarkdownHtml, MarkdownSummaryLine};
/// A type used for the search index.
#[derive(Debug)]
crate struct RenderType {
- ty: Option<DefId>,
- idx: Option<usize>,
name: Option<String>,
- generics: Option<Vec<Generic>>,
-}
-
-impl Serialize for RenderType {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- if let Some(name) = &self.name {
- let mut seq = serializer.serialize_seq(None)?;
- if let Some(id) = self.idx {
- seq.serialize_element(&id)?;
- } else {
- seq.serialize_element(&name)?;
- }
- if let Some(generics) = &self.generics {
- seq.serialize_element(&generics)?;
- }
- seq.end()
- } else {
- serializer.serialize_none()
- }
- }
-}
-
-/// A type used for the search index.
-#[derive(Debug)]
-crate struct Generic {
- name: String,
- defid: Option<DefId>,
- idx: Option<usize>,
-}
-
-impl Serialize for Generic {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- if let Some(id) = self.idx {
- serializer.serialize_some(&id)
- } else {
- serializer.serialize_some(&self.name)
- }
- }
+ generics: Option<Vec<String>>,
}
/// Full type of functions/methods in the search index.
where
S: Serializer,
{
- (&self.ty.name, self.kind).serialize(serializer)
+ let mut seq = serializer.serialize_seq(None)?;
+ seq.serialize_element(&self.ty.name)?;
+ seq.serialize_element(&self.kind)?;
+ if let Some(generics) = &self.ty.generics {
+ seq.serialize_element(generics)?;
+ }
+ seq.end()
}
}
.into(),
("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(),
("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(),
- ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", true)
+ ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", false)
.into(),
- ("auto-collapse-implementors", "Auto-hide implementors of a trait", true).into(),
("go-to-only-result", "Directly go to item in search if there is only one result", false)
.into(),
("line-numbers", "Show line numbers on code examples", false).into(),
extra_info
}
+// Render the list of items inside one of the sections "Trait Implementations",
+// "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
fn render_impls(
cx: &Context<'_>,
w: &mut Buffer,
containing_item,
assoc_link,
RenderMode::Normal,
- containing_item.stable_since(tcx).as_deref(),
- containing_item.const_stable_since(tcx).as_deref(),
- true,
None,
- false,
- true,
&[],
+ ImplRenderingParameters {
+ show_def_docs: true,
+ is_on_foreign_type: false,
+ show_default_items: true,
+ show_non_assoc_items: true,
+ toggle_open_by_default: true,
+ },
);
buffer.into_inner()
})
AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
AssocItemLink::Anchor(None) => anchor,
AssocItemLink::GotoSource(did, _) => {
- href(did.expect_real(), cx).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
+ href(did.expect_def_id(), cx).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
}
}
}
fn render_stability_since_raw(
w: &mut Buffer,
ver: Option<&str>,
- const_ver: Option<&str>,
+ const_stability: Option<&ConstStability>,
containing_ver: Option<&str>,
containing_const_ver: Option<&str>,
) {
let ver = ver.filter(|inner| !inner.is_empty());
- let const_ver = const_ver.filter(|inner| !inner.is_empty());
- match (ver, const_ver) {
- (Some(v), Some(cv)) if const_ver != containing_const_ver => {
+ match (ver, const_stability) {
+ // stable and const stable
+ (Some(v), Some(ConstStability { level: StabilityLevel::Stable { since }, .. }))
+ if Some(since.as_str()).as_deref() != containing_const_ver =>
+ {
write!(
w,
"<span class=\"since\" title=\"Stable since Rust version {0}, const since {1}\">{0} (const: {1})</span>",
- v, cv
+ v, since
+ );
+ }
+ // stable and const unstable
+ (
+ Some(v),
+ Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }),
+ ) => {
+ write!(
+ w,
+ "<span class=\"since\" title=\"Stable since Rust version {0}, const unstable\">{0} (const: ",
+ v
);
+ if let Some(n) = issue {
+ write!(
+ w,
+ "<a href=\"https://github.com/rust-lang/rust/issues/{}\" title=\"Tracking issue for {}\">unstable</a>",
+ n, feature
+ );
+ } else {
+ write!(w, "unstable");
+ }
+ write!(w, ")</span>");
}
+ // stable
(Some(v), _) if ver != containing_ver => {
write!(
w,
) {
let name = meth.name.as_ref().unwrap();
let href = match link {
- AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
- AssocItemLink::Anchor(None) => format!("#{}.{}", meth.type_(), name),
+ AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{}", id)),
+ AssocItemLink::Anchor(None) => Some(format!("#{}.{}", meth.type_(), name)),
AssocItemLink::GotoSource(did, provided_methods) => {
// We're creating a link from an impl-item to the corresponding
// trait-item and need to map the anchored type accordingly.
ItemType::TyMethod
};
- href(did.expect_real(), cx)
- .map(|p| format!("{}#{}.{}", p.0, ty, name))
- .unwrap_or_else(|| format!("#{}.{}", ty, name))
+ match (href(did.expect_def_id(), cx), ty) {
+ (Ok(p), ty) => Some(format!("{}#{}.{}", p.0, ty, name)),
+ (Err(HrefError::DocumentationNotBuilt), ItemType::TyMethod) => None,
+ (Err(_), ty) => Some(format!("#{}.{}", ty, name)),
+ }
}
};
let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string();
- let constness = header.constness.print_with_space();
+ let constness =
+ print_constness_with_space(&header.constness, meth.const_stability(cx.tcx()));
let asyncness = header.asyncness.print_with_space();
let unsafety = header.unsafety.print_with_space();
let defaultness = print_default_space(meth.is_default());
let abi = print_abi_with_space(header.abi).to_string();
+
// NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
let generics_len = format!("{:#}", g.print(cx)).len();
let mut header_len = "fn ".len()
w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
write!(
w,
- "{}{}{}{}{}{}{}fn <a href=\"{href}\" class=\"fnname\">{name}</a>\
+ "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a {href} class=\"fnname\">{name}</a>\
{generics}{decl}{notable_traits}{where_clause}",
- indent_str,
- vis,
- constness,
- asyncness,
- unsafety,
- defaultness,
- abi,
- href = href,
+ indent = indent_str,
+ vis = vis,
+ constness = constness,
+ asyncness = asyncness,
+ unsafety = unsafety,
+ defaultness = defaultness,
+ abi = abi,
+ // links without a href are valid - https://www.w3schools.com/tags/att_a_href.asp
+ href = href.map(|href| format!("href=\"{}\"", href)).unwrap_or_else(|| "".to_string()),
name = name,
generics = g.print(cx),
decl = d.full_print(header_len, indent, header.asyncness, cx),
}
}
-const ALLOWED_ATTRIBUTES: &[Symbol] = &[
- sym::export_name,
- sym::link_section,
- sym::must_use,
- sym::no_mangle,
- sym::repr,
- sym::non_exhaustive,
-];
+const ALLOWED_ATTRIBUTES: &[Symbol] =
+ &[sym::export_name, sym::link_section, sym::no_mangle, sym::repr, sym::non_exhaustive];
fn attributes(it: &clean::Item) -> Vec<String> {
it.attrs
#[derive(Copy, Clone)]
enum AssocItemLink<'a> {
Anchor(Option<&'a str>),
- GotoSource(FakeDefId, &'a FxHashSet<Symbol>),
+ GotoSource(ItemId, &'a FxHashSet<Symbol>),
}
impl<'a> AssocItemLink<'a> {
Some(v) => v,
None => return,
};
- let tcx = cx.tcx();
let cache = cx.cache();
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() {
write!(
w,
"<h2 id=\"deref-methods\" class=\"small-section-header\">\
- Methods from {trait_}<Target = {type_}>\
+ <span>Methods from {trait_}<Target = {type_}></span>\
<a href=\"#deref-methods\" class=\"anchor\"></a>\
</h2>",
trait_ = trait_.print(cx),
containing_item,
AssocItemLink::Anchor(None),
render_mode,
- containing_item.stable_since(tcx).as_deref(),
- containing_item.const_stable_since(tcx).as_deref(),
- true,
None,
- false,
- true,
&[],
+ ImplRenderingParameters {
+ show_def_docs: true,
+ is_on_foreign_type: false,
+ show_default_items: true,
+ show_non_assoc_items: true,
+ toggle_open_by_default: true,
+ },
);
}
}
out.into_inner()
}
+#[derive(Clone, Copy, Debug)]
+struct ImplRenderingParameters {
+ show_def_docs: bool,
+ is_on_foreign_type: bool,
+ show_default_items: bool,
+ /// Whether or not to show methods.
+ show_non_assoc_items: bool,
+ toggle_open_by_default: bool,
+}
+
fn render_impl(
w: &mut Buffer,
cx: &Context<'_>,
parent: &clean::Item,
link: AssocItemLink<'_>,
render_mode: RenderMode,
- outer_version: Option<&str>,
- outer_const_version: Option<&str>,
- show_def_docs: bool,
use_absolute: Option<bool>,
- is_on_foreign_type: bool,
- show_default_items: bool,
- // This argument is used to reference same type with different paths to avoid duplication
- // in documentation pages for trait with automatic implementations like "Send" and "Sync".
aliases: &[String],
+ rendering_params: ImplRenderingParameters,
) {
let cache = cx.cache();
let traits = &cache.traits;
// For trait implementations, the `interesting` output contains all methods that have doc
// comments, and the `boring` output contains all methods that do not. The distinction is
// used to allow hiding the boring methods.
+ // `containing_item` is used for rendering stability info. If the parent is a trait impl,
+ // `containing_item` will the grandparent, since trait impls can't have stability attached.
fn doc_impl_item(
boring: &mut Buffer,
interesting: &mut Buffer,
cx: &Context<'_>,
item: &clean::Item,
parent: &clean::Item,
+ containing_item: &clean::Item,
link: AssocItemLink<'_>,
render_mode: RenderMode,
is_default_item: bool,
- outer_version: Option<&str>,
- outer_const_version: Option<&str>,
trait_: Option<&clean::Trait>,
- show_def_docs: bool,
+ rendering_params: ImplRenderingParameters,
) {
let item_type = item.type_();
let name = item.name.as_ref().unwrap();
- let tcx = cx.tcx();
- let render_method_item = match render_mode {
- RenderMode::Normal => true,
- RenderMode::ForDeref { mut_: deref_mut_ } => {
- should_render_item(&item, deref_mut_, &cx.cache)
- }
- };
+ let render_method_item = rendering_params.show_non_assoc_items
+ && match render_mode {
+ RenderMode::Normal => true,
+ RenderMode::ForDeref { mut_: deref_mut_ } => {
+ should_render_item(&item, deref_mut_, &cx.cache)
+ }
+ };
let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" };
} else {
// In case the item isn't documented,
// provide short documentation from the trait.
- document_short(&mut doc_buffer, it, cx, link, parent, show_def_docs);
+ document_short(
+ &mut doc_buffer,
+ it,
+ cx,
+ link,
+ parent,
+ rendering_params.show_def_docs,
+ );
}
}
} else {
document_item_info(&mut info_buffer, cx, item, Some(parent));
- if show_def_docs {
+ if rendering_params.show_def_docs {
document_full(&mut doc_buffer, item, cx);
short_documented = false;
}
}
} else {
- document_short(&mut doc_buffer, item, cx, link, parent, show_def_docs);
+ document_short(
+ &mut doc_buffer,
+ item,
+ cx,
+ link,
+ parent,
+ rendering_params.show_def_docs,
+ );
}
}
let w = if short_documented && trait_.is_some() { interesting } else { boring };
"<div id=\"{}\" class=\"{}{} has-srclink\">",
id, item_type, in_trait_class,
);
+ render_rightside(w, cx, item, containing_item);
+ write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("<h4 class=\"code-header\">");
render_assoc_item(
w,
cx,
);
w.write_str("</h4>");
- render_stability_since_raw(
- w,
- item.stable_since(tcx).as_deref(),
- item.const_stable_since(tcx).as_deref(),
- outer_version,
- outer_const_version,
- );
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
- write_srclink(cx, item, w);
w.write_str("</div>");
}
}
"<div id=\"{}\" class=\"{}{} has-srclink\">",
id, item_type, in_trait_class
);
+ write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("<h4 class=\"code-header\">");
assoc_type(
w,
cx,
);
w.write_str("</h4>");
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("</div>");
}
clean::AssocConstItem(ref ty, ref default) => {
"<div id=\"{}\" class=\"{}{} has-srclink\">",
id, item_type, in_trait_class
);
+ render_rightside(w, cx, item, containing_item);
+ write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("<h4 class=\"code-header\">");
assoc_const(
w,
cx,
);
w.write_str("</h4>");
- render_stability_since_raw(
- w,
- item.stable_since(tcx).as_deref(),
- item.const_stable_since(tcx).as_deref(),
- outer_version,
- outer_const_version,
- );
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
- write_srclink(cx, item, w);
w.write_str("</div>");
}
clean::AssocTypeItem(ref bounds, ref default) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
write!(w, "<div id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,);
+ write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("<h4 class=\"code-header\">");
assoc_type(
w,
cx,
);
w.write_str("</h4>");
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("</div>");
}
clean::StrippedItem(..) => return,
cx,
trait_item,
if trait_.is_some() { &i.impl_item } else { parent },
+ parent,
link,
render_mode,
false,
- outer_version,
- outer_const_version,
trait_.map(|t| &t.trait_),
- show_def_docs,
+ rendering_params,
);
}
t: &clean::Trait,
i: &clean::Impl,
parent: &clean::Item,
+ containing_item: &clean::Item,
render_mode: RenderMode,
- outer_version: Option<&str>,
- outer_const_version: Option<&str>,
- show_def_docs: bool,
+ rendering_params: ImplRenderingParameters,
) {
for trait_item in &t.items {
let n = trait_item.name;
cx,
trait_item,
parent,
+ containing_item,
assoc_link,
render_mode,
true,
- outer_version,
- outer_const_version,
Some(t),
- show_def_docs,
+ rendering_params,
);
}
}
// default items which weren't overridden in the implementation block.
// We don't emit documentation for default items if they appear in the
// Implementations on Foreign Types or Implementors sections.
- if show_default_items {
+ if rendering_params.show_default_items {
if let Some(t) = trait_ {
render_default_items(
&mut default_impl_items,
&t.trait_,
&i.inner_impl(),
&i.impl_item,
+ parent,
render_mode,
- outer_version,
- outer_const_version,
- show_def_docs,
+ rendering_params,
);
}
}
if render_mode == RenderMode::Normal {
- let is_implementing_trait = i.inner_impl().trait_.is_some();
- let toggled = !impl_items.is_empty() || !default_impl_items.is_empty();
+ let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
if toggled {
close_tags.insert_str(0, "</details>");
- if is_implementing_trait {
- write!(w, "<details class=\"rustdoc-toggle implementors-toggle\">");
- } else {
- write!(w, "<details class=\"rustdoc-toggle implementors-toggle\" open>");
- }
- }
- if toggled {
+ write!(
+ w,
+ "<details class=\"rustdoc-toggle implementors-toggle\"{}>",
+ if rendering_params.toggle_open_by_default { " open" } else { "" }
+ );
write!(w, "<summary>")
}
render_impl_summary(
w,
cx,
i,
- outer_version,
- outer_const_version,
- show_def_docs,
+ parent,
+ parent,
+ rendering_params.show_def_docs,
use_absolute,
- is_on_foreign_type,
+ rendering_params.is_on_foreign_type,
aliases,
);
if toggled {
write!(w, "</summary>")
}
- if trait_.is_some() {
- if let Some(portability) = portability(&i.impl_item, Some(parent)) {
- write!(w, "<div class=\"item-info\">{}</div>", portability);
- }
- }
if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
let mut ids = cx.id_map.borrow_mut();
w.write_str(&close_tags);
}
-fn render_impl_summary(
+// Render the items that appear on the right side of methods, impls, and
+// associated types. For example "1.0.0 (const: 1.39.0) [src]".
+fn render_rightside(
+ w: &mut Buffer,
+ cx: &Context<'_>,
+ item: &clean::Item,
+ containing_item: &clean::Item,
+) {
+ let tcx = cx.tcx();
+
+ write!(w, "<div class=\"rightside\">");
+ render_stability_since_raw(
+ w,
+ item.stable_since(tcx).as_deref(),
+ item.const_stability(tcx),
+ containing_item.stable_since(tcx).as_deref(),
+ containing_item.const_stable_since(tcx).as_deref(),
+ );
+
+ write_srclink(cx, item, w);
+ w.write_str("</div>");
+}
+
+pub(crate) fn render_impl_summary(
w: &mut Buffer,
cx: &Context<'_>,
i: &Impl,
- outer_version: Option<&str>,
- outer_const_version: Option<&str>,
+ parent: &clean::Item,
+ containing_item: &clean::Item,
show_def_docs: bool,
use_absolute: Option<bool>,
is_on_foreign_type: bool,
// in documentation pages for trait with automatic implementations like "Send" and "Sync".
aliases: &[String],
) {
- let tcx = cx.tcx();
let id = cx.derive_id(match i.inner_impl().trait_ {
Some(ref t) => {
if is_on_foreign_type {
} else {
format!(" data-aliases=\"{}\"", aliases.join(","))
};
+ write!(w, "<div id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases);
+ render_rightside(w, cx, &i.impl_item, containing_item);
+ write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<h3 class=\"code-header in-band\">");
+
if let Some(use_absolute) = use_absolute {
- write!(w, "<div id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases);
- write!(w, "<h3 class=\"code-header in-band\">");
write!(w, "{}", i.inner_impl().print(use_absolute, cx));
if show_def_docs {
for it in &i.inner_impl().items {
}
}
}
- w.write_str("</h3>");
} else {
- write!(
- w,
- "<div id=\"{}\" class=\"impl has-srclink\"{}>\
- <h3 class=\"code-header in-band\">{}</h3>",
- id,
- aliases,
- i.inner_impl().print(false, cx)
- );
+ write!(w, "{}", i.inner_impl().print(false, cx));
}
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
- render_stability_since_raw(
- w,
- i.impl_item.stable_since(tcx).as_deref(),
- i.impl_item.const_stable_since(tcx).as_deref(),
- outer_version,
- outer_const_version,
- );
- write_srclink(cx, &i.impl_item, w);
+ write!(w, "</h3>");
+
+ let is_trait = i.inner_impl().trait_.is_some();
+ if is_trait {
+ if let Some(portability) = portability(&i.impl_item, Some(parent)) {
+ write!(w, "<div class=\"item-info\">{}</div>", portability);
+ }
+ }
+
w.write_str("</div>");
}
write!(
buffer,
"<div class=\"block version\">\
+ <div class=\"narrow-helper\"></div>\
<p>Version {}</p>\
</div>",
Escape(version),
}
fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
- let did = it.def_id.expect_real();
+ let did = it.def_id.expect_def_id();
if let Some(v) = cx.cache.impls.get(&did) {
let mut used_links = FxHashSet::default();
let cache = cx.cache();
out.push_str("</div>");
}
}
-
- // Recurse into any further impls that might exist for `target`
- if let Some(target_did) = target.def_id_full(c) {
- if let Some(target_impls) = c.impls.get(&target_did) {
- if let Some(target_deref_impl) = target_impls
- .iter()
- .filter(|i| i.inner_impl().trait_.is_some())
- .find(|i| i.inner_impl().trait_.def_id_full(c) == c.deref_trait_did)
- {
- sidebar_deref_methods(cx, out, target_deref_impl, target_impls);
- }
- }
- }
}
}
"</div>",
);
- if let Some(implementors) = cx.cache.implementors.get(&it.def_id.expect_real()) {
+ if let Some(implementors) = cx.cache.implementors.get(&it.def_id.expect_def_id()) {
let cache = cx.cache();
let mut res = implementors
.iter()