mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 15:23:46 +00:00
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,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<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 {
|
||||
|
@ -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<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) }
|
||||
}
|
||||
|
@ -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::<Vec<_>>(),
|
||||
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::<Vec<_>>(),
|
||||
polarity: ty::ImplPolarity::Positive,
|
||||
kind: ImplKind::Blanket(box trait_ref.self_ty().clean(cx)),
|
||||
}),
|
||||
cfg: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
impls
|
||||
}
|
||||
}
|
||||
|
@ -291,6 +291,7 @@ crate fn build_impls(
|
||||
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 @@ 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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<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 @@ 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(
|
||||
tcx: TyCtxt<'_>,
|
||||
resolver: Rc<RefCell<interface::BoxedResolver>>,
|
||||
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(),
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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<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_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.
|
||||
}
|
||||
|
@ -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<DefId, &Type> = 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);
|
||||
|
Loading…
Reference in New Issue
Block a user