@@ -25,6 +25,8 @@ use rustc_span::symbol::Symbol;
2525use rustc_span:: DUMMY_SP ;
2626use smallvec:: { smallvec, SmallVec } ;
2727
28+ use pulldown_cmark:: LinkType ;
29+
2830use std:: borrow:: Cow ;
2931use std:: cell:: Cell ;
3032use std:: convert:: { TryFrom , TryInto } ;
@@ -34,7 +36,7 @@ use std::ops::Range;
3436use crate :: clean:: { self , utils:: find_nearest_parent_module, Crate , Item , ItemLink , PrimitiveType } ;
3537use crate :: core:: DocContext ;
3638use crate :: fold:: DocFolder ;
37- use crate :: html:: markdown:: markdown_links;
39+ use crate :: html:: markdown:: { markdown_links, MarkdownLink } ;
3840use crate :: passes:: Pass ;
3941
4042use super :: span_of_attrs;
@@ -265,8 +267,9 @@ struct LinkCollector<'a, 'tcx> {
265267 /// because `clean` and the disambiguator code expect them to be different.
266268 /// See the code for associated items on inherent impls for details.
267269 kind_side_channel : Cell < Option < ( DefKind , DefId ) > > ,
268- /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link
269- visited_links : FxHashMap < ResolutionInfo , CachedLink > ,
270+ /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
271+ /// The link will be `None` if it could not be resolved (i.e. the error was cached).
272+ visited_links : FxHashMap < ResolutionInfo , Option < CachedLink > > ,
270273}
271274
272275impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
@@ -901,16 +904,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
901904 } ;
902905 // NOTE: if there are links that start in one crate and end in another, this will not resolve them.
903906 // This is a degenerate case and it's not supported by rustdoc.
904- for ( ori_link, link_range) in markdown_links ( & doc) {
905- let link = self . resolve_link (
906- & item,
907- & doc,
908- & self_name,
909- parent_node,
910- krate,
911- ori_link,
912- link_range,
913- ) ;
907+ for md_link in markdown_links ( & doc) {
908+ let link = self . resolve_link ( & item, & doc, & self_name, parent_node, krate, md_link) ;
914909 if let Some ( link) = link {
915910 item. attrs . links . push ( link) ;
916911 }
@@ -942,27 +937,26 @@ impl LinkCollector<'_, '_> {
942937 self_name : & Option < String > ,
943938 parent_node : Option < DefId > ,
944939 krate : CrateNum ,
945- ori_link : String ,
946- link_range : Range < usize > ,
940+ ori_link : MarkdownLink ,
947941 ) -> Option < ItemLink > {
948- trace ! ( "considering link '{}'" , ori_link) ;
942+ trace ! ( "considering link '{}'" , ori_link. link ) ;
949943
950944 // Bail early for real links.
951- if ori_link. contains ( '/' ) {
945+ if ori_link. link . contains ( '/' ) {
952946 return None ;
953947 }
954948
955949 // [] is mostly likely not supposed to be a link
956- if ori_link. is_empty ( ) {
950+ if ori_link. link . is_empty ( ) {
957951 return None ;
958952 }
959953
960954 let cx = self . cx ;
961- let link = ori_link. replace ( "`" , "" ) ;
955+ let link = ori_link. link . replace ( "`" , "" ) ;
962956 let parts = link. split ( '#' ) . collect :: < Vec < _ > > ( ) ;
963957 let ( link, extra_fragment) = if parts. len ( ) > 2 {
964958 // A valid link can't have multiple #'s
965- anchor_failure ( cx, & item, & link, dox, link_range , AnchorFailure :: MultipleAnchors ) ;
959+ anchor_failure ( cx, & item, & link, dox, ori_link . range , AnchorFailure :: MultipleAnchors ) ;
966960 return None ;
967961 } else if parts. len ( ) == 2 {
968962 if parts[ 0 ] . trim ( ) . is_empty ( ) {
@@ -1018,7 +1012,7 @@ impl LinkCollector<'_, '_> {
10181012 path_str,
10191013 disambiguator,
10201014 dox,
1021- link_range ,
1015+ ori_link . range ,
10221016 smallvec ! [ ResolutionFailure :: NoParentItem ] ,
10231017 ) ;
10241018 return None ;
@@ -1058,7 +1052,7 @@ impl LinkCollector<'_, '_> {
10581052 path_str,
10591053 disambiguator,
10601054 dox,
1061- link_range ,
1055+ ori_link . range ,
10621056 smallvec ! [ err_kind] ,
10631057 ) ;
10641058 return None ;
@@ -1074,15 +1068,22 @@ impl LinkCollector<'_, '_> {
10741068 return None ;
10751069 }
10761070
1077- let key = ResolutionInfo {
1078- module_id ,
1079- dis : disambiguator ,
1080- path_str : path_str . to_owned ( ) ,
1081- extra_fragment ,
1071+ let diag_info = DiagnosticInfo {
1072+ item ,
1073+ dox ,
1074+ ori_link : & ori_link . link ,
1075+ link_range : ori_link . range . clone ( ) ,
10821076 } ;
1083- let diag =
1084- DiagnosticInfo { item, dox, ori_link : & ori_link, link_range : link_range. clone ( ) } ;
1085- let ( mut res, mut fragment) = self . resolve_with_disambiguator_cached ( key, diag) ?;
1077+ let ( mut res, mut fragment) = self . resolve_with_disambiguator_cached (
1078+ ResolutionInfo {
1079+ module_id,
1080+ dis : disambiguator,
1081+ path_str : path_str. to_owned ( ) ,
1082+ extra_fragment,
1083+ } ,
1084+ diag_info,
1085+ matches ! ( ori_link. kind, LinkType :: Reference | LinkType :: Shortcut ) ,
1086+ ) ?;
10861087
10871088 // Check for a primitive which might conflict with a module
10881089 // Report the ambiguity and require that the user specify which one they meant.
@@ -1101,7 +1102,7 @@ impl LinkCollector<'_, '_> {
11011102 & item,
11021103 path_str,
11031104 dox,
1104- link_range ,
1105+ ori_link . range ,
11051106 AnchorFailure :: RustdocAnchorConflict ( prim) ,
11061107 ) ;
11071108 return None ;
@@ -1111,7 +1112,7 @@ impl LinkCollector<'_, '_> {
11111112 } else {
11121113 // `[char]` when a `char` module is in scope
11131114 let candidates = vec ! [ res, prim] ;
1114- ambiguity_error ( cx, & item, path_str, dox, link_range , candidates) ;
1115+ ambiguity_error ( cx, & item, path_str, dox, ori_link . range , candidates) ;
11151116 return None ;
11161117 }
11171118 }
@@ -1129,14 +1130,22 @@ impl LinkCollector<'_, '_> {
11291130 specified. descr( )
11301131 ) ;
11311132 diag. note ( & note) ;
1132- suggest_disambiguator ( resolved, diag, path_str, dox, sp, & link_range ) ;
1133+ suggest_disambiguator ( resolved, diag, path_str, dox, sp, & ori_link . range ) ;
11331134 } ;
1134- report_diagnostic ( cx, BROKEN_INTRA_DOC_LINKS , & msg, & item, dox, & link_range, callback) ;
1135+ report_diagnostic (
1136+ cx,
1137+ BROKEN_INTRA_DOC_LINKS ,
1138+ & msg,
1139+ & item,
1140+ dox,
1141+ & ori_link. range ,
1142+ callback,
1143+ ) ;
11351144 } ;
11361145 match res {
11371146 Res :: Primitive ( _) => match disambiguator {
11381147 Some ( Disambiguator :: Primitive | Disambiguator :: Namespace ( _) ) | None => {
1139- Some ( ItemLink { link : ori_link, link_text, did : None , fragment } )
1148+ Some ( ItemLink { link : ori_link. link , link_text, did : None , fragment } )
11401149 }
11411150 Some ( other) => {
11421151 report_mismatch ( other, Disambiguator :: Primitive ) ;
@@ -1179,11 +1188,11 @@ impl LinkCollector<'_, '_> {
11791188 if self . cx . tcx . privacy_access_levels ( LOCAL_CRATE ) . is_exported ( hir_src)
11801189 && !self . cx . tcx . privacy_access_levels ( LOCAL_CRATE ) . is_exported ( hir_dst)
11811190 {
1182- privacy_error ( cx, & item, & path_str, dox, link_range ) ;
1191+ privacy_error ( cx, & item, & path_str, dox, & ori_link ) ;
11831192 }
11841193 }
11851194 let id = clean:: register_res ( cx, rustc_hir:: def:: Res :: Def ( kind, id) ) ;
1186- Some ( ItemLink { link : ori_link, link_text, did : Some ( id) , fragment } )
1195+ Some ( ItemLink { link : ori_link. link , link_text, did : Some ( id) , fragment } )
11871196 }
11881197 }
11891198 }
@@ -1192,28 +1201,47 @@ impl LinkCollector<'_, '_> {
11921201 & mut self ,
11931202 key : ResolutionInfo ,
11941203 diag : DiagnosticInfo < ' _ > ,
1204+ cache_resolution_failure : bool ,
11951205 ) -> Option < ( Res , Option < String > ) > {
11961206 // Try to look up both the result and the corresponding side channel value
11971207 if let Some ( ref cached) = self . visited_links . get ( & key) {
1198- self . kind_side_channel . set ( cached. side_channel ) ;
1199- return Some ( cached. res . clone ( ) ) ;
1208+ match cached {
1209+ Some ( cached) => {
1210+ self . kind_side_channel . set ( cached. side_channel . clone ( ) ) ;
1211+ return Some ( cached. res . clone ( ) ) ;
1212+ }
1213+ None if cache_resolution_failure => return None ,
1214+ None => {
1215+ // Although we hit the cache and found a resolution error, this link isn't
1216+ // supposed to cache those. Run link resolution again to emit the expected
1217+ // resolution error.
1218+ }
1219+ }
12001220 }
12011221
12021222 let res = self . resolve_with_disambiguator ( & key, diag) ;
12031223
12041224 // Cache only if resolved successfully - don't silence duplicate errors
1205- if let Some ( res) = & res {
1225+ if let Some ( res) = res {
12061226 // Store result for the actual namespace
12071227 self . visited_links . insert (
12081228 key,
1209- CachedLink {
1229+ Some ( CachedLink {
12101230 res : res. clone ( ) ,
12111231 side_channel : self . kind_side_channel . clone ( ) . into_inner ( ) ,
1212- } ,
1232+ } ) ,
12131233 ) ;
1214- }
12151234
1216- res
1235+ Some ( res)
1236+ } else {
1237+ if cache_resolution_failure {
1238+ // For reference-style links we only want to report one resolution error
1239+ // so let's cache them as well.
1240+ self . visited_links . insert ( key, None ) ;
1241+ }
1242+
1243+ None
1244+ }
12171245 }
12181246
12191247 /// After parsing the disambiguator, resolve the main part of the link.
@@ -1964,13 +1992,7 @@ fn suggest_disambiguator(
19641992}
19651993
19661994/// Report a link from a public item to a private one.
1967- fn privacy_error (
1968- cx : & DocContext < ' _ > ,
1969- item : & Item ,
1970- path_str : & str ,
1971- dox : & str ,
1972- link_range : Range < usize > ,
1973- ) {
1995+ fn privacy_error ( cx : & DocContext < ' _ > , item : & Item , path_str : & str , dox : & str , link : & MarkdownLink ) {
19741996 let sym;
19751997 let item_name = match item. name {
19761998 Some ( name) => {
@@ -1982,7 +2004,7 @@ fn privacy_error(
19822004 let msg =
19832005 format ! ( "public documentation for `{}` links to private item `{}`" , item_name, path_str) ;
19842006
1985- report_diagnostic ( cx, PRIVATE_INTRA_DOC_LINKS , & msg, item, dox, & link_range , |diag, sp| {
2007+ report_diagnostic ( cx, PRIVATE_INTRA_DOC_LINKS , & msg, item, dox, & link . range , |diag, sp| {
19862008 if let Some ( sp) = sp {
19872009 diag. span_label ( sp, "this item is private" ) ;
19882010 }
0 commit comments