@@ -20,7 +20,6 @@ use rustc_span::BytePos;
2020use smallvec:: { smallvec, SmallVec } ;
2121
2222use std:: borrow:: Cow ;
23- use std:: fmt:: Write ;
2423use std:: mem;
2524use std:: ops:: Range ;
2625
@@ -220,80 +219,43 @@ enum MalformedGenerics {
220219
221220#[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
222221pub ( crate ) enum UrlFragment {
223- Item ( ItemFragment ) ,
222+ Item ( DefId ) ,
224223 UserWritten ( String ) ,
225224}
226225
227226impl UrlFragment {
228227 /// 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 ( '#' ) ;
230230 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+ }
294239 }
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 ( ) ) ;
296257 }
258+ UrlFragment :: UserWritten ( raw) => s. push_str ( & raw ) ,
297259 }
298260 }
299261}
@@ -315,11 +277,6 @@ struct DiagnosticInfo<'a> {
315277 link_range : Range < usize > ,
316278}
317279
318- #[ derive( Clone , Debug , Hash ) ]
319- struct CachedLink {
320- res : ( Res , Option < UrlFragment > ) ,
321- }
322-
323280struct LinkCollector < ' a , ' tcx > {
324281 cx : & ' a mut DocContext < ' tcx > ,
325282 /// A stack of modules used to decide what scope to resolve in.
@@ -329,7 +286,7 @@ struct LinkCollector<'a, 'tcx> {
329286 mod_ids : Vec < DefId > ,
330287 /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
331288 /// 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 > ) > > ,
333290}
334291
335292impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
@@ -1097,6 +1054,9 @@ impl LinkCollector<'_, '_> {
10971054 extra_fragment : extra_fragment. clone ( ) ,
10981055 } ,
10991056 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.
11001060 matches ! ( ori_link. kind, LinkType :: Reference | LinkType :: Shortcut ) ,
11011061 ) ?;
11021062
@@ -1123,7 +1083,7 @@ impl LinkCollector<'_, '_> {
11231083
11241084 match res {
11251085 Res :: Primitive ( prim) => {
1126- if let Some ( UrlFragment :: Item ( ItemFragment ( _ , id ) ) ) = fragment {
1086+ if let Some ( UrlFragment :: Item ( id ) ) = fragment {
11271087 // We're actually resolving an associated item of a primitive, so we need to
11281088 // verify the disambiguator (if any) matches the type of the associated item.
11291089 // This case should really follow the same flow as the `Res::Def` branch below,
@@ -1171,12 +1131,11 @@ impl LinkCollector<'_, '_> {
11711131 } )
11721132 }
11731133 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+ } ;
11801139 self . verify_disambiguator (
11811140 path_str,
11821141 ori_link,
@@ -1294,53 +1253,33 @@ impl LinkCollector<'_, '_> {
12941253 & mut self ,
12951254 key : ResolutionInfo ,
12961255 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 ,
12981259 ) -> 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 ( ) ;
13101263 }
13111264 }
13121265
13131266 let res = self . resolve_with_disambiguator ( & key, diag. clone ( ) ) . and_then ( |( res, def_id) | {
13141267 let fragment = match ( & key. extra_fragment , def_id) {
13151268 ( 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) ;
13171270 return None ;
13181271 }
13191272 ( 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) ) ,
13241274 ( None , None ) => None ,
13251275 } ;
13261276 Some ( ( res, fragment) )
13271277 } ) ;
13281278
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 ( ) ) ;
13431281 }
1282+ res
13441283 }
13451284
13461285 /// After parsing the disambiguator, resolve the main part of the link.
@@ -1916,8 +1855,8 @@ fn report_multiple_anchors(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>) {
19161855 anchor_failure ( cx, diag_info, & msg, 1 )
19171856}
19181857
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 ( ) ) ;
19211860 let msg = format ! ( "`{link}` contains an anchor, but links to {kind}s are already anchored" ) ;
19221861 anchor_failure ( cx, diag_info, & msg, 0 )
19231862}
0 commit comments