rustdoc: Introduce a resolver cache for sharing data between early doc link resolution and later passes

This commit is contained in:
Vadim Petrochenkov 2021-08-29 21:41:46 +03:00
parent e012a191d7
commit 5acd1f91a0
10 changed files with 297 additions and 266 deletions

View File

@ -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 {

View File

@ -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) }
}

View 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);
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
}
}

View File

@ -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);

View File

@ -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);
}

View 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;
@ -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(),

View File

@ -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,

View File

@ -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",

View File

@ -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.
}

View File

@ -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);