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,27 @@ enum AnchorFailure {
168
168
RustdocAnchorConflict ( Res ) ,
169
169
}
170
170
171
+ #[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
172
+ struct ResolutionInfo {
173
+ module_id : DefId ,
174
+ dis : Option < Disambiguator > ,
175
+ path_str : String ,
176
+ extra_fragment : Option < String > ,
177
+ }
178
+
179
+ struct DiagnosticInfo < ' a > {
180
+ item : & ' a Item ,
181
+ dox : & ' a str ,
182
+ ori_link : & ' a str ,
183
+ link_range : Option < Range < usize > > ,
184
+ }
185
+
186
+ #[ derive( Clone , Debug , Hash ) ]
187
+ struct CachedLink {
188
+ pub res : ( Res , Option < String > ) ,
189
+ pub side_channel : Option < ( DefKind , DefId ) > ,
190
+ }
191
+
171
192
struct LinkCollector < ' a , ' tcx > {
172
193
cx : & ' a DocContext < ' tcx > ,
173
194
/// A stack of modules used to decide what scope to resolve in.
@@ -179,11 +200,18 @@ struct LinkCollector<'a, 'tcx> {
179
200
/// because `clean` and the disambiguator code expect them to be different.
180
201
/// See the code for associated items on inherent impls for details.
181
202
kind_side_channel : Cell < Option < ( DefKind , DefId ) > > ,
203
+ /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link
204
+ visited_links : FxHashMap < ResolutionInfo , CachedLink > ,
182
205
}
183
206
184
207
impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
185
208
fn new ( cx : & ' a DocContext < ' tcx > ) -> Self {
186
- LinkCollector { cx, mod_ids : Vec :: new ( ) , kind_side_channel : Cell :: new ( None ) }
209
+ LinkCollector {
210
+ cx,
211
+ mod_ids : Vec :: new ( ) ,
212
+ kind_side_channel : Cell :: new ( None ) ,
213
+ visited_links : FxHashMap :: default ( ) ,
214
+ }
187
215
}
188
216
189
217
/// Given a full link, parse it as an [enum struct variant].
@@ -937,7 +965,7 @@ impl LinkCollector<'_, '_> {
937
965
///
938
966
/// FIXME(jynelson): this is way too many arguments
939
967
fn resolve_link (
940
- & self ,
968
+ & mut self ,
941
969
item : & Item ,
942
970
dox : & str ,
943
971
self_name : & Option < String > ,
@@ -962,6 +990,7 @@ impl LinkCollector<'_, '_> {
962
990
let link = ori_link. replace ( "`" , "" ) ;
963
991
let parts = link. split ( '#' ) . collect :: < Vec < _ > > ( ) ;
964
992
let ( link, extra_fragment) = if parts. len ( ) > 2 {
993
+ // A valid link can't have multiple #'s
965
994
anchor_failure ( cx, & item, & link, dox, link_range, AnchorFailure :: MultipleAnchors ) ;
966
995
return None ;
967
996
} else if parts. len ( ) == 2 {
@@ -1075,16 +1104,15 @@ impl LinkCollector<'_, '_> {
1075
1104
return None ;
1076
1105
}
1077
1106
1078
- let ( mut res, mut fragment) = self . resolve_with_disambiguator (
1079
- disambiguator,
1080
- item,
1081
- dox,
1082
- path_str,
1107
+ let key = ResolutionInfo {
1083
1108
module_id,
1109
+ dis : disambiguator,
1110
+ path_str : path_str. to_owned ( ) ,
1084
1111
extra_fragment,
1085
- & ori_link,
1086
- link_range. clone ( ) ,
1087
- ) ?;
1112
+ } ;
1113
+ let diag =
1114
+ DiagnosticInfo { item, dox, ori_link : & ori_link, link_range : link_range. clone ( ) } ;
1115
+ let ( mut res, mut fragment) = self . resolve_with_disambiguator_cached ( key, diag) ?;
1088
1116
1089
1117
// Check for a primitive which might conflict with a module
1090
1118
// Report the ambiguity and require that the user specify which one they meant.
@@ -1192,22 +1220,49 @@ impl LinkCollector<'_, '_> {
1192
1220
}
1193
1221
}
1194
1222
1223
+ fn resolve_with_disambiguator_cached (
1224
+ & mut self ,
1225
+ key : ResolutionInfo ,
1226
+ diag : DiagnosticInfo < ' _ > ,
1227
+ ) -> Option < ( Res , Option < String > ) > {
1228
+ // Try to look up both the result and the corresponding side channel value
1229
+ if let Some ( ref cached) = self . visited_links . get ( & key) {
1230
+ self . kind_side_channel . set ( cached. side_channel . clone ( ) ) ;
1231
+ return Some ( cached. res . clone ( ) ) ;
1232
+ }
1233
+
1234
+ let res = self . resolve_with_disambiguator ( & key, diag) ;
1235
+
1236
+ // Cache only if resolved successfully - don't silence duplicate errors
1237
+ if let Some ( res) = & res {
1238
+ // Store result for the actual namespace
1239
+ self . visited_links . insert (
1240
+ key,
1241
+ CachedLink {
1242
+ res : res. clone ( ) ,
1243
+ side_channel : self . kind_side_channel . clone ( ) . into_inner ( ) ,
1244
+ } ,
1245
+ ) ;
1246
+ }
1247
+
1248
+ res
1249
+ }
1250
+
1195
1251
/// After parsing the disambiguator, resolve the main part of the link.
1196
1252
// FIXME(jynelson): wow this is just so much
1197
1253
fn resolve_with_disambiguator (
1198
1254
& self ,
1199
- disambiguator : Option < Disambiguator > ,
1200
- item : & Item ,
1201
- dox : & str ,
1202
- path_str : & str ,
1203
- base_node : DefId ,
1204
- extra_fragment : Option < String > ,
1205
- ori_link : & str ,
1206
- link_range : Option < Range < usize > > ,
1255
+ key : & ResolutionInfo ,
1256
+ diag : DiagnosticInfo < ' _ > ,
1207
1257
) -> Option < ( Res , Option < String > ) > {
1258
+ let disambiguator = key. dis ;
1259
+ let path_str = & key. path_str ;
1260
+ let base_node = key. module_id ;
1261
+ let extra_fragment = & key. extra_fragment ;
1262
+
1208
1263
match disambiguator. map ( Disambiguator :: ns) {
1209
1264
Some ( ns @ ( ValueNS | TypeNS ) ) => {
1210
- match self . resolve ( path_str, ns, base_node, & extra_fragment) {
1265
+ match self . resolve ( path_str, ns, base_node, extra_fragment) {
1211
1266
Ok ( res) => Some ( res) ,
1212
1267
Err ( ErrorKind :: Resolve ( box mut kind) ) => {
1213
1268
// We only looked in one namespace. Try to give a better error if possible.
@@ -1216,24 +1271,21 @@ impl LinkCollector<'_, '_> {
1216
1271
// FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
1217
1272
// See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
1218
1273
for & new_ns in & [ other_ns, MacroNS ] {
1219
- if let Some ( res) = self . check_full_res (
1220
- new_ns,
1221
- path_str,
1222
- base_node,
1223
- & extra_fragment,
1224
- ) {
1274
+ if let Some ( res) =
1275
+ self . check_full_res ( new_ns, path_str, base_node, extra_fragment)
1276
+ {
1225
1277
kind = ResolutionFailure :: WrongNamespace ( res, ns) ;
1226
1278
break ;
1227
1279
}
1228
1280
}
1229
1281
}
1230
1282
resolution_failure (
1231
1283
self ,
1232
- & item,
1284
+ diag . item ,
1233
1285
path_str,
1234
1286
disambiguator,
1235
- dox,
1236
- link_range,
1287
+ diag . dox ,
1288
+ diag . link_range ,
1237
1289
smallvec ! [ kind] ,
1238
1290
) ;
1239
1291
// This could just be a normal link or a broken link
@@ -1242,7 +1294,14 @@ impl LinkCollector<'_, '_> {
1242
1294
return None ;
1243
1295
}
1244
1296
Err ( ErrorKind :: AnchorFailure ( msg) ) => {
1245
- anchor_failure ( self . cx , & item, & ori_link, dox, link_range, msg) ;
1297
+ anchor_failure (
1298
+ self . cx ,
1299
+ diag. item ,
1300
+ diag. ori_link ,
1301
+ diag. dox ,
1302
+ diag. link_range ,
1303
+ msg,
1304
+ ) ;
1246
1305
return None ;
1247
1306
}
1248
1307
}
@@ -1253,21 +1312,35 @@ impl LinkCollector<'_, '_> {
1253
1312
macro_ns : self
1254
1313
. resolve_macro ( path_str, base_node)
1255
1314
. map ( |res| ( res, extra_fragment. clone ( ) ) ) ,
1256
- type_ns : match self . resolve ( path_str, TypeNS , base_node, & extra_fragment) {
1315
+ type_ns : match self . resolve ( path_str, TypeNS , base_node, extra_fragment) {
1257
1316
Ok ( res) => {
1258
1317
debug ! ( "got res in TypeNS: {:?}" , res) ;
1259
1318
Ok ( res)
1260
1319
}
1261
1320
Err ( ErrorKind :: AnchorFailure ( msg) ) => {
1262
- anchor_failure ( self . cx , & item, ori_link, dox, link_range, msg) ;
1321
+ anchor_failure (
1322
+ self . cx ,
1323
+ diag. item ,
1324
+ diag. ori_link ,
1325
+ diag. dox ,
1326
+ diag. link_range ,
1327
+ msg,
1328
+ ) ;
1263
1329
return None ;
1264
1330
}
1265
1331
Err ( ErrorKind :: Resolve ( box kind) ) => Err ( kind) ,
1266
1332
} ,
1267
- value_ns : match self . resolve ( path_str, ValueNS , base_node, & extra_fragment) {
1333
+ value_ns : match self . resolve ( path_str, ValueNS , base_node, extra_fragment) {
1268
1334
Ok ( res) => Ok ( res) ,
1269
1335
Err ( ErrorKind :: AnchorFailure ( msg) ) => {
1270
- anchor_failure ( self . cx , & item, ori_link, dox, link_range, msg) ;
1336
+ anchor_failure (
1337
+ self . cx ,
1338
+ diag. item ,
1339
+ diag. ori_link ,
1340
+ diag. dox ,
1341
+ diag. link_range ,
1342
+ msg,
1343
+ ) ;
1271
1344
return None ;
1272
1345
}
1273
1346
Err ( ErrorKind :: Resolve ( box kind) ) => Err ( kind) ,
@@ -1278,7 +1351,7 @@ impl LinkCollector<'_, '_> {
1278
1351
Res :: Def ( DefKind :: Ctor ( ..) , _) | Res :: SelfCtor ( ..) => {
1279
1352
Err ( ResolutionFailure :: WrongNamespace ( res, TypeNS ) )
1280
1353
}
1281
- _ => match ( fragment, extra_fragment) {
1354
+ _ => match ( fragment, extra_fragment. clone ( ) ) {
1282
1355
( Some ( fragment) , Some ( _) ) => {
1283
1356
// Shouldn't happen but who knows?
1284
1357
Ok ( ( res, Some ( fragment) ) )
@@ -1294,11 +1367,11 @@ impl LinkCollector<'_, '_> {
1294
1367
if len == 0 {
1295
1368
resolution_failure (
1296
1369
self ,
1297
- & item,
1370
+ diag . item ,
1298
1371
path_str,
1299
1372
disambiguator,
1300
- dox,
1301
- link_range,
1373
+ diag . dox ,
1374
+ diag . link_range ,
1302
1375
candidates. into_iter ( ) . filter_map ( |res| res. err ( ) ) . collect ( ) ,
1303
1376
) ;
1304
1377
// this could just be a normal link
@@ -1317,35 +1390,35 @@ impl LinkCollector<'_, '_> {
1317
1390
let candidates = candidates. map ( |candidate| candidate. ok ( ) . map ( |( res, _) | res) ) ;
1318
1391
ambiguity_error (
1319
1392
self . cx ,
1320
- & item,
1393
+ diag . item ,
1321
1394
path_str,
1322
- dox,
1323
- link_range,
1395
+ diag . dox ,
1396
+ diag . link_range ,
1324
1397
candidates. present_items ( ) . collect ( ) ,
1325
1398
) ;
1326
1399
return None ;
1327
1400
}
1328
1401
}
1329
1402
Some ( MacroNS ) => {
1330
1403
match self . resolve_macro ( path_str, base_node) {
1331
- Ok ( res) => Some ( ( res, extra_fragment) ) ,
1404
+ Ok ( res) => Some ( ( res, extra_fragment. clone ( ) ) ) ,
1332
1405
Err ( mut kind) => {
1333
1406
// `resolve_macro` only looks in the macro namespace. Try to give a better error if possible.
1334
1407
for & ns in & [ TypeNS , ValueNS ] {
1335
1408
if let Some ( res) =
1336
- self . check_full_res ( ns, path_str, base_node, & extra_fragment)
1409
+ self . check_full_res ( ns, path_str, base_node, extra_fragment)
1337
1410
{
1338
1411
kind = ResolutionFailure :: WrongNamespace ( res, MacroNS ) ;
1339
1412
break ;
1340
1413
}
1341
1414
}
1342
1415
resolution_failure (
1343
1416
self ,
1344
- & item,
1417
+ diag . item ,
1345
1418
path_str,
1346
1419
disambiguator,
1347
- dox,
1348
- link_range,
1420
+ diag . dox ,
1421
+ diag . link_range ,
1349
1422
smallvec ! [ kind] ,
1350
1423
) ;
1351
1424
return None ;
@@ -1356,7 +1429,7 @@ impl LinkCollector<'_, '_> {
1356
1429
}
1357
1430
}
1358
1431
1359
- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
1432
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
1360
1433
/// Disambiguators for a link.
1361
1434
enum Disambiguator {
1362
1435
/// `prim@`
0 commit comments