summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /src/librustdoc/html
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/librustdoc/html')
-rw-r--r--src/librustdoc/html/format.rs49
-rw-r--r--src/librustdoc/html/highlight.rs14
-rw-r--r--src/librustdoc/html/markdown.rs136
-rw-r--r--src/librustdoc/html/render/context.rs19
-rw-r--r--src/librustdoc/html/render/mod.rs77
-rw-r--r--src/librustdoc/html/render/print_item.rs269
-rw-r--r--src/librustdoc/html/render/search_index.rs40
-rw-r--r--src/librustdoc/html/render/type_layout.rs86
-rw-r--r--src/librustdoc/html/sources.rs2
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css4
-rw-r--r--src/librustdoc/html/static/css/settings.css10
-rw-r--r--src/librustdoc/html/static/js/externs.js5
-rw-r--r--src/librustdoc/html/static/js/main.js15
-rw-r--r--src/librustdoc/html/static/js/search.js424
-rw-r--r--src/librustdoc/html/static/js/settings.js19
-rw-r--r--src/librustdoc/html/static/js/source-script.js2
-rw-r--r--src/librustdoc/html/templates/item_union.html8
-rw-r--r--src/librustdoc/html/templates/type_layout.html58
-rw-r--r--src/librustdoc/html/templates/type_layout_size.html12
19 files changed, 731 insertions, 518 deletions
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 1b445b898..d963d6092 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -167,7 +167,7 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
display_fn(move |f| {
let mut bounds_dup = FxHashSet::default();
- for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(b.clone())).enumerate() {
+ for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(*b)).enumerate() {
if i > 0 {
f.write_str(" + ")?;
}
@@ -439,6 +439,7 @@ impl clean::GenericBound {
let modifier_str = match modifier {
hir::TraitBoundModifier::None => "",
hir::TraitBoundModifier::Maybe => "?",
+ hir::TraitBoundModifier::Negative => "!",
// ~const is experimental; do not display those bounds in rustdoc
hir::TraitBoundModifier::MaybeConst => "",
};
@@ -1115,14 +1116,17 @@ fn fmt_type<'cx>(
ref trait_,
should_show_cast,
}) => {
+ // FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719),
+ // we need to surround them with angle brackets in some cases (e.g. `<dyn …>::P`).
+
if f.alternate() {
- if should_show_cast {
+ if let Some(trait_) = trait_ && 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 {
+ if let Some(trait_) = trait_ && should_show_cast {
write!(f, "&lt;{} as {}&gt;::", self_type.print(cx), trait_.print(cx))?
} else {
write!(f, "{}::", self_type.print(cx))?
@@ -1138,15 +1142,36 @@ fn fmt_type<'cx>(
// the ugliness comes from inlining across crates where
// everything comes in as a fully resolved QPath (hard to
// look at).
- if !f.alternate() && let Ok((url, _, path)) = href(trait_.def_id(), cx) {
- write!(
- f,
- "<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
- title=\"type {path}::{name}\">{name}</a>",
- shortty = ItemType::AssocType,
- name = assoc.name,
- path = join_with_double_colon(&path),
- )
+ 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,
+ "<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
+ title=\"type {path}::{name}\">{name}</a>",
+ shortty = ItemType::AssocType,
+ name = assoc.name,
+ path = join_with_double_colon(&path),
+ )
+ } else {
+ write!(f, "{}", assoc.name)
+ }
} else {
write!(f, "{}", assoc.name)
}?;
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index b61dd5714..c94968b48 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -514,7 +514,7 @@ struct Classifier<'src> {
impl<'src> Classifier<'src> {
/// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
- /// file span which will be used later on by the `span_correspondance_map`.
+ /// file span which will be used later on by the `span_correspondence_map`.
fn new(src: &str, file_span: Span, decoration_info: Option<DecorationInfo>) -> Classifier<'_> {
let tokens = PeekIter::new(TokenIter { src, cursor: Cursor::new(src) });
let decorations = decoration_info.map(Decorations::new);
@@ -649,7 +649,7 @@ impl<'src> Classifier<'src> {
///
/// `before` is the position of the given token in the `source` string and is used as "lo" byte
/// in case we want to try to generate a link for this token using the
- /// `span_correspondance_map`.
+ /// `span_correspondence_map`.
fn advance(
&mut self,
token: TokenKind,
@@ -811,7 +811,9 @@ impl<'src> Classifier<'src> {
| LiteralKind::Str { .. }
| LiteralKind::ByteStr { .. }
| LiteralKind::RawStr { .. }
- | LiteralKind::RawByteStr { .. } => Class::String,
+ | LiteralKind::RawByteStr { .. }
+ | LiteralKind::CStr { .. }
+ | LiteralKind::RawCStr { .. } => Class::String,
// Number literals.
LiteralKind::Float { .. } | LiteralKind::Int { .. } => Class::Number,
},
@@ -895,7 +897,7 @@ fn exit_span(out: &mut impl Write, closing_tag: &str) {
/// flexible.
///
/// Note that if `context` is not `None` and that the given `klass` contains a `Span`, the function
-/// will then try to find this `span` in the `span_correspondance_map`. If found, it'll then
+/// will then try to find this `span` in the `span_correspondence_map`. If found, it'll then
/// generate a link for this element (which corresponds to where its definition is located).
fn string<T: Display>(
out: &mut impl Write,
@@ -916,7 +918,7 @@ fn string<T: Display>(
/// * If `klass` is `Some` but `klass.get_span()` is `None`, it writes the text wrapped in a
/// `<span>` with the provided `klass`.
/// * If `klass` is `Some` and has a [`rustc_span::Span`], it then tries to generate a link (`<a>`
-/// element) by retrieving the link information from the `span_correspondance_map` that was filled
+/// element) by retrieving the link information from the `span_correspondence_map` that was filled
/// in `span_map.rs::collect_spans_and_sources`. If it cannot retrieve the information, then it's
/// the same as the second point (`klass` is `Some` but doesn't have a [`rustc_span::Span`]).
fn string_without_closing_tag<T: Display>(
@@ -963,7 +965,7 @@ fn string_without_closing_tag<T: Display>(
if let Some(href_context) = href_context {
if let Some(href) =
- href_context.context.shared.span_correspondance_map.get(&def_span).and_then(|href| {
+ href_context.context.shared.span_correspondence_map.get(&def_span).and_then(|href| {
let context = href_context.context;
// FIXME: later on, it'd be nice to provide two links (if possible) for all items:
// one to the documentation page and one to the source definition.
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 00aadb8e8..9bb20022c 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -551,7 +551,15 @@ impl<'a, I: Iterator<Item = Event<'a>>> SummaryLine<'a, I> {
}
fn check_if_allowed_tag(t: &Tag<'_>) -> bool {
- matches!(t, Tag::Paragraph | Tag::Emphasis | Tag::Strong | Tag::Link(..) | Tag::BlockQuote)
+ matches!(
+ t,
+ Tag::Paragraph
+ | Tag::Emphasis
+ | Tag::Strong
+ | Tag::Strikethrough
+ | Tag::Link(..)
+ | Tag::BlockQuote
+ )
}
fn is_forbidden_tag(t: &Tag<'_>) -> bool {
@@ -773,7 +781,7 @@ impl<'tcx> ExtraInfo<'tcx> {
ExtraInfo { def_id, sp, tcx }
}
- fn error_invalid_codeblock_attr(&self, msg: &str, help: &str) {
+ fn error_invalid_codeblock_attr(&self, msg: String, help: &str) {
if let Some(def_id) = self.def_id.as_local() {
self.tcx.struct_span_lint_hir(
crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
@@ -948,7 +956,7 @@ impl LangString {
} {
if let Some(extra) = extra {
extra.error_invalid_codeblock_attr(
- &format!("unknown attribute `{}`. Did you mean `{}`?", x, flag),
+ format!("unknown attribute `{}`. Did you mean `{}`?", x, flag),
help,
);
}
@@ -1229,7 +1237,27 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
pub(crate) struct MarkdownLink {
pub kind: LinkType,
pub link: String,
- pub range: Range<usize>,
+ pub range: MarkdownLinkRange,
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum MarkdownLinkRange {
+ /// Normally, markdown link warnings point only at the destination.
+ Destination(Range<usize>),
+ /// In some cases, it's not possible to point at the destination.
+ /// Usually, this happens because backslashes `\\` are used.
+ /// When that happens, point at the whole link, and don't provide structured suggestions.
+ WholeLink(Range<usize>),
+}
+
+impl MarkdownLinkRange {
+ /// Extracts the inner range.
+ pub fn inner_range(&self) -> &Range<usize> {
+ match self {
+ MarkdownLinkRange::Destination(range) => range,
+ MarkdownLinkRange::WholeLink(range) => range,
+ }
+ }
}
pub(crate) fn markdown_links<R>(
@@ -1249,9 +1277,9 @@ pub(crate) fn markdown_links<R>(
if md_start <= s_start && s_end <= md_end {
let start = s_start.offset_from(md_start) as usize;
let end = s_end.offset_from(md_start) as usize;
- start..end
+ MarkdownLinkRange::Destination(start..end)
} else {
- fallback
+ MarkdownLinkRange::WholeLink(fallback)
}
};
@@ -1259,6 +1287,7 @@ pub(crate) fn markdown_links<R>(
// For diagnostics, we want to underline the link's definition but `span` will point at
// where the link is used. This is a problem for reference-style links, where the definition
// is separate from the usage.
+
match link {
// `Borrowed` variant means the string (the link's destination) may come directly from
// the markdown text and we can locate the original link destination.
@@ -1267,10 +1296,82 @@ pub(crate) fn markdown_links<R>(
CowStr::Borrowed(s) => locate(s, span),
// For anything else, we can only use the provided range.
- CowStr::Boxed(_) | CowStr::Inlined(_) => span,
+ CowStr::Boxed(_) | CowStr::Inlined(_) => MarkdownLinkRange::WholeLink(span),
}
};
+ let span_for_offset_backward = |span: Range<usize>, open: u8, close: u8| {
+ let mut open_brace = !0;
+ let mut close_brace = !0;
+ for (i, b) in md.as_bytes()[span.clone()].iter().copied().enumerate().rev() {
+ let i = i + span.start;
+ if b == close {
+ close_brace = i;
+ break;
+ }
+ }
+ if close_brace < span.start || close_brace >= span.end {
+ return MarkdownLinkRange::WholeLink(span);
+ }
+ let mut nesting = 1;
+ for (i, b) in md.as_bytes()[span.start..close_brace].iter().copied().enumerate().rev() {
+ let i = i + span.start;
+ if b == close {
+ nesting += 1;
+ }
+ if b == open {
+ nesting -= 1;
+ }
+ if nesting == 0 {
+ open_brace = i;
+ break;
+ }
+ }
+ assert!(open_brace != close_brace);
+ if open_brace < span.start || open_brace >= span.end {
+ return MarkdownLinkRange::WholeLink(span);
+ }
+ // do not actually include braces in the span
+ let range = (open_brace + 1)..close_brace;
+ MarkdownLinkRange::Destination(range.clone())
+ };
+
+ let span_for_offset_forward = |span: Range<usize>, open: u8, close: u8| {
+ let mut open_brace = !0;
+ let mut close_brace = !0;
+ for (i, b) in md.as_bytes()[span.clone()].iter().copied().enumerate() {
+ let i = i + span.start;
+ if b == open {
+ open_brace = i;
+ break;
+ }
+ }
+ if open_brace < span.start || open_brace >= span.end {
+ return MarkdownLinkRange::WholeLink(span);
+ }
+ let mut nesting = 0;
+ for (i, b) in md.as_bytes()[open_brace..span.end].iter().copied().enumerate() {
+ let i = i + open_brace;
+ if b == close {
+ nesting -= 1;
+ }
+ if b == open {
+ nesting += 1;
+ }
+ if nesting == 0 {
+ close_brace = i;
+ break;
+ }
+ }
+ assert!(open_brace != close_brace);
+ if open_brace < span.start || open_brace >= span.end {
+ return MarkdownLinkRange::WholeLink(span);
+ }
+ // do not actually include braces in the span
+ let range = (open_brace + 1)..close_brace;
+ MarkdownLinkRange::Destination(range.clone())
+ };
+
Parser::new_with_broken_link_callback(
md,
main_body_opts(),
@@ -1279,11 +1380,20 @@ pub(crate) fn markdown_links<R>(
.into_offset_iter()
.filter_map(|(event, span)| match event {
Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
- preprocess_link(MarkdownLink {
- kind: link_type,
- range: span_for_link(&dest, span),
- link: dest.into_string(),
- })
+ let range = match link_type {
+ // Link is pulled from the link itself.
+ LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => {
+ span_for_offset_backward(span, b'[', b']')
+ }
+ LinkType::CollapsedUnknown => span_for_offset_forward(span, b'[', b']'),
+ LinkType::Inline => span_for_offset_backward(span, b'(', b')'),
+ // Link is pulled from elsewhere in the document.
+ LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => {
+ span_for_link(&dest, span)
+ }
+ LinkType::Autolink | LinkType::Email => unreachable!(),
+ };
+ preprocess_link(MarkdownLink { kind: link_type, range, link: dest.into_string() })
}
_ => None,
})
@@ -1392,7 +1502,7 @@ static DEFAULT_ID_MAP: Lazy<FxHashMap<Cow<'static, str>, usize>> = Lazy::new(||
fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
let mut map = FxHashMap::default();
- // This is the list of IDs used in Javascript.
+ // This is the list of IDs used in JavaScript.
map.insert("help".into(), 1);
map.insert("settings".into(), 1);
map.insert("not-displayed".into(), 1);
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index ac5054ce1..56af257fd 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -122,9 +122,9 @@ pub(crate) struct SharedContext<'tcx> {
/// the crate.
redirections: Option<RefCell<FxHashMap<String, String>>>,
- /// Correspondance map used to link types used in the source code pages to allow to click on
+ /// Correspondence map used to link types used in the source code pages to allow to click on
/// links to jump to the type's definition.
- pub(crate) span_correspondance_map: FxHashMap<rustc_span::Span, LinkFromSrc>,
+ pub(crate) span_correspondence_map: FxHashMap<rustc_span::Span, LinkFromSrc>,
/// The [`Cache`] used during rendering.
pub(crate) cache: Cache,
@@ -184,11 +184,8 @@ impl<'tcx> Context<'tcx> {
};
title.push_str(" - Rust");
let tyname = it.type_();
- let desc = it
- .doc_value()
- .as_ref()
- .map(|doc| plain_text_summary(doc, &it.link_names(&self.cache())));
- let desc = if let Some(desc) = desc {
+ let desc = plain_text_summary(&it.doc_value(), &it.link_names(&self.cache()));
+ let desc = if !desc.is_empty() {
desc
} else if it.is_crate() {
format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate)
@@ -531,7 +528,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
errors: receiver,
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
show_type_layout,
- span_correspondance_map: matches,
+ span_correspondence_map: matches,
cache,
call_locations,
};
@@ -647,7 +644,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
</div>\
<noscript>\
<section>\
- You need to enable Javascript be able to update your settings.\
+ You need to enable JavaScript be able to update your settings.\
</section>\
</noscript>\
<link rel=\"stylesheet\" \
@@ -709,7 +706,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
</div>\
<noscript>\
<section>\
- <p>You need to enable Javascript to use keyboard commands or search.</p>\
+ <p>You need to enable JavaScript to use keyboard commands or search.</p>\
<p>For more information, browse the <a href=\"https://doc.rust-lang.org/rustdoc/\">rustdoc handbook</a>.</p>\
</section>\
</noscript>",
@@ -746,7 +743,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
// Flush pending errors.
Rc::get_mut(&mut self.shared).unwrap().fs.close();
let nb_errors =
- self.shared.errors.iter().map(|err| self.tcx().sess.struct_err(&err).emit()).count();
+ self.shared.errors.iter().map(|err| self.tcx().sess.struct_err(err).emit()).count();
if nb_errors > 0 {
Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
} else {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 463184aca..9e3b5d10a 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -32,6 +32,7 @@ mod context;
mod print_item;
mod sidebar;
mod span_map;
+mod type_layout;
mod write_shared;
pub(crate) use self::context::*;
@@ -47,7 +48,6 @@ use std::str;
use std::string::ToString;
use askama::Template;
-use rustc_ast_pretty::pprust;
use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -468,7 +468,8 @@ fn document_short<'a, 'cx: 'a>(
if !show_def_docs {
return Ok(());
}
- if let Some(s) = item.doc_value() {
+ let s = item.doc_value();
+ if !s.is_empty() {
let (mut summary_html, has_more_content) =
MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
@@ -511,7 +512,7 @@ fn document_full_inner<'a, 'cx: 'a>(
heading_offset: HeadingOffset,
) -> impl fmt::Display + 'a + Captures<'cx> {
display_fn(move |f| {
- if let Some(s) = item.collapsed_doc_value() {
+ if let Some(s) = item.opt_doc_value() {
debug!("Doc block: =====\n{}\n=====", s);
if is_collapsible {
write!(
@@ -848,10 +849,10 @@ fn assoc_method(
let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
let indent_str = " ";
- write!(w, "{}", render_attributes_in_pre(meth, indent_str));
+ write!(w, "{}", render_attributes_in_pre(meth, indent_str, tcx));
(4, indent_str, Ending::NoNewline)
} else {
- render_attributes_in_code(w, meth);
+ render_attributes_in_code(w, meth, tcx);
(0, "", Ending::Newline)
};
w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
@@ -1020,36 +1021,15 @@ fn render_assoc_item(
}
}
-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
- .other_attrs
- .iter()
- .filter_map(|attr| {
- if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) {
- Some(
- pprust::attribute_to_string(attr)
- .replace("\\\n", "")
- .replace('\n', "")
- .replace(" ", " "),
- )
- } else {
- None
- }
- })
- .collect()
-}
-
// When an attribute is rendered inside a `<pre>` tag, it is formatted using
// a whitespace prefix and newline.
-fn render_attributes_in_pre<'a>(
+fn render_attributes_in_pre<'a, 'b: 'a>(
it: &'a clean::Item,
prefix: &'a str,
-) -> impl fmt::Display + Captures<'a> {
+ tcx: TyCtxt<'b>,
+) -> impl fmt::Display + Captures<'a> + Captures<'b> {
crate::html::format::display_fn(move |f| {
- for a in attributes(it) {
+ for a in it.attributes(tcx, false) {
writeln!(f, "{}{}", prefix, a)?;
}
Ok(())
@@ -1058,8 +1038,8 @@ fn render_attributes_in_pre<'a>(
// When an attribute is rendered inside a <code> tag, it is formatted using
// a div to produce a newline after it.
-fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item) {
- for a in attributes(it) {
+fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item, tcx: TyCtxt<'_>) {
+ for a in it.attributes(tcx, false) {
write!(w, "<div class=\"code-attribute\">{}</div>", a);
}
}
@@ -1154,10 +1134,10 @@ fn render_assoc_items_inner(
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() {
let mut tmp_buf = Buffer::html();
- let (render_mode, id) = match what {
+ let (render_mode, id, class_html) = match what {
AssocItemRender::All => {
write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations");
- (RenderMode::Normal, "implementations-list".to_owned())
+ (RenderMode::Normal, "implementations-list".to_owned(), "")
}
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
let id =
@@ -1174,7 +1154,11 @@ fn render_assoc_items_inner(
),
&id,
);
- (RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id))
+ (
+ RenderMode::ForDeref { mut_: deref_mut_ },
+ cx.derive_id(id),
+ r#" class="impl-items""#,
+ )
}
};
let mut impls_buf = Buffer::html();
@@ -1198,7 +1182,7 @@ fn render_assoc_items_inner(
}
if !impls_buf.is_empty() {
write!(w, "{}", tmp_buf.into_inner()).unwrap();
- write!(w, "<div id=\"{}\">", id).unwrap();
+ write!(w, "<div id=\"{id}\"{class_html}>").unwrap();
write!(w, "{}", impls_buf.into_inner()).unwrap();
w.write_str("</div>").unwrap();
}
@@ -1493,7 +1477,7 @@ fn render_impl(
if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
// We need the stability of the item from the trait
// because impls can't have a stability.
- if item.doc_value().is_some() {
+ if !item.doc_value().is_empty() {
document_item_info(cx, it, Some(parent))
.render_into(&mut info_buffer)
.unwrap();
@@ -1764,11 +1748,11 @@ fn render_impl(
write!(w, "</summary>")
}
- if let Some(ref dox) = i.impl_item.collapsed_doc_value() {
+ if let Some(ref dox) = i.impl_item.opt_doc_value() {
if trait_.is_none() && i.inner_impl().items.is_empty() {
w.write_str(
"<div class=\"item-info\">\
- <div class=\"stab empty-impl\">This impl block contains no items.</div>
+ <div class=\"stab empty-impl\">This impl block contains no items.</div>\
</div>",
);
}
@@ -1787,12 +1771,14 @@ fn render_impl(
.into_string()
);
}
+ if !default_impl_items.is_empty() || !impl_items.is_empty() {
+ w.write_str("<div class=\"impl-items\">");
+ close_tags.insert_str(0, "</div>");
+ }
}
if !default_impl_items.is_empty() || !impl_items.is_empty() {
- w.write_str("<div class=\"impl-items\">");
w.push_buffer(default_impl_items);
w.push_buffer(impl_items);
- close_tags.insert_str(0, "</div>");
}
w.write_str(&close_tags);
}
@@ -1947,8 +1933,6 @@ pub(crate) fn small_url_encode(s: String) -> String {
// While the same is not true for hashes, rustdoc only needs to be
// consistent with itself when encoding them.
st += "+";
- } else if b == b'%' {
- st += "%%";
} else {
write!(st, "%{:02X}", b).unwrap();
}
@@ -2217,7 +2201,9 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
}
clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
work.push_back(self_type);
- process_path(trait_.def_id());
+ if let Some(trait_) = trait_ {
+ process_path(trait_.def_id());
+ }
}
_ => {}
}
@@ -2271,8 +2257,7 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c
Ok(contents) => contents,
Err(err) => {
let span = item.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner());
- tcx.sess
- .span_err(span, &format!("failed to read file {}: {}", path.display(), err));
+ tcx.sess.span_err(span, format!("failed to read file {}: {}", path.display(), err));
return false;
}
};
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 9a968e48b..62027a3fa 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -6,16 +6,16 @@ use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::DefId;
use rustc_middle::middle::stability;
-use rustc_middle::span_bug;
-use rustc_middle::ty::layout::LayoutError;
-use rustc_middle::ty::{self, Adt, TyCtxt};
+use rustc_middle::ty::{self, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_target::abi::{LayoutS, Primitive, TagEncoding, Variants};
+use std::borrow::Borrow;
+use std::cell::{RefCell, RefMut};
use std::cmp::Ordering;
use std::fmt;
use std::rc::Rc;
+use super::type_layout::document_type_layout;
use super::{
collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
@@ -218,6 +218,53 @@ fn toggle_close(mut w: impl fmt::Write) {
w.write_str("</details>").unwrap();
}
+trait ItemTemplate<'a, 'cx: 'a>: askama::Template + fmt::Display {
+ fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>);
+}
+
+fn item_template_document<'a: 'b, 'b, 'cx: 'a>(
+ templ: &'b impl ItemTemplate<'a, 'cx>,
+) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let (item, mut cx) = templ.item_and_mut_cx();
+ let v = document(*cx, item, None, HeadingOffset::H2);
+ write!(f, "{v}")
+ })
+}
+
+fn item_template_document_type_layout<'a: 'b, 'b, 'cx: 'a>(
+ templ: &'b impl ItemTemplate<'a, 'cx>,
+) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let (item, cx) = templ.item_and_mut_cx();
+ let def_id = item.item_id.expect_def_id();
+ let v = document_type_layout(*cx, def_id);
+ write!(f, "{v}")
+ })
+}
+
+fn item_template_render_attributes_in_pre<'a: 'b, 'b, 'cx: 'a>(
+ templ: &'b impl ItemTemplate<'a, 'cx>,
+) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let (item, cx) = templ.item_and_mut_cx();
+ let tcx = cx.tcx();
+ let v = render_attributes_in_pre(item, "", tcx);
+ write!(f, "{v}")
+ })
+}
+
+fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>(
+ templ: &'b impl ItemTemplate<'a, 'cx>,
+) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+ display_fn(move |f| {
+ let (item, mut cx) = templ.item_and_mut_cx();
+ let def_id = item.item_id.expect_def_id();
+ let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All);
+ write!(f, "{v}")
+ })
+}
+
fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) {
write!(w, "{}", document(cx, item, None, HeadingOffset::H2));
@@ -358,18 +405,18 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
clean::ImportItem(ref import) => {
let stab_tags = if let Some(import_def_id) = import.source.did {
- let ast_attrs = cx.tcx().get_attrs_unchecked(import_def_id);
+ let ast_attrs = tcx.get_attrs_unchecked(import_def_id);
let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs));
// Just need an item with the correct def_id and attrs
let import_item = clean::Item {
item_id: import_def_id.into(),
attrs: import_attrs,
- cfg: ast_attrs.cfg(cx.tcx(), &cx.cache().hidden_cfg),
+ cfg: ast_attrs.cfg(tcx, &cx.cache().hidden_cfg),
..myitem.clone()
};
- let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()).to_string());
+ let stab_tags = Some(extra_info_tags(&import_item, item, tcx).to_string());
stab_tags
} else {
None
@@ -407,8 +454,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
let unsafety_flag = match *myitem.kind {
clean::FunctionItem(_) | clean::ForeignFunctionItem(_)
- if myitem.fn_header(cx.tcx()).unwrap().unsafety
- == hir::Unsafety::Unsafe =>
+ if myitem.fn_header(tcx).unwrap().unsafety == hir::Unsafety::Unsafe =>
{
"<sup title=\"unsafe function\">⚠</sup>"
}
@@ -422,9 +468,9 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
_ => "",
};
- let doc_value = myitem.doc_value().unwrap_or_default();
w.write_str(ITEM_TABLE_ROW_OPEN);
- let docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string();
+ let docs =
+ MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string();
let (docs_before, docs_after) = if docs.is_empty() {
("", "")
} else {
@@ -441,7 +487,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
{docs_before}{docs}{docs_after}",
name = myitem.name.unwrap(),
visibility_emoji = visibility_emoji,
- stab_tags = extra_info_tags(myitem, item, cx.tcx()),
+ stab_tags = extra_info_tags(myitem, item, tcx),
class = myitem.type_(),
unsafety_flag = unsafety_flag,
href = item_path(myitem.type_(), myitem.name.unwrap().as_str()),
@@ -550,7 +596,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
w,
"{attrs}{vis}{constness}{asyncness}{unsafety}{abi}fn \
{name}{generics}{decl}{notable_traits}{where_clause}",
- attrs = render_attributes_in_pre(it, ""),
+ attrs = render_attributes_in_pre(it, "", tcx),
vis = visibility,
constness = constness,
asyncness = asyncness,
@@ -591,7 +637,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
it.name.unwrap(),
t.generics.print(cx),
bounds,
- attrs = render_attributes_in_pre(it, ""),
+ attrs = render_attributes_in_pre(it, "", tcx),
);
if !t.generics.where_predicates.is_empty() {
@@ -888,7 +934,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
for implementor in foreign {
- let provided_methods = implementor.inner_impl().provided_trait_methods(cx.tcx());
+ let provided_methods = implementor.inner_impl().provided_trait_methods(tcx);
let assoc_link =
AssocItemLink::GotoSource(implementor.impl_item.item_id, &provided_methods);
render_impl(
@@ -921,7 +967,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
w.write_str("</div>");
- if t.is_auto(cx.tcx()) {
+ if t.is_auto(tcx) {
write_small_section_header(
w,
"synthetic-implementors",
@@ -950,7 +996,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
"<div id=\"implementors-list\"></div>",
);
- if t.is_auto(cx.tcx()) {
+ if t.is_auto(tcx) {
write_small_section_header(
w,
"synthetic-implementors",
@@ -1065,7 +1111,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &
t.generics.print(cx),
print_where_clause(&t.generics, cx, 0, Ending::Newline),
bounds(&t.bounds, true, cx),
- attrs = render_attributes_in_pre(it, ""),
+ attrs = render_attributes_in_pre(it, "", cx.tcx()),
);
});
@@ -1087,7 +1133,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl
t.generics.print(cx),
where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
bounds = bounds(&t.bounds, false, cx),
- attrs = render_attributes_in_pre(it, ""),
+ attrs = render_attributes_in_pre(it, "", cx.tcx()),
);
});
@@ -1111,7 +1157,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
t.generics.print(cx),
where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
type_ = t.type_.print(cx),
- attrs = render_attributes_in_pre(it, ""),
+ attrs = render_attributes_in_pre(it, "", cx.tcx()),
);
});
}
@@ -1133,32 +1179,18 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
#[derive(Template)]
#[template(path = "item_union.html")]
struct ItemUnion<'a, 'cx> {
- cx: std::cell::RefCell<&'a mut Context<'cx>>,
+ cx: RefCell<&'a mut Context<'cx>>,
it: &'a clean::Item,
s: &'a clean::Union,
}
- impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
- fn render_assoc_items<'b>(
- &'b self,
- ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
- display_fn(move |f| {
- let def_id = self.it.item_id.expect_def_id();
- let mut cx = self.cx.borrow_mut();
- let v = render_assoc_items(*cx, self.it, def_id, AssocItemRender::All);
- write!(f, "{v}")
- })
- }
- fn document_type_layout<'b>(
- &'b self,
- ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
- display_fn(move |f| {
- let def_id = self.it.item_id.expect_def_id();
- let cx = self.cx.borrow_mut();
- let v = document_type_layout(*cx, def_id);
- write!(f, "{v}")
- })
+ impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for ItemUnion<'a, 'cx> {
+ fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>) {
+ (self.it, self.cx.borrow_mut())
}
+ }
+
+ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
fn render_union<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
display_fn(move |f| {
let cx = self.cx.borrow_mut();
@@ -1166,21 +1198,6 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
write!(f, "{v}")
})
}
- fn render_attributes_in_pre<'b>(
- &'b self,
- ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
- display_fn(move |f| {
- let v = render_attributes_in_pre(self.it, "");
- write!(f, "{v}")
- })
- }
- fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
- display_fn(move |f| {
- let mut cx = self.cx.borrow_mut();
- let v = document(*cx, self.it, None, HeadingOffset::H2);
- write!(f, "{v}")
- })
- }
fn document_field<'b>(
&'b self,
field: &'a clean::Item,
@@ -1220,7 +1237,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
}
}
- ItemUnion { cx: std::cell::RefCell::new(cx), it, s }.render_into(w).unwrap();
+ ItemUnion { cx: RefCell::new(cx), it, s }.render_into(w).unwrap();
}
fn print_tuple_struct_fields<'a, 'cx: 'a>(
@@ -1246,13 +1263,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
let tcx = cx.tcx();
let count_variants = e.variants().count();
wrap_item(w, |mut w| {
+ render_attributes_in_code(w, it, tcx);
write!(
w,
- "{attrs}{}enum {}{}",
+ "{}enum {}{}",
visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
it.name.unwrap(),
e.generics.print(cx),
- attrs = render_attributes_in_pre(it, ""),
);
if !print_where_clause_and_check(w, &e.generics, cx) {
// If there wasn't a `where` clause, we add a whitespace.
@@ -1339,7 +1356,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
clean::VariantKind::Tuple(fields) => {
// Documentation on tuple variant fields is rare, so to reduce noise we only emit
// the section if at least one field is documented.
- if fields.iter().any(|f| f.doc_value().is_some()) {
+ if fields.iter().any(|f| !f.doc_value().is_empty()) {
Some(("Tuple Fields", fields))
} else {
None
@@ -1447,7 +1464,7 @@ fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
wrap_item(w, |w| {
let tcx = cx.tcx();
- render_attributes_in_code(w, it);
+ render_attributes_in_code(w, it, tcx);
write!(
w,
@@ -1494,7 +1511,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) {
wrap_item(w, |w| {
- render_attributes_in_code(w, it);
+ render_attributes_in_code(w, it, cx.tcx());
render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
});
@@ -1542,11 +1559,12 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
write!(w, "{}", document_type_layout(cx, def_id));
}
-fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
- wrap_item(w, |w| {
- render_attributes_in_code(w, it);
+fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
+ let mut buffer = Buffer::new();
+ wrap_item(&mut buffer, |buffer| {
+ render_attributes_in_code(buffer, it, cx.tcx());
write!(
- w,
+ buffer,
"{vis}static {mutability}{name}: {typ}",
vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
mutability = s.mutability.print_with_space(),
@@ -1554,13 +1572,16 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
typ = s.type_.print(cx)
);
});
- write!(w, "{}", document(cx, it, None, HeadingOffset::H2))
+
+ write!(w, "{}", buffer.into_inner()).unwrap();
+
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap();
}
fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
wrap_item(w, |w| {
w.write_str("extern {\n");
- render_attributes_in_code(w, it);
+ render_attributes_in_code(w, it, cx.tcx());
write!(
w,
" {}type {};\n}}",
@@ -1933,118 +1954,6 @@ fn document_non_exhaustive<'a>(item: &'a clean::Item) -> impl fmt::Display + 'a
})
}
-fn document_type_layout<'a, 'cx: 'a>(
- cx: &'a Context<'cx>,
- ty_def_id: DefId,
-) -> impl fmt::Display + 'a + Captures<'cx> {
- fn write_size_of_layout(mut w: impl fmt::Write, layout: &LayoutS, tag_size: u64) {
- if layout.abi.is_unsized() {
- write!(w, "(unsized)").unwrap();
- } else {
- let size = layout.size.bytes() - tag_size;
- write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" }).unwrap();
- if layout.abi.is_uninhabited() {
- write!(
- w,
- " (<a href=\"https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited\">uninhabited</a>)"
- ).unwrap();
- }
- }
- }
-
- display_fn(move |mut f| {
- if !cx.shared.show_type_layout {
- return Ok(());
- }
-
- writeln!(
- f,
- "<h2 id=\"layout\" class=\"small-section-header\"> \
- Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
- )?;
- writeln!(f, "<div class=\"docblock\">")?;
-
- let tcx = cx.tcx();
- let param_env = tcx.param_env(ty_def_id);
- let ty = tcx.type_of(ty_def_id).subst_identity();
- match tcx.layout_of(param_env.and(ty)) {
- Ok(ty_layout) => {
- writeln!(
- f,
- "<div class=\"warning\"><p><strong>Note:</strong> Most layout information is \
- <strong>completely unstable</strong> and may even differ between compilations. \
- The only exception is types with certain <code>repr(...)</code> attributes. \
- Please see the Rust Reference’s \
- <a href=\"https://doc.rust-lang.org/reference/type-layout.html\">“Type Layout”</a> \
- chapter for details on type layout guarantees.</p></div>"
- )?;
- f.write_str("<p><strong>Size:</strong> ")?;
- write_size_of_layout(&mut f, &ty_layout.layout.0, 0);
- writeln!(f, "</p>")?;
- if let Variants::Multiple { variants, tag, tag_encoding, .. } =
- &ty_layout.layout.variants()
- {
- if !variants.is_empty() {
- f.write_str(
- "<p><strong>Size for each variant:</strong></p>\
- <ul>",
- )?;
-
- let Adt(adt, _) = ty_layout.ty.kind() else {
- span_bug!(tcx.def_span(ty_def_id), "not an adt")
- };
-
- let tag_size = if let TagEncoding::Niche { .. } = tag_encoding {
- 0
- } else if let Primitive::Int(i, _) = tag.primitive() {
- i.size().bytes()
- } else {
- span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
- };
-
- for (index, layout) in variants.iter_enumerated() {
- let name = adt.variant(index).name;
- write!(&mut f, "<li><code>{name}</code>: ")?;
- write_size_of_layout(&mut f, layout, tag_size);
- writeln!(&mut f, "</li>")?;
- }
- f.write_str("</ul>")?;
- }
- }
- }
- // 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 `Vec<T>`.
- Err(LayoutError::Unknown(_)) => {
- writeln!(
- f,
- "<p><strong>Note:</strong> Unable to compute type layout, \
- possibly due to this type having generic parameters. \
- Layout can only be computed for concrete, fully-instantiated types.</p>"
- )?;
- }
- // 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!(
- f,
- "<p><strong>Note:</strong> Encountered an error during type layout; \
- the type was too big.</p>"
- )?;
- }
- Err(LayoutError::NormalizationFailure(_, _)) => {
- writeln!(
- f,
- "<p><strong>Note:</strong> Encountered an error during type layout; \
- the type failed to be normalized.</p>"
- )?;
- }
- }
-
- writeln!(f, "</div>")
- })
-}
-
fn pluralize(count: usize) -> &'static str {
if count > 1 { "s" } else { "" }
}
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index f5b4a3f5a..846299f02 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -28,9 +28,7 @@ pub(crate) fn build_index<'tcx>(
// has since been learned.
for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items {
if let Some((fqp, _)) = cache.paths.get(&parent) {
- let desc = item
- .doc_value()
- .map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache)));
+ let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
cache.search_index.push(IndexItem {
ty: item.type_(),
name: item.name.unwrap(),
@@ -45,10 +43,8 @@ pub(crate) fn build_index<'tcx>(
}
}
- let crate_doc = krate
- .module
- .doc_value()
- .map_or_else(String::new, |s| short_markdown_summary(&s, &krate.module.link_names(cache)));
+ let crate_doc =
+ short_markdown_summary(&krate.module.doc_value(), &krate.module.link_names(cache));
// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
// we need the alias element to have an array of items.
@@ -391,12 +387,14 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
get_index_type_id(type_)
}
+ // The type parameters are converted to generics in `add_generics_and_bounds_as_types`
+ clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
+ clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
+ // Not supported yet
clean::BareFunction(_)
| clean::Generic(_)
| clean::ImplTrait(_)
| clean::Tuple(_)
- | clean::Slice(_)
- | clean::Array(_, _)
| clean::QPath { .. }
| clean::Infer => None,
}
@@ -563,6 +561,30 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
}
}
insert_ty(res, arg.clone(), ty_generics);
+ } else if let Type::Slice(ref ty) = *arg {
+ let mut ty_generics = Vec::new();
+ add_generics_and_bounds_as_types(
+ self_,
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut ty_generics,
+ cache,
+ );
+ insert_ty(res, arg.clone(), ty_generics);
+ } else if let Type::Array(ref ty, _) = *arg {
+ let mut ty_generics = Vec::new();
+ add_generics_and_bounds_as_types(
+ self_,
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut ty_generics,
+ cache,
+ );
+ insert_ty(res, arg.clone(), ty_generics);
} else {
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs
new file mode 100644
index 000000000..c9b95b1e6
--- /dev/null
+++ b/src/librustdoc/html/render/type_layout.rs
@@ -0,0 +1,86 @@
+use askama::Template;
+
+use rustc_data_structures::captures::Captures;
+use rustc_hir::def_id::DefId;
+use rustc_middle::span_bug;
+use rustc_middle::ty::layout::LayoutError;
+use rustc_middle::ty::Adt;
+use rustc_span::symbol::Symbol;
+use rustc_target::abi::{Primitive, TagEncoding, Variants};
+
+use std::fmt;
+
+use crate::html::format::display_fn;
+use crate::html::render::Context;
+
+#[derive(Template)]
+#[template(path = "type_layout.html")]
+struct TypeLayout<'cx> {
+ variants: Vec<(Symbol, TypeLayoutSize)>,
+ type_layout_size: Result<TypeLayoutSize, LayoutError<'cx>>,
+}
+
+#[derive(Template)]
+#[template(path = "type_layout_size.html")]
+struct TypeLayoutSize {
+ is_unsized: bool,
+ is_uninhabited: bool,
+ size: u64,
+}
+
+pub(crate) fn document_type_layout<'a, 'cx: 'a>(
+ cx: &'a Context<'cx>,
+ ty_def_id: DefId,
+) -> impl fmt::Display + 'a + Captures<'cx> {
+ display_fn(move |f| {
+ if !cx.shared.show_type_layout {
+ return Ok(());
+ }
+
+ let tcx = cx.tcx();
+ let param_env = tcx.param_env(ty_def_id);
+ let ty = tcx.type_of(ty_def_id).subst_identity();
+ let type_layout = tcx.layout_of(param_env.and(ty));
+
+ let variants =
+ if let Ok(type_layout) = type_layout &&
+ let Variants::Multiple { variants, tag, tag_encoding, .. } =
+ type_layout.layout.variants() &&
+ !variants.is_empty()
+ {
+ let tag_size =
+ if let TagEncoding::Niche { .. } = tag_encoding {
+ 0
+ } else if let Primitive::Int(i, _) = tag.primitive() {
+ i.size().bytes()
+ } else {
+ span_bug!(tcx.def_span(ty_def_id), "tag is neither niche nor int")
+ };
+ variants
+ .iter_enumerated()
+ .map(|(variant_idx, variant_layout)| {
+ let Adt(adt, _) = type_layout.ty.kind() else {
+ span_bug!(tcx.def_span(ty_def_id), "not an adt")
+ };
+ let name = adt.variant(variant_idx).name;
+ let is_unsized = variant_layout.abi.is_unsized();
+ let is_uninhabited = variant_layout.abi.is_uninhabited();
+ let size = variant_layout.size.bytes() - tag_size;
+ let type_layout_size = TypeLayoutSize { is_unsized, is_uninhabited, size };
+ (name, type_layout_size)
+ })
+ .collect()
+ } else {
+ Vec::new()
+ };
+
+ let type_layout_size = tcx.layout_of(param_env.and(ty)).map(|layout| {
+ let is_unsized = layout.abi.is_unsized();
+ let is_uninhabited = layout.abi.is_uninhabited();
+ let size = layout.size.bytes();
+ TypeLayoutSize { is_unsized, is_uninhabited, size }
+ });
+
+ Ok(TypeLayout { variants, type_layout_size }.render_into(f).unwrap())
+ })
+}
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index c8397967c..a26fa3749 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -145,7 +145,7 @@ impl DocVisitor for SourceCollector<'_, '_> {
Err(e) => {
self.cx.shared.tcx.sess.span_err(
span,
- &format!(
+ format!(
"failed to render source code for `{}`: {}",
filename.prefer_local(),
e,
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 6fbb45086..a7d5f4977 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1259,6 +1259,10 @@ a.tooltip:hover::after {
background-color: var(--search-error-code-background-color);
}
+.search-corrections {
+ font-weight: normal;
+}
+
#src-sidebar-toggle {
position: sticky;
top: 0;
diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css
index d13c783d2..c1324c076 100644
--- a/src/librustdoc/html/static/css/settings.css
+++ b/src/librustdoc/html/static/css/settings.css
@@ -1,13 +1,11 @@
.setting-line {
margin: 1.2em 0.6em;
- position: relative;
}
.setting-radio input, .setting-check input {
margin-right: 0.3em;
height: 1.2rem;
width: 1.2rem;
- color: inherit;
border: 2px solid var(--settings-input-border-color);
outline: none;
-webkit-appearance: none;
@@ -16,11 +14,6 @@
.setting-radio input {
border-radius: 50%;
}
-.setting-check input:checked {
- content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
- <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
- <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
-}
.setting-radio span, .setting-check span {
padding-bottom: 1px;
@@ -53,6 +46,9 @@
.setting-check input:checked {
background-color: var(--settings-input-color);
border-width: 1px;
+ content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
+ <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
+ <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
}
.setting-radio input:focus, .setting-check input:focus {
box-shadow: 0 0 1px 1px var(--settings-input-color);
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index 4c81a0979..8b931f74e 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -9,6 +9,7 @@ function initSearch(searchIndex){}
/**
* @typedef {{
* name: string,
+ * id: integer,
* fullPath: Array<string>,
* pathWithoutLast: Array<string>,
* pathLast: string,
@@ -36,6 +37,8 @@ let ParserState;
* args: Array<QueryElement>,
* returned: Array<QueryElement>,
* foundElems: number,
+ * literalSearch: boolean,
+ * corrections: Array<{from: string, to: integer}>,
* }}
*/
let ParsedQuery;
@@ -139,7 +142,7 @@ let FunctionSearchType;
/**
* @typedef {{
- * name: (null|string),
+ * id: (null|number),
* ty: (null|number),
* generics: Array<FunctionType>,
* }}
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 6f5987e68..bccf675c1 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -275,8 +275,7 @@ function preLoadCss(cssUrl) {
document.title = searchState.titleBeforeSearch;
// We also remove the query parameter from the URL.
if (browserSupportsHistoryApi()) {
- history.replaceState(null, window.currentCrate + " - Rust",
- getNakedUrl() + window.location.hash);
+ history.replaceState(null, "", getNakedUrl() + window.location.hash);
}
},
getQueryStringParams: () => {
@@ -376,11 +375,7 @@ function preLoadCss(cssUrl) {
function handleEscape(ev) {
searchState.clearInputTimeout();
- switchDisplayedElement(null);
- if (browserSupportsHistoryApi()) {
- history.replaceState(null, window.currentCrate + " - Rust",
- getNakedUrl() + window.location.hash);
- }
+ searchState.hideResults();
ev.preventDefault();
searchState.defocus();
window.hideAllModals(true); // true = reset focus for tooltips
@@ -535,9 +530,11 @@ function preLoadCss(cssUrl) {
// ignored are included in the attribute `data-ignore-extern-crates`.
const script = document
.querySelector("script[data-ignore-extern-crates]");
- const ignoreExternCrates = script ? script.getAttribute("data-ignore-extern-crates") : "";
+ const ignoreExternCrates = new Set(
+ (script ? script.getAttribute("data-ignore-extern-crates") : "").split(",")
+ );
for (const lib of libs) {
- if (lib === window.currentCrate || ignoreExternCrates.indexOf(lib) !== -1) {
+ if (lib === window.currentCrate || ignoreExternCrates.has(lib)) {
continue;
}
const structs = imp[lib];
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 929dae81c..62afe40bb 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -58,6 +58,7 @@ function printTab(nb) {
}
iter += 1;
});
+ const isTypeSearch = (nb > 0 || iter === 1);
iter = 0;
onEachLazy(document.getElementById("results").childNodes, elem => {
if (nb === iter) {
@@ -70,6 +71,13 @@ function printTab(nb) {
});
if (foundCurrentTab && foundCurrentResultSet) {
searchState.currentTab = nb;
+ // Corrections only kick in on type-based searches.
+ const correctionsElem = document.getElementsByClassName("search-corrections");
+ if (isTypeSearch) {
+ removeClass(correctionsElem[0], "hidden");
+ } else {
+ addClass(correctionsElem[0], "hidden");
+ }
} else if (nb !== 0) {
printTab(0);
}
@@ -191,6 +199,13 @@ function initSearch(rawSearchIndex) {
*/
let searchIndex;
let currentResults;
+ /**
+ * Map from normalized type names to integers. Used to make type search
+ * more efficient.
+ *
+ * @type {Map<string, integer>}
+ */
+ let typeNameIdMap;
const ALIASES = new Map();
function isWhitespace(c) {
@@ -358,6 +373,7 @@ function initSearch(rawSearchIndex) {
parserState.typeFilter = null;
return {
name: name,
+ id: -1,
fullPath: pathSegments,
pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
pathLast: pathSegments[pathSegments.length - 1],
@@ -718,6 +734,7 @@ function initSearch(rawSearchIndex) {
foundElems: 0,
literalSearch: false,
error: null,
+ correction: null,
};
}
@@ -873,7 +890,7 @@ function initSearch(rawSearchIndex) {
*
* @param {Array<Result>} results_in_args
* @param {Array<Result>} results_returned
- * @param {Array<Result>} results_in_args
+ * @param {Array<Result>} results_others
* @param {ParsedQuery} parsedQuery
*
* @return {ResultsTable}
@@ -1091,48 +1108,50 @@ function initSearch(rawSearchIndex) {
*
* @param {Row} row - The object to check.
* @param {QueryElement} elem - The element from the parsed query.
- * @param {integer} defaultDistance - This is the value to return in case there are no
- * generics.
*
- * @return {integer} - Returns the best match (if any) or `maxEditDistance + 1`.
+ * @return {boolean} - Returns true if a match, false otherwise.
*/
- function checkGenerics(row, elem, defaultDistance, maxEditDistance) {
- if (row.generics.length === 0) {
- return elem.generics.length === 0 ? defaultDistance : maxEditDistance + 1;
- } else if (row.generics.length > 0 && row.generics[0].name === null) {
- return checkGenerics(row.generics[0], elem, defaultDistance, maxEditDistance);
- }
- // The names match, but we need to be sure that all generics kinda
- // match as well.
+ function checkGenerics(row, elem) {
+ if (row.generics.length === 0 || elem.generics.length === 0) {
+ return false;
+ }
+ // This function is called if the names match, but we need to make
+ // sure that all generics match as well.
+ //
+ // This search engine implements order-agnostic unification. There
+ // should be no missing duplicates (generics have "bag semantics"),
+ // and the row is allowed to have extras.
if (elem.generics.length > 0 && row.generics.length >= elem.generics.length) {
const elems = new Map();
- for (const entry of row.generics) {
- if (entry.name === "") {
+ const addEntryToElems = function addEntryToElems(entry) {
+ if (entry.id === -1) {
// Pure generic, needs to check into it.
- if (checkGenerics(entry, elem, maxEditDistance + 1, maxEditDistance)
- !== 0) {
- return maxEditDistance + 1;
+ for (const inner_entry of entry.generics) {
+ addEntryToElems(inner_entry);
}
- continue;
+ return;
}
let currentEntryElems;
- if (elems.has(entry.name)) {
- currentEntryElems = elems.get(entry.name);
+ if (elems.has(entry.id)) {
+ currentEntryElems = elems.get(entry.id);
} else {
currentEntryElems = [];
- elems.set(entry.name, currentEntryElems);
+ elems.set(entry.id, currentEntryElems);
}
currentEntryElems.push(entry);
+ };
+ for (const entry of row.generics) {
+ addEntryToElems(entry);
}
// We need to find the type that matches the most to remove it in order
// to move forward.
const handleGeneric = generic => {
- if (!elems.has(generic.name)) {
+ if (!elems.has(generic.id)) {
return false;
}
- const matchElems = elems.get(generic.name);
+ const matchElems = elems.get(generic.id);
const matchIdx = matchElems.findIndex(tmp_elem => {
- if (checkGenerics(tmp_elem, generic, 0, maxEditDistance) !== 0) {
+ if (generic.generics.length > 0 && !checkGenerics(tmp_elem, generic)) {
return false;
}
return typePassesFilter(generic.typeFilter, tmp_elem.ty);
@@ -1142,7 +1161,7 @@ function initSearch(rawSearchIndex) {
}
matchElems.splice(matchIdx, 1);
if (matchElems.length === 0) {
- elems.delete(generic.name);
+ elems.delete(generic.id);
}
return true;
};
@@ -1152,17 +1171,17 @@ function initSearch(rawSearchIndex) {
// own type.
for (const generic of elem.generics) {
if (generic.typeFilter !== -1 && !handleGeneric(generic)) {
- return maxEditDistance + 1;
+ return false;
}
}
for (const generic of elem.generics) {
if (generic.typeFilter === -1 && !handleGeneric(generic)) {
- return maxEditDistance + 1;
+ return false;
}
}
- return 0;
+ return true;
}
- return maxEditDistance + 1;
+ return false;
}
/**
@@ -1172,17 +1191,15 @@ function initSearch(rawSearchIndex) {
* @param {Row} row
* @param {QueryElement} elem - The element from the parsed query.
*
- * @return {integer} - Returns an edit distance to the best match.
+ * @return {boolean} - Returns true if found, false otherwise.
*/
- function checkIfInGenerics(row, elem, maxEditDistance) {
- let dist = maxEditDistance + 1;
+ function checkIfInGenerics(row, elem) {
for (const entry of row.generics) {
- dist = Math.min(checkType(entry, elem, true, maxEditDistance), dist);
- if (dist === 0) {
- break;
+ if (checkType(entry, elem)) {
+ return true;
}
}
- return dist;
+ return false;
}
/**
@@ -1191,75 +1208,26 @@ function initSearch(rawSearchIndex) {
*
* @param {Row} row
* @param {QueryElement} elem - The element from the parsed query.
- * @param {boolean} literalSearch
*
- * @return {integer} - Returns an edit distance to the best match. If there is
- * no match, returns `maxEditDistance + 1`.
+ * @return {boolean} - Returns true if the type matches, false otherwise.
*/
- function checkType(row, elem, literalSearch, maxEditDistance) {
- if (row.name === null) {
+ function checkType(row, elem) {
+ if (row.id === -1) {
// This is a pure "generic" search, no need to run other checks.
- if (row.generics.length > 0) {
- return checkIfInGenerics(row, elem, maxEditDistance);
- }
- return maxEditDistance + 1;
+ return row.generics.length > 0 ? checkIfInGenerics(row, elem) : false;
}
- let dist;
- if (typePassesFilter(elem.typeFilter, row.ty)) {
- dist = editDistance(row.name, elem.name, maxEditDistance);
- } else {
- dist = maxEditDistance + 1;
- }
- if (literalSearch) {
- if (dist !== 0) {
- // The name didn't match, let's try to check if the generics do.
- if (elem.generics.length === 0) {
- const checkGeneric = row.generics.length > 0;
- if (checkGeneric && row.generics
- .findIndex(tmp_elem => tmp_elem.name === elem.name &&
- typePassesFilter(elem.typeFilter, tmp_elem.ty)) !== -1) {
- return 0;
- }
- }
- return maxEditDistance + 1;
- } else if (elem.generics.length > 0) {
- return checkGenerics(row, elem, maxEditDistance + 1, maxEditDistance);
- }
- return 0;
- } else if (row.generics.length > 0) {
- if (elem.generics.length === 0) {
- if (dist === 0) {
- return 0;
- }
- // The name didn't match so we now check if the type we're looking for is inside
- // the generics!
- dist = Math.min(dist, checkIfInGenerics(row, elem, maxEditDistance));
- return dist;
- } else if (dist > maxEditDistance) {
- // So our item's name doesn't match at all and has generics.
- //
- // Maybe it's present in a sub generic? For example "f<A<B<C>>>()", if we're
- // looking for "B<C>", we'll need to go down.
- return checkIfInGenerics(row, elem, maxEditDistance);
- } else {
- // At this point, the name kinda match and we have generics to check, so
- // let's go!
- const tmp_dist = checkGenerics(row, elem, dist, maxEditDistance);
- if (tmp_dist > maxEditDistance) {
- return maxEditDistance + 1;
- }
- // We compute the median value of both checks and return it.
- return (tmp_dist + dist) / 2;
+ if (row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty)) {
+ if (elem.generics.length > 0) {
+ return checkGenerics(row, elem);
}
- } else if (elem.generics.length > 0) {
- // In this case, we were expecting generics but there isn't so we simply reject this
- // one.
- return maxEditDistance + 1;
+ return true;
}
- // No generics on our query or on the target type so we can return without doing
- // anything else.
- return dist;
+
+ // If the current item does not match, try [unboxing] the generic.
+ // [unboxing]:
+ // https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
+ return checkIfInGenerics(row, elem);
}
/**
@@ -1267,17 +1235,11 @@ function initSearch(rawSearchIndex) {
*
* @param {Row} row
* @param {QueryElement} elem - The element from the parsed query.
- * @param {integer} maxEditDistance
* @param {Array<integer>} skipPositions - Do not return one of these positions.
*
- * @return {dist: integer, position: integer} - Returns an edit distance to the best match.
- * If there is no match, returns
- * `maxEditDistance + 1` and position: -1.
+ * @return {integer} - Returns the position of the match, or -1 if none.
*/
- function findArg(row, elem, maxEditDistance, skipPositions) {
- let dist = maxEditDistance + 1;
- let position = -1;
-
+ function findArg(row, elem, skipPositions) {
if (row && row.type && row.type.inputs && row.type.inputs.length > 0) {
let i = 0;
for (const input of row.type.inputs) {
@@ -1285,24 +1247,13 @@ function initSearch(rawSearchIndex) {
i += 1;
continue;
}
- const typeDist = checkType(
- input,
- elem,
- parsedQuery.literalSearch,
- maxEditDistance
- );
- if (typeDist === 0) {
- return {dist: 0, position: i};
- }
- if (typeDist < dist) {
- dist = typeDist;
- position = i;
+ if (checkType(input, elem)) {
+ return i;
}
i += 1;
}
}
- dist = parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
- return {dist, position};
+ return -1;
}
/**
@@ -1310,43 +1261,25 @@ function initSearch(rawSearchIndex) {
*
* @param {Row} row
* @param {QueryElement} elem - The element from the parsed query.
- * @param {integer} maxEditDistance
* @param {Array<integer>} skipPositions - Do not return one of these positions.
*
- * @return {dist: integer, position: integer} - Returns an edit distance to the best match.
- * If there is no match, returns
- * `maxEditDistance + 1` and position: -1.
+ * @return {integer} - Returns the position of the matching item, or -1 if none.
*/
- function checkReturned(row, elem, maxEditDistance, skipPositions) {
- let dist = maxEditDistance + 1;
- let position = -1;
-
+ function checkReturned(row, elem, skipPositions) {
if (row && row.type && row.type.output.length > 0) {
- const ret = row.type.output;
let i = 0;
- for (const ret_ty of ret) {
+ for (const ret_ty of row.type.output) {
if (skipPositions.indexOf(i) !== -1) {
i += 1;
continue;
}
- const typeDist = checkType(
- ret_ty,
- elem,
- parsedQuery.literalSearch,
- maxEditDistance
- );
- if (typeDist === 0) {
- return {dist: 0, position: i};
- }
- if (typeDist < dist) {
- dist = typeDist;
- position = i;
+ if (checkType(ret_ty, elem)) {
+ return i;
}
i += 1;
}
}
- dist = parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
- return {dist, position};
+ return -1;
}
function checkPath(contains, ty, maxEditDistance) {
@@ -1543,17 +1476,20 @@ function initSearch(rawSearchIndex) {
if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
return;
}
- let dist, index = -1, path_dist = 0;
+ let index = -1, path_dist = 0;
const fullId = row.id;
const searchWord = searchWords[pos];
- const in_args = findArg(row, elem, maxEditDistance, []);
- const returned = checkReturned(row, elem, maxEditDistance, []);
-
- // path_dist is 0 because no parent path information is currently stored
- // in the search index
- addIntoResults(results_in_args, fullId, pos, -1, in_args.dist, 0, maxEditDistance);
- addIntoResults(results_returned, fullId, pos, -1, returned.dist, 0, maxEditDistance);
+ const in_args = findArg(row, elem, []);
+ if (in_args !== -1) {
+ // path_dist is 0 because no parent path information is currently stored
+ // in the search index
+ addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance);
+ }
+ const returned = checkReturned(row, elem, []);
+ if (returned !== -1) {
+ addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance);
+ }
if (!typePassesFilter(elem.typeFilter, row.ty)) {
return;
@@ -1574,16 +1510,6 @@ function initSearch(rawSearchIndex) {
index = row_index;
}
- // No need to check anything else if it's a "pure" generics search.
- if (elem.name.length === 0) {
- if (row.type !== null) {
- dist = checkGenerics(row.type, elem, maxEditDistance + 1, maxEditDistance);
- // path_dist is 0 because we know it's empty
- addIntoResults(results_others, fullId, pos, index, dist, 0, maxEditDistance);
- }
- return;
- }
-
if (elem.fullPath.length > 1) {
path_dist = checkPath(elem.pathWithoutLast, row, maxEditDistance);
if (path_dist > maxEditDistance) {
@@ -1598,7 +1524,7 @@ function initSearch(rawSearchIndex) {
return;
}
- dist = editDistance(searchWord, elem.pathLast, maxEditDistance);
+ const dist = editDistance(searchWord, elem.pathLast, maxEditDistance);
if (index === -1 && dist + path_dist > maxEditDistance) {
return;
@@ -1616,28 +1542,22 @@ function initSearch(rawSearchIndex) {
* @param {integer} pos - Position in the `searchIndex`.
* @param {Object} results
*/
- function handleArgs(row, pos, results, maxEditDistance) {
+ function handleArgs(row, pos, results) {
if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
return;
}
- let totalDist = 0;
- let nbDist = 0;
-
// If the result is too "bad", we return false and it ends this search.
function checkArgs(elems, callback) {
const skipPositions = [];
for (const elem of elems) {
// There is more than one parameter to the query so all checks should be "exact"
- const { dist, position } = callback(
+ const position = callback(
row,
elem,
- maxEditDistance,
skipPositions
);
- if (dist <= 1) {
- nbDist += 1;
- totalDist += dist;
+ if (position !== -1) {
skipPositions.push(position);
} else {
return false;
@@ -1652,11 +1572,7 @@ function initSearch(rawSearchIndex) {
return;
}
- if (nbDist === 0) {
- return;
- }
- const dist = Math.round(totalDist / nbDist);
- addIntoResults(results, row.id, pos, 0, dist, 0, maxEditDistance);
+ addIntoResults(results, row.id, pos, 0, 0, 0, Number.MAX_VALUE);
}
function innerRunQuery() {
@@ -1671,6 +1587,53 @@ function initSearch(rawSearchIndex) {
}
const maxEditDistance = Math.floor(queryLen / 3);
+ /**
+ * Convert names to ids in parsed query elements.
+ * This is not used for the "In Names" tab, but is used for the
+ * "In Params", "In Returns", and "In Function Signature" tabs.
+ *
+ * If there is no matching item, but a close-enough match, this
+ * function also that correction.
+ *
+ * See `buildTypeMapIndex` for more information.
+ *
+ * @param {QueryElement} elem
+ */
+ function convertNameToId(elem) {
+ if (typeNameIdMap.has(elem.name)) {
+ elem.id = typeNameIdMap.get(elem.name);
+ } else if (!parsedQuery.literalSearch) {
+ let match = -1;
+ let matchDist = maxEditDistance + 1;
+ let matchName = "";
+ for (const [name, id] of typeNameIdMap) {
+ const dist = editDistance(name, elem.name, maxEditDistance);
+ if (dist <= matchDist && dist <= maxEditDistance) {
+ if (dist === matchDist && matchName > name) {
+ continue;
+ }
+ match = id;
+ matchDist = dist;
+ matchName = name;
+ }
+ }
+ if (match !== -1) {
+ parsedQuery.correction = matchName;
+ }
+ elem.id = match;
+ }
+ for (const elem2 of elem.generics) {
+ convertNameToId(elem2);
+ }
+ }
+
+ for (const elem of parsedQuery.elems) {
+ convertNameToId(elem);
+ }
+ for (const elem of parsedQuery.returned) {
+ convertNameToId(elem);
+ }
+
if (parsedQuery.foundElems === 1) {
if (parsedQuery.elems.length === 1) {
elem = parsedQuery.elems[0];
@@ -1695,22 +1658,23 @@ function initSearch(rawSearchIndex) {
in_returned = checkReturned(
row,
elem,
- maxEditDistance,
[]
);
- addIntoResults(
- results_others,
- row.id,
- i,
- -1,
- in_returned.dist,
- maxEditDistance
- );
+ if (in_returned !== -1) {
+ addIntoResults(
+ results_others,
+ row.id,
+ i,
+ -1,
+ 0,
+ Number.MAX_VALUE
+ );
+ }
}
}
} else if (parsedQuery.foundElems > 0) {
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
- handleArgs(searchIndex[i], i, results_others, maxEditDistance);
+ handleArgs(searchIndex[i], i, results_others);
}
}
}
@@ -2030,6 +1994,16 @@ function initSearch(rawSearchIndex) {
currentTab = 0;
}
+ if (results.query.correction !== null) {
+ const orig = results.query.returned.length > 0
+ ? results.query.returned[0].name
+ : results.query.elems[0].name;
+ output += "<h3 class=\"search-corrections\">" +
+ `Type "${orig}" not found. ` +
+ "Showing results for closest type name " +
+ `"${results.query.correction}" instead.</h3>`;
+ }
+
const resultsElem = document.createElement("div");
resultsElem.id = "results";
resultsElem.appendChild(ret_others[0]);
@@ -2109,6 +2083,34 @@ function initSearch(rawSearchIndex) {
}
/**
+ * Add an item to the type Name->ID map, or, if one already exists, use it.
+ * Returns the number. If name is "" or null, return -1 (pure generic).
+ *
+ * This is effectively string interning, so that function matching can be
+ * done more quickly. Two types with the same name but different item kinds
+ * get the same ID.
+ *
+ * @param {Map<string, integer>} typeNameIdMap
+ * @param {string} name
+ *
+ * @returns {integer}
+ */
+ function buildTypeMapIndex(typeNameIdMap, name) {
+
+ if (name === "" || name === null) {
+ return -1;
+ }
+
+ if (typeNameIdMap.has(name)) {
+ return typeNameIdMap.get(name);
+ } else {
+ const id = typeNameIdMap.size;
+ typeNameIdMap.set(name, id);
+ return id;
+ }
+ }
+
+ /**
* Convert a list of RawFunctionType / ID to object-based FunctionType.
*
* Crates often have lots of functions in them, and it's common to have a large number of
@@ -2126,7 +2128,7 @@ function initSearch(rawSearchIndex) {
*
* @return {Array<FunctionSearchType>}
*/
- function buildItemSearchTypeAll(types, lowercasePaths) {
+ function buildItemSearchTypeAll(types, lowercasePaths, typeNameIdMap) {
const PATH_INDEX_DATA = 0;
const GENERICS_DATA = 1;
return types.map(type => {
@@ -2136,11 +2138,17 @@ function initSearch(rawSearchIndex) {
generics = [];
} else {
pathIndex = type[PATH_INDEX_DATA];
- generics = buildItemSearchTypeAll(type[GENERICS_DATA], lowercasePaths);
+ generics = buildItemSearchTypeAll(
+ type[GENERICS_DATA],
+ lowercasePaths,
+ typeNameIdMap
+ );
}
return {
// `0` is used as a sentinel because it's fewer bytes than `null`
- name: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].name,
+ id: pathIndex === 0
+ ? -1
+ : buildTypeMapIndex(typeNameIdMap, lowercasePaths[pathIndex - 1].name),
ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
generics: generics,
};
@@ -2159,10 +2167,11 @@ function initSearch(rawSearchIndex) {
*
* @param {RawFunctionSearchType} functionSearchType
* @param {Array<{name: string, ty: number}>} lowercasePaths
+ * @param {Map<string, integer>}
*
* @return {null|FunctionSearchType}
*/
- function buildFunctionSearchType(functionSearchType, lowercasePaths) {
+ function buildFunctionSearchType(functionSearchType, lowercasePaths, typeNameIdMap) {
const INPUTS_DATA = 0;
const OUTPUT_DATA = 1;
// `0` is used as a sentinel because it's fewer bytes than `null`
@@ -2173,23 +2182,35 @@ function initSearch(rawSearchIndex) {
if (typeof functionSearchType[INPUTS_DATA] === "number") {
const pathIndex = functionSearchType[INPUTS_DATA];
inputs = [{
- name: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].name,
+ id: pathIndex === 0
+ ? -1
+ : buildTypeMapIndex(typeNameIdMap, lowercasePaths[pathIndex - 1].name),
ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
generics: [],
}];
} else {
- inputs = buildItemSearchTypeAll(functionSearchType[INPUTS_DATA], lowercasePaths);
+ inputs = buildItemSearchTypeAll(
+ functionSearchType[INPUTS_DATA],
+ lowercasePaths,
+ typeNameIdMap
+ );
}
if (functionSearchType.length > 1) {
if (typeof functionSearchType[OUTPUT_DATA] === "number") {
const pathIndex = functionSearchType[OUTPUT_DATA];
output = [{
- name: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].name,
+ id: pathIndex === 0
+ ? -1
+ : buildTypeMapIndex(typeNameIdMap, lowercasePaths[pathIndex - 1].name),
ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
generics: [],
}];
} else {
- output = buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA], lowercasePaths);
+ output = buildItemSearchTypeAll(
+ functionSearchType[OUTPUT_DATA],
+ lowercasePaths,
+ typeNameIdMap
+ );
}
} else {
output = [];
@@ -2202,9 +2223,12 @@ function initSearch(rawSearchIndex) {
function buildIndex(rawSearchIndex) {
searchIndex = [];
/**
+ * List of normalized search words (ASCII lowercased, and undescores removed).
+ *
* @type {Array<string>}
*/
const searchWords = [];
+ typeNameIdMap = new Map();
const charA = "A".charCodeAt(0);
let currentIndex = 0;
let id = 0;
@@ -2337,7 +2361,11 @@ function initSearch(rawSearchIndex) {
path: itemPaths.has(i) ? itemPaths.get(i) : lastPath,
desc: itemDescs[i],
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
- type: buildFunctionSearchType(itemFunctionSearchTypes[i], lowercasePaths),
+ type: buildFunctionSearchType(
+ itemFunctionSearchTypes[i],
+ lowercasePaths,
+ typeNameIdMap
+ ),
id: id,
normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
deprecated: deprecatedItems.has(i),
@@ -2412,10 +2440,6 @@ function initSearch(rawSearchIndex) {
const searchAfter500ms = () => {
searchState.clearInputTimeout();
if (searchState.input.value.length === 0) {
- if (browserSupportsHistoryApi()) {
- history.replaceState(null, window.currentCrate + " - Rust",
- getNakedUrl() + window.location.hash);
- }
searchState.hideResults();
} else {
searchState.timeout = setTimeout(search, 500);
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index ebbe6c1ca..2cba32c1b 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -1,5 +1,5 @@
// Local js definitions:
-/* global getSettingValue, getVirtualKey, updateLocalStorage, updateTheme */
+/* global getSettingValue, updateLocalStorage, updateTheme */
/* global addClass, removeClass, onEach, onEachLazy, blurHandler, elemIsInParent */
/* global MAIN_ID, getVar, getSettingsButton */
@@ -32,21 +32,6 @@
}
}
- function handleKey(ev) {
- // Don't interfere with browser shortcuts
- if (ev.ctrlKey || ev.altKey || ev.metaKey) {
- return;
- }
- switch (getVirtualKey(ev)) {
- case "Enter":
- case "Return":
- case "Space":
- ev.target.checked = !ev.target.checked;
- ev.preventDefault();
- break;
- }
- }
-
function showLightAndDark() {
removeClass(document.getElementById("preferred-light-theme"), "hidden");
removeClass(document.getElementById("preferred-dark-theme"), "hidden");
@@ -77,8 +62,6 @@
toggle.onchange = function() {
changeSetting(this.id, this.checked);
};
- toggle.onkeyup = handleKey;
- toggle.onkeyrelease = handleKey;
});
onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
const settingId = elem.name;
diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js
index 9aa755173..d999f3b36 100644
--- a/src/librustdoc/html/static/js/source-script.js
+++ b/src/librustdoc/html/static/js/source-script.js
@@ -52,12 +52,12 @@ function createDirEntry(elem, parent, fullPath, hasFoundFile) {
const files = document.createElement("div");
files.className = "files";
if (elem[FILES_OFFSET]) {
+ const w = window.location.href.split("#")[0];
for (const file_text of elem[FILES_OFFSET]) {
const file = document.createElement("a");
file.innerText = file_text;
file.href = rootPath + "src/" + fullPath + file_text + ".html";
file.addEventListener("click", closeSidebarIfMobile);
- const w = window.location.href.split("#")[0];
if (!hasFoundFile && w === file.href) {
file.className = "selected";
dirEntry.open = true;
diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html
index a01457971..c21967005 100644
--- a/src/librustdoc/html/templates/item_union.html
+++ b/src/librustdoc/html/templates/item_union.html
@@ -1,8 +1,8 @@
<pre class="rust item-decl"><code>
- {{ self.render_attributes_in_pre() | safe }}
+ {{ self::item_template_render_attributes_in_pre(self.borrow()) | safe }}
{{ self.render_union() | safe }}
</code></pre>
-{{ self.document() | safe }}
+{{ self::item_template_document(self.borrow()) | safe }}
{% if self.fields_iter().peek().is_some() %}
<h2 id="fields" class="fields small-section-header">
Fields<a href="#fields" class="anchor">§</a>
@@ -19,5 +19,5 @@
{{ self.document_field(field) | safe }}
{% endfor %}
{% endif %}
-{{ self.render_assoc_items() | safe }}
-{{ self.document_type_layout() | safe }}
+{{ self::item_template_render_assoc_items(self.borrow()) | safe }}
+{{ self::item_template_document_type_layout(self.borrow()) | safe }}
diff --git a/src/librustdoc/html/templates/type_layout.html b/src/librustdoc/html/templates/type_layout.html
new file mode 100644
index 000000000..20e09a548
--- /dev/null
+++ b/src/librustdoc/html/templates/type_layout.html
@@ -0,0 +1,58 @@
+<h2 id="layout" class="small-section-header"> {# #}
+ Layout<a href="#layout" class="anchor">§</a> {# #}
+</h2> {# #}
+<div class="docblock"> {# #}
+ {% match type_layout_size %}
+ {% when Ok(type_layout_size) %}
+ <div class="warning"> {# #}
+ <p> {# #}
+ <strong>Note:</strong> Most layout information is <strong>completely {#+ #}
+ unstable</strong> and may even differ between compilations. {#+ #}
+ The only exception is types with certain <code>repr(...)</code> {#+ #}
+ attributes. Please see the Rust Reference’s {#+ #}
+ <a href="https://doc.rust-lang.org/reference/type-layout.html">“Type Layout”</a> {#+ #}
+ chapter for details on type layout guarantees. {# #}
+ </p> {# #}
+ </div> {# #}
+ <p><strong>Size:</strong> {{ type_layout_size|safe }}</p> {# #}
+ {% if !variants.is_empty() %}
+ <p> {# #}
+ <strong>Size for each variant:</strong> {# #}
+ </p> {# #}
+ <ul> {# #}
+ {% for (name, layout_size) in variants %}
+ <li> {# #}
+ <code>{{ name }}</code>: {#+ #}
+ {{ layout_size|safe }}
+ </li> {# #}
+ {% endfor %}
+ </ul> {# #}
+ {% endif %}
+ {# 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 `Vec<T>`. #}
+ {% when Err(LayoutError::Unknown(_)) %}
+ <p> {# #}
+ <strong>Note:</strong> Unable to compute type layout, {#+ #}
+ possibly due to this type having generic parameters. {#+ #}
+ Layout can only be computed for concrete, fully-instantiated types. {# #}
+ </p> {# #}
+ {# 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. #}
+ {% when Err(LayoutError::SizeOverflow(_)) %}
+ <p> {# #}
+ <strong>Note:</strong> Encountered an error during type layout; {#+ #}
+ the type was too big. {# #}
+ </p> {# #}
+ {% when Err(LayoutError::NormalizationFailure(_, _)) %}
+ <p> {# #}
+ <strong>Note:</strong> Encountered an error during type layout; {#+ #}
+ the type failed to be normalized. {# #}
+ </p> {# #}
+ {% when Err(LayoutError::Cycle) %}
+ <p> {# #}
+ <strong>Note:</strong> Encountered an error during type layout; {#+ #}
+ the type's layout depended on the type's layout itself. {# #}
+ </p> {# #}
+ {% endmatch %}
+</div> {# #}
diff --git a/src/librustdoc/html/templates/type_layout_size.html b/src/librustdoc/html/templates/type_layout_size.html
new file mode 100644
index 000000000..9c2b39edc
--- /dev/null
+++ b/src/librustdoc/html/templates/type_layout_size.html
@@ -0,0 +1,12 @@
+{% if is_unsized %}
+ (unsized)
+{% else %}
+ {% if size == 1 %}
+ 1 byte
+ {% else %}
+ {{ size +}} bytes
+ {% endif %}
+ {% if is_uninhabited %}
+ {# +#} (<a href="https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited">uninhabited</a>)
+ {% endif %}
+{% endif %}