rustdoc: Introduce a resolver cache for sharing data between early doc link resolution and later passes
This commit is contained in:
parent
e012a191d7
commit
5acd1f91a0
10 changed files with 297 additions and 266 deletions
|
@ -10,6 +10,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
|
||||||
use rustc_middle::hir::exports::Export;
|
use rustc_middle::hir::exports::Export;
|
||||||
use rustc_middle::middle::exported_symbols::ExportedSymbol;
|
use rustc_middle::middle::exported_symbols::ExportedSymbol;
|
||||||
use rustc_middle::middle::stability::DeprecationEntry;
|
use rustc_middle::middle::stability::DeprecationEntry;
|
||||||
|
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||||
use rustc_middle::ty::query::{ExternProviders, Providers};
|
use rustc_middle::ty::query::{ExternProviders, Providers};
|
||||||
use rustc_middle::ty::{self, TyCtxt, Visibility};
|
use rustc_middle::ty::{self, TyCtxt, Visibility};
|
||||||
use rustc_session::cstore::{CrateSource, CrateStore, ForeignModule};
|
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() }
|
extra_filename => { cdata.root.extra_filename.clone() }
|
||||||
|
|
||||||
traits_in_crate => { tcx.arena.alloc_from_iter(cdata.get_traits()) }
|
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) }
|
implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) }
|
||||||
|
|
||||||
visibility => { cdata.get_visibility(def_id.index) }
|
visibility => { cdata.get_visibility(def_id.index) }
|
||||||
|
@ -473,6 +472,17 @@ impl CStore {
|
||||||
) -> Span {
|
) -> Span {
|
||||||
self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
|
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 {
|
impl CrateStore for CStore {
|
||||||
|
|
|
@ -1416,13 +1416,6 @@ rustc_queries! {
|
||||||
separate_provide_extern
|
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 {
|
query is_dllimport_foreign_item(def_id: DefId) -> bool {
|
||||||
desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) }
|
desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,23 +19,24 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
|
||||||
|
|
||||||
trace!("get_blanket_impls({:?})", ty);
|
trace!("get_blanket_impls({:?})", ty);
|
||||||
let mut impls = Vec::new();
|
let mut impls = Vec::new();
|
||||||
for trait_def_id in self.cx.tcx.all_traits() {
|
self.cx.with_all_traits(|cx, all_traits| {
|
||||||
if !self.cx.cache.access_levels.is_public(trait_def_id)
|
for &trait_def_id in all_traits {
|
||||||
|| self.cx.generated_synthetics.get(&(ty, trait_def_id)).is_some()
|
if !cx.cache.access_levels.is_public(trait_def_id)
|
||||||
|
|| cx.generated_synthetics.get(&(ty, trait_def_id)).is_some()
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls
|
// 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);
|
let trait_impls = cx.tcx.trait_impls_of(trait_def_id);
|
||||||
for &impl_def_id in trait_impls.blanket_impls() {
|
for &impl_def_id in trait_impls.blanket_impls() {
|
||||||
trace!(
|
trace!(
|
||||||
"get_blanket_impls: Considering impl for trait '{:?}' {:?}",
|
"get_blanket_impls: Considering impl for trait '{:?}' {:?}",
|
||||||
trait_def_id,
|
trait_def_id,
|
||||||
impl_def_id
|
impl_def_id
|
||||||
);
|
);
|
||||||
let trait_ref = self.cx.tcx.impl_trait_ref(impl_def_id).unwrap();
|
let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap();
|
||||||
let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_));
|
let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_));
|
||||||
let may_apply = is_param && self.cx.tcx.infer_ctxt().enter(|infcx| {
|
let may_apply = is_param && cx.tcx.infer_ctxt().enter(|infcx| {
|
||||||
let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id);
|
let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id);
|
||||||
let ty = ty.subst(infcx.tcx, substs);
|
let ty = ty.subst(infcx.tcx, substs);
|
||||||
let param_env = param_env.subst(infcx.tcx, substs);
|
let param_env = param_env.subst(infcx.tcx, substs);
|
||||||
|
@ -57,11 +58,10 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
|
||||||
trait_ref,
|
trait_ref,
|
||||||
ty
|
ty
|
||||||
);
|
);
|
||||||
let predicates = self
|
let predicates = cx
|
||||||
.cx
|
|
||||||
.tcx
|
.tcx
|
||||||
.predicates_of(impl_def_id)
|
.predicates_of(impl_def_id)
|
||||||
.instantiate(self.cx.tcx, impl_substs)
|
.instantiate(cx.tcx, impl_substs)
|
||||||
.predicates
|
.predicates
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(Some(
|
.chain(Some(
|
||||||
|
@ -99,7 +99,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cx.generated_synthetics.insert((ty, trait_def_id));
|
cx.generated_synthetics.insert((ty, trait_def_id));
|
||||||
|
|
||||||
impls.push(Item {
|
impls.push(Item {
|
||||||
name: None,
|
name: None,
|
||||||
|
@ -109,28 +109,29 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
|
||||||
kind: box ImplItem(Impl {
|
kind: box ImplItem(Impl {
|
||||||
unsafety: hir::Unsafety::Normal,
|
unsafety: hir::Unsafety::Normal,
|
||||||
generics: clean_ty_generics(
|
generics: clean_ty_generics(
|
||||||
self.cx,
|
cx,
|
||||||
self.cx.tcx.generics_of(impl_def_id),
|
cx.tcx.generics_of(impl_def_id),
|
||||||
self.cx.tcx.explicit_predicates_of(impl_def_id),
|
cx.tcx.explicit_predicates_of(impl_def_id),
|
||||||
),
|
),
|
||||||
// FIXME(eddyb) compute both `trait_` and `for_` from
|
// FIXME(eddyb) compute both `trait_` and `for_` from
|
||||||
// the post-inference `trait_ref`, as it's more accurate.
|
// the post-inference `trait_ref`, as it's more accurate.
|
||||||
trait_: Some(trait_ref.clean(self.cx)),
|
trait_: Some(trait_ref.clean(cx)),
|
||||||
for_: ty.clean(self.cx),
|
for_: ty.clean(cx),
|
||||||
items: self
|
items: cx
|
||||||
.cx
|
|
||||||
.tcx
|
.tcx
|
||||||
.associated_items(impl_def_id)
|
.associated_items(impl_def_id)
|
||||||
.in_definition_order()
|
.in_definition_order()
|
||||||
.map(|x| x.clean(self.cx))
|
.map(|x| x.clean(cx))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
polarity: ty::ImplPolarity::Positive,
|
polarity: ty::ImplPolarity::Positive,
|
||||||
kind: ImplKind::Blanket(box trait_ref.self_ty().clean(self.cx)),
|
kind: ImplKind::Blanket(box trait_ref.self_ty().clean(cx)),
|
||||||
}),
|
}),
|
||||||
cfg: None,
|
cfg: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
impls
|
impls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,6 +291,7 @@ crate fn build_impls(
|
||||||
attrs: Option<Attrs<'_>>,
|
attrs: Option<Attrs<'_>>,
|
||||||
ret: &mut Vec<clean::Item>,
|
ret: &mut Vec<clean::Item>,
|
||||||
) {
|
) {
|
||||||
|
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls");
|
||||||
let tcx = cx.tcx;
|
let tcx = cx.tcx;
|
||||||
|
|
||||||
// for each implementation of an item represented by `did`, build the clean::Item for that impl
|
// 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;
|
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 tcx = cx.tcx;
|
||||||
let associated_trait = tcx.impl_trait_ref(did);
|
let associated_trait = tcx.impl_trait_ref(did);
|
||||||
|
|
|
@ -179,6 +179,7 @@ crate fn build_deref_target_impls(cx: &mut DocContext<'_>, items: &[Item], ret:
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(prim) = target.primitive_type() {
|
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()) {
|
for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) {
|
||||||
inline::build_impl(cx, None, did, None, ret);
|
inline::build_impl(cx, None, did, None, ret);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,23 @@
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_data_structures::sync::{self, Lrc};
|
use rustc_data_structures::sync::{self, Lrc};
|
||||||
use rustc_driver::abort_on_err;
|
|
||||||
use rustc_errors::emitter::{Emitter, EmitterWriter};
|
use rustc_errors::emitter::{Emitter, EmitterWriter};
|
||||||
use rustc_errors::json::JsonEmitter;
|
use rustc_errors::json::JsonEmitter;
|
||||||
use rustc_feature::UnstableFeatures;
|
use rustc_feature::UnstableFeatures;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::HirId;
|
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::{
|
use rustc_hir::{HirId, Path};
|
||||||
intravisit::{self, NestedVisitorMap, Visitor},
|
use rustc_interface::interface;
|
||||||
Path,
|
|
||||||
};
|
|
||||||
use rustc_interface::{interface, Queries};
|
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
use rustc_middle::middle::privacy::AccessLevels;
|
use rustc_middle::middle::privacy::AccessLevels;
|
||||||
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
||||||
use rustc_resolve as resolve;
|
use rustc_resolve as resolve;
|
||||||
use rustc_resolve::Namespace::TypeNS;
|
|
||||||
use rustc_session::config::{self, CrateType, ErrorOutputType};
|
use rustc_session::config::{self, CrateType, ErrorOutputType};
|
||||||
use rustc_session::lint;
|
use rustc_session::lint;
|
||||||
use rustc_session::DiagnosticOutput;
|
use rustc_session::DiagnosticOutput;
|
||||||
use rustc_session::Session;
|
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::symbol::sym;
|
||||||
use rustc_span::{Span, DUMMY_SP};
|
use rustc_span::{source_map, Span};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::lazy::SyncLazy;
|
use std::lazy::SyncLazy;
|
||||||
|
@ -39,14 +32,20 @@ use crate::passes::{self, Condition::*};
|
||||||
|
|
||||||
crate use rustc_session::config::{DebuggingOptions, Input, Options};
|
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 struct DocContext<'tcx> {
|
||||||
crate tcx: TyCtxt<'tcx>,
|
crate tcx: TyCtxt<'tcx>,
|
||||||
/// Name resolver. Used for intra-doc links.
|
/// Name resolver. Used for intra-doc links.
|
||||||
///
|
///
|
||||||
/// The `Rc<RefCell<...>>` wrapping is needed because that is what's returned by
|
/// 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
|
// FIXME: see if we can get rid of this RefCell somehow
|
||||||
crate resolver: Rc<RefCell<interface::BoxedResolver>>,
|
crate resolver: Rc<RefCell<interface::BoxedResolver>>,
|
||||||
|
crate resolver_caches: ResolverCaches,
|
||||||
/// Used for normalization.
|
/// Used for normalization.
|
||||||
///
|
///
|
||||||
/// Most of this logic is copied from rustc_lint::late.
|
/// Most of this logic is copied from rustc_lint::late.
|
||||||
|
@ -123,6 +122,18 @@ impl<'tcx> DocContext<'tcx> {
|
||||||
_ => None,
|
_ => 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.
|
/// 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<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(
|
crate fn run_global_ctxt(
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
resolver: Rc<RefCell<interface::BoxedResolver>>,
|
resolver: Rc<RefCell<interface::BoxedResolver>>,
|
||||||
|
resolver_caches: ResolverCaches,
|
||||||
show_coverage: bool,
|
show_coverage: bool,
|
||||||
render_options: RenderOptions,
|
render_options: RenderOptions,
|
||||||
output_format: OutputFormat,
|
output_format: OutputFormat,
|
||||||
|
@ -355,6 +327,14 @@ crate fn run_global_ctxt(
|
||||||
});
|
});
|
||||||
rustc_passes::stability::check_unused_or_stable_features(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 {
|
let access_levels = AccessLevels {
|
||||||
map: tcx.privacy_access_levels(()).map.iter().map(|(k, v)| (k.to_def_id(), *v)).collect(),
|
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 {
|
let mut ctxt = DocContext {
|
||||||
tcx,
|
tcx,
|
||||||
resolver,
|
resolver,
|
||||||
|
resolver_caches,
|
||||||
param_env: ParamEnv::empty(),
|
param_env: ParamEnv::empty(),
|
||||||
external_traits: Default::default(),
|
external_traits: Default::default(),
|
||||||
active_extern_traits: Default::default(),
|
active_extern_traits: Default::default(),
|
||||||
substs: Default::default(),
|
substs: Default::default(),
|
||||||
impl_trait_bounds: Default::default(),
|
impl_trait_bounds: Default::default(),
|
||||||
generated_synthetics: Default::default(),
|
generated_synthetics: Default::default(),
|
||||||
auto_traits: tcx
|
auto_traits,
|
||||||
.all_traits()
|
|
||||||
.filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id))
|
|
||||||
.collect(),
|
|
||||||
module_trait_cache: FxHashMap::default(),
|
module_trait_cache: FxHashMap::default(),
|
||||||
cache: Cache::new(access_levels, render_options.document_private),
|
cache: Cache::new(access_levels, render_options.document_private),
|
||||||
inlined: FxHashSet::default(),
|
inlined: FxHashSet::default(),
|
||||||
|
|
|
@ -82,6 +82,7 @@ use rustc_session::getopts;
|
||||||
use rustc_session::{early_error, early_warn};
|
use rustc_session::{early_error, early_warn};
|
||||||
|
|
||||||
use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL;
|
use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL;
|
||||||
|
use crate::passes::collect_intra_doc_links;
|
||||||
|
|
||||||
/// A macro to create a FxHashMap.
|
/// 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
|
// 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
|
// cloned for the analysis passes to use. Suboptimal, but necessary in the
|
||||||
// current architecture.
|
// 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() {
|
if sess.diagnostic().has_errors_or_lint_errors() {
|
||||||
sess.fatal("Compilation failed, aborting rustdoc");
|
sess.fatal("Compilation failed, aborting rustdoc");
|
||||||
|
@ -811,6 +820,7 @@ fn main_options(options: config::Options) -> MainResult {
|
||||||
core::run_global_ctxt(
|
core::run_global_ctxt(
|
||||||
tcx,
|
tcx,
|
||||||
resolver,
|
resolver,
|
||||||
|
resolver_caches,
|
||||||
show_coverage,
|
show_coverage,
|
||||||
render_options,
|
render_options,
|
||||||
output_format,
|
output_format,
|
||||||
|
|
|
@ -38,7 +38,7 @@ use crate::passes::Pass;
|
||||||
use crate::visit::DocVisitor;
|
use crate::visit::DocVisitor;
|
||||||
|
|
||||||
mod early;
|
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 {
|
crate const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
|
||||||
name: "collect-intra-doc-links",
|
name: "collect-intra-doc-links",
|
||||||
|
|
|
@ -1,98 +1,136 @@
|
||||||
use ast::visit;
|
use crate::clean;
|
||||||
use rustc_ast as ast;
|
use crate::core::ResolverCaches;
|
||||||
use rustc_hir::def::Namespace::TypeNS;
|
|
||||||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
|
|
||||||
use rustc_interface::interface;
|
|
||||||
use rustc_span::Span;
|
|
||||||
|
|
||||||
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.
|
|
||||||
loader.load_links_in_attrs(&krate.attrs, krate.span);
|
|
||||||
visit::walk_crate(&mut loader, krate);
|
|
||||||
loader.resolver
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IntraLinkCrateLoader {
|
|
||||||
current_mod: LocalDefId,
|
|
||||||
resolver: Rc<RefCell<interface::BoxedResolver>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntraLinkCrateLoader {
|
|
||||||
fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) {
|
|
||||||
use crate::html::markdown::markdown_links;
|
use crate::html::markdown::markdown_links;
|
||||||
use crate::passes::collect_intra_doc_links::preprocess_link;
|
use crate::passes::collect_intra_doc_links::preprocess_link;
|
||||||
|
|
||||||
// FIXME: this probably needs to consider inlining
|
use rustc_ast::visit::{self, AssocCtxt, Visitor};
|
||||||
let attrs = crate::clean::Attributes::from_ast(attrs, None);
|
use rustc_ast::{self as ast, ItemKind};
|
||||||
|
use rustc_ast_lowering::ResolverAstLowering;
|
||||||
|
use rustc_hir::def::Namespace::TypeNS;
|
||||||
|
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::mem;
|
||||||
|
|
||||||
|
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.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<'r, 'ra> {
|
||||||
|
resolver: &'r mut Resolver<'ra>,
|
||||||
|
current_mod: LocalDefId,
|
||||||
|
all_traits: Vec<DefId>,
|
||||||
|
all_trait_impls: Vec<DefId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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() {
|
for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() {
|
||||||
debug!(?doc);
|
let module_id = parent_module.unwrap_or(self.current_mod.to_def_id());
|
||||||
for link in markdown_links(doc.as_str()) {
|
|
||||||
debug!(?link.link);
|
for link in markdown_links(&doc.as_str()) {
|
||||||
let path_str = if let Some(Ok(x)) = preprocess_link(&link) {
|
let path_str = if let Some(Ok(x)) = preprocess_link(&link) {
|
||||||
x.path_str
|
x.path_str
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
self.resolver.borrow_mut().access(|resolver| {
|
let _ = self.resolver.resolve_str_path_error(span, &path_str, TypeNS, module_id);
|
||||||
let _ = resolver.resolve_str_path_error(
|
|
||||||
span,
|
|
||||||
&path_str,
|
|
||||||
TypeNS,
|
|
||||||
parent_module.unwrap_or_else(|| self.current_mod.to_def_id()),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl visit::Visitor<'_> for IntraLinkCrateLoader {
|
impl 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_item(&mut self, item: &ast::Item) {
|
fn visit_item(&mut self, item: &ast::Item) {
|
||||||
use rustc_ast_lowering::ResolverAstLowering;
|
if let ItemKind::Mod(..) = item.kind {
|
||||||
|
let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id));
|
||||||
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);
|
|
||||||
|
|
||||||
self.load_links_in_attrs(&item.attrs, item.span);
|
self.load_links_in_attrs(&item.attrs, item.span);
|
||||||
visit::walk_item(self, item);
|
visit::walk_item(self, item);
|
||||||
|
|
||||||
self.current_mod = old_mod;
|
self.current_mod = old_mod;
|
||||||
} else {
|
} 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);
|
self.load_links_in_attrs(&item.attrs, item.span);
|
||||||
visit::walk_item(self, item);
|
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: AssocCtxt) {
|
||||||
|
|
||||||
fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: visit::AssocCtxt) {
|
|
||||||
self.load_links_in_attrs(&item.attrs, item.span);
|
self.load_links_in_attrs(&item.attrs, item.span);
|
||||||
visit::walk_assoc_item(self, item, ctxt)
|
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) {
|
fn visit_field_def(&mut self, field: &ast::FieldDef) {
|
||||||
self.load_links_in_attrs(&field.attrs, field.span);
|
self.load_links_in_attrs(&field.attrs, field.span);
|
||||||
visit::walk_field_def(self, field)
|
visit::walk_field_def(self, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_variant(&mut self, v: &ast::Variant) {
|
// NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
|
||||||
self.load_links_in_attrs(&v.attrs, v.span);
|
// then this will have to implement other visitor methods too.
|
||||||
visit::walk_variant(self, v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,16 +34,18 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate
|
||||||
|
|
||||||
let mut new_items = Vec::new();
|
let mut new_items = Vec::new();
|
||||||
|
|
||||||
for &cnum in cx.tcx.crates(()).iter() {
|
// External trait impls.
|
||||||
for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() {
|
cx.with_all_trait_impls(|cx, all_trait_impls| {
|
||||||
inline::build_impl(cx, None, did, None, &mut new_items);
|
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.
|
// Also try to inline primitive impls from other crates.
|
||||||
|
cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
|
||||||
for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() {
|
for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() {
|
||||||
if !def_id.is_local() {
|
if !def_id.is_local() {
|
||||||
cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
|
|
||||||
inline::build_impl(cx, None, def_id, None, &mut new_items);
|
inline::build_impl(cx, None, def_id, None, &mut new_items);
|
||||||
|
|
||||||
// FIXME(eddyb) is this `doc(hidden)` check needed?
|
// 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);
|
let impls = get_auto_trait_and_blanket_impls(cx, def_id);
|
||||||
new_items.extend(impls.filter(|i| cx.inlined.insert(i.def_id)));
|
new_items.extend(impls.filter(|i| cx.inlined.insert(i.def_id)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cleaner = BadImplStripper { prims, items: crate_items };
|
let mut cleaner = BadImplStripper { prims, items: crate_items };
|
||||||
let mut type_did_to_deref_target: FxHashMap<DefId, &Type> = FxHashMap::default();
|
let mut type_did_to_deref_target: FxHashMap<DefId, &Type> = FxHashMap::default();
|
||||||
|
@ -126,16 +128,14 @@ 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`
|
// Local trait impls.
|
||||||
// doesn't work with it anyway, so pull them from the HIR map instead
|
cx.with_all_trait_impls(|cx, all_trait_impls| {
|
||||||
let mut extra_attrs = Vec::new();
|
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls");
|
||||||
for trait_did in cx.tcx.all_traits() {
|
let mut attr_buf = Vec::new();
|
||||||
for &impl_did in cx.tcx.hir().trait_impls(trait_did) {
|
for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) {
|
||||||
let impl_did = impl_did.to_def_id();
|
let mut parent = cx.tcx.parent(impl_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 {
|
while let Some(did) = parent {
|
||||||
extra_attrs.extend(
|
attr_buf.extend(
|
||||||
cx.tcx
|
cx.tcx
|
||||||
.get_attrs(did)
|
.get_attrs(did)
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -151,11 +151,10 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate
|
||||||
);
|
);
|
||||||
parent = cx.tcx.parent(did);
|
parent = cx.tcx.parent(did);
|
||||||
}
|
}
|
||||||
inline::build_impl(cx, None, impl_did, Some(&extra_attrs), &mut new_items);
|
inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items);
|
||||||
extra_attrs.clear();
|
attr_buf.clear();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ModuleItem(Module { items, .. }) = &mut *krate.module.kind {
|
if let ModuleItem(Module { items, .. }) = &mut *krate.module.kind {
|
||||||
items.extend(synth_impls);
|
items.extend(synth_impls);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue