@@ -25,6 +25,8 @@ use rustc_span::symbol::Symbol;
25
25
use rustc_span:: DUMMY_SP ;
26
26
use smallvec:: { smallvec, SmallVec } ;
27
27
28
+ use pulldown_cmark:: LinkType ;
29
+
28
30
use std:: borrow:: Cow ;
29
31
use std:: cell:: Cell ;
30
32
use std:: convert:: { TryFrom , TryInto } ;
@@ -34,7 +36,7 @@ use std::ops::Range;
34
36
use crate :: clean:: { self , utils:: find_nearest_parent_module, Crate , Item , ItemLink , PrimitiveType } ;
35
37
use crate :: core:: DocContext ;
36
38
use crate :: fold:: DocFolder ;
37
- use crate :: html:: markdown:: markdown_links;
39
+ use crate :: html:: markdown:: { markdown_links, MarkdownLink } ;
38
40
use crate :: passes:: Pass ;
39
41
40
42
use super :: span_of_attrs;
@@ -265,8 +267,9 @@ struct LinkCollector<'a, 'tcx> {
265
267
/// because `clean` and the disambiguator code expect them to be different.
266
268
/// See the code for associated items on inherent impls for details.
267
269
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 > > ,
270
273
}
271
274
272
275
impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
@@ -901,16 +904,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
901
904
} ;
902
905
// NOTE: if there are links that start in one crate and end in another, this will not resolve them.
903
906
// 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) ;
914
909
if let Some ( link) = link {
915
910
item. attrs . links . push ( link) ;
916
911
}
@@ -942,27 +937,26 @@ impl LinkCollector<'_, '_> {
942
937
self_name : & Option < String > ,
943
938
parent_node : Option < DefId > ,
944
939
krate : CrateNum ,
945
- ori_link : String ,
946
- link_range : Range < usize > ,
940
+ ori_link : MarkdownLink ,
947
941
) -> Option < ItemLink > {
948
- trace ! ( "considering link '{}'" , ori_link) ;
942
+ trace ! ( "considering link '{}'" , ori_link. link ) ;
949
943
950
944
// Bail early for real links.
951
- if ori_link. contains ( '/' ) {
945
+ if ori_link. link . contains ( '/' ) {
952
946
return None ;
953
947
}
954
948
955
949
// [] is mostly likely not supposed to be a link
956
- if ori_link. is_empty ( ) {
950
+ if ori_link. link . is_empty ( ) {
957
951
return None ;
958
952
}
959
953
960
954
let cx = self . cx ;
961
- let link = ori_link. replace ( "`" , "" ) ;
955
+ let link = ori_link. link . replace ( "`" , "" ) ;
962
956
let parts = link. split ( '#' ) . collect :: < Vec < _ > > ( ) ;
963
957
let ( link, extra_fragment) = if parts. len ( ) > 2 {
964
958
// 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 ) ;
966
960
return None ;
967
961
} else if parts. len ( ) == 2 {
968
962
if parts[ 0 ] . trim ( ) . is_empty ( ) {
@@ -1018,7 +1012,7 @@ impl LinkCollector<'_, '_> {
1018
1012
path_str,
1019
1013
disambiguator,
1020
1014
dox,
1021
- link_range ,
1015
+ ori_link . range ,
1022
1016
smallvec ! [ ResolutionFailure :: NoParentItem ] ,
1023
1017
) ;
1024
1018
return None ;
@@ -1058,7 +1052,7 @@ impl LinkCollector<'_, '_> {
1058
1052
path_str,
1059
1053
disambiguator,
1060
1054
dox,
1061
- link_range ,
1055
+ ori_link . range ,
1062
1056
smallvec ! [ err_kind] ,
1063
1057
) ;
1064
1058
return None ;
@@ -1074,15 +1068,22 @@ impl LinkCollector<'_, '_> {
1074
1068
return None ;
1075
1069
}
1076
1070
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 ( ) ,
1082
1076
} ;
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
+ ) ?;
1086
1087
1087
1088
// Check for a primitive which might conflict with a module
1088
1089
// Report the ambiguity and require that the user specify which one they meant.
@@ -1101,7 +1102,7 @@ impl LinkCollector<'_, '_> {
1101
1102
& item,
1102
1103
path_str,
1103
1104
dox,
1104
- link_range ,
1105
+ ori_link . range ,
1105
1106
AnchorFailure :: RustdocAnchorConflict ( prim) ,
1106
1107
) ;
1107
1108
return None ;
@@ -1111,7 +1112,7 @@ impl LinkCollector<'_, '_> {
1111
1112
} else {
1112
1113
// `[char]` when a `char` module is in scope
1113
1114
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) ;
1115
1116
return None ;
1116
1117
}
1117
1118
}
@@ -1129,14 +1130,22 @@ impl LinkCollector<'_, '_> {
1129
1130
specified. descr( )
1130
1131
) ;
1131
1132
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 ) ;
1133
1134
} ;
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
+ ) ;
1135
1144
} ;
1136
1145
match res {
1137
1146
Res :: Primitive ( _) => match disambiguator {
1138
1147
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 } )
1140
1149
}
1141
1150
Some ( other) => {
1142
1151
report_mismatch ( other, Disambiguator :: Primitive ) ;
@@ -1179,11 +1188,11 @@ impl LinkCollector<'_, '_> {
1179
1188
if self . cx . tcx . privacy_access_levels ( LOCAL_CRATE ) . is_exported ( hir_src)
1180
1189
&& !self . cx . tcx . privacy_access_levels ( LOCAL_CRATE ) . is_exported ( hir_dst)
1181
1190
{
1182
- privacy_error ( cx, & item, & path_str, dox, link_range ) ;
1191
+ privacy_error ( cx, & item, & path_str, dox, & ori_link ) ;
1183
1192
}
1184
1193
}
1185
1194
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 } )
1187
1196
}
1188
1197
}
1189
1198
}
@@ -1192,28 +1201,47 @@ impl LinkCollector<'_, '_> {
1192
1201
& mut self ,
1193
1202
key : ResolutionInfo ,
1194
1203
diag : DiagnosticInfo < ' _ > ,
1204
+ cache_resolution_failure : bool ,
1195
1205
) -> Option < ( Res , Option < String > ) > {
1196
1206
// Try to look up both the result and the corresponding side channel value
1197
1207
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
+ }
1200
1220
}
1201
1221
1202
1222
let res = self . resolve_with_disambiguator ( & key, diag) ;
1203
1223
1204
1224
// Cache only if resolved successfully - don't silence duplicate errors
1205
- if let Some ( res) = & res {
1225
+ if let Some ( res) = res {
1206
1226
// Store result for the actual namespace
1207
1227
self . visited_links . insert (
1208
1228
key,
1209
- CachedLink {
1229
+ Some ( CachedLink {
1210
1230
res : res. clone ( ) ,
1211
1231
side_channel : self . kind_side_channel . clone ( ) . into_inner ( ) ,
1212
- } ,
1232
+ } ) ,
1213
1233
) ;
1214
- }
1215
1234
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
+ }
1217
1245
}
1218
1246
1219
1247
/// After parsing the disambiguator, resolve the main part of the link.
@@ -1964,13 +1992,7 @@ fn suggest_disambiguator(
1964
1992
}
1965
1993
1966
1994
/// 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 ) {
1974
1996
let sym;
1975
1997
let item_name = match item. name {
1976
1998
Some ( name) => {
@@ -1982,7 +2004,7 @@ fn privacy_error(
1982
2004
let msg =
1983
2005
format ! ( "public documentation for `{}` links to private item `{}`" , item_name, path_str) ;
1984
2006
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| {
1986
2008
if let Some ( sp) = sp {
1987
2009
diag. span_label ( sp, "this item is private" ) ;
1988
2010
}
0 commit comments