@@ -34,8 +34,17 @@ use crate::clean::{self, Crate, Item, ItemId, ItemLink, PrimitiveType};
3434use crate :: core:: DocContext ;
3535use crate :: html:: markdown:: { MarkdownLink , MarkdownLinkRange , markdown_links} ;
3636use crate :: lint:: { BROKEN_INTRA_DOC_LINKS , PRIVATE_INTRA_DOC_LINKS } ;
37+ use crate :: passes:: Pass ;
3738use crate :: visit:: DocVisitor ;
3839
40+ /// We create an empty pass for `collect_intra_doc_link` so it still listed when displaying
41+ /// passes list.
42+ pub ( crate ) const COLLECT_INTRA_DOC_LINKS : Pass = Pass {
43+ name : "collect-intra-doc-links" ,
44+ run : |krate : Crate , _: & mut DocContext < ' _ > | krate,
45+ description : "resolves intra-doc links" ,
46+ } ;
47+
3948pub ( crate ) fn collect_intra_doc_links < ' a , ' tcx > (
4049 krate : Crate ,
4150 cx : & ' a mut DocContext < ' tcx > ,
@@ -247,6 +256,7 @@ pub(crate) struct ResolutionInfo {
247256 dis : Option < Disambiguator > ,
248257 path_str : Box < str > ,
249258 extra_fragment : Option < String > ,
259+ link_range : MarkdownLinkRange ,
250260}
251261
252262#[ derive( Clone , Debug ) ]
@@ -290,7 +300,7 @@ pub(crate) struct LinkCollector<'a, 'tcx> {
290300 pub ( crate ) cx : & ' a mut DocContext < ' tcx > ,
291301 /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
292302 /// The link will be `None` if it could not be resolved (i.e. the error was cached).
293- pub ( crate ) visited_links : FxHashMap < ResolutionInfo , Option < ( Res , Option < UrlFragment > ) > > ,
303+ pub ( crate ) visited_links : FxHashMap < ResolutionInfo , Option < Vec < ( Res , Option < UrlFragment > ) > > > ,
294304 /// These links are ambiguous. We need for the cache to have its paths filled. Unfortunately,
295305 /// if we run the `LinkCollector` pass after `Cache::populate`, a lot of items that we need
296306 /// to go through will be removed, making a lot of intra-doc links to not be inferred.
@@ -299,7 +309,7 @@ pub(crate) struct LinkCollector<'a, 'tcx> {
299309 /// inferring them (if possible).
300310 ///
301311 /// Key is `(item ID, path str)`.
302- pub ( crate ) ambiguous_links : FxHashMap < ( ItemId , String ) , AmbiguousLinks > ,
312+ pub ( crate ) ambiguous_links : FxHashMap < ( ItemId , String ) , Vec < AmbiguousLinks > > ,
303313}
304314
305315impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
@@ -1079,6 +1089,7 @@ impl LinkCollector<'_, '_> {
10791089 dis : disambiguator,
10801090 path_str : path_str. clone ( ) ,
10811091 extra_fragment : extra_fragment. clone ( ) ,
1092+ link_range : ori_link. range . clone ( ) ,
10821093 } ,
10831094 diag_info. clone ( ) , // this struct should really be Copy, but Range is not :(
10841095 // For reference-style links we want to report only one error so unsuccessful
@@ -1095,7 +1106,11 @@ impl LinkCollector<'_, '_> {
10951106 diag_info : diag_info. into ( ) ,
10961107 resolved,
10971108 } ;
1098- self . ambiguous_links . insert ( ( item. item_id , path_str. to_string ( ) ) , links) ;
1109+
1110+ self . ambiguous_links
1111+ . entry ( ( item. item_id , path_str. to_string ( ) ) )
1112+ . or_default ( )
1113+ . push ( links) ;
10991114 None
11001115 } else if let Some ( ( res, fragment) ) = resolved. pop ( ) {
11011116 self . compute_link ( res, fragment, path_str, disambiguator, diag_info, link_text)
@@ -1152,51 +1167,60 @@ impl LinkCollector<'_, '_> {
11521167 pub ( crate ) fn resolve_ambiguities ( & mut self ) {
11531168 let mut ambiguous_links = mem:: take ( & mut self . ambiguous_links ) ;
11541169
1155- for ( ( item_id, path_str) , info) in ambiguous_links. iter_mut ( ) {
1156- info. resolved . retain ( |( res, _) | match res {
1157- Res :: Def ( _, def_id) => self . validate_link ( * def_id) ,
1158- // Primitive types are always valid.
1159- Res :: Primitive ( _) => true ,
1160- } ) ;
1161- let diag_info = info. diag_info . into_info ( ) ;
1162- match info. resolved . len ( ) {
1163- 1 => {
1164- let ( res, fragment) = info. resolved . pop ( ) . unwrap ( ) ;
1165- if let Some ( link) = self . compute_link (
1166- res,
1167- fragment,
1168- path_str,
1169- info. disambiguator ,
1170- diag_info,
1171- & info. link_text ,
1172- ) {
1173- self . save_link ( * item_id, link) ;
1170+ for ( ( item_id, path_str) , info_items) in ambiguous_links. iter_mut ( ) {
1171+ for info in info_items {
1172+ info. resolved . retain ( |( res, _) | match res {
1173+ Res :: Def ( _, def_id) => self . validate_link ( * def_id) ,
1174+ // Primitive types are always valid.
1175+ Res :: Primitive ( _) => true ,
1176+ } ) ;
1177+ let diag_info = info. diag_info . into_info ( ) ;
1178+ match info. resolved . len ( ) {
1179+ 1 => {
1180+ let ( res, fragment) = info. resolved . pop ( ) . unwrap ( ) ;
1181+ if let Some ( link) = self . compute_link (
1182+ res,
1183+ fragment,
1184+ path_str,
1185+ info. disambiguator ,
1186+ diag_info,
1187+ & info. link_text ,
1188+ ) {
1189+ self . save_link ( * item_id, link) ;
1190+ }
1191+ }
1192+ 0 => {
1193+ report_diagnostic (
1194+ self . cx . tcx ,
1195+ BROKEN_INTRA_DOC_LINKS ,
1196+ format ! (
1197+ "all items matching `{path_str}` are either private or doc(hidden)"
1198+ ) ,
1199+ & diag_info,
1200+ |diag, sp, _| {
1201+ if let Some ( sp) = sp {
1202+ diag. span_label ( sp, "unresolved link" ) ;
1203+ } else {
1204+ diag. note ( "unresolved link" ) ;
1205+ }
1206+ } ,
1207+ ) ;
1208+ }
1209+ _ => {
1210+ let candidates = info
1211+ . resolved
1212+ . iter ( )
1213+ . map ( |( res, fragment) | {
1214+ let def_id = if let Some ( UrlFragment :: Item ( def_id) ) = fragment {
1215+ Some ( * def_id)
1216+ } else {
1217+ None
1218+ } ;
1219+ ( * res, def_id)
1220+ } )
1221+ . collect :: < Vec < _ > > ( ) ;
1222+ ambiguity_error ( self . cx , & diag_info, path_str, & candidates, true ) ;
11741223 }
1175- }
1176- 0 => {
1177- report_diagnostic (
1178- self . cx . tcx ,
1179- BROKEN_INTRA_DOC_LINKS ,
1180- format ! (
1181- "all items matching `{path_str}` are either private or doc(hidden)"
1182- ) ,
1183- & diag_info,
1184- |diag, sp, _| {
1185- if let Some ( sp) = sp {
1186- diag. span_label ( sp, "unresolved link" ) ;
1187- } else {
1188- diag. note ( "unresolved link" ) ;
1189- }
1190- } ,
1191- ) ;
1192- }
1193- _ => {
1194- let candidates = info
1195- . resolved
1196- . iter ( )
1197- . map ( |( res, _) | ( * res, res. def_id ( self . cx . tcx ) ) )
1198- . collect :: < Vec < _ > > ( ) ;
1199- ambiguity_error ( self . cx , & diag_info, path_str, & candidates, true ) ;
12001224 }
12011225 }
12021226 }
@@ -1387,15 +1411,13 @@ impl LinkCollector<'_, '_> {
13871411 diag : DiagnosticInfo < ' _ > ,
13881412 // If errors are cached then they are only reported on first occurrence
13891413 // which we want in some cases but not in others.
1390- cache_errors : bool ,
1414+ _cache_errors : bool ,
13911415 // If this call is intended to be recoverable, then pass true to silence.
13921416 // This is only recoverable when path is failed to resolved.
13931417 recoverable : bool ,
13941418 ) -> Option < Vec < ( Res , Option < UrlFragment > ) > > {
13951419 if let Some ( res) = self . visited_links . get ( & key) {
1396- if res. is_some ( ) || cache_errors {
1397- return res. clone ( ) . map ( |r| vec ! [ r] ) ;
1398- }
1420+ return res. clone ( ) ;
13991421 }
14001422
14011423 let mut candidates = self . resolve_with_disambiguator ( & key, diag. clone ( ) , recoverable) ;
@@ -1426,25 +1448,24 @@ impl LinkCollector<'_, '_> {
14261448 }
14271449
14281450 let mut resolved = Vec :: with_capacity ( candidates. len ( ) ) ;
1429- for ( res, def_id) in candidates. as_slice ( ) {
1451+ for ( res, def_id) in candidates {
14301452 let fragment = match ( & key. extra_fragment , def_id) {
14311453 ( Some ( _) , Some ( def_id) ) => {
1432- report_anchor_conflict ( self . cx , diag, * def_id) ;
1454+ report_anchor_conflict ( self . cx , diag, def_id) ;
14331455 return None ;
14341456 }
14351457 ( Some ( u_frag) , None ) => Some ( UrlFragment :: UserWritten ( u_frag. clone ( ) ) ) ,
1436- ( None , Some ( def_id) ) => Some ( UrlFragment :: Item ( * def_id) ) ,
1458+ ( None , Some ( def_id) ) => Some ( UrlFragment :: Item ( def_id) ) ,
14371459 ( None , None ) => None ,
14381460 } ;
1439- let r = ( res. clone ( ) , fragment. clone ( ) ) ;
1440- self . visited_links . insert ( key. clone ( ) , Some ( r. clone ( ) ) ) ;
1441- resolved. push ( r) ;
1461+ resolved. push ( ( res, fragment) ) ;
14421462 }
14431463
1444- if resolved. is_empty ( ) && cache_errors {
1464+ if resolved. is_empty ( ) {
14451465 self . visited_links . insert ( key, None ) ;
14461466 None
14471467 } else {
1468+ self . visited_links . insert ( key. clone ( ) , Some ( resolved. clone ( ) ) ) ;
14481469 Some ( resolved)
14491470 }
14501471 }
0 commit comments