Skip to content

Commit 5acd1f9

Browse files
committedJan 7, 2022
rustdoc: Introduce a resolver cache for sharing data between early doc link resolution and later passes
·
1.89.01.60.0
1 parent e012a19 commit 5acd1f9

File tree

10 files changed

+290
-259
lines changed

10 files changed

+290
-259
lines changed
 

‎compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
1010
use rustc_middle::hir::exports::Export;
1111
use rustc_middle::middle::exported_symbols::ExportedSymbol;
1212
use rustc_middle::middle::stability::DeprecationEntry;
13+
use rustc_middle::ty::fast_reject::SimplifiedType;
1314
use rustc_middle::ty::query::{ExternProviders, Providers};
1415
use rustc_middle::ty::{self, TyCtxt, Visibility};
1516
use rustc_session::cstore::{CrateSource, CrateStore, ForeignModule};
@@ -192,8 +193,6 @@ provide! { <'tcx> tcx, def_id, other, cdata,
192193
extra_filename => { cdata.root.extra_filename.clone() }
193194

194195
traits_in_crate => { tcx.arena.alloc_from_iter(cdata.get_traits()) }
195-
all_trait_implementations => { tcx.arena.alloc_from_iter(cdata.get_trait_impls()) }
196-
197196
implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) }
198197

199198
visibility => { cdata.get_visibility(def_id.index) }
@@ -473,6 +472,17 @@ impl CStore {
473472
) -> Span {
474473
self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
475474
}
475+
476+
pub fn traits_in_crate_untracked(&self, cnum: CrateNum) -> Vec<DefId> {
477+
self.get_crate_data(cnum).get_traits().collect()
478+
}
479+
480+
pub fn trait_impls_in_crate_untracked(
481+
&self,
482+
cnum: CrateNum,
483+
) -> Vec<(DefId, Option<SimplifiedType>)> {
484+
self.get_crate_data(cnum).get_trait_impls().collect()
485+
}
476486
}
477487

478488
impl CrateStore for CStore {

‎compiler/rustc_middle/src/query/mod.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,13 +1416,6 @@ rustc_queries! {
14161416
separate_provide_extern
14171417
}
14181418

1419-
/// Given a crate, look up all trait impls in that crate.
1420-
/// Return `(impl_id, self_ty)`.
1421-
query all_trait_implementations(_: CrateNum) -> &'tcx [(DefId, Option<SimplifiedType>)] {
1422-
desc { "looking up all (?) trait implementations" }
1423-
separate_provide_extern
1424-
}
1425-
14261419
query is_dllimport_foreign_item(def_id: DefId) -> bool {
14271420
desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) }
14281421
}

‎src/librustdoc/clean/blanket_impl.rs

Lines changed: 101 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -19,118 +19,119 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
1919

2020
trace!("get_blanket_impls({:?})", ty);
2121
let mut impls = Vec::new();
22-
for trait_def_id in self.cx.tcx.all_traits() {
23-
if !self.cx.cache.access_levels.is_public(trait_def_id)
24-
|| self.cx.generated_synthetics.get(&(ty, trait_def_id)).is_some()
25-
{
26-
continue;
27-
}
28-
// NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls
29-
let trait_impls = self.cx.tcx.trait_impls_of(trait_def_id);
30-
for &impl_def_id in trait_impls.blanket_impls() {
31-
trace!(
32-
"get_blanket_impls: Considering impl for trait '{:?}' {:?}",
33-
trait_def_id,
34-
impl_def_id
35-
);
36-
let trait_ref = self.cx.tcx.impl_trait_ref(impl_def_id).unwrap();
37-
let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_));
38-
let may_apply = is_param && self.cx.tcx.infer_ctxt().enter(|infcx| {
39-
let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id);
40-
let ty = ty.subst(infcx.tcx, substs);
41-
let param_env = param_env.subst(infcx.tcx, substs);
22+
self.cx.with_all_traits(|cx, all_traits| {
23+
for &trait_def_id in all_traits {
24+
if !cx.cache.access_levels.is_public(trait_def_id)
25+
|| cx.generated_synthetics.get(&(ty, trait_def_id)).is_some()
26+
{
27+
continue;
28+
}
29+
// NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls
30+
let trait_impls = cx.tcx.trait_impls_of(trait_def_id);
31+
for &impl_def_id in trait_impls.blanket_impls() {
32+
trace!(
33+
"get_blanket_impls: Considering impl for trait '{:?}' {:?}",
34+
trait_def_id,
35+
impl_def_id
36+
);
37+
let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap();
38+
let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_));
39+
let may_apply = is_param && cx.tcx.infer_ctxt().enter(|infcx| {
40+
let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id);
41+
let ty = ty.subst(infcx.tcx, substs);
42+
let param_env = param_env.subst(infcx.tcx, substs);
4243

43-
let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
44-
let trait_ref = trait_ref.subst(infcx.tcx, impl_substs);
44+
let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
45+
let trait_ref = trait_ref.subst(infcx.tcx, impl_substs);
4546

46-
// Require the type the impl is implemented on to match
47-
// our type, and ignore the impl if there was a mismatch.
48-
let cause = traits::ObligationCause::dummy();
49-
let eq_result = infcx.at(&cause, param_env).eq(trait_ref.self_ty(), ty);
50-
if let Ok(InferOk { value: (), obligations }) = eq_result {
51-
// FIXME(eddyb) ignoring `obligations` might cause false positives.
52-
drop(obligations);
47+
// Require the type the impl is implemented on to match
48+
// our type, and ignore the impl if there was a mismatch.
49+
let cause = traits::ObligationCause::dummy();
50+
let eq_result = infcx.at(&cause, param_env).eq(trait_ref.self_ty(), ty);
51+
if let Ok(InferOk { value: (), obligations }) = eq_result {
52+
// FIXME(eddyb) ignoring `obligations` might cause false positives.
53+
drop(obligations);
5354

54-
trace!(
55-
"invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}",
56-
param_env,
57-
trait_ref,
58-
ty
59-
);
60-
let predicates = self
61-
.cx
62-
.tcx
63-
.predicates_of(impl_def_id)
64-
.instantiate(self.cx.tcx, impl_substs)
65-
.predicates
66-
.into_iter()
67-
.chain(Some(
68-
ty::Binder::dummy(trait_ref)
69-
.to_poly_trait_predicate()
70-
.map_bound(ty::PredicateKind::Trait)
71-
.to_predicate(infcx.tcx),
72-
));
73-
for predicate in predicates {
74-
debug!("testing predicate {:?}", predicate);
75-
let obligation = traits::Obligation::new(
76-
traits::ObligationCause::dummy(),
55+
trace!(
56+
"invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}",
7757
param_env,
78-
predicate,
58+
trait_ref,
59+
ty
7960
);
80-
match infcx.evaluate_obligation(&obligation) {
81-
Ok(eval_result) if eval_result.may_apply() => {}
82-
Err(traits::OverflowError::Canonical) => {}
83-
Err(traits::OverflowError::ErrorReporting) => {}
84-
_ => {
85-
return false;
61+
let predicates = cx
62+
.tcx
63+
.predicates_of(impl_def_id)
64+
.instantiate(cx.tcx, impl_substs)
65+
.predicates
66+
.into_iter()
67+
.chain(Some(
68+
ty::Binder::dummy(trait_ref)
69+
.to_poly_trait_predicate()
70+
.map_bound(ty::PredicateKind::Trait)
71+
.to_predicate(infcx.tcx),
72+
));
73+
for predicate in predicates {
74+
debug!("testing predicate {:?}", predicate);
75+
let obligation = traits::Obligation::new(
76+
traits::ObligationCause::dummy(),
77+
param_env,
78+
predicate,
79+
);
80+
match infcx.evaluate_obligation(&obligation) {
81+
Ok(eval_result) if eval_result.may_apply() => {}
82+
Err(traits::OverflowError::Canonical) => {}
83+
Err(traits::OverflowError::ErrorReporting) => {}
84+
_ => {
85+
return false;
86+
}
8687
}
8788
}
89+
true
90+
} else {
91+
false
8892
}
89-
true
90-
} else {
91-
false
93+
});
94+
debug!(
95+
"get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}",
96+
may_apply, trait_ref, ty
97+
);
98+
if !may_apply {
99+
continue;
92100
}
93-
});
94-
debug!(
95-
"get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}",
96-
may_apply, trait_ref, ty
97-
);
98-
if !may_apply {
99-
continue;
100-
}
101101

102-
self.cx.generated_synthetics.insert((ty, trait_def_id));
102+
cx.generated_synthetics.insert((ty, trait_def_id));
103103

104-
impls.push(Item {
105-
name: None,
106-
attrs: Default::default(),
107-
visibility: Inherited,
108-
def_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
109-
kind: box ImplItem(Impl {
110-
unsafety: hir::Unsafety::Normal,
111-
generics: clean_ty_generics(
112-
self.cx,
113-
self.cx.tcx.generics_of(impl_def_id),
114-
self.cx.tcx.explicit_predicates_of(impl_def_id),
115-
),
116-
// FIXME(eddyb) compute both `trait_` and `for_` from
117-
// the post-inference `trait_ref`, as it's more accurate.
118-
trait_: Some(trait_ref.clean(self.cx)),
119-
for_: ty.clean(self.cx),
120-
items: self
121-
.cx
122-
.tcx
123-
.associated_items(impl_def_id)
124-
.in_definition_order()
125-
.map(|x| x.clean(self.cx))
126-
.collect::<Vec<_>>(),
127-
polarity: ty::ImplPolarity::Positive,
128-
kind: ImplKind::Blanket(box trait_ref.self_ty().clean(self.cx)),
129-
}),
130-
cfg: None,
131-
});
104+
impls.push(Item {
105+
name: None,
106+
attrs: Default::default(),
107+
visibility: Inherited,
108+
def_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
109+
kind: box ImplItem(Impl {
110+
unsafety: hir::Unsafety::Normal,
111+
generics: clean_ty_generics(
112+
cx,
113+
cx.tcx.generics_of(impl_def_id),
114+
cx.tcx.explicit_predicates_of(impl_def_id),
115+
),
116+
// FIXME(eddyb) compute both `trait_` and `for_` from
117+
// the post-inference `trait_ref`, as it's more accurate.
118+
trait_: Some(trait_ref.clean(cx)),
119+
for_: ty.clean(cx),
120+
items: cx
121+
.tcx
122+
.associated_items(impl_def_id)
123+
.in_definition_order()
124+
.map(|x| x.clean(cx))
125+
.collect::<Vec<_>>(),
126+
polarity: ty::ImplPolarity::Positive,
127+
kind: ImplKind::Blanket(box trait_ref.self_ty().clean(cx)),
128+
}),
129+
cfg: None,
130+
});
131+
}
132132
}
133-
}
133+
});
134+
134135
impls
135136
}
136137
}

‎src/librustdoc/clean/inline.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ crate fn build_impls(
291291
attrs: Option<Attrs<'_>>,
292292
ret: &mut Vec<clean::Item>,
293293
) {
294+
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls");
294295
let tcx = cx.tcx;
295296

296297
// for each implementation of an item represented by `did`, build the clean::Item for that impl
@@ -338,7 +339,7 @@ crate fn build_impl(
338339
return;
339340
}
340341

341-
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impl");
342+
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_impl");
342343

343344
let tcx = cx.tcx;
344345
let associated_trait = tcx.impl_trait_ref(did);

‎src/librustdoc/clean/utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ crate fn build_deref_target_impls(cx: &mut DocContext<'_>, items: &[Item], ret:
179179
};
180180

181181
if let Some(prim) = target.primitive_type() {
182+
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_primitive_inherent_impls");
182183
for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) {
183184
inline::build_impl(cx, None, did, None, ret);
184185
}

‎src/librustdoc/core.rs

Lines changed: 34 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,23 @@
11
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
22
use rustc_data_structures::sync::{self, Lrc};
3-
use rustc_driver::abort_on_err;
43
use rustc_errors::emitter::{Emitter, EmitterWriter};
54
use rustc_errors::json::JsonEmitter;
65
use rustc_feature::UnstableFeatures;
76
use rustc_hir::def::Res;
87
use rustc_hir::def_id::{DefId, LocalDefId};
9-
use rustc_hir::HirId;
10-
use rustc_hir::{
11-
intravisit::{self, NestedVisitorMap, Visitor},
12-
Path,
13-
};
14-
use rustc_interface::{interface, Queries};
8+
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
9+
use rustc_hir::{HirId, Path};
10+
use rustc_interface::interface;
1511
use rustc_middle::hir::map::Map;
1612
use rustc_middle::middle::privacy::AccessLevels;
1713
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
1814
use rustc_resolve as resolve;
19-
use rustc_resolve::Namespace::TypeNS;
2015
use rustc_session::config::{self, CrateType, ErrorOutputType};
2116
use rustc_session::lint;
2217
use rustc_session::DiagnosticOutput;
2318
use rustc_session::Session;
24-
use rustc_span::def_id::CRATE_DEF_INDEX;
25-
use rustc_span::source_map;
2619
use rustc_span::symbol::sym;
27-
use rustc_span::{Span, DUMMY_SP};
20+
use rustc_span::{source_map, Span};
2821

2922
use std::cell::RefCell;
3023
use std::lazy::SyncLazy;
@@ -39,14 +32,20 @@ use crate::passes::{self, Condition::*};
3932

4033
crate use rustc_session::config::{DebuggingOptions, Input, Options};
4134

35+
crate struct ResolverCaches {
36+
pub all_traits: Option<Vec<DefId>>,
37+
pub all_trait_impls: Option<Vec<DefId>>,
38+
}
39+
4240
crate struct DocContext<'tcx> {
4341
crate tcx: TyCtxt<'tcx>,
4442
/// Name resolver. Used for intra-doc links.
4543
///
4644
/// The `Rc<RefCell<...>>` wrapping is needed because that is what's returned by
47-
/// [`Queries::expansion()`].
45+
/// [`rustc_interface::Queries::expansion()`].
4846
// FIXME: see if we can get rid of this RefCell somehow
4947
crate resolver: Rc<RefCell<interface::BoxedResolver>>,
48+
crate resolver_caches: ResolverCaches,
5049
/// Used for normalization.
5150
///
5251
/// Most of this logic is copied from rustc_lint::late.
@@ -123,6 +122,18 @@ impl<'tcx> DocContext<'tcx> {
123122
_ => None,
124123
}
125124
}
125+
126+
crate fn with_all_traits(&mut self, f: impl FnOnce(&mut Self, &[DefId])) {
127+
let all_traits = self.resolver_caches.all_traits.take();
128+
f(self, all_traits.as_ref().expect("`all_traits` are already borrowed"));
129+
self.resolver_caches.all_traits = all_traits;
130+
}
131+
132+
crate fn with_all_trait_impls(&mut self, f: impl FnOnce(&mut Self, &[DefId])) {
133+
let all_trait_impls = self.resolver_caches.all_trait_impls.take();
134+
f(self, all_trait_impls.as_ref().expect("`all_trait_impls` are already borrowed"));
135+
self.resolver_caches.all_trait_impls = all_trait_impls;
136+
}
126137
}
127138

128139
/// Creates a new diagnostic `Handler` that can be used to emit warnings and errors.
@@ -284,49 +295,10 @@ crate fn create_config(
284295
}
285296
}
286297

287-
crate fn create_resolver<'a>(
288-
externs: config::Externs,
289-
queries: &Queries<'a>,
290-
sess: &Session,
291-
) -> Rc<RefCell<interface::BoxedResolver>> {
292-
let (krate, resolver, _) = &*abort_on_err(queries.expansion(), sess).peek();
293-
let resolver = resolver.clone();
294-
295-
let resolver = crate::passes::collect_intra_doc_links::load_intra_link_crates(resolver, krate);
296-
297-
// FIXME: somehow rustdoc is still missing crates even though we loaded all
298-
// the known necessary crates. Load them all unconditionally until we find a way to fix this.
299-
// DO NOT REMOVE THIS without first testing on the reproducer in
300-
// https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
301-
let extern_names: Vec<String> = externs
302-
.iter()
303-
.filter(|(_, entry)| entry.add_prelude)
304-
.map(|(name, _)| name)
305-
.cloned()
306-
.collect();
307-
resolver.borrow_mut().access(|resolver| {
308-
sess.time("load_extern_crates", || {
309-
for extern_name in &extern_names {
310-
debug!("loading extern crate {}", extern_name);
311-
if let Err(()) = resolver
312-
.resolve_str_path_error(
313-
DUMMY_SP,
314-
extern_name,
315-
TypeNS,
316-
LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(),
317-
) {
318-
warn!("unable to resolve external crate {} (do you have an unused `--extern` crate?)", extern_name)
319-
}
320-
}
321-
});
322-
});
323-
324-
resolver
325-
}
326-
327298
crate fn run_global_ctxt(
328299
tcx: TyCtxt<'_>,
329300
resolver: Rc<RefCell<interface::BoxedResolver>>,
301+
resolver_caches: ResolverCaches,
330302
show_coverage: bool,
331303
render_options: RenderOptions,
332304
output_format: OutputFormat,
@@ -355,23 +327,29 @@ crate fn run_global_ctxt(
355327
});
356328
rustc_passes::stability::check_unused_or_stable_features(tcx);
357329

330+
let auto_traits = resolver_caches
331+
.all_traits
332+
.as_ref()
333+
.expect("`all_traits` are already borrowed")
334+
.iter()
335+
.copied()
336+
.filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id))
337+
.collect();
358338
let access_levels = AccessLevels {
359339
map: tcx.privacy_access_levels(()).map.iter().map(|(k, v)| (k.to_def_id(), *v)).collect(),
360340
};
361341

362342
let mut ctxt = DocContext {
363343
tcx,
364344
resolver,
345+
resolver_caches,
365346
param_env: ParamEnv::empty(),
366347
external_traits: Default::default(),
367348
active_extern_traits: Default::default(),
368349
substs: Default::default(),
369350
impl_trait_bounds: Default::default(),
370351
generated_synthetics: Default::default(),
371-
auto_traits: tcx
372-
.all_traits()
373-
.filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id))
374-
.collect(),
352+
auto_traits,
375353
module_trait_cache: FxHashMap::default(),
376354
cache: Cache::new(access_levels, render_options.document_private),
377355
inlined: FxHashSet::default(),

‎src/librustdoc/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ use rustc_session::getopts;
8282
use rustc_session::{early_error, early_warn};
8383

8484
use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL;
85+
use crate::passes::collect_intra_doc_links;
8586

8687
/// A macro to create a FxHashMap.
8788
///
@@ -798,7 +799,15 @@ fn main_options(options: config::Options) -> MainResult {
798799
// We need to hold on to the complete resolver, so we cause everything to be
799800
// cloned for the analysis passes to use. Suboptimal, but necessary in the
800801
// current architecture.
801-
let resolver = core::create_resolver(externs, queries, sess);
802+
// FIXME(#83761): Resolver cloning can lead to inconsistencies between data in the
803+
// two copies because one of the copies can be modified after `TyCtxt` construction.
804+
let (resolver, resolver_caches) = {
805+
let (krate, resolver, _) = &*abort_on_err(queries.expansion(), sess).peek();
806+
let resolver_caches = resolver.borrow_mut().access(|resolver| {
807+
collect_intra_doc_links::early_resolve_intra_doc_links(resolver, krate, externs)
808+
});
809+
(resolver.clone(), resolver_caches)
810+
};
802811

803812
if sess.diagnostic().has_errors_or_lint_errors() {
804813
sess.fatal("Compilation failed, aborting rustdoc");
@@ -811,6 +820,7 @@ fn main_options(options: config::Options) -> MainResult {
811820
core::run_global_ctxt(
812821
tcx,
813822
resolver,
823+
resolver_caches,
814824
show_coverage,
815825
render_options,
816826
output_format,

‎src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use crate::passes::Pass;
3838
use crate::visit::DocVisitor;
3939

4040
mod early;
41-
crate use early::load_intra_link_crates;
41+
crate use early::early_resolve_intra_doc_links;
4242

4343
crate const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
4444
name: "collect-intra-doc-links",
Lines changed: 91 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,136 @@
1-
use ast::visit;
2-
use rustc_ast as ast;
1+
use crate::clean;
2+
use crate::core::ResolverCaches;
3+
use crate::html::markdown::markdown_links;
4+
use crate::passes::collect_intra_doc_links::preprocess_link;
5+
6+
use rustc_ast::visit::{self, AssocCtxt, Visitor};
7+
use rustc_ast::{self as ast, ItemKind};
8+
use rustc_ast_lowering::ResolverAstLowering;
39
use rustc_hir::def::Namespace::TypeNS;
4-
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
5-
use rustc_interface::interface;
6-
use rustc_span::Span;
10+
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
11+
use rustc_resolve::Resolver;
12+
use rustc_session::config::Externs;
13+
use rustc_span::{Span, DUMMY_SP};
714

8-
use std::cell::RefCell;
915
use std::mem;
10-
use std::rc::Rc;
11-
12-
type Resolver = Rc<RefCell<interface::BoxedResolver>>;
13-
// Letting the resolver escape at the end of the function leads to inconsistencies between the
14-
// crates the TyCtxt sees and the resolver sees (because the resolver could load more crates
15-
// after escaping). Hopefully `IntraLinkCrateLoader` gets all the crates we need ...
16-
crate fn load_intra_link_crates(resolver: Resolver, krate: &ast::Crate) -> Resolver {
17-
let mut loader = IntraLinkCrateLoader { current_mod: CRATE_DEF_ID, resolver };
18-
// `walk_crate` doesn't visit the crate itself for some reason.
16+
17+
crate fn early_resolve_intra_doc_links(
18+
resolver: &mut Resolver<'_>,
19+
krate: &ast::Crate,
20+
externs: Externs,
21+
) -> ResolverCaches {
22+
let mut loader = IntraLinkCrateLoader {
23+
resolver,
24+
current_mod: CRATE_DEF_ID,
25+
all_traits: Default::default(),
26+
all_trait_impls: Default::default(),
27+
};
28+
29+
// Overridden `visit_item` below doesn't apply to the crate root,
30+
// so we have to visit its attributes and exports separately.
1931
loader.load_links_in_attrs(&krate.attrs, krate.span);
2032
visit::walk_crate(&mut loader, krate);
21-
loader.resolver
33+
loader.fill_resolver_caches();
34+
35+
// FIXME: somehow rustdoc is still missing crates even though we loaded all
36+
// the known necessary crates. Load them all unconditionally until we find a way to fix this.
37+
// DO NOT REMOVE THIS without first testing on the reproducer in
38+
// https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
39+
for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
40+
let _ = loader.resolver.resolve_str_path_error(
41+
DUMMY_SP,
42+
extern_name,
43+
TypeNS,
44+
CRATE_DEF_ID.to_def_id(),
45+
);
46+
}
47+
48+
ResolverCaches {
49+
all_traits: Some(loader.all_traits),
50+
all_trait_impls: Some(loader.all_trait_impls),
51+
}
2252
}
2353

24-
struct IntraLinkCrateLoader {
54+
struct IntraLinkCrateLoader<'r, 'ra> {
55+
resolver: &'r mut Resolver<'ra>,
2556
current_mod: LocalDefId,
26-
resolver: Rc<RefCell<interface::BoxedResolver>>,
57+
all_traits: Vec<DefId>,
58+
all_trait_impls: Vec<DefId>,
2759
}
2860

29-
impl IntraLinkCrateLoader {
30-
fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) {
31-
use crate::html::markdown::markdown_links;
32-
use crate::passes::collect_intra_doc_links::preprocess_link;
61+
impl IntraLinkCrateLoader<'_, '_> {
62+
fn fill_resolver_caches(&mut self) {
63+
for cnum in self.resolver.cstore().crates_untracked() {
64+
let all_traits = self.resolver.cstore().traits_in_crate_untracked(cnum);
65+
let all_trait_impls = self.resolver.cstore().trait_impls_in_crate_untracked(cnum);
3366

34-
// FIXME: this probably needs to consider inlining
35-
let attrs = crate::clean::Attributes::from_ast(attrs, None);
67+
self.all_traits.extend(all_traits);
68+
self.all_trait_impls.extend(all_trait_impls.into_iter().map(|(def_id, _)| def_id));
69+
}
70+
}
71+
72+
fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) {
73+
// FIXME: this needs to consider export inlining.
74+
let attrs = clean::Attributes::from_ast(attrs, None);
3675
for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() {
37-
debug!(?doc);
38-
for link in markdown_links(doc.as_str()) {
39-
debug!(?link.link);
76+
let module_id = parent_module.unwrap_or(self.current_mod.to_def_id());
77+
78+
for link in markdown_links(&doc.as_str()) {
4079
let path_str = if let Some(Ok(x)) = preprocess_link(&link) {
4180
x.path_str
4281
} else {
4382
continue;
4483
};
45-
self.resolver.borrow_mut().access(|resolver| {
46-
let _ = resolver.resolve_str_path_error(
47-
span,
48-
&path_str,
49-
TypeNS,
50-
parent_module.unwrap_or_else(|| self.current_mod.to_def_id()),
51-
);
52-
});
84+
let _ = self.resolver.resolve_str_path_error(span, &path_str, TypeNS, module_id);
5385
}
5486
}
5587
}
5688
}
5789

58-
impl visit::Visitor<'_> for IntraLinkCrateLoader {
59-
fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
60-
self.load_links_in_attrs(&item.attrs, item.span);
61-
visit::walk_foreign_item(self, item)
62-
}
63-
90+
impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> {
6491
fn visit_item(&mut self, item: &ast::Item) {
65-
use rustc_ast_lowering::ResolverAstLowering;
66-
67-
if let ast::ItemKind::Mod(..) = item.kind {
68-
let new_mod =
69-
self.resolver.borrow_mut().access(|resolver| resolver.local_def_id(item.id));
70-
let old_mod = mem::replace(&mut self.current_mod, new_mod);
92+
if let ItemKind::Mod(..) = item.kind {
93+
let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id));
7194

7295
self.load_links_in_attrs(&item.attrs, item.span);
7396
visit::walk_item(self, item);
7497

7598
self.current_mod = old_mod;
7699
} else {
100+
match item.kind {
101+
ItemKind::Trait(..) => {
102+
self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
103+
}
104+
ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
105+
self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
106+
}
107+
_ => {}
108+
}
77109
self.load_links_in_attrs(&item.attrs, item.span);
78110
visit::walk_item(self, item);
79111
}
80112
}
81113

82-
// NOTE: if doc-comments are ever allowed on function parameters, this will have to implement `visit_param` too.
83-
84-
fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: visit::AssocCtxt) {
114+
fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
85115
self.load_links_in_attrs(&item.attrs, item.span);
86116
visit::walk_assoc_item(self, item, ctxt)
87117
}
88118

89-
fn visit_field_def(&mut self, field: &ast::FieldDef) {
90-
self.load_links_in_attrs(&field.attrs, field.span);
91-
visit::walk_field_def(self, field)
119+
fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
120+
self.load_links_in_attrs(&item.attrs, item.span);
121+
visit::walk_foreign_item(self, item)
92122
}
93123

94124
fn visit_variant(&mut self, v: &ast::Variant) {
95125
self.load_links_in_attrs(&v.attrs, v.span);
96126
visit::walk_variant(self, v)
97127
}
128+
129+
fn visit_field_def(&mut self, field: &ast::FieldDef) {
130+
self.load_links_in_attrs(&field.attrs, field.span);
131+
visit::walk_field_def(self, field)
132+
}
133+
134+
// NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
135+
// then this will have to implement other visitor methods too.
98136
}

‎src/librustdoc/passes/collect_trait_impls.rs

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -34,26 +34,28 @@ crate fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate
3434

3535
let mut new_items = Vec::new();
3636

37-
for &cnum in cx.tcx.crates(()).iter() {
38-
for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() {
39-
inline::build_impl(cx, None, did, None, &mut new_items);
37+
// External trait impls.
38+
cx.with_all_trait_impls(|cx, all_trait_impls| {
39+
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impls");
40+
for &impl_def_id in all_trait_impls.iter().skip_while(|def_id| def_id.is_local()) {
41+
inline::build_impl(cx, None, impl_def_id, None, &mut new_items);
4042
}
41-
}
43+
});
4244

4345
// Also try to inline primitive impls from other crates.
44-
for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() {
45-
if !def_id.is_local() {
46-
cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
46+
cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
47+
for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() {
48+
if !def_id.is_local() {
4749
inline::build_impl(cx, None, def_id, None, &mut new_items);
4850

4951
// FIXME(eddyb) is this `doc(hidden)` check needed?
5052
if !cx.tcx.is_doc_hidden(def_id) {
5153
let impls = get_auto_trait_and_blanket_impls(cx, def_id);
5254
new_items.extend(impls.filter(|i| cx.inlined.insert(i.def_id)));
5355
}
54-
});
56+
}
5557
}
56-
}
58+
});
5759

5860
let mut cleaner = BadImplStripper { prims, items: crate_items };
5961
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
126128
}
127129
});
128130

129-
// `tcx.crates(())` doesn't include the local crate, and `tcx.all_trait_implementations`
130-
// doesn't work with it anyway, so pull them from the HIR map instead
131-
let mut extra_attrs = Vec::new();
132-
for trait_did in cx.tcx.all_traits() {
133-
for &impl_did in cx.tcx.hir().trait_impls(trait_did) {
134-
let impl_did = impl_did.to_def_id();
135-
cx.tcx.sess.prof.generic_activity("build_local_trait_impl").run(|| {
136-
let mut parent = cx.tcx.parent(impl_did);
137-
while let Some(did) = parent {
138-
extra_attrs.extend(
139-
cx.tcx
140-
.get_attrs(did)
141-
.iter()
142-
.filter(|attr| attr.has_name(sym::doc))
143-
.filter(|attr| {
144-
if let Some([attr]) = attr.meta_item_list().as_deref() {
145-
attr.has_name(sym::cfg)
146-
} else {
147-
false
148-
}
149-
})
150-
.cloned(),
151-
);
152-
parent = cx.tcx.parent(did);
153-
}
154-
inline::build_impl(cx, None, impl_did, Some(&extra_attrs), &mut new_items);
155-
extra_attrs.clear();
156-
});
131+
// Local trait impls.
132+
cx.with_all_trait_impls(|cx, all_trait_impls| {
133+
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls");
134+
let mut attr_buf = Vec::new();
135+
for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) {
136+
let mut parent = cx.tcx.parent(impl_def_id);
137+
while let Some(did) = parent {
138+
attr_buf.extend(
139+
cx.tcx
140+
.get_attrs(did)
141+
.iter()
142+
.filter(|attr| attr.has_name(sym::doc))
143+
.filter(|attr| {
144+
if let Some([attr]) = attr.meta_item_list().as_deref() {
145+
attr.has_name(sym::cfg)
146+
} else {
147+
false
148+
}
149+
})
150+
.cloned(),
151+
);
152+
parent = cx.tcx.parent(did);
153+
}
154+
inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items);
155+
attr_buf.clear();
157156
}
158-
}
157+
});
159158

160159
if let ModuleItem(Module { items, .. }) = &mut *krate.module.kind {
161160
items.extend(synth_impls);

0 commit comments

Comments
 (0)
Please sign in to comment.