diff options
Diffstat (limited to 'src/librustdoc/html/render')
| -rw-r--r-- | src/librustdoc/html/render/mod.rs | 388 | ||||
| -rw-r--r-- | src/librustdoc/html/render/print_item.rs | 8 | ||||
| -rw-r--r-- | src/librustdoc/html/render/search_index.rs | 40 | ||||
| -rw-r--r-- | src/librustdoc/html/render/span_map.rs | 124 | ||||
| -rw-r--r-- | src/librustdoc/html/render/write_shared.rs | 9 |
5 files changed, 358 insertions, 211 deletions
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6d684449b6d..97dcaf57cdf 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -48,18 +48,19 @@ use std::path::PathBuf; use std::{fs, str}; use askama::Template; +use indexmap::IndexMap; use itertools::Either; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_hir::attrs::{DeprecatedSince, Deprecation}; +use rustc_hir as hir; +use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince}; use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; -use serde::ser::SerializeMap; -use serde::{Serialize, Serializer}; use tracing::{debug, info}; pub(crate) use self::context::*; @@ -75,7 +76,6 @@ use crate::html::escape::Escape; use crate::html::format::{ Ending, HrefError, PrintWithSpace, href, print_abi_with_space, print_constness_with_space, print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space, - write_str, }; use crate::html::markdown::{ HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, @@ -601,7 +601,12 @@ impl AllTypes { } fmt::from_fn(|f| { - f.write_str("<h1>List of all items</h1>")?; + f.write_str( + "<div class=\"main-heading\">\ + <h1>List of all items</h1>\ + <rustdoc-toolbar></rustdoc-toolbar>\ + </div>", + )?; // Note: print_entries does not escape the title, because we know the current set of titles // doesn't require escaping. print_entries(&self.structs, ItemSection::Structs).fmt(f)?; @@ -1310,43 +1315,6 @@ fn render_assoc_item( }) } -struct CodeAttribute(String); - -fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) { - write!( - w, - "<div class=\"code-attribute\">{prefix}{attr}</div>", - prefix = prefix, - attr = code_attr.0 - ) - .unwrap(); -} - -// 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 impl fmt::Write, - it: &clean::Item, - prefix: &str, - cx: &Context<'_>, -) { - for attr in it.attributes(cx.tcx(), cx.cache()) { - render_code_attribute(prefix, CodeAttribute(attr), w); - } -} - -/// used for type aliases to only render their `repr` attribute. -fn render_repr_attributes_in_code( - w: &mut impl fmt::Write, - cx: &Context<'_>, - def_id: DefId, - item_type: ItemType, -) { - if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { - render_code_attribute("", CodeAttribute(repr), w); - } -} - #[derive(Copy, Clone)] enum AssocItemLink<'a> { Anchor(Option<&'a str>), @@ -1507,12 +1475,10 @@ fn render_assoc_items_inner( ) } }; - let mut impls_buf = String::new(); - for i in &non_trait { - write_str( - &mut impls_buf, - format_args!( - "{}", + let impls_buf = fmt::from_fn(|f| { + non_trait + .iter() + .map(|i| { render_impl( cx, i, @@ -1528,9 +1494,11 @@ fn render_assoc_items_inner( toggle_open_by_default: true, }, ) - ), - ); - } + }) + .joined("", f) + }) + .to_string(); + if !impls_buf.is_empty() { write!( w, @@ -1682,91 +1650,85 @@ fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt: } fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { - let mut out = String::new(); - let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this"); let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this"); - for i in impls { - let impl_ = i.inner_impl(); - if impl_.polarity != ty::ImplPolarity::Positive { - continue; - } - - if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) { - // Two different types might have the same did, - // without actually being the same. - continue; - } - if let Some(trait_) = &impl_.trait_ { - let trait_did = trait_.def_id(); - - if cx.cache().traits.get(&trait_did).is_some_and(|t| t.is_notable_trait(cx.tcx())) { - if out.is_empty() { - write_str( - &mut out, - format_args!( - "<h3>Notable traits for <code>{}</code></h3>\ - <pre><code>", - impl_.for_.print(cx) - ), - ); + let out = fmt::from_fn(|f| { + let mut notable_impls = impls + .iter() + .map(|impl_| impl_.inner_impl()) + .filter(|impl_| impl_.polarity == ty::ImplPolarity::Positive) + .filter(|impl_| { + // Two different types might have the same did, without actually being the same. + ty.is_doc_subtype_of(&impl_.for_, cx.cache()) + }) + .filter_map(|impl_| { + if let Some(trait_) = &impl_.trait_ + && let trait_did = trait_.def_id() + && let Some(trait_) = cx.cache().traits.get(&trait_did) + && trait_.is_notable_trait(cx.tcx()) + { + Some((impl_, trait_did)) + } else { + None } + }) + .peekable(); - write_str( - &mut out, - format_args!("<div class=\"where\">{}</div>", impl_.print(false, cx)), - ); - for it in &impl_.items { - if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind { - let empty_set = FxIndexSet::default(); - let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); - write_str( - &mut out, - format_args!( - "<div class=\"where\"> {};</div>", - assoc_type( - it, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(&tydef.type_), - src_link, - 0, - cx, - ) - ), - ); - } - } + let has_notable_impl = if let Some((impl_, _)) = notable_impls.peek() { + write!( + f, + "<h3>Notable traits for <code>{}</code></h3>\ + <pre><code>", + impl_.for_.print(cx) + )?; + true + } else { + false + }; + + for (impl_, trait_did) in notable_impls { + write!(f, "<div class=\"where\">{}</div>", impl_.print(false, cx))?; + for it in &impl_.items { + let clean::AssocTypeItem(tydef, ..) = &it.kind else { + continue; + }; + + let empty_set = FxIndexSet::default(); + let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set); + + write!( + f, + "<div class=\"where\"> {};</div>", + assoc_type( + it, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(&tydef.type_), + src_link, + 0, + cx, + ) + )?; } } - } - if out.is_empty() { - out.push_str("</code></pre>"); - } + + if !has_notable_impl { + f.write_str("</code></pre>")?; + } + + Ok(()) + }) + .to_string(); (format!("{:#}", ty.print(cx)), out) } fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String { - let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect(); - mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2)); - struct NotableTraitsMap(Vec<(String, String)>); - impl Serialize for NotableTraitsMap { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - let mut map = serializer.serialize_map(Some(self.0.len()))?; - for item in &self.0 { - map.serialize_entry(&item.0, &item.1)?; - } - map.end() - } - } - serde_json::to_string(&NotableTraitsMap(mp)) - .expect("serialize (string, string) -> json object cannot fail") + let mut mp = tys.map(|ty| notable_traits_decl(ty, cx)).collect::<IndexMap<_, _>>(); + mp.sort_unstable_keys(); + serde_json::to_string(&mp).expect("serialize (string, string) -> json object cannot fail") } #[derive(Clone, Copy, Debug)] @@ -1840,27 +1802,19 @@ fn render_impl( document_item_info(cx, it, Some(parent)) .render_into(&mut info_buffer) .unwrap(); - write_str( - &mut doc_buffer, - format_args!("{}", document_full(item, cx, HeadingOffset::H5)), - ); + doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string(); short_documented = false; } else { // In case the item isn't documented, // provide short documentation from the trait. - write_str( - &mut doc_buffer, - format_args!( - "{}", - document_short( - it, - cx, - link, - parent, - rendering_params.show_def_docs, - ) - ), - ); + doc_buffer = document_short( + it, + cx, + link, + parent, + rendering_params.show_def_docs, + ) + .to_string(); } } } else { @@ -1868,21 +1822,14 @@ fn render_impl( .render_into(&mut info_buffer) .unwrap(); if rendering_params.show_def_docs { - write_str( - &mut doc_buffer, - format_args!("{}", document_full(item, cx, HeadingOffset::H5)), - ); + doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string(); short_documented = false; } } } else { - write_str( - &mut doc_buffer, - format_args!( - "{}", - document_short(item, cx, link, parent, rendering_params.show_def_docs) - ), - ); + doc_buffer = + document_short(item, cx, link, parent, rendering_params.show_def_docs) + .to_string(); } } let mut w = if short_documented && trait_.is_some() { @@ -2959,3 +2906,142 @@ fn render_call_locations<W: fmt::Write>( w.write_str("</div>") } + +fn render_attributes_in_code( + w: &mut impl fmt::Write, + item: &clean::Item, + prefix: &str, + cx: &Context<'_>, +) { + for attr in &item.attrs.other_attrs { + let hir::Attribute::Parsed(kind) = attr else { continue }; + let attr = match kind { + AttributeKind::LinkSection { name, .. } => { + Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}")))) + } + AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"), + AttributeKind::ExportName { name, .. } => { + Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}")))) + } + AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"), + _ => continue, + }; + render_code_attribute(prefix, attr.as_ref(), w); + } + + if let Some(def_id) = item.def_id() + && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) + { + render_code_attribute(prefix, &repr, w); + } +} + +fn render_repr_attribute_in_code(w: &mut impl fmt::Write, cx: &Context<'_>, def_id: DefId) { + if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) { + render_code_attribute("", &repr, w); + } +} + +fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) { + write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>").unwrap(); +} + +/// Compute the *public* `#[repr]` of the item given by `DefId`. +/// +/// Read more about it here: +/// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>. +fn repr_attribute<'tcx>( + tcx: TyCtxt<'tcx>, + cache: &Cache, + def_id: DefId, +) -> Option<Cow<'static, str>> { + let adt = match tcx.def_kind(def_id) { + DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id), + _ => return None, + }; + let repr = adt.repr(); + + let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id); + let is_public_field = |field: &ty::FieldDef| { + (cache.document_private || field.vis.is_public()) && is_visible(field.did) + }; + + if repr.transparent() { + // The transparent repr is public iff the non-1-ZST field is public and visible or + // – in case all fields are 1-ZST fields — at least one field is public and visible. + let is_public = 'is_public: { + // `#[repr(transparent)]` can only be applied to structs and single-variant enums. + let var = adt.variant(rustc_abi::FIRST_VARIANT); // the first and only variant + + if !is_visible(var.def_id) { + break 'is_public false; + } + + // Side note: There can only ever be one or zero non-1-ZST fields. + let non_1zst_field = var.fields.iter().find(|field| { + let ty = ty::TypingEnv::post_analysis(tcx, field.did) + .as_query_input(tcx.type_of(field.did).instantiate_identity()); + tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst()) + }); + + match non_1zst_field { + Some(field) => is_public_field(field), + None => var.fields.is_empty() || var.fields.iter().any(is_public_field), + } + }; + + // Since the transparent repr can't have any other reprs or + // repr modifiers beside it, we can safely return early here. + return is_public.then(|| "#[repr(transparent)]".into()); + } + + // Fast path which avoids looking through the variants and fields in + // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`. + // FIXME: This check is not very robust / forward compatible! + if !repr.c() + && !repr.simd() + && repr.int.is_none() + && repr.pack.is_none() + && repr.align.is_none() + { + return None; + } + + // The repr is public iff all components are public and visible. + let is_public = adt + .variants() + .iter() + .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field)); + if !is_public { + return None; + } + + let mut result = Vec::<Cow<'_, _>>::new(); + + if repr.c() { + result.push("C".into()); + } + if repr.simd() { + result.push("simd".into()); + } + if let Some(int) = repr.int { + let prefix = if int.is_signed() { 'i' } else { 'u' }; + let int = match int { + rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"), + rustc_abi::IntegerType::Fixed(int, _) => { + format!("{prefix}{}", int.size().bytes() * 8) + } + }; + result.push(int.into()); + } + + // Render modifiers last. + if let Some(pack) = repr.pack { + result.push(format!("packed({})", pack.bytes()).into()); + } + if let Some(align) = repr.align { + result.push(format!("align({})", align.bytes()).into()); + } + + (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into()) +} diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index afa438f2596..adfc7481c73 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -21,7 +21,7 @@ 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, render_assoc_item, render_assoc_items, render_attributes_in_code, render_impl, - render_repr_attributes_in_code, render_rightside, render_stability_since_raw, + render_repr_attribute_in_code, render_rightside, render_stability_since_raw, render_stability_since_raw_with_extra, write_section_heading, }; use crate::clean; @@ -1555,7 +1555,7 @@ impl<'clean> DisplayEnum<'clean> { wrap_item(w, |w| { if is_type_alias { // For now the only attributes we render for type aliases are `repr` attributes. - render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); + render_repr_attribute_in_code(w, cx, self.def_id); } else { render_attributes_in_code(w, it, "", cx); } @@ -2017,7 +2017,7 @@ impl<'a> DisplayStruct<'a> { wrap_item(w, |w| { if is_type_alias { // For now the only attributes we render for type aliases are `repr` attributes. - render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); + render_repr_attribute_in_code(w, cx, self.def_id); } else { render_attributes_in_code(w, it, "", cx); } @@ -2371,7 +2371,7 @@ fn render_union( fmt::from_fn(move |mut f| { if is_type_alias { // For now the only attributes we render for type aliases are `repr` attributes. - render_repr_attributes_in_code(f, cx, def_id, ItemType::Union); + render_repr_attribute_in_code(f, cx, def_id); } else { render_attributes_in_code(f, it, "", cx); } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 3ffce61f7c6..7b21c68d2e9 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -241,6 +241,34 @@ impl SerializedSearchIndex { self.alias_pointers.push(alias_pointer); index } + /// Add potential search result to the database and return the row ID. + /// + /// The returned ID can be used to attach more data to the search result. + fn add_entry(&mut self, name: Symbol, entry_data: EntryData, desc: String) -> usize { + let fqp = if let Some(module_path_index) = entry_data.module_path { + let mut fqp = self.path_data[module_path_index].as_ref().unwrap().module_path.clone(); + fqp.push(Symbol::intern(&self.names[module_path_index])); + fqp.push(name); + fqp + } else { + vec![name] + }; + // If a path with the same name already exists, but no entry does, + // we can fill in the entry without having to allocate a new row ID. + // + // Because paths and entries both share the same index, using the same + // ID saves space by making the tree smaller. + if let Some(&other_path) = self.crate_paths_index.get(&(entry_data.ty, fqp)) + && self.entry_data[other_path].is_none() + && self.descs[other_path].is_empty() + { + self.entry_data[other_path] = Some(entry_data); + self.descs[other_path] = desc; + other_path + } else { + self.push(name.as_str().to_string(), None, Some(entry_data), desc, None, None, None) + } + } fn push_path(&mut self, name: String, path_data: PathData) -> usize { self.push(name, Some(path_data), None, String::new(), None, None, None) } @@ -1516,10 +1544,9 @@ pub(crate) fn build_index( .as_ref() .map(|path| serialized_index.get_id_by_module_path(path)); - let new_entry_id = serialized_index.push( - item.name.as_str().to_string(), - None, - Some(EntryData { + let new_entry_id = serialized_index.add_entry( + item.name, + EntryData { ty: item.ty, parent: item.parent_idx, module_path, @@ -1538,11 +1565,8 @@ pub(crate) fn build_index( None }, krate: crate_idx, - }), + }, item.desc.to_string(), - None, // filled in after all the types have been indexed - None, - None, ); // Aliases diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 8bc2e0bd957..ef7ce33298d 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -2,11 +2,9 @@ use std::path::{Path, PathBuf}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ - ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, -}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; @@ -67,7 +65,7 @@ struct SpanMapVisitor<'tcx> { impl SpanMapVisitor<'_> { /// This function is where we handle `hir::Path` elements and add them into the "span map". - fn handle_path(&mut self, path: &rustc_hir::Path<'_>) { + fn handle_path(&mut self, path: &rustc_hir::Path<'_>, only_use_last_segment: bool) { match path.res { // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`. // Would be nice to support them too alongside the other `DefKind` @@ -79,24 +77,36 @@ impl SpanMapVisitor<'_> { LinkFromSrc::External(def_id) }; // In case the path ends with generics, we remove them from the span. - let span = path - .segments - .last() - .map(|last| { - // In `use` statements, the included item is not in the path segments. - // However, it doesn't matter because you can't have generics on `use` - // statements. - if path.span.contains(last.ident.span) { - path.span.with_hi(last.ident.span.hi()) - } else { - path.span - } - }) - .unwrap_or(path.span); + let span = if only_use_last_segment + && let Some(path_span) = path.segments.last().map(|segment| segment.ident.span) + { + path_span + } else { + path.segments + .last() + .map(|last| { + // In `use` statements, the included item is not in the path segments. + // However, it doesn't matter because you can't have generics on `use` + // statements. + if path.span.contains(last.ident.span) { + path.span.with_hi(last.ident.span.hi()) + } else { + path.span + } + }) + .unwrap_or(path.span) + }; self.matches.insert(span, link); } Res::Local(_) if let Some(span) = self.tcx.hir_res_span(path.res) => { - self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span))); + let path_span = if only_use_last_segment + && let Some(path_span) = path.segments.last().map(|segment| segment.ident.span) + { + path_span + } else { + path.span + }; + self.matches.insert(path_span, LinkFromSrc::Local(clean::Span::new(span))); } Res::PrimTy(p) => { // FIXME: Doesn't handle "path-like" primitives like arrays or tuples. @@ -189,31 +199,23 @@ impl SpanMapVisitor<'_> { self.matches.insert(span, link); } } +} - fn handle_pat(&mut self, p: &Pat<'_>) { - let mut check_qpath = |qpath, hir_id| match qpath { - QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { - self.infer_id(path.hir_id, Some(hir_id), qpath.span()); - } - QPath::Resolved(_, path) => self.handle_path(path), - _ => {} - }; - match p.kind { - PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p), - PatKind::Struct(qpath, _, _) | PatKind::TupleStruct(qpath, _, _) => { - check_qpath(qpath, p.hir_id) - } - PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, .. }) => { - check_qpath(*qpath, *hir_id) - } - PatKind::Or(pats) => { - for pat in pats { - self.handle_pat(pat); - } - } - _ => {} +// This is a reimplementation of `hir_enclosing_body_owner` which allows to fail without +// panicking. +fn hir_enclosing_body_owner(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<LocalDefId> { + for (_, node) in tcx.hir_parent_iter(hir_id) { + // FIXME: associated type impl items don't have an associated body, so we don't handle + // them currently. + if let Node::ImplItem(impl_item) = node + && matches!(impl_item.kind, rustc_hir::ImplItemKind::Type(_)) + { + return None; + } else if let Some((def_id, _)) = node.associated_body() { + return Some(def_id); } } + None } impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { @@ -227,12 +229,42 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { if self.handle_macro(path.span) { return; } - self.handle_path(path); + self.handle_path(path, false); intravisit::walk_path(self, path); } - fn visit_pat(&mut self, p: &Pat<'tcx>) { - self.handle_pat(p); + fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: Span) { + match *qpath { + QPath::TypeRelative(qself, path) => { + if matches!(path.res, Res::Err) { + let tcx = self.tcx; + if let Some(body_id) = hir_enclosing_body_owner(tcx, id) { + let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id()); + let path = rustc_hir::Path { + // We change the span to not include parens. + span: path.ident.span, + res: typeck_results.qpath_res(qpath, id), + segments: &[], + }; + self.handle_path(&path, false); + } + } else { + self.infer_id(path.hir_id, Some(id), path.ident.span); + } + + rustc_ast::visit::try_visit!(self.visit_ty_unambig(qself)); + self.visit_path_segment(path); + } + QPath::Resolved(maybe_qself, path) => { + self.handle_path(path, true); + + rustc_ast::visit::visit_opt!(self, visit_ty_unambig, maybe_qself); + if !self.handle_macro(path.span) { + intravisit::walk_path(self, path); + } + } + _ => {} + } } fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) { diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index e37a5246a76..3a1db805d01 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -386,8 +386,13 @@ impl CratesIndexPart { let layout = &cx.shared.layout; let style_files = &cx.shared.style_files; const DELIMITER: &str = "\u{FFFC}"; // users are being naughty if they have this - let content = - format!("<h1>List of all crates</h1><ul class=\"all-items\">{DELIMITER}</ul>"); + let content = format!( + "<div class=\"main-heading\">\ + <h1>List of all crates</h1>\ + <rustdoc-toolbar></rustdoc-toolbar>\ + </div>\ + <ul class=\"all-items\">{DELIMITER}</ul>" + ); let template = layout::render(layout, &page, "", content, style_files); SortedTemplate::from_template(&template, DELIMITER) .expect("Object Replacement Character (U+FFFC) should not appear in the --index-page") |
