From d1e62d65c3c6a710e7b9b9c891ddf31d070021a7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 28 Jul 2020 17:10:48 +0200 Subject: [PATCH 1/4] cache primitives before running clean over all crates items --- src/librustdoc/clean/mod.rs | 18 +++++++++++++++++- src/librustdoc/clean/types.rs | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index cc3a60c596ae7..8c18f1211aeba 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -28,10 +28,12 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, Pos}; use rustc_typeck::hir_ty_to_ty; +use std::cell::RefCell; use std::collections::hash_map::Entry; use std::default::Default; use std::hash::Hash; use std::rc::Rc; +use std::sync::Arc; use std::{mem, vec}; use crate::core::{self, DocContext, ImplTraitParam}; @@ -48,6 +50,13 @@ pub use self::types::Type::*; pub use self::types::Visibility::{Inherited, Public}; pub use self::types::*; +thread_local!(static PRIMITIVES: RefCell>> = + Default::default()); + +crate fn primitives() -> Arc> { + PRIMITIVES.with(|c| c.borrow().clone()) +} + const FN_OUTPUT_NAME: &str = "Output"; pub trait Clean { @@ -126,7 +135,7 @@ impl Clean for CrateNum { } None }; - let primitives = if root.is_local() { + let primitives: Vec<(DefId, PrimitiveType, Attributes)> = if root.is_local() { cx.tcx .hir() .krate() @@ -161,6 +170,13 @@ impl Clean for CrateNum { .filter_map(as_primitive) .collect() }; + PRIMITIVES.with(|v| { + let mut tmp = v.borrow_mut(); + let stored_primitives = Arc::make_mut(&mut *tmp); + for (prim, did) in primitives.iter().map(|x| (x.1, x.0)) { + stored_primitives.insert(prim, did); + } + }); let as_keyword = |res: Res| { if let Res::Def(DefKind::Mod, def_id) = res { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 89549eae2cb0e..f41620e718e1a 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1173,7 +1173,7 @@ impl GetDefId for Type { fn def_id(&self) -> Option { match *self { ResolvedPath { did, .. } => Some(did), - Primitive(p) => cache().primitive_locations.get(&p).cloned(), + Primitive(p) => super::primitives().get(&p).cloned(), BorrowedRef { type_: box Generic(..), .. } => { Primitive(PrimitiveType::Reference).def_id() } From 054e3bf92d8fff6217f80dbae53083ee58c4f5aa Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 28 Jul 2020 17:11:03 +0200 Subject: [PATCH 2/4] Add test for fn-like search --- src/test/rustdoc-js/primitive-fn-like-search.js | 13 +++++++++++++ src/test/rustdoc-js/primitive-fn-like-search.rs | 5 +++++ 2 files changed, 18 insertions(+) create mode 100644 src/test/rustdoc-js/primitive-fn-like-search.js create mode 100644 src/test/rustdoc-js/primitive-fn-like-search.rs diff --git a/src/test/rustdoc-js/primitive-fn-like-search.js b/src/test/rustdoc-js/primitive-fn-like-search.js new file mode 100644 index 0000000000000..6b052aa4aadb1 --- /dev/null +++ b/src/test/rustdoc-js/primitive-fn-like-search.js @@ -0,0 +1,13 @@ +// exact-check +const QUERY = 'u32 -> u32'; + +const EXPECTED = { + 'others': [ + ], + 'returned': [ + { 'path': 'primitive_fn_like_search', 'name': 'foo' }, + ], + 'in_args': [ + { 'path': 'primitive_fn_like_search', 'name': 'foo' }, + ], +}; diff --git a/src/test/rustdoc-js/primitive-fn-like-search.rs b/src/test/rustdoc-js/primitive-fn-like-search.rs new file mode 100644 index 0000000000000..fc5d7cb3f5931 --- /dev/null +++ b/src/test/rustdoc-js/primitive-fn-like-search.rs @@ -0,0 +1,5 @@ +pub struct Foo; + +pub trait Bar {} + +pub fn foo(a: Foo, b: u32, c: T, d: D) -> u32 {0} From b419a1dc2f650354d9182a40feeff4dab607f627 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 29 Jul 2020 13:59:04 +0200 Subject: [PATCH 3/4] Remove "primitive_locations" field from Cache --- src/librustdoc/formats/cache.rs | 28 +++++----------------------- src/librustdoc/html/format.rs | 9 ++++++--- src/librustdoc/html/render/mod.rs | 6 +++--- 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 99b31473f87a3..b1b8bdc8fadba 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -9,7 +9,7 @@ use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_middle::middle::privacy::AccessLevels; use rustc_span::source_map::FileName; -use crate::clean::{self, GetDefId}; +use crate::clean::{self, primitives, GetDefId}; use crate::config::RenderInfo; use crate::fold::DocFolder; use crate::formats::item_type::ItemType; @@ -76,9 +76,6 @@ pub struct Cache { /// Cache of where external crate documentation can be found. pub extern_locations: FxHashMap, - /// Cache of where documentation for primitives can be found. - pub primitive_locations: FxHashMap, - // Note that external items for which `doc(hidden)` applies to are shown as // non-reachable while local items aren't. This is because we're reusing // the access levels from the privacy check pass. @@ -182,19 +179,6 @@ impl Cache { cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); } - // Cache where all known primitives have their documentation located. - // - // Favor linking to as local extern as possible, so iterate all crates in - // reverse topological order. - for &(_, ref e) in krate.externs.iter().rev() { - for &(def_id, prim, _) in &e.primitives { - cache.primitive_locations.insert(prim, def_id); - } - } - for &(def_id, prim, _) in &krate.primitives { - cache.primitive_locations.insert(prim, def_id); - } - cache.stack.push(krate.name.clone()); krate = cache.fold_crate(krate); @@ -403,9 +387,8 @@ impl DocFolder for Cache { true } ref t => { - let prim_did = t - .primitive_type() - .and_then(|t| self.primitive_locations.get(&t).cloned()); + let prim_did = + t.primitive_type().and_then(|t| primitives().get(&t).cloned()); match prim_did { Some(did) => { self.parent_stack.push(did); @@ -436,9 +419,8 @@ impl DocFolder for Cache { dids.insert(did); } ref t => { - let did = t - .primitive_type() - .and_then(|t| self.primitive_locations.get(&t).cloned()); + let did = + t.primitive_type().and_then(|t| primitives().get(&t).cloned()); if let Some(did) = did { dids.insert(did); diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 699f8c36cba6a..cb58b7133469c 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -14,7 +14,7 @@ use rustc_hir as hir; use rustc_span::def_id::DefId; use rustc_target::spec::abi::Abi; -use crate::clean::{self, PrimitiveType}; +use crate::clean::{self, primitives, PrimitiveType}; use crate::formats::cache::cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; @@ -559,10 +559,13 @@ fn primitive_link( prim: clean::PrimitiveType, name: &str, ) -> fmt::Result { - let m = cache(); let mut needs_termination = false; + if !f.alternate() { - match m.primitive_locations.get(&prim) { + let m = cache(); + let primitives = primitives(); + + match primitives.get(&prim) { Some(&def_id) if def_id.is_local() => { let len = CURRENT_DEPTH.with(|s| s.get()); let len = if len == 0 { 0 } else { len - 1 }; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 5fb2d9f6f917c..129044f31a721 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -63,7 +63,7 @@ use rustc_span::symbol::{sym, Symbol}; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; -use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind}; +use crate::clean::{self, primitives, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind}; use crate::config::RenderInfo; use crate::config::RenderOptions; use crate::docfs::{DocFS, PathError}; @@ -3405,7 +3405,7 @@ fn render_deref_methods( render_assoc_items(w, cx, container_item, did, what, cache); } else { if let Some(prim) = target.primitive_type() { - if let Some(&did) = cache.primitive_locations.get(&prim) { + if let Some(&did) = primitives().get(&prim) { render_assoc_items(w, cx, container_item, did, what, cache); } } @@ -4058,7 +4058,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { .def_id() .or(target .primitive_type() - .and_then(|prim| c.primitive_locations.get(&prim).cloned())) + .and_then(|prim| primitives().get(&prim).cloned())) .and_then(|did| c.impls.get(&did)); if let Some(impls) = inner_impl { out.push_str(""); From 374f9b5c392078c68cafd12530d27564b63c7286 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 31 Jul 2020 18:25:42 +0200 Subject: [PATCH 4/4] Move primitive_locations build before the crate.clean --- src/librustdoc/clean/mod.rs | 101 ++++++++++++---------------- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/core.rs | 73 +++++++++++++++++++- src/librustdoc/formats/cache.rs | 3 +- src/librustdoc/html/format.rs | 21 ++++-- src/librustdoc/html/render/cache.rs | 1 + src/librustdoc/html/render/mod.rs | 3 +- 7 files changed, 135 insertions(+), 69 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8c18f1211aeba..b9c75ff2fcff6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -28,12 +28,10 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, Pos}; use rustc_typeck::hir_ty_to_ty; -use std::cell::RefCell; use std::collections::hash_map::Entry; use std::default::Default; use std::hash::Hash; use std::rc::Rc; -use std::sync::Arc; use std::{mem, vec}; use crate::core::{self, DocContext, ImplTraitParam}; @@ -50,13 +48,6 @@ pub use self::types::Type::*; pub use self::types::Visibility::{Inherited, Public}; pub use self::types::*; -thread_local!(static PRIMITIVES: RefCell>> = - Default::default()); - -crate fn primitives() -> Arc> { - PRIMITIVES.with(|c| c.borrow().clone()) -} - const FN_OUTPUT_NAME: &str = "Output"; pub trait Clean { @@ -93,48 +84,49 @@ impl, U> Clean> for Option { } } +/// Collect all inner modules which are tagged as implementations of +/// primitives. +/// +/// Note that this loop only searches the top-level items of the crate, +/// and this is intentional. If we were to search the entire crate for an +/// item tagged with `#[doc(primitive)]` then we would also have to +/// search the entirety of external modules for items tagged +/// `#[doc(primitive)]`, which is a pretty inefficient process (decoding +/// all that metadata unconditionally). +/// +/// In order to keep the metadata load under control, the +/// `#[doc(primitive)]` feature is explicitly designed to only allow the +/// primitive tags to show up as the top level items in a crate. +/// +/// Also note that this does not attempt to deal with modules tagged +/// duplicately for the same primitive. This is handled later on when +/// rendering by delegating everything to a hash map. +crate fn as_primitive(cx: &DocContext<'_>, res: Res) -> Option<(DefId, PrimitiveType, Attributes)> { + if let Res::Def(DefKind::Mod, def_id) = res { + let attrs = cx.tcx.get_attrs(def_id).clean(cx); + let mut prim = None; + for attr in attrs.lists(sym::doc) { + if let Some(v) = attr.value_str() { + if attr.check_name(sym::primitive) { + prim = PrimitiveType::from_symbol(v); + if prim.is_some() { + break; + } + // FIXME: should warn on unknown primitives? + } + } + } + return prim.map(|p| (def_id, p, attrs)); + } + None +} + impl Clean for CrateNum { fn clean(&self, cx: &DocContext<'_>) -> ExternalCrate { let root = DefId { krate: *self, index: CRATE_DEF_INDEX }; let krate_span = cx.tcx.def_span(root); let krate_src = cx.sess().source_map().span_to_filename(krate_span); - // Collect all inner modules which are tagged as implementations of - // primitives. - // - // Note that this loop only searches the top-level items of the crate, - // and this is intentional. If we were to search the entire crate for an - // item tagged with `#[doc(primitive)]` then we would also have to - // search the entirety of external modules for items tagged - // `#[doc(primitive)]`, which is a pretty inefficient process (decoding - // all that metadata unconditionally). - // - // In order to keep the metadata load under control, the - // `#[doc(primitive)]` feature is explicitly designed to only allow the - // primitive tags to show up as the top level items in a crate. - // - // Also note that this does not attempt to deal with modules tagged - // duplicately for the same primitive. This is handled later on when - // rendering by delegating everything to a hash map. - let as_primitive = |res: Res| { - if let Res::Def(DefKind::Mod, def_id) = res { - let attrs = cx.tcx.get_attrs(def_id).clean(cx); - let mut prim = None; - for attr in attrs.lists(sym::doc) { - if let Some(v) = attr.value_str() { - if attr.check_name(sym::primitive) { - prim = PrimitiveType::from_symbol(v); - if prim.is_some() { - break; - } - // FIXME: should warn on unknown primitives? - } - } - } - return prim.map(|p| (def_id, p, attrs)); - } - None - }; let primitives: Vec<(DefId, PrimitiveType, Attributes)> = if root.is_local() { cx.tcx .hir() @@ -146,14 +138,14 @@ impl Clean for CrateNum { .filter_map(|&id| { let item = cx.tcx.hir().expect_item(id.id); match item.kind { - hir::ItemKind::Mod(_) => as_primitive(Res::Def( - DefKind::Mod, - cx.tcx.hir().local_def_id(id.id).to_def_id(), - )), + hir::ItemKind::Mod(_) => as_primitive( + cx, + Res::Def(DefKind::Mod, cx.tcx.hir().local_def_id(id.id).to_def_id()), + ), hir::ItemKind::Use(ref path, hir::UseKind::Single) if item.vis.node.is_pub() => { - as_primitive(path.res).map(|(_, prim, attrs)| { + as_primitive(cx, path.res).map(|(_, prim, attrs)| { // Pretend the primitive is local. (cx.tcx.hir().local_def_id(id.id).to_def_id(), prim, attrs) }) @@ -167,16 +159,9 @@ impl Clean for CrateNum { .item_children(root) .iter() .map(|item| item.res) - .filter_map(as_primitive) + .filter_map(|res| as_primitive(cx, res)) .collect() }; - PRIMITIVES.with(|v| { - let mut tmp = v.borrow_mut(); - let stored_primitives = Arc::make_mut(&mut *tmp); - for (prim, did) in primitives.iter().map(|x| (x.1, x.0)) { - stored_primitives.insert(prim, did); - } - }); let as_keyword = |res: Res| { if let Res::Def(DefKind::Mod, def_id) = res { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index f41620e718e1a..971f7fba3ac68 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1173,7 +1173,7 @@ impl GetDefId for Type { fn def_id(&self) -> Option { match *self { ResolvedPath { did, .. } => Some(did), - Primitive(p) => super::primitives().get(&p).cloned(), + Primitive(p) => crate::core::primitives().get(&p).cloned(), BorrowedRef { type_: box Generic(..), .. } => { Primitive(PrimitiveType::Reference).def_id() } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index cbd0ca0de6414..96023f5a9555d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -5,7 +5,8 @@ use rustc_driver::abort_on_err; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::json::JsonEmitter; use rustc_feature::UnstableFeatures; -use rustc_hir::def::{Namespace::TypeNS, Res}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Namespace::TypeNS, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::HirId; use rustc_hir::{ @@ -29,9 +30,11 @@ use rustc_span::DUMMY_SP; use std::cell::RefCell; use std::mem; use std::rc::Rc; +use std::sync::Arc; use crate::clean; -use crate::clean::{AttributesExt, MAX_DEF_ID}; +use crate::clean::types::PrimitiveType; +use crate::clean::{as_primitive, AttributesExt, MAX_DEF_ID}; use crate::config::RenderInfo; use crate::config::{Options as RustdocOptions, RenderOptions}; use crate::passes::{self, Condition::*, ConditionalPass}; @@ -39,6 +42,13 @@ use crate::passes::{self, Condition::*, ConditionalPass}; pub use rustc_session::config::{CodegenOptions, DebuggingOptions, Input, Options}; pub use rustc_session::search_paths::SearchPath; +thread_local!(static PRIMITIVES: RefCell>> = + Default::default()); + +crate fn primitives() -> Arc> { + PRIMITIVES.with(|c| c.borrow().clone()) +} + pub type ExternalPaths = FxHashMap, clean::TypeKind)>; pub struct DocContext<'tcx> { @@ -499,6 +509,65 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt }; debug!("crate: {:?}", tcx.hir().krate()); + PRIMITIVES.with(|v| { + let mut tmp = v.borrow_mut(); + let stored_primitives = Arc::make_mut(&mut *tmp); + + let mut externs = Vec::new(); + for &cnum in ctxt.tcx.crates().iter() { + externs.push(cnum); + } + externs.sort_by(|a, b| a.cmp(&b)); + + for krate in externs.iter().chain([LOCAL_CRATE].iter()) { + let root = DefId { krate: *krate, index: CRATE_DEF_INDEX }; + let iter: Vec<(DefId, PrimitiveType)> = if root.is_local() { + ctxt.tcx.hir_crate(*krate).item.module.item_ids.iter().filter_map( + |&id| { + let item = ctxt.tcx.hir().expect_item(id.id); + match item.kind { + hir::ItemKind::Mod(_) => as_primitive( + &ctxt, + Res::Def( + DefKind::Mod, + ctxt.tcx.hir().local_def_id(id.id).to_def_id(), + ), + ) + .map(|(did, prim, _)| (did, prim)), + hir::ItemKind::Use(ref path, hir::UseKind::Single) + if item.vis.node.is_pub() => + { + as_primitive(&ctxt, path.res).map(|(_, prim, _)| { + // Pretend the primitive is local. + ( + ctxt.tcx + .hir() + .local_def_id(id.id) + .to_def_id(), + prim, + ) + }) + } + _ => None, + } + }, + ) + .collect() + } else { + ctxt + .tcx + .item_children(root) + .iter() + .map(|item| item.res) + .filter_map(|res| as_primitive(&ctxt, res).map(|(did, prim, _)| (did, prim))) + .collect() + }; + for (did, prim) in iter { + stored_primitives.insert(prim, did); + } + } + }); + let mut krate = clean::krate(&mut ctxt); if let Some(ref m) = krate.module { diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index b1b8bdc8fadba..f6a1f333316a3 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -9,8 +9,9 @@ use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_middle::middle::privacy::AccessLevels; use rustc_span::source_map::FileName; -use crate::clean::{self, primitives, GetDefId}; +use crate::clean::{self, GetDefId}; use crate::config::RenderInfo; +use crate::core::primitives; use crate::fold::DocFolder; use crate::formats::item_type::ItemType; use crate::formats::Impl; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index cb58b7133469c..f416fae6e0719 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -14,7 +14,8 @@ use rustc_hir as hir; use rustc_span::def_id::DefId; use rustc_target::spec::abi::Abi; -use crate::clean::{self, primitives, PrimitiveType}; +use crate::clean::{self, PrimitiveType}; +use crate::core::primitives; use crate::formats::cache::cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; @@ -578,13 +579,21 @@ fn primitive_link( needs_termination = true; } Some(&def_id) => { - let loc = match m.extern_locations[&def_id.krate] { - (ref cname, _, ExternalLocation::Remote(ref s)) => Some((cname, s.to_string())), - (ref cname, _, ExternalLocation::Local) => { + let loc = match m.extern_locations.get(&def_id.krate) { + Some((ref cname, _, ExternalLocation::Remote(ref s))) => { + Some((cname.as_str(), s.to_string())) + } + Some((ref cname, _, ExternalLocation::Local)) => { + let len = CURRENT_DEPTH.with(|s| s.get()); + Some((cname.as_str(), "../".repeat(len))) + } + Some((.., ExternalLocation::Unknown)) => None, + None => { + // It means we're in core, since the primitive types aren't "accessible" + // from it so we have to simulate it. let len = CURRENT_DEPTH.with(|s| s.get()); - Some((cname, "../".repeat(len))) + Some(("core", "../".repeat(len))) } - (.., ExternalLocation::Unknown) => None, }; if let Some((cname, root)) = loc { write!( diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index 378efa1a1bed7..afe8be039072e 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -13,6 +13,7 @@ use crate::html::render::{plain_summary_line, shorten}; use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind}; /// Indicates where an external crate can be found. +#[derive(Debug)] pub enum ExternalLocation { /// Remote URL root of the external crate Remote(String), diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 129044f31a721..9b6df6cd66153 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -63,9 +63,10 @@ use rustc_span::symbol::{sym, Symbol}; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; -use crate::clean::{self, primitives, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind}; +use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind}; use crate::config::RenderInfo; use crate::config::RenderOptions; +use crate::core::primitives; use crate::docfs::{DocFS, PathError}; use crate::doctree; use crate::error::Error;