diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index aac0aa61ea6..a1fffd2ebbb 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -10,6 +10,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_middle::hir::exports::Export; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::middle::stability::DeprecationEntry; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_middle::ty::{self, TyCtxt, Visibility}; use rustc_session::cstore::{CrateSource, CrateStore, ForeignModule}; @@ -192,8 +193,6 @@ provide! { <'tcx> tcx, def_id, other, cdata, extra_filename => { cdata.root.extra_filename.clone() } traits_in_crate => { tcx.arena.alloc_from_iter(cdata.get_traits()) } - all_trait_implementations => { tcx.arena.alloc_from_iter(cdata.get_trait_impls()) } - implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) } visibility => { cdata.get_visibility(def_id.index) } @@ -473,6 +472,17 @@ impl CStore { ) -> Span { self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess) } + + pub fn traits_in_crate_untracked(&self, cnum: CrateNum) -> Vec { + self.get_crate_data(cnum).get_traits().collect() + } + + pub fn trait_impls_in_crate_untracked( + &self, + cnum: CrateNum, + ) -> Vec<(DefId, Option)> { + self.get_crate_data(cnum).get_trait_impls().collect() + } } impl CrateStore for CStore { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b3db2e63400..44306375fe6 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1416,13 +1416,6 @@ rustc_queries! { separate_provide_extern } - /// Given a crate, look up all trait impls in that crate. - /// Return `(impl_id, self_ty)`. - query all_trait_implementations(_: CrateNum) -> &'tcx [(DefId, Option)] { - desc { "looking up all (?) trait implementations" } - separate_provide_extern - } - query is_dllimport_foreign_item(def_id: DefId) -> bool { desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) } } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index f54ab9f2b11..eafc74b9945 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -19,118 +19,119 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { trace!("get_blanket_impls({:?})", ty); let mut impls = Vec::new(); - for trait_def_id in self.cx.tcx.all_traits() { - if !self.cx.cache.access_levels.is_public(trait_def_id) - || self.cx.generated_synthetics.get(&(ty, trait_def_id)).is_some() - { - continue; - } - // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls - let trait_impls = self.cx.tcx.trait_impls_of(trait_def_id); - for &impl_def_id in trait_impls.blanket_impls() { - trace!( - "get_blanket_impls: Considering impl for trait '{:?}' {:?}", - trait_def_id, - impl_def_id - ); - let trait_ref = self.cx.tcx.impl_trait_ref(impl_def_id).unwrap(); - let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_)); - let may_apply = is_param && self.cx.tcx.infer_ctxt().enter(|infcx| { - let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id); - let ty = ty.subst(infcx.tcx, substs); - let param_env = param_env.subst(infcx.tcx, substs); - - let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); - let trait_ref = trait_ref.subst(infcx.tcx, impl_substs); - - // Require the type the impl is implemented on to match - // our type, and ignore the impl if there was a mismatch. - let cause = traits::ObligationCause::dummy(); - let eq_result = infcx.at(&cause, param_env).eq(trait_ref.self_ty(), ty); - if let Ok(InferOk { value: (), obligations }) = eq_result { - // FIXME(eddyb) ignoring `obligations` might cause false positives. - drop(obligations); - - trace!( - "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}", - param_env, - trait_ref, - ty - ); - let predicates = self - .cx - .tcx - .predicates_of(impl_def_id) - .instantiate(self.cx.tcx, impl_substs) - .predicates - .into_iter() - .chain(Some( - ty::Binder::dummy(trait_ref) - .to_poly_trait_predicate() - .map_bound(ty::PredicateKind::Trait) - .to_predicate(infcx.tcx), - )); - for predicate in predicates { - debug!("testing predicate {:?}", predicate); - let obligation = traits::Obligation::new( - traits::ObligationCause::dummy(), - param_env, - predicate, - ); - match infcx.evaluate_obligation(&obligation) { - Ok(eval_result) if eval_result.may_apply() => {} - Err(traits::OverflowError::Canonical) => {} - Err(traits::OverflowError::ErrorReporting) => {} - _ => { - return false; - } - } - } - true - } else { - false - } - }); - debug!( - "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}", - may_apply, trait_ref, ty - ); - if !may_apply { + self.cx.with_all_traits(|cx, all_traits| { + for &trait_def_id in all_traits { + if !cx.cache.access_levels.is_public(trait_def_id) + || cx.generated_synthetics.get(&(ty, trait_def_id)).is_some() + { continue; } + // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls + let trait_impls = cx.tcx.trait_impls_of(trait_def_id); + for &impl_def_id in trait_impls.blanket_impls() { + trace!( + "get_blanket_impls: Considering impl for trait '{:?}' {:?}", + trait_def_id, + impl_def_id + ); + let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap(); + let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_)); + let may_apply = is_param && cx.tcx.infer_ctxt().enter(|infcx| { + let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id); + let ty = ty.subst(infcx.tcx, substs); + let param_env = param_env.subst(infcx.tcx, substs); - self.cx.generated_synthetics.insert((ty, trait_def_id)); + let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let trait_ref = trait_ref.subst(infcx.tcx, impl_substs); - impls.push(Item { - name: None, - attrs: Default::default(), - visibility: Inherited, - def_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, - kind: box ImplItem(Impl { - unsafety: hir::Unsafety::Normal, - generics: clean_ty_generics( - self.cx, - self.cx.tcx.generics_of(impl_def_id), - self.cx.tcx.explicit_predicates_of(impl_def_id), - ), - // FIXME(eddyb) compute both `trait_` and `for_` from - // the post-inference `trait_ref`, as it's more accurate. - trait_: Some(trait_ref.clean(self.cx)), - for_: ty.clean(self.cx), - items: self - .cx - .tcx - .associated_items(impl_def_id) - .in_definition_order() - .map(|x| x.clean(self.cx)) - .collect::>(), - polarity: ty::ImplPolarity::Positive, - kind: ImplKind::Blanket(box trait_ref.self_ty().clean(self.cx)), - }), - cfg: None, - }); + // Require the type the impl is implemented on to match + // our type, and ignore the impl if there was a mismatch. + let cause = traits::ObligationCause::dummy(); + let eq_result = infcx.at(&cause, param_env).eq(trait_ref.self_ty(), ty); + if let Ok(InferOk { value: (), obligations }) = eq_result { + // FIXME(eddyb) ignoring `obligations` might cause false positives. + drop(obligations); + + trace!( + "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}", + param_env, + trait_ref, + ty + ); + let predicates = cx + .tcx + .predicates_of(impl_def_id) + .instantiate(cx.tcx, impl_substs) + .predicates + .into_iter() + .chain(Some( + ty::Binder::dummy(trait_ref) + .to_poly_trait_predicate() + .map_bound(ty::PredicateKind::Trait) + .to_predicate(infcx.tcx), + )); + for predicate in predicates { + debug!("testing predicate {:?}", predicate); + let obligation = traits::Obligation::new( + traits::ObligationCause::dummy(), + param_env, + predicate, + ); + match infcx.evaluate_obligation(&obligation) { + Ok(eval_result) if eval_result.may_apply() => {} + Err(traits::OverflowError::Canonical) => {} + Err(traits::OverflowError::ErrorReporting) => {} + _ => { + return false; + } + } + } + true + } else { + false + } + }); + debug!( + "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}", + may_apply, trait_ref, ty + ); + if !may_apply { + continue; + } + + cx.generated_synthetics.insert((ty, trait_def_id)); + + impls.push(Item { + name: None, + attrs: Default::default(), + visibility: Inherited, + def_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, + kind: box ImplItem(Impl { + unsafety: hir::Unsafety::Normal, + generics: clean_ty_generics( + cx, + cx.tcx.generics_of(impl_def_id), + cx.tcx.explicit_predicates_of(impl_def_id), + ), + // FIXME(eddyb) compute both `trait_` and `for_` from + // the post-inference `trait_ref`, as it's more accurate. + trait_: Some(trait_ref.clean(cx)), + for_: ty.clean(cx), + items: cx + .tcx + .associated_items(impl_def_id) + .in_definition_order() + .map(|x| x.clean(cx)) + .collect::>(), + polarity: ty::ImplPolarity::Positive, + kind: ImplKind::Blanket(box trait_ref.self_ty().clean(cx)), + }), + cfg: None, + }); + } } - } + }); + impls } } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index ce0ac322af9..a8101845fb6 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -291,6 +291,7 @@ crate fn build_impls( attrs: Option>, ret: &mut Vec, ) { + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls"); let tcx = cx.tcx; // for each implementation of an item represented by `did`, build the clean::Item for that impl @@ -338,7 +339,7 @@ crate fn build_impl( return; } - let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impl"); + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_impl"); let tcx = cx.tcx; let associated_trait = tcx.impl_trait_ref(did); diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 43dcb611a37..f0e9b716081 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -179,6 +179,7 @@ crate fn build_deref_target_impls(cx: &mut DocContext<'_>, items: &[Item], ret: }; if let Some(prim) = target.primitive_type() { + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_primitive_inherent_impls"); for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) { inline::build_impl(cx, None, did, None, ret); } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 32b66278bf4..22f59d39799 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,30 +1,23 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; -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::Res; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::HirId; -use rustc_hir::{ - intravisit::{self, NestedVisitorMap, Visitor}, - Path, -}; -use rustc_interface::{interface, Queries}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{HirId, Path}; +use rustc_interface::interface; use rustc_middle::hir::map::Map; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_resolve as resolve; -use rustc_resolve::Namespace::TypeNS; use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::lint; use rustc_session::DiagnosticOutput; use rustc_session::Session; -use rustc_span::def_id::CRATE_DEF_INDEX; -use rustc_span::source_map; use rustc_span::symbol::sym; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{source_map, Span}; use std::cell::RefCell; use std::lazy::SyncLazy; @@ -39,14 +32,20 @@ use crate::passes::{self, Condition::*}; crate use rustc_session::config::{DebuggingOptions, Input, Options}; +crate struct ResolverCaches { + pub all_traits: Option>, + pub all_trait_impls: Option>, +} + crate struct DocContext<'tcx> { crate tcx: TyCtxt<'tcx>, /// Name resolver. Used for intra-doc links. /// /// The `Rc>` wrapping is needed because that is what's returned by - /// [`Queries::expansion()`]. + /// [`rustc_interface::Queries::expansion()`]. // FIXME: see if we can get rid of this RefCell somehow crate resolver: Rc>, + crate resolver_caches: ResolverCaches, /// Used for normalization. /// /// Most of this logic is copied from rustc_lint::late. @@ -123,6 +122,18 @@ impl<'tcx> DocContext<'tcx> { _ => None, } } + + crate fn with_all_traits(&mut self, f: impl FnOnce(&mut Self, &[DefId])) { + let all_traits = self.resolver_caches.all_traits.take(); + f(self, all_traits.as_ref().expect("`all_traits` are already borrowed")); + self.resolver_caches.all_traits = all_traits; + } + + crate fn with_all_trait_impls(&mut self, f: impl FnOnce(&mut Self, &[DefId])) { + let all_trait_impls = self.resolver_caches.all_trait_impls.take(); + f(self, all_trait_impls.as_ref().expect("`all_trait_impls` are already borrowed")); + self.resolver_caches.all_trait_impls = all_trait_impls; + } } /// Creates a new diagnostic `Handler` that can be used to emit warnings and errors. @@ -284,49 +295,10 @@ crate fn create_config( } } -crate fn create_resolver<'a>( - externs: config::Externs, - queries: &Queries<'a>, - sess: &Session, -) -> Rc> { - let (krate, resolver, _) = &*abort_on_err(queries.expansion(), sess).peek(); - let resolver = resolver.clone(); - - let resolver = crate::passes::collect_intra_doc_links::load_intra_link_crates(resolver, krate); - - // FIXME: somehow rustdoc is still missing crates even though we loaded all - // the known necessary crates. Load them all unconditionally until we find a way to fix this. - // DO NOT REMOVE THIS without first testing on the reproducer in - // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb - let extern_names: Vec = externs - .iter() - .filter(|(_, entry)| entry.add_prelude) - .map(|(name, _)| name) - .cloned() - .collect(); - resolver.borrow_mut().access(|resolver| { - sess.time("load_extern_crates", || { - for extern_name in &extern_names { - debug!("loading extern crate {}", extern_name); - if let Err(()) = resolver - .resolve_str_path_error( - DUMMY_SP, - extern_name, - TypeNS, - LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(), - ) { - warn!("unable to resolve external crate {} (do you have an unused `--extern` crate?)", extern_name) - } - } - }); - }); - - resolver -} - crate fn run_global_ctxt( tcx: TyCtxt<'_>, resolver: Rc>, + resolver_caches: ResolverCaches, show_coverage: bool, render_options: RenderOptions, output_format: OutputFormat, @@ -355,6 +327,14 @@ crate fn run_global_ctxt( }); rustc_passes::stability::check_unused_or_stable_features(tcx); + let auto_traits = resolver_caches + .all_traits + .as_ref() + .expect("`all_traits` are already borrowed") + .iter() + .copied() + .filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)) + .collect(); let access_levels = AccessLevels { map: tcx.privacy_access_levels(()).map.iter().map(|(k, v)| (k.to_def_id(), *v)).collect(), }; @@ -362,16 +342,14 @@ crate fn run_global_ctxt( let mut ctxt = DocContext { tcx, resolver, + resolver_caches, param_env: ParamEnv::empty(), external_traits: Default::default(), active_extern_traits: Default::default(), substs: Default::default(), impl_trait_bounds: Default::default(), generated_synthetics: Default::default(), - auto_traits: tcx - .all_traits() - .filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)) - .collect(), + auto_traits, module_trait_cache: FxHashMap::default(), cache: Cache::new(access_levels, render_options.document_private), inlined: FxHashSet::default(), diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 014ac484dcf..d7741c4fde2 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -82,6 +82,7 @@ use rustc_session::getopts; use rustc_session::{early_error, early_warn}; use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL; +use crate::passes::collect_intra_doc_links; /// A macro to create a FxHashMap. /// @@ -798,7 +799,15 @@ fn main_options(options: config::Options) -> MainResult { // We need to hold on to the complete resolver, so we cause everything to be // cloned for the analysis passes to use. Suboptimal, but necessary in the // current architecture. - let resolver = core::create_resolver(externs, queries, sess); + // FIXME(#83761): Resolver cloning can lead to inconsistencies between data in the + // two copies because one of the copies can be modified after `TyCtxt` construction. + let (resolver, resolver_caches) = { + let (krate, resolver, _) = &*abort_on_err(queries.expansion(), sess).peek(); + let resolver_caches = resolver.borrow_mut().access(|resolver| { + collect_intra_doc_links::early_resolve_intra_doc_links(resolver, krate, externs) + }); + (resolver.clone(), resolver_caches) + }; if sess.diagnostic().has_errors_or_lint_errors() { sess.fatal("Compilation failed, aborting rustdoc"); @@ -811,6 +820,7 @@ fn main_options(options: config::Options) -> MainResult { core::run_global_ctxt( tcx, resolver, + resolver_caches, show_coverage, render_options, output_format, diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 79530086282..49ff4517a4e 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -38,7 +38,7 @@ use crate::passes::Pass; use crate::visit::DocVisitor; mod early; -crate use early::load_intra_link_crates; +crate use early::early_resolve_intra_doc_links; crate const COLLECT_INTRA_DOC_LINKS: Pass = Pass { name: "collect-intra-doc-links", diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index 4cebf741e20..31d6ac44a94 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -1,98 +1,136 @@ -use ast::visit; -use rustc_ast as ast; +use crate::clean; +use crate::core::ResolverCaches; +use crate::html::markdown::markdown_links; +use crate::passes::collect_intra_doc_links::preprocess_link; + +use rustc_ast::visit::{self, AssocCtxt, Visitor}; +use rustc_ast::{self as ast, ItemKind}; +use rustc_ast_lowering::ResolverAstLowering; use rustc_hir::def::Namespace::TypeNS; -use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; -use rustc_interface::interface; -use rustc_span::Span; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; +use rustc_resolve::Resolver; +use rustc_session::config::Externs; +use rustc_span::{Span, DUMMY_SP}; -use std::cell::RefCell; use std::mem; -use std::rc::Rc; -type Resolver = Rc>; -// Letting the resolver escape at the end of the function leads to inconsistencies between the -// crates the TyCtxt sees and the resolver sees (because the resolver could load more crates -// after escaping). Hopefully `IntraLinkCrateLoader` gets all the crates we need ... -crate fn load_intra_link_crates(resolver: Resolver, krate: &ast::Crate) -> Resolver { - let mut loader = IntraLinkCrateLoader { current_mod: CRATE_DEF_ID, resolver }; - // `walk_crate` doesn't visit the crate itself for some reason. +crate fn early_resolve_intra_doc_links( + resolver: &mut Resolver<'_>, + krate: &ast::Crate, + externs: Externs, +) -> ResolverCaches { + let mut loader = IntraLinkCrateLoader { + resolver, + current_mod: CRATE_DEF_ID, + all_traits: Default::default(), + all_trait_impls: Default::default(), + }; + + // Overridden `visit_item` below doesn't apply to the crate root, + // so we have to visit its attributes and exports separately. loader.load_links_in_attrs(&krate.attrs, krate.span); visit::walk_crate(&mut loader, krate); - loader.resolver + loader.fill_resolver_caches(); + + // FIXME: somehow rustdoc is still missing crates even though we loaded all + // the known necessary crates. Load them all unconditionally until we find a way to fix this. + // DO NOT REMOVE THIS without first testing on the reproducer in + // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb + for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) { + let _ = loader.resolver.resolve_str_path_error( + DUMMY_SP, + extern_name, + TypeNS, + CRATE_DEF_ID.to_def_id(), + ); + } + + ResolverCaches { + all_traits: Some(loader.all_traits), + all_trait_impls: Some(loader.all_trait_impls), + } } -struct IntraLinkCrateLoader { +struct IntraLinkCrateLoader<'r, 'ra> { + resolver: &'r mut Resolver<'ra>, current_mod: LocalDefId, - resolver: Rc>, + all_traits: Vec, + all_trait_impls: Vec, } -impl IntraLinkCrateLoader { - fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) { - use crate::html::markdown::markdown_links; - use crate::passes::collect_intra_doc_links::preprocess_link; +impl IntraLinkCrateLoader<'_, '_> { + fn fill_resolver_caches(&mut self) { + for cnum in self.resolver.cstore().crates_untracked() { + let all_traits = self.resolver.cstore().traits_in_crate_untracked(cnum); + let all_trait_impls = self.resolver.cstore().trait_impls_in_crate_untracked(cnum); - // FIXME: this probably needs to consider inlining - let attrs = crate::clean::Attributes::from_ast(attrs, None); + self.all_traits.extend(all_traits); + self.all_trait_impls.extend(all_trait_impls.into_iter().map(|(def_id, _)| def_id)); + } + } + + fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) { + // FIXME: this needs to consider export inlining. + let attrs = clean::Attributes::from_ast(attrs, None); for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() { - debug!(?doc); - for link in markdown_links(doc.as_str()) { - debug!(?link.link); + let module_id = parent_module.unwrap_or(self.current_mod.to_def_id()); + + for link in markdown_links(&doc.as_str()) { let path_str = if let Some(Ok(x)) = preprocess_link(&link) { x.path_str } else { continue; }; - self.resolver.borrow_mut().access(|resolver| { - let _ = resolver.resolve_str_path_error( - span, - &path_str, - TypeNS, - parent_module.unwrap_or_else(|| self.current_mod.to_def_id()), - ); - }); + let _ = self.resolver.resolve_str_path_error(span, &path_str, TypeNS, module_id); } } } } -impl visit::Visitor<'_> for IntraLinkCrateLoader { - fn visit_foreign_item(&mut self, item: &ast::ForeignItem) { - self.load_links_in_attrs(&item.attrs, item.span); - visit::walk_foreign_item(self, item) - } - +impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> { fn visit_item(&mut self, item: &ast::Item) { - use rustc_ast_lowering::ResolverAstLowering; - - if let ast::ItemKind::Mod(..) = item.kind { - let new_mod = - self.resolver.borrow_mut().access(|resolver| resolver.local_def_id(item.id)); - let old_mod = mem::replace(&mut self.current_mod, new_mod); + if let ItemKind::Mod(..) = item.kind { + let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id)); self.load_links_in_attrs(&item.attrs, item.span); visit::walk_item(self, item); self.current_mod = old_mod; } else { + match item.kind { + ItemKind::Trait(..) => { + self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id()); + } + ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => { + self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id()); + } + _ => {} + } self.load_links_in_attrs(&item.attrs, item.span); visit::walk_item(self, item); } } - // NOTE: if doc-comments are ever allowed on function parameters, this will have to implement `visit_param` too. - - fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: visit::AssocCtxt) { + fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) { self.load_links_in_attrs(&item.attrs, item.span); visit::walk_assoc_item(self, item, ctxt) } + fn visit_foreign_item(&mut self, item: &ast::ForeignItem) { + self.load_links_in_attrs(&item.attrs, item.span); + visit::walk_foreign_item(self, item) + } + + fn visit_variant(&mut self, v: &ast::Variant) { + self.load_links_in_attrs(&v.attrs, v.span); + visit::walk_variant(self, v) + } + fn visit_field_def(&mut self, field: &ast::FieldDef) { self.load_links_in_attrs(&field.attrs, field.span); visit::walk_field_def(self, field) } - fn visit_variant(&mut self, v: &ast::Variant) { - self.load_links_in_attrs(&v.attrs, v.span); - visit::walk_variant(self, v) - } + // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters), + // then this will have to implement other visitor methods too. } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index cc1d994dc99..66ac612ea37 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -34,16 +34,18 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate let mut new_items = Vec::new(); - for &cnum in cx.tcx.crates(()).iter() { - for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() { - inline::build_impl(cx, None, did, None, &mut new_items); + // External trait impls. + cx.with_all_trait_impls(|cx, all_trait_impls| { + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impls"); + for &impl_def_id in all_trait_impls.iter().skip_while(|def_id| def_id.is_local()) { + inline::build_impl(cx, None, impl_def_id, None, &mut new_items); } - } + }); // Also try to inline primitive impls from other crates. - for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() { - if !def_id.is_local() { - cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| { + cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| { + for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() { + if !def_id.is_local() { inline::build_impl(cx, None, def_id, None, &mut new_items); // FIXME(eddyb) is this `doc(hidden)` check needed? @@ -51,9 +53,9 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate let impls = get_auto_trait_and_blanket_impls(cx, def_id); new_items.extend(impls.filter(|i| cx.inlined.insert(i.def_id))); } - }); + } } - } + }); let mut cleaner = BadImplStripper { prims, items: crate_items }; let mut type_did_to_deref_target: FxHashMap = FxHashMap::default(); @@ -126,36 +128,33 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate } }); - // `tcx.crates(())` doesn't include the local crate, and `tcx.all_trait_implementations` - // doesn't work with it anyway, so pull them from the HIR map instead - let mut extra_attrs = Vec::new(); - for trait_did in cx.tcx.all_traits() { - for &impl_did in cx.tcx.hir().trait_impls(trait_did) { - let impl_did = impl_did.to_def_id(); - cx.tcx.sess.prof.generic_activity("build_local_trait_impl").run(|| { - let mut parent = cx.tcx.parent(impl_did); - while let Some(did) = parent { - extra_attrs.extend( - cx.tcx - .get_attrs(did) - .iter() - .filter(|attr| attr.has_name(sym::doc)) - .filter(|attr| { - if let Some([attr]) = attr.meta_item_list().as_deref() { - attr.has_name(sym::cfg) - } else { - false - } - }) - .cloned(), - ); - parent = cx.tcx.parent(did); - } - inline::build_impl(cx, None, impl_did, Some(&extra_attrs), &mut new_items); - extra_attrs.clear(); - }); + // Local trait impls. + cx.with_all_trait_impls(|cx, all_trait_impls| { + let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls"); + let mut attr_buf = Vec::new(); + for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) { + let mut parent = cx.tcx.parent(impl_def_id); + while let Some(did) = parent { + attr_buf.extend( + cx.tcx + .get_attrs(did) + .iter() + .filter(|attr| attr.has_name(sym::doc)) + .filter(|attr| { + if let Some([attr]) = attr.meta_item_list().as_deref() { + attr.has_name(sym::cfg) + } else { + false + } + }) + .cloned(), + ); + parent = cx.tcx.parent(did); + } + inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items); + attr_buf.clear(); } - } + }); if let ModuleItem(Module { items, .. }) = &mut *krate.module.kind { items.extend(synth_impls);