@@ -20,7 +20,6 @@ use rustc_span::BytePos;
20
20
use smallvec:: { smallvec, SmallVec } ;
21
21
22
22
use std:: borrow:: Cow ;
23
- use std:: fmt:: Write ;
24
23
use std:: mem;
25
24
use std:: ops:: Range ;
26
25
@@ -220,80 +219,43 @@ enum MalformedGenerics {
220
219
221
220
#[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
222
221
pub ( crate ) enum UrlFragment {
223
- Item ( ItemFragment ) ,
222
+ Item ( DefId ) ,
224
223
UserWritten ( String ) ,
225
224
}
226
225
227
226
impl UrlFragment {
228
227
/// Render the fragment, including the leading `#`.
229
- pub ( crate ) fn render ( & self , s : & mut String , tcx : TyCtxt < ' _ > ) -> std:: fmt:: Result {
228
+ pub ( crate ) fn render ( & self , s : & mut String , tcx : TyCtxt < ' _ > ) {
229
+ s. push ( '#' ) ;
230
230
match self {
231
- UrlFragment :: Item ( frag) => frag. render ( s, tcx) ,
232
- UrlFragment :: UserWritten ( raw) => write ! ( s, "#{}" , raw) ,
233
- }
234
- }
235
- }
236
-
237
- #[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
238
- pub ( crate ) struct ItemFragment ( FragmentKind , DefId ) ;
239
-
240
- #[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
241
- pub ( crate ) enum FragmentKind {
242
- Method ,
243
- TyMethod ,
244
- AssociatedConstant ,
245
- AssociatedType ,
246
-
247
- StructField ,
248
- Variant ,
249
- VariantField ,
250
- }
251
-
252
- impl FragmentKind {
253
- fn from_def_id ( tcx : TyCtxt < ' _ > , def_id : DefId ) -> FragmentKind {
254
- match tcx. def_kind ( def_id) {
255
- DefKind :: AssocFn => {
256
- if tcx. associated_item ( def_id) . defaultness . has_value ( ) {
257
- FragmentKind :: Method
258
- } else {
259
- FragmentKind :: TyMethod
260
- }
261
- }
262
- DefKind :: AssocConst => FragmentKind :: AssociatedConstant ,
263
- DefKind :: AssocTy => FragmentKind :: AssociatedType ,
264
- DefKind :: Variant => FragmentKind :: Variant ,
265
- DefKind :: Field => {
266
- if tcx. def_kind ( tcx. parent ( def_id) ) == DefKind :: Variant {
267
- FragmentKind :: VariantField
268
- } else {
269
- FragmentKind :: StructField
270
- }
271
- }
272
- kind => bug ! ( "unexpected associated item kind: {:?}" , kind) ,
273
- }
274
- }
275
- }
276
-
277
- impl ItemFragment {
278
- /// Render the fragment, including the leading `#`.
279
- pub ( crate ) fn render ( & self , s : & mut String , tcx : TyCtxt < ' _ > ) -> std:: fmt:: Result {
280
- write ! ( s, "#" ) ?;
281
- match * self {
282
- ItemFragment ( kind, def_id) => {
283
- let name = tcx. item_name ( def_id) ;
284
- match kind {
285
- FragmentKind :: Method => write ! ( s, "method.{}" , name) ,
286
- FragmentKind :: TyMethod => write ! ( s, "tymethod.{}" , name) ,
287
- FragmentKind :: AssociatedConstant => write ! ( s, "associatedconstant.{}" , name) ,
288
- FragmentKind :: AssociatedType => write ! ( s, "associatedtype.{}" , name) ,
289
- FragmentKind :: StructField => write ! ( s, "structfield.{}" , name) ,
290
- FragmentKind :: Variant => write ! ( s, "variant.{}" , name) ,
291
- FragmentKind :: VariantField => {
292
- let variant = tcx. item_name ( tcx. parent ( def_id) ) ;
293
- write ! ( s, "variant.{}.field.{}" , variant, name)
231
+ & UrlFragment :: Item ( def_id) => {
232
+ let kind = match tcx. def_kind ( def_id) {
233
+ DefKind :: AssocFn => {
234
+ if tcx. associated_item ( def_id) . defaultness . has_value ( ) {
235
+ "method."
236
+ } else {
237
+ "tymethod."
238
+ }
294
239
}
295
- }
240
+ DefKind :: AssocConst => "associatedconstant." ,
241
+ DefKind :: AssocTy => "associatedtype." ,
242
+ DefKind :: Variant => "variant." ,
243
+ DefKind :: Field => {
244
+ let parent_id = tcx. parent ( def_id) ;
245
+ if tcx. def_kind ( parent_id) == DefKind :: Variant {
246
+ s. push_str ( "variant." ) ;
247
+ s. push_str ( tcx. item_name ( parent_id) . as_str ( ) ) ;
248
+ ".field."
249
+ } else {
250
+ "structfield."
251
+ }
252
+ }
253
+ kind => bug ! ( "unexpected associated item kind: {:?}" , kind) ,
254
+ } ;
255
+ s. push_str ( kind) ;
256
+ s. push_str ( tcx. item_name ( def_id) . as_str ( ) ) ;
296
257
}
258
+ UrlFragment :: UserWritten ( raw) => s. push_str ( & raw ) ,
297
259
}
298
260
}
299
261
}
@@ -315,11 +277,6 @@ struct DiagnosticInfo<'a> {
315
277
link_range : Range < usize > ,
316
278
}
317
279
318
- #[ derive( Clone , Debug , Hash ) ]
319
- struct CachedLink {
320
- res : ( Res , Option < UrlFragment > ) ,
321
- }
322
-
323
280
struct LinkCollector < ' a , ' tcx > {
324
281
cx : & ' a mut DocContext < ' tcx > ,
325
282
/// A stack of modules used to decide what scope to resolve in.
@@ -329,7 +286,7 @@ struct LinkCollector<'a, 'tcx> {
329
286
mod_ids : Vec < DefId > ,
330
287
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
331
288
/// The link will be `None` if it could not be resolved (i.e. the error was cached).
332
- visited_links : FxHashMap < ResolutionInfo , Option < CachedLink > > ,
289
+ visited_links : FxHashMap < ResolutionInfo , Option < ( Res , Option < UrlFragment > ) > > ,
333
290
}
334
291
335
292
impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
@@ -1097,6 +1054,9 @@ impl LinkCollector<'_, '_> {
1097
1054
extra_fragment : extra_fragment. clone ( ) ,
1098
1055
} ,
1099
1056
diag_info. clone ( ) , // this struct should really be Copy, but Range is not :(
1057
+ // For reference-style links we want to report only one error so unsuccessful
1058
+ // resolutions are cached, for other links we want to report an error every
1059
+ // time so they are not cached.
1100
1060
matches ! ( ori_link. kind, LinkType :: Reference | LinkType :: Shortcut ) ,
1101
1061
) ?;
1102
1062
@@ -1123,7 +1083,7 @@ impl LinkCollector<'_, '_> {
1123
1083
1124
1084
match res {
1125
1085
Res :: Primitive ( prim) => {
1126
- if let Some ( UrlFragment :: Item ( ItemFragment ( _ , id ) ) ) = fragment {
1086
+ if let Some ( UrlFragment :: Item ( id ) ) = fragment {
1127
1087
// We're actually resolving an associated item of a primitive, so we need to
1128
1088
// verify the disambiguator (if any) matches the type of the associated item.
1129
1089
// This case should really follow the same flow as the `Res::Def` branch below,
@@ -1171,12 +1131,11 @@ impl LinkCollector<'_, '_> {
1171
1131
} )
1172
1132
}
1173
1133
Res :: Def ( kind, id) => {
1174
- let ( kind_for_dis, id_for_dis) =
1175
- if let Some ( UrlFragment :: Item ( ItemFragment ( _, id) ) ) = fragment {
1176
- ( self . cx . tcx . def_kind ( id) , id)
1177
- } else {
1178
- ( kind, id)
1179
- } ;
1134
+ let ( kind_for_dis, id_for_dis) = if let Some ( UrlFragment :: Item ( id) ) = fragment {
1135
+ ( self . cx . tcx . def_kind ( id) , id)
1136
+ } else {
1137
+ ( kind, id)
1138
+ } ;
1180
1139
self . verify_disambiguator (
1181
1140
path_str,
1182
1141
ori_link,
@@ -1294,53 +1253,33 @@ impl LinkCollector<'_, '_> {
1294
1253
& mut self ,
1295
1254
key : ResolutionInfo ,
1296
1255
diag : DiagnosticInfo < ' _ > ,
1297
- cache_resolution_failure : bool ,
1256
+ // If errors are cached then they are only reported on first ocurrence
1257
+ // which we want in some cases but not in others.
1258
+ cache_errors : bool ,
1298
1259
) -> Option < ( Res , Option < UrlFragment > ) > {
1299
- if let Some ( ref cached) = self . visited_links . get ( & key) {
1300
- match cached {
1301
- Some ( cached) => {
1302
- return Some ( cached. res . clone ( ) ) ;
1303
- }
1304
- None if cache_resolution_failure => return None ,
1305
- None => {
1306
- // Although we hit the cache and found a resolution error, this link isn't
1307
- // supposed to cache those. Run link resolution again to emit the expected
1308
- // resolution error.
1309
- }
1260
+ if let Some ( res) = self . visited_links . get ( & key) {
1261
+ if res. is_some ( ) || cache_errors {
1262
+ return res. clone ( ) ;
1310
1263
}
1311
1264
}
1312
1265
1313
1266
let res = self . resolve_with_disambiguator ( & key, diag. clone ( ) ) . and_then ( |( res, def_id) | {
1314
1267
let fragment = match ( & key. extra_fragment , def_id) {
1315
1268
( Some ( _) , Some ( def_id) ) => {
1316
- report_anchor_conflict ( self . cx , diag, Res :: from_def_id ( self . cx . tcx , def_id) ) ;
1269
+ report_anchor_conflict ( self . cx , diag, def_id) ;
1317
1270
return None ;
1318
1271
}
1319
1272
( Some ( u_frag) , None ) => Some ( UrlFragment :: UserWritten ( u_frag. clone ( ) ) ) ,
1320
- ( None , Some ( def_id) ) => Some ( UrlFragment :: Item ( ItemFragment (
1321
- FragmentKind :: from_def_id ( self . cx . tcx , def_id) ,
1322
- def_id,
1323
- ) ) ) ,
1273
+ ( None , Some ( def_id) ) => Some ( UrlFragment :: Item ( def_id) ) ,
1324
1274
( None , None ) => None ,
1325
1275
} ;
1326
1276
Some ( ( res, fragment) )
1327
1277
} ) ;
1328
1278
1329
- // Cache only if resolved successfully - don't silence duplicate errors
1330
- if let Some ( res) = res {
1331
- // Store result for the actual namespace
1332
- self . visited_links . insert ( key, Some ( CachedLink { res : res. clone ( ) } ) ) ;
1333
-
1334
- Some ( res)
1335
- } else {
1336
- if cache_resolution_failure {
1337
- // For reference-style links we only want to report one resolution error
1338
- // so let's cache them as well.
1339
- self . visited_links . insert ( key, None ) ;
1340
- }
1341
-
1342
- None
1279
+ if res. is_some ( ) || cache_errors {
1280
+ self . visited_links . insert ( key, res. clone ( ) ) ;
1343
1281
}
1282
+ res
1344
1283
}
1345
1284
1346
1285
/// After parsing the disambiguator, resolve the main part of the link.
@@ -1916,8 +1855,8 @@ fn report_multiple_anchors(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>) {
1916
1855
anchor_failure ( cx, diag_info, & msg, 1 )
1917
1856
}
1918
1857
1919
- fn report_anchor_conflict ( cx : & DocContext < ' _ > , diag_info : DiagnosticInfo < ' _ > , res : Res ) {
1920
- let ( link, kind) = ( diag_info. ori_link , res . descr ( ) ) ;
1858
+ fn report_anchor_conflict ( cx : & DocContext < ' _ > , diag_info : DiagnosticInfo < ' _ > , def_id : DefId ) {
1859
+ let ( link, kind) = ( diag_info. ori_link , Res :: from_def_id ( cx . tcx , def_id ) . descr ( ) ) ;
1921
1860
let msg = format ! ( "`{link}` contains an anchor, but links to {kind}s are already anchored" ) ;
1922
1861
anchor_failure ( cx, diag_info, & msg, 0 )
1923
1862
}
0 commit comments