@@ -7,11 +7,15 @@ use rustc_ast::visit::{self, AssocCtxt, Visitor};
7
7
use rustc_ast:: { self as ast, ItemKind } ;
8
8
use rustc_ast_lowering:: ResolverAstLowering ;
9
9
use rustc_hir:: def:: Namespace :: TypeNS ;
10
- use rustc_hir:: def_id:: { DefId , LocalDefId , CRATE_DEF_ID } ;
11
- use rustc_resolve:: Resolver ;
10
+ use rustc_hir:: def:: { DefKind , Res } ;
11
+ use rustc_hir:: def_id:: { DefId , DefIdMap , DefIdSet , LocalDefId , CRATE_DEF_ID } ;
12
+ use rustc_hir:: TraitCandidate ;
13
+ use rustc_middle:: ty:: { DefIdTree , Visibility } ;
14
+ use rustc_resolve:: { ParentScope , Resolver } ;
12
15
use rustc_session:: config:: Externs ;
13
- use rustc_span:: { Span , DUMMY_SP } ;
16
+ use rustc_span:: { Span , SyntaxContext , DUMMY_SP } ;
14
17
18
+ use std:: collections:: hash_map:: Entry ;
15
19
use std:: mem;
16
20
17
21
crate fn early_resolve_intra_doc_links (
@@ -22,15 +26,18 @@ crate fn early_resolve_intra_doc_links(
22
26
let mut loader = IntraLinkCrateLoader {
23
27
resolver,
24
28
current_mod : CRATE_DEF_ID ,
29
+ visited_mods : Default :: default ( ) ,
30
+ traits_in_scope : Default :: default ( ) ,
25
31
all_traits : Default :: default ( ) ,
26
32
all_trait_impls : Default :: default ( ) ,
27
33
} ;
28
34
29
35
// Overridden `visit_item` below doesn't apply to the crate root,
30
- // so we have to visit its attributes and exports separately.
36
+ // so we have to visit its attributes and reexports separately.
31
37
loader. load_links_in_attrs ( & krate. attrs , krate. span ) ;
38
+ loader. process_module_children_or_reexports ( CRATE_DEF_ID . to_def_id ( ) ) ;
32
39
visit:: walk_crate ( & mut loader, krate) ;
33
- loader. fill_resolver_caches ( ) ;
40
+ loader. add_foreign_traits_in_scope ( ) ;
34
41
35
42
// FIXME: somehow rustdoc is still missing crates even though we loaded all
36
43
// the known necessary crates. Load them all unconditionally until we find a way to fix this.
@@ -46,6 +53,7 @@ crate fn early_resolve_intra_doc_links(
46
53
}
47
54
48
55
ResolverCaches {
56
+ traits_in_scope : loader. traits_in_scope ,
49
57
all_traits : Some ( loader. all_traits ) ,
50
58
all_trait_impls : Some ( loader. all_trait_impls ) ,
51
59
}
@@ -54,27 +62,87 @@ crate fn early_resolve_intra_doc_links(
54
62
struct IntraLinkCrateLoader < ' r , ' ra > {
55
63
resolver : & ' r mut Resolver < ' ra > ,
56
64
current_mod : LocalDefId ,
65
+ visited_mods : DefIdSet ,
66
+ traits_in_scope : DefIdMap < Vec < TraitCandidate > > ,
57
67
all_traits : Vec < DefId > ,
58
68
all_trait_impls : Vec < DefId > ,
59
69
}
60
70
61
71
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) ;
72
+ fn add_traits_in_scope ( & mut self , def_id : DefId ) {
73
+ // Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
74
+ // Keys in the `traits_in_scope` cache are always module IDs.
75
+ if let Entry :: Vacant ( entry) = self . traits_in_scope . entry ( def_id) {
76
+ let module = self . resolver . get_nearest_non_block_module ( def_id) ;
77
+ let module_id = module. def_id ( ) ;
78
+ let entry = if module_id == def_id {
79
+ Some ( entry)
80
+ } else if let Entry :: Vacant ( entry) = self . traits_in_scope . entry ( module_id) {
81
+ Some ( entry)
82
+ } else {
83
+ None
84
+ } ;
85
+ if let Some ( entry) = entry {
86
+ entry. insert ( self . resolver . traits_in_scope (
87
+ None ,
88
+ & ParentScope :: module ( module, self . resolver ) ,
89
+ SyntaxContext :: root ( ) ,
90
+ None ,
91
+ ) ) ;
92
+ }
93
+ }
94
+ }
95
+
96
+ fn add_traits_in_parent_scope ( & mut self , def_id : DefId ) {
97
+ if let Some ( module_id) = self . resolver . parent ( def_id) {
98
+ self . add_traits_in_scope ( module_id) ;
99
+ }
100
+ }
101
+
102
+ /// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass.
103
+ /// That pass filters impls using type-based information, but we don't yet have such
104
+ /// information here, so we just conservatively calculate traits in scope for *all* modules
105
+ /// having impls in them.
106
+ fn add_foreign_traits_in_scope ( & mut self ) {
107
+ for cnum in Vec :: from_iter ( self . resolver . cstore ( ) . crates_untracked ( ) ) {
108
+ // FIXME: Due to #78696 rustdoc can query traits in scope for any crate root.
109
+ self . add_traits_in_scope ( cnum. as_def_id ( ) ) ;
110
+
111
+ let all_traits = Vec :: from_iter ( self . resolver . cstore ( ) . traits_in_crate_untracked ( cnum) ) ;
112
+ let all_trait_impls =
113
+ Vec :: from_iter ( self . resolver . cstore ( ) . trait_impls_in_crate_untracked ( cnum) ) ;
114
+
115
+ // Querying traits in scope is expensive so we try to prune the impl and traits lists
116
+ // using privacy, private traits and impls from other crates are never documented in
117
+ // the current crate, and links in their doc comments are not resolved.
118
+ for & def_id in & all_traits {
119
+ if self . resolver . cstore ( ) . visibility_untracked ( def_id) == Visibility :: Public {
120
+ self . add_traits_in_parent_scope ( def_id) ;
121
+ }
122
+ }
123
+ for & ( trait_def_id, impl_def_id, simplified_self_ty) in & all_trait_impls {
124
+ if self . resolver . cstore ( ) . visibility_untracked ( trait_def_id) == Visibility :: Public
125
+ && simplified_self_ty. and_then ( |ty| ty. def ( ) ) . map_or ( true , |ty_def_id| {
126
+ self . resolver . cstore ( ) . visibility_untracked ( ty_def_id) == Visibility :: Public
127
+ } )
128
+ {
129
+ self . add_traits_in_parent_scope ( impl_def_id) ;
130
+ }
131
+ }
66
132
67
133
self . all_traits . extend ( all_traits) ;
68
- self . all_trait_impls . extend ( all_trait_impls. into_iter ( ) . map ( |( def_id, _) | def_id) ) ;
134
+ self . all_trait_impls . extend ( all_trait_impls. into_iter ( ) . map ( |( _ , def_id, _) | def_id) ) ;
69
135
}
70
136
}
71
137
72
138
fn load_links_in_attrs ( & mut self , attrs : & [ ast:: Attribute ] , span : Span ) {
73
- // FIXME: this needs to consider export inlining.
139
+ // FIXME: this needs to consider reexport inlining.
74
140
let attrs = clean:: Attributes :: from_ast ( attrs, None ) ;
75
141
for ( parent_module, doc) in attrs. collapsed_doc_value_by_module_level ( ) {
76
142
let module_id = parent_module. unwrap_or ( self . current_mod . to_def_id ( ) ) ;
77
143
144
+ self . add_traits_in_scope ( module_id) ;
145
+
78
146
for link in markdown_links ( & doc. as_str ( ) ) {
79
147
let path_str = if let Some ( Ok ( x) ) = preprocess_link ( & link) {
80
148
x. path_str
@@ -85,6 +153,26 @@ impl IntraLinkCrateLoader<'_, '_> {
85
153
}
86
154
}
87
155
}
156
+
157
+ /// When reexports are inlined, they are replaced with item which they refer to, those items
158
+ /// may have links in their doc comments, those links are resolved at the item definition site,
159
+ /// so we need to know traits in scope at that definition site.
160
+ fn process_module_children_or_reexports ( & mut self , module_id : DefId ) {
161
+ if !self . visited_mods . insert ( module_id) {
162
+ return ; // avoid infinite recursion
163
+ }
164
+
165
+ for child in self . resolver . module_children_or_reexports ( module_id) {
166
+ if child. vis == Visibility :: Public {
167
+ if let Some ( def_id) = child. res . opt_def_id ( ) {
168
+ self . add_traits_in_parent_scope ( def_id) ;
169
+ }
170
+ if let Res :: Def ( DefKind :: Mod , module_id) = child. res {
171
+ self . process_module_children_or_reexports ( module_id) ;
172
+ }
173
+ }
174
+ }
175
+ }
88
176
}
89
177
90
178
impl Visitor < ' _ > for IntraLinkCrateLoader < ' _ , ' _ > {
@@ -93,6 +181,7 @@ impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> {
93
181
let old_mod = mem:: replace ( & mut self . current_mod , self . resolver . local_def_id ( item. id ) ) ;
94
182
95
183
self . load_links_in_attrs ( & item. attrs , item. span ) ;
184
+ self . process_module_children_or_reexports ( self . current_mod . to_def_id ( ) ) ;
96
185
visit:: walk_item ( self , item) ;
97
186
98
187
self . current_mod = old_mod;
0 commit comments