Skip to content

Commit 5b45024

Browse files
committed
Auto merge of #94857 - petrochenkov:doclink2, r=oli-obk
Resolve documentation links in rustc and store the results in metadata This PR implements MCP rust-lang/compiler-team#584. Doc links are now resolved in rustc and stored into metadata, so rustdoc simply retrieves them through a query (local or extern), Code that is no longer used is removed, and some code that no longer needs to be public is privatized. The removed code includes resolver cloning, so this PR fixes #83761.
2 parents 71f6675 + da4ce6b commit 5b45024

File tree

30 files changed

+764
-873
lines changed

30 files changed

+764
-873
lines changed

Cargo.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -4615,6 +4615,7 @@ name = "rustc_resolve"
46154615
version = "0.0.0"
46164616
dependencies = [
46174617
"bitflags",
4618+
"pulldown-cmark 0.9.2",
46184619
"rustc_arena",
46194620
"rustc_ast",
46204621
"rustc_ast_pretty",
@@ -4881,7 +4882,6 @@ dependencies = [
48814882
"itertools",
48824883
"minifier",
48834884
"once_cell",
4884-
"pulldown-cmark 0.9.2",
48854885
"rayon",
48864886
"regex",
48874887
"rustdoc-json-types",

compiler/rustc_data_structures/src/stable_hasher.rs

+8
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,14 @@ impl<HCX> ToStableHashKey<HCX> for String {
486486
}
487487
}
488488

489+
impl<HCX, T1: ToStableHashKey<HCX>, T2: ToStableHashKey<HCX>> ToStableHashKey<HCX> for (T1, T2) {
490+
type KeyType = (T1::KeyType, T2::KeyType);
491+
#[inline]
492+
fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType {
493+
(self.0.to_stable_hash_key(hcx), self.1.to_stable_hash_key(hcx))
494+
}
495+
}
496+
489497
impl<CTX> HashStable<CTX> for bool {
490498
#[inline]
491499
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {

compiler/rustc_hir/src/def.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use crate::hir;
22

33
use rustc_ast as ast;
44
use rustc_ast::NodeId;
5+
use rustc_data_structures::fx::FxHashMap;
6+
use rustc_data_structures::stable_hasher::ToStableHashKey;
57
use rustc_macros::HashStable_Generic;
68
use rustc_span::def_id::{DefId, LocalDefId};
79
use rustc_span::hygiene::MacroKind;
@@ -472,7 +474,8 @@ impl PartialRes {
472474

473475
/// Different kinds of symbols can coexist even if they share the same textual name.
474476
/// Therefore, they each have a separate universe (known as a "namespace").
475-
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
477+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)]
478+
#[derive(HashStable_Generic)]
476479
pub enum Namespace {
477480
/// The type namespace includes `struct`s, `enum`s, `union`s, `trait`s, and `mod`s
478481
/// (and, by extension, crates).
@@ -499,6 +502,15 @@ impl Namespace {
499502
}
500503
}
501504

505+
impl<CTX: crate::HashStableContext> ToStableHashKey<CTX> for Namespace {
506+
type KeyType = Namespace;
507+
508+
#[inline]
509+
fn to_stable_hash_key(&self, _: &CTX) -> Namespace {
510+
*self
511+
}
512+
}
513+
502514
/// Just a helper ‒ separate structure for each namespace.
503515
#[derive(Copy, Clone, Default, Debug)]
504516
pub struct PerNS<T> {
@@ -760,3 +772,5 @@ pub enum LifetimeRes {
760772
/// HACK: This is used to recover the NodeId of an elided lifetime.
761773
ElidedAnchor { start: NodeId, end: NodeId },
762774
}
775+
776+
pub type DocLinkResMap = FxHashMap<(Symbol, Namespace), Option<Res<NodeId>>>;

compiler/rustc_metadata/src/rmeta/decoder.rs

+19-22
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_data_structures::sync::{Lock, LockGuard, Lrc, OnceCell};
1111
use rustc_data_structures::unhash::UnhashMap;
1212
use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
1313
use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro};
14-
use rustc_hir::def::{CtorKind, DefKind, Res};
14+
use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap, Res};
1515
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE};
1616
use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash};
1717
use rustc_hir::diagnostic_items::DiagnosticItems;
@@ -1163,20 +1163,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
11631163
)
11641164
}
11651165

1166-
/// Decodes all inherent impls in the crate (for rustdoc).
1167-
fn get_inherent_impls(self) -> impl Iterator<Item = (DefId, DefId)> + 'a {
1168-
(0..self.root.tables.inherent_impls.size()).flat_map(move |i| {
1169-
let ty_index = DefIndex::from_usize(i);
1170-
let ty_def_id = self.local_def_id(ty_index);
1171-
self.root
1172-
.tables
1173-
.inherent_impls
1174-
.get(self, ty_index)
1175-
.decode(self)
1176-
.map(move |impl_index| (ty_def_id, self.local_def_id(impl_index)))
1177-
})
1178-
}
1179-
11801166
/// Decodes all traits in the crate (for rustdoc and rustc diagnostics).
11811167
fn get_traits(self) -> impl Iterator<Item = DefId> + 'a {
11821168
self.root.traits.decode(self).map(move |index| self.local_def_id(index))
@@ -1195,13 +1181,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
11951181
})
11961182
}
11971183

1198-
fn get_all_incoherent_impls(self) -> impl Iterator<Item = DefId> + 'a {
1199-
self.cdata
1200-
.incoherent_impls
1201-
.values()
1202-
.flat_map(move |impls| impls.decode(self).map(move |idx| self.local_def_id(idx)))
1203-
}
1204-
12051184
fn get_incoherent_impls(self, tcx: TyCtxt<'tcx>, simp: SimplifiedType) -> &'tcx [DefId] {
12061185
if let Some(impls) = self.cdata.incoherent_impls.get(&simp) {
12071186
tcx.arena.alloc_from_iter(impls.decode(self).map(|idx| self.local_def_id(idx)))
@@ -1598,6 +1577,24 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
15981577
fn get_is_intrinsic(self, index: DefIndex) -> bool {
15991578
self.root.tables.is_intrinsic.get(self, index)
16001579
}
1580+
1581+
fn get_doc_link_resolutions(self, index: DefIndex) -> DocLinkResMap {
1582+
self.root
1583+
.tables
1584+
.doc_link_resolutions
1585+
.get(self, index)
1586+
.expect("no resolutions for a doc link")
1587+
.decode(self)
1588+
}
1589+
1590+
fn get_doc_link_traits_in_scope(self, index: DefIndex) -> impl Iterator<Item = DefId> + 'a {
1591+
self.root
1592+
.tables
1593+
.doc_link_traits_in_scope
1594+
.get(self, index)
1595+
.expect("no traits in scope for a doc link")
1596+
.decode(self)
1597+
}
16011598
}
16021599

16031600
impl CrateMetadata {

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

+4-30
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,10 @@ provide! { tcx, def_id, other, cdata,
345345
expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) }
346346
generator_diagnostic_data => { cdata.get_generator_diagnostic_data(tcx, def_id.index) }
347347
is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) }
348+
doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(def_id.index)) }
349+
doc_link_traits_in_scope => {
350+
tcx.arena.alloc_from_iter(cdata.get_doc_link_traits_in_scope(def_id.index))
351+
}
348352
}
349353

350354
pub(in crate::rmeta) fn provide(providers: &mut Providers) {
@@ -613,36 +617,6 @@ impl CStore {
613617
self.get_crate_data(cnum).get_trait_impls()
614618
}
615619

616-
/// Decodes all inherent impls in the crate (for rustdoc).
617-
pub fn inherent_impls_in_crate_untracked(
618-
&self,
619-
cnum: CrateNum,
620-
) -> impl Iterator<Item = (DefId, DefId)> + '_ {
621-
self.get_crate_data(cnum).get_inherent_impls()
622-
}
623-
624-
/// Decodes all incoherent inherent impls in the crate (for rustdoc).
625-
pub fn incoherent_impls_in_crate_untracked(
626-
&self,
627-
cnum: CrateNum,
628-
) -> impl Iterator<Item = DefId> + '_ {
629-
self.get_crate_data(cnum).get_all_incoherent_impls()
630-
}
631-
632-
pub fn associated_item_def_ids_untracked<'a>(
633-
&'a self,
634-
def_id: DefId,
635-
sess: &'a Session,
636-
) -> impl Iterator<Item = DefId> + 'a {
637-
self.get_crate_data(def_id.krate).get_associated_item_def_ids(def_id.index, sess)
638-
}
639-
640-
pub fn may_have_doc_links_untracked(&self, def_id: DefId) -> bool {
641-
self.get_crate_data(def_id.krate)
642-
.get_attr_flags(def_id.index)
643-
.contains(AttrFlags::MAY_HAVE_DOC_LINKS)
644-
}
645-
646620
pub fn is_doc_hidden_untracked(&self, def_id: DefId) -> bool {
647621
self.get_crate_data(def_id.krate)
648622
.get_attr_flags(def_id.index)

compiler/rustc_metadata/src/rmeta/encoder.rs

+27-10
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::rmeta::def_path_hash_map::DefPathHashMapRef;
33
use crate::rmeta::table::TableBuilder;
44
use crate::rmeta::*;
55

6-
use rustc_ast::util::comments;
76
use rustc_ast::Attribute;
87
use rustc_data_structures::fingerprint::Fingerprint;
98
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
@@ -772,7 +771,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
772771

773772
struct AnalyzeAttrState {
774773
is_exported: bool,
775-
may_have_doc_links: bool,
776774
is_doc_hidden: bool,
777775
}
778776

@@ -790,15 +788,12 @@ fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool {
790788
let mut should_encode = false;
791789
if rustc_feature::is_builtin_only_local(attr.name_or_empty()) {
792790
// Attributes marked local-only don't need to be encoded for downstream crates.
793-
} else if let Some(s) = attr.doc_str() {
791+
} else if attr.doc_str().is_some() {
794792
// We keep all doc comments reachable to rustdoc because they might be "imported" into
795793
// downstream crates if they use `#[doc(inline)]` to copy an item's documentation into
796794
// their own.
797795
if state.is_exported {
798796
should_encode = true;
799-
if comments::may_have_doc_links(s.as_str()) {
800-
state.may_have_doc_links = true;
801-
}
802797
}
803798
} else if attr.has_name(sym::doc) {
804799
// If this is a `doc` attribute that doesn't have anything except maybe `inline` (as in
@@ -1139,7 +1134,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
11391134
let tcx = self.tcx;
11401135
let mut state = AnalyzeAttrState {
11411136
is_exported: tcx.effective_visibilities(()).is_exported(def_id),
1142-
may_have_doc_links: false,
11431137
is_doc_hidden: false,
11441138
};
11451139
let attr_iter = tcx
@@ -1151,9 +1145,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
11511145
record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter);
11521146

11531147
let mut attr_flags = AttrFlags::empty();
1154-
if state.may_have_doc_links {
1155-
attr_flags |= AttrFlags::MAY_HAVE_DOC_LINKS;
1156-
}
11571148
if state.is_doc_hidden {
11581149
attr_flags |= AttrFlags::IS_DOC_HIDDEN;
11591150
}
@@ -1231,6 +1222,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
12311222
def_id.index
12321223
}));
12331224
}
1225+
1226+
for (def_id, res_map) in &tcx.resolutions(()).doc_link_resolutions {
1227+
record!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map);
1228+
}
1229+
1230+
for (def_id, traits) in &tcx.resolutions(()).doc_link_traits_in_scope {
1231+
record_array!(self.tables.doc_link_traits_in_scope[def_id.to_def_id()] <- traits);
1232+
}
12341233
}
12351234

12361235
#[instrument(level = "trace", skip(self))]
@@ -1715,6 +1714,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
17151714
record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability);
17161715
}
17171716
self.encode_deprecation(LOCAL_CRATE.as_def_id());
1717+
if let Some(res_map) = tcx.resolutions(()).doc_link_resolutions.get(&CRATE_DEF_ID) {
1718+
record!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map);
1719+
}
1720+
if let Some(traits) = tcx.resolutions(()).doc_link_traits_in_scope.get(&CRATE_DEF_ID) {
1721+
record_array!(self.tables.doc_link_traits_in_scope[LOCAL_CRATE.as_def_id()] <- traits);
1722+
}
17181723

17191724
// Normally, this information is encoded when we walk the items
17201725
// defined in this crate. However, we skip doing that for proc-macro crates,
@@ -2225,6 +2230,18 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
22252230

22262231
pub fn provide(providers: &mut Providers) {
22272232
*providers = Providers {
2233+
doc_link_resolutions: |tcx, def_id| {
2234+
tcx.resolutions(())
2235+
.doc_link_resolutions
2236+
.get(&def_id.expect_local())
2237+
.expect("no resolutions for a doc link")
2238+
},
2239+
doc_link_traits_in_scope: |tcx, def_id| {
2240+
tcx.resolutions(())
2241+
.doc_link_traits_in_scope
2242+
.get(&def_id.expect_local())
2243+
.expect("no traits in scope for a doc link")
2244+
},
22282245
traits_in_crate: |tcx, cnum| {
22292246
assert_eq!(cnum, LOCAL_CRATE);
22302247

compiler/rustc_metadata/src/rmeta/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_attr as attr;
99
use rustc_data_structures::svh::Svh;
1010
use rustc_data_structures::sync::MetadataRef;
1111
use rustc_hir as hir;
12-
use rustc_hir::def::{CtorKind, DefKind};
12+
use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap};
1313
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, DefPathHash, StableCrateId};
1414
use rustc_hir::definitions::DefKey;
1515
use rustc_hir::lang_items::LangItem;
@@ -413,6 +413,8 @@ define_tables! {
413413
module_reexports: Table<DefIndex, LazyArray<ModChild>>,
414414
deduced_param_attrs: Table<DefIndex, LazyArray<DeducedParamAttrs>>,
415415
trait_impl_trait_tys: Table<DefIndex, LazyValue<FxHashMap<DefId, Ty<'static>>>>,
416+
doc_link_resolutions: Table<DefIndex, LazyValue<DocLinkResMap>>,
417+
doc_link_traits_in_scope: Table<DefIndex, LazyArray<DefId>>,
416418
}
417419

418420
#[derive(TyEncodable, TyDecodable)]
@@ -426,8 +428,7 @@ struct VariantData {
426428
bitflags::bitflags! {
427429
#[derive(Default)]
428430
pub struct AttrFlags: u8 {
429-
const MAY_HAVE_DOC_LINKS = 1 << 0;
430-
const IS_DOC_HIDDEN = 1 << 1;
431+
const IS_DOC_HIDDEN = 1 << 0;
431432
}
432433
}
433434

compiler/rustc_middle/src/arena.rs

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ macro_rules! arena_types {
113113
[decode] trait_impl_trait_tys: rustc_data_structures::fx::FxHashMap<rustc_hir::def_id::DefId, rustc_middle::ty::Ty<'tcx>>,
114114
[] bit_set_u32: rustc_index::bit_set::BitSet<u32>,
115115
[] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>,
116+
[decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap,
116117
]);
117118
)
118119
}

compiler/rustc_middle/src/query/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -2156,4 +2156,16 @@ rustc_queries! {
21562156
desc { |tcx| "deducing parameter attributes for {}", tcx.def_path_str(def_id) }
21572157
separate_provide_extern
21582158
}
2159+
2160+
query doc_link_resolutions(def_id: DefId) -> &'tcx DocLinkResMap {
2161+
eval_always
2162+
desc { "resolutions for documentation links for a module" }
2163+
separate_provide_extern
2164+
}
2165+
2166+
query doc_link_traits_in_scope(def_id: DefId) -> &'tcx [DefId] {
2167+
eval_always
2168+
desc { "traits in scope for documentation links for a module" }
2169+
separate_provide_extern
2170+
}
21592171
}

compiler/rustc_middle/src/ty/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use rustc_data_structures::intern::Interned;
3636
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
3737
use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
3838
use rustc_hir as hir;
39-
use rustc_hir::def::{CtorKind, CtorOf, DefKind, LifetimeRes, Res};
39+
use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
4040
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
4141
use rustc_hir::Node;
4242
use rustc_index::vec::IndexVec;
@@ -181,6 +181,8 @@ pub struct ResolverGlobalCtxt {
181181
/// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`.
182182
pub confused_type_with_std_module: FxHashMap<Span, Span>,
183183
pub registered_tools: RegisteredTools,
184+
pub doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
185+
pub doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
184186
}
185187

186188
/// Resolutions that should only be used for lowering.

compiler/rustc_middle/src/ty/parameterized.rs

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ trivially_parameterized_over_tcx! {
8181
rustc_hir::IsAsync,
8282
rustc_hir::LangItem,
8383
rustc_hir::def::DefKind,
84+
rustc_hir::def::DocLinkResMap,
85+
rustc_hir::def_id::DefId,
8486
rustc_hir::def_id::DefIndex,
8587
rustc_hir::definitions::DefKey,
8688
rustc_index::bit_set::BitSet<u32>,

compiler/rustc_middle/src/ty/query.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use rustc_data_structures::sync::Lrc;
4545
use rustc_data_structures::unord::UnordSet;
4646
use rustc_errors::ErrorGuaranteed;
4747
use rustc_hir as hir;
48-
use rustc_hir::def::DefKind;
48+
use rustc_hir::def::{DefKind, DocLinkResMap};
4949
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId};
5050
use rustc_hir::hir_id::OwnerId;
5151
use rustc_hir::lang_items::{LangItem, LanguageItems};

compiler/rustc_resolve/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ edition = "2021"
77

88
[dependencies]
99
bitflags = "1.2.1"
10+
pulldown-cmark = { version = "0.9.2", default-features = false }
1011
rustc_arena = { path = "../rustc_arena" }
1112
rustc_ast = { path = "../rustc_ast" }
1213
rustc_ast_pretty = { path = "../rustc_ast_pretty" }

0 commit comments

Comments
 (0)