3
3
//! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
4
4
5
5
use rustc_ast as ast;
6
- use rustc_data_structures:: stable_set:: FxHashSet ;
6
+ use rustc_data_structures:: { fx :: FxHashMap , stable_set:: FxHashSet } ;
7
7
use rustc_errors:: { Applicability , DiagnosticBuilder } ;
8
8
use rustc_expand:: base:: SyntaxExtensionKind ;
9
9
use rustc_hir as hir;
@@ -168,6 +168,31 @@ enum AnchorFailure {
168
168
RustdocAnchorConflict ( Res ) ,
169
169
}
170
170
171
+ #[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
172
+ struct CacheKey {
173
+ module_id : DefId ,
174
+ dis : Option < Disambiguator > ,
175
+ path_str : String ,
176
+ extra_fragment : Option < String > ,
177
+ }
178
+
179
+ impl CacheKey {
180
+ fn new (
181
+ module_id : DefId ,
182
+ dis : Option < Disambiguator > ,
183
+ path_str : String ,
184
+ extra_fragment : Option < String > ,
185
+ ) -> Self {
186
+ Self { module_id, dis, path_str, extra_fragment }
187
+ }
188
+ }
189
+
190
+ #[ derive( Clone , Debug , Hash ) ]
191
+ struct CachedLink {
192
+ pub res : ( Res , Option < String > ) ,
193
+ pub side_channel : Option < ( DefKind , DefId ) > ,
194
+ }
195
+
171
196
struct LinkCollector < ' a , ' tcx > {
172
197
cx : & ' a DocContext < ' tcx > ,
173
198
/// A stack of modules used to decide what scope to resolve in.
@@ -179,11 +204,18 @@ struct LinkCollector<'a, 'tcx> {
179
204
/// because `clean` and the disambiguator code expect them to be different.
180
205
/// See the code for associated items on inherent impls for details.
181
206
kind_side_channel : Cell < Option < ( DefKind , DefId ) > > ,
207
+ /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link
208
+ visited_links : FxHashMap < CacheKey , CachedLink > ,
182
209
}
183
210
184
211
impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
185
212
fn new ( cx : & ' a DocContext < ' tcx > ) -> Self {
186
- LinkCollector { cx, mod_ids : Vec :: new ( ) , kind_side_channel : Cell :: new ( None ) }
213
+ LinkCollector {
214
+ cx,
215
+ mod_ids : Vec :: new ( ) ,
216
+ kind_side_channel : Cell :: new ( None ) ,
217
+ visited_links : FxHashMap :: default ( ) ,
218
+ }
187
219
}
188
220
189
221
/// Given a full link, parse it as an [enum struct variant].
@@ -937,7 +969,7 @@ impl LinkCollector<'_, '_> {
937
969
///
938
970
/// FIXME(jynelson): this is way too many arguments
939
971
fn resolve_link (
940
- & self ,
972
+ & mut self ,
941
973
item : & Item ,
942
974
dox : & str ,
943
975
self_name : & Option < String > ,
@@ -962,6 +994,7 @@ impl LinkCollector<'_, '_> {
962
994
let link = ori_link. replace ( "`" , "" ) ;
963
995
let parts = link. split ( '#' ) . collect :: < Vec < _ > > ( ) ;
964
996
let ( link, extra_fragment) = if parts. len ( ) > 2 {
997
+ // A valid link can't have multiple #'s
965
998
anchor_failure ( cx, & item, & link, dox, link_range, AnchorFailure :: MultipleAnchors ) ;
966
999
return None ;
967
1000
} else if parts. len ( ) == 2 {
@@ -1075,16 +1108,9 @@ impl LinkCollector<'_, '_> {
1075
1108
return None ;
1076
1109
}
1077
1110
1078
- let ( mut res, mut fragment) = self . resolve_with_disambiguator (
1079
- disambiguator,
1080
- item,
1081
- dox,
1082
- path_str,
1083
- module_id,
1084
- extra_fragment,
1085
- & ori_link,
1086
- link_range. clone ( ) ,
1087
- ) ?;
1111
+ let key = CacheKey :: new ( module_id, disambiguator, path_str. to_owned ( ) , extra_fragment) ;
1112
+ let ( mut res, mut fragment) =
1113
+ self . resolve_with_disambiguator_cached ( key, item, dox, & ori_link, link_range. clone ( ) ) ?;
1088
1114
1089
1115
// Check for a primitive which might conflict with a module
1090
1116
// Report the ambiguity and require that the user specify which one they meant.
@@ -1192,6 +1218,45 @@ impl LinkCollector<'_, '_> {
1192
1218
}
1193
1219
}
1194
1220
1221
+ fn resolve_with_disambiguator_cached (
1222
+ & mut self ,
1223
+ key : CacheKey ,
1224
+ item : & Item ,
1225
+ dox : & str ,
1226
+ ori_link : & str ,
1227
+ link_range : Option < Range < usize > > ,
1228
+ ) -> Option < ( Res , Option < String > ) > {
1229
+ // Try to look up both the result and the corresponding side channel value
1230
+ if let Some ( ref cached) = self . visited_links . get ( & key) {
1231
+ self . kind_side_channel . set ( cached. side_channel . clone ( ) ) ;
1232
+ Some ( cached. res . clone ( ) )
1233
+ } else {
1234
+ match self . resolve_with_disambiguator (
1235
+ key. dis ,
1236
+ item,
1237
+ dox,
1238
+ & key. path_str ,
1239
+ key. module_id ,
1240
+ key. extra_fragment . clone ( ) ,
1241
+ ori_link,
1242
+ link_range,
1243
+ ) {
1244
+ Some ( res) => {
1245
+ // Store result for the actual namespace
1246
+ self . visited_links . insert (
1247
+ key,
1248
+ CachedLink {
1249
+ res : res. clone ( ) ,
1250
+ side_channel : self . kind_side_channel . clone ( ) . into_inner ( ) ,
1251
+ } ,
1252
+ ) ;
1253
+ Some ( res)
1254
+ }
1255
+ _ => None ,
1256
+ }
1257
+ }
1258
+ }
1259
+
1195
1260
/// After parsing the disambiguator, resolve the main part of the link.
1196
1261
// FIXME(jynelson): wow this is just so much
1197
1262
fn resolve_with_disambiguator (
@@ -1356,7 +1421,7 @@ impl LinkCollector<'_, '_> {
1356
1421
}
1357
1422
}
1358
1423
1359
- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
1424
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
1360
1425
/// Disambiguators for a link.
1361
1426
enum Disambiguator {
1362
1427
/// `prim@`
0 commit comments