]> git.lizzy.rs Git - rust.git/commitdiff
rustdoc: Introduce a resolver cache for sharing data between early doc link resolutio...
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sun, 29 Aug 2021 18:41:46 +0000 (21:41 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Fri, 7 Jan 2022 08:21:53 +0000 (16:21 +0800)
compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
compiler/rustc_middle/src/query/mod.rs
src/librustdoc/clean/blanket_impl.rs
src/librustdoc/clean/inline.rs
src/librustdoc/clean/utils.rs
src/librustdoc/core.rs
src/librustdoc/lib.rs
src/librustdoc/passes/collect_intra_doc_links.rs
src/librustdoc/passes/collect_intra_doc_links/early.rs
src/librustdoc/passes/collect_trait_impls.rs

index aac0aa61ea65eccaf291b6c67c86160e9ca6b09b..a1fffd2ebbbf20759c8839d38af1c26287a13f63 100644 (file)
@@ -10,6 +10,7 @@
 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 @@ fn into_args(self) -> (DefId, DefId) {
     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 @@ pub fn get_proc_macro_quoted_span_untracked(
     ) -> Span {
         self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
     }
+
+    pub fn traits_in_crate_untracked(&self, cnum: CrateNum) -> Vec<DefId> {
+        self.get_crate_data(cnum).get_traits().collect()
+    }
+
+    pub fn trait_impls_in_crate_untracked(
+        &self,
+        cnum: CrateNum,
+    ) -> Vec<(DefId, Option<SimplifiedType>)> {
+        self.get_crate_data(cnum).get_trait_impls().collect()
+    }
 }
 
 impl CrateStore for CStore {
index b3db2e6340024529a8b8e25e18cd90a0f1e034eb..44306375fe69f21369bc61621a406aa4f01e7e5d 100644 (file)
         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<SimplifiedType>)] {
-        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) }
     }
index f54ab9f2b11aac9bc210bbc836c91bd8b087c821..eafc74b9945baf21b485c226eb36cd738213643f 100644 (file)
@@ -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);
+        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);
 
-                    let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
-                    let trait_ref = trait_ref.subst(infcx.tcx, impl_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);
+                        // 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(),
+                            trace!(
+                                "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}",
                                 param_env,
-                                predicate,
+                                trait_ref,
+                                ty
                             );
-                            match infcx.evaluate_obligation(&obligation) {
-                                Ok(eval_result) if eval_result.may_apply() => {}
-                                Err(traits::OverflowError::Canonical) => {}
-                                Err(traits::OverflowError::ErrorReporting) => {}
-                                _ => {
-                                    return false;
+                            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
                         }
-                        true
-                    } else {
-                        false
+                    });
+                    debug!(
+                        "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}",
+                        may_apply, trait_ref, ty
+                    );
+                    if !may_apply {
+                        continue;
                     }
-                });
-                debug!(
-                    "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}",
-                    may_apply, trait_ref, ty
-                );
-                if !may_apply {
-                    continue;
-                }
 
-                self.cx.generated_synthetics.insert((ty, trait_def_id));
+                    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(
-                            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::<Vec<_>>(),
-                        polarity: ty::ImplPolarity::Positive,
-                        kind: ImplKind::Blanket(box trait_ref.self_ty().clean(self.cx)),
-                    }),
-                    cfg: None,
-                });
+                    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::<Vec<_>>(),
+                            polarity: ty::ImplPolarity::Positive,
+                            kind: ImplKind::Blanket(box trait_ref.self_ty().clean(cx)),
+                        }),
+                        cfg: None,
+                    });
+                }
             }
-        }
+        });
+
         impls
     }
 }
index ce0ac322af914e87d69937853950faf7b9f9e9f4..a8101845fb647fc8aeec6c6eee455b3b2c5e2ad8 100644 (file)
@@ -291,6 +291,7 @@ fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> clean::Typedef {
     attrs: Option<Attrs<'_>>,
     ret: &mut Vec<clean::Item>,
 ) {
+    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 @@ fn merge_attrs(
         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);
index 43dcb611a377df1a0c75196f4565717b80ff7612..f0e9b716081ac7b31a7246060ecbe0f1d96d1ca9 100644 (file)
@@ -179,6 +179,7 @@ pub(super) fn external_path(
         };
 
         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);
             }
index 32b66278bf4230f8afe78c843cf104700271d172..22f59d39799c4fbb56353affc95856af16f2ea47 100644 (file)
@@ -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;
 
 crate use rustc_session::config::{DebuggingOptions, Input, Options};
 
+crate struct ResolverCaches {
+    pub all_traits: Option<Vec<DefId>>,
+    pub all_trait_impls: Option<Vec<DefId>>,
+}
+
 crate struct DocContext<'tcx> {
     crate tcx: TyCtxt<'tcx>,
     /// Name resolver. Used for intra-doc links.
     ///
     /// The `Rc<RefCell<...>>` 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<RefCell<interface::BoxedResolver>>,
+    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 @@ impl<'tcx> DocContext<'tcx> {
     }
 }
 
-crate fn create_resolver<'a>(
-    externs: config::Externs,
-    queries: &Queries<'a>,
-    sess: &Session,
-) -> Rc<RefCell<interface::BoxedResolver>> {
-    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<String> = 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<RefCell<interface::BoxedResolver>>,
+    resolver_caches: ResolverCaches,
     show_coverage: bool,
     render_options: RenderOptions,
     output_format: OutputFormat,
@@ -355,6 +327,14 @@ impl<'tcx> DocContext<'tcx> {
     });
     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 @@ impl<'tcx> DocContext<'tcx> {
     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(),
index 014ac484dcfaec553a34f87d375ed6b9f882d86b..d7741c4fde2391a0cb75c3e8c44d3c71f13b2cce 100644 (file)
@@ -82,6 +82,7 @@
 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,
index 7953008628204f3e72b0ff460d14b7260e06ff3d..49ff4517a4eb01d4f6b3ab8f48b95b9940d55b17 100644 (file)
@@ -38,7 +38,7 @@
 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",
index 4cebf741e200271ce837e047fd81cc587c673e4b..31d6ac44a9460c732ee288d4750c8915dc363d13 100644 (file)
-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<RefCell<interface::BoxedResolver>>;
-// 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<RefCell<interface::BoxedResolver>>,
+    all_traits: Vec<DefId>,
+    all_trait_impls: Vec<DefId>,
 }
 
-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_field_def(&mut self, field: &ast::FieldDef) {
-        self.load_links_in_attrs(&field.attrs, field.span);
-        visit::walk_field_def(self, field)
+    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)
+    }
+
+    // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
+    // then this will have to implement other visitor methods too.
 }
index cc1d994dc99f03349272601f01bd828686bdf7a2..66ac612ea37c40d0011d8ca00598fb31c6f300c0 100644 (file)
 
     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 @@
                     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<DefId, &Type> = FxHashMap::default();
@@ -126,36 +128,33 @@ fn add_deref_target(
         }
     });
 
-    // `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);