@@ -14,7 +14,7 @@ use rustc_data_structures::intern::Interned;
14
14
use rustc_errors:: { Applicability , Diag , DiagMessage } ;
15
15
use rustc_hir:: def:: Namespace :: * ;
16
16
use rustc_hir:: def:: { DefKind , Namespace , PerNS } ;
17
- use rustc_hir:: def_id:: { CRATE_DEF_ID , DefId } ;
17
+ use rustc_hir:: def_id:: { CRATE_DEF_ID , DefId , LOCAL_CRATE } ;
18
18
use rustc_hir:: { Mutability , Safety } ;
19
19
use rustc_middle:: ty:: { Ty , TyCtxt } ;
20
20
use rustc_middle:: { bug, span_bug, ty} ;
@@ -30,23 +30,30 @@ use smallvec::{SmallVec, smallvec};
30
30
use tracing:: { debug, info, instrument, trace} ;
31
31
32
32
use crate :: clean:: utils:: find_nearest_parent_module;
33
- use crate :: clean:: { self , Crate , Item , ItemLink , PrimitiveType } ;
33
+ use crate :: clean:: { self , Crate , Item , ItemId , ItemLink , PrimitiveType } ;
34
34
use crate :: core:: DocContext ;
35
35
use crate :: html:: markdown:: { MarkdownLink , MarkdownLinkRange , markdown_links} ;
36
36
use crate :: lint:: { BROKEN_INTRA_DOC_LINKS , PRIVATE_INTRA_DOC_LINKS } ;
37
- use crate :: passes:: Pass ;
38
37
use crate :: visit:: DocVisitor ;
39
38
40
- pub ( crate ) const COLLECT_INTRA_DOC_LINKS : Pass = Pass {
41
- name : "collect-intra-doc-links" ,
42
- run : collect_intra_doc_links,
43
- description : "resolves intra-doc links" ,
44
- } ;
45
-
46
- fn collect_intra_doc_links ( krate : Crate , cx : & mut DocContext < ' _ > ) -> Crate {
47
- let mut collector = LinkCollector { cx, visited_links : FxHashMap :: default ( ) } ;
39
+ pub ( crate ) fn collect_intra_doc_links < ' a , ' tcx > (
40
+ krate : Crate ,
41
+ cx : & ' a mut DocContext < ' tcx > ,
42
+ ) -> ( Crate , LinkCollector < ' a , ' tcx > ) {
43
+ let mut collector = LinkCollector {
44
+ cx,
45
+ visited_links : FxHashMap :: default ( ) ,
46
+ ambiguous_links : FxHashMap :: default ( ) ,
47
+ } ;
48
48
collector. visit_crate ( & krate) ;
49
- krate
49
+ ( krate, collector)
50
+ }
51
+
52
+ pub ( crate ) struct AmbiguousLinks {
53
+ pub ( crate ) disambiguator : Option < Disambiguator > ,
54
+ pub ( crate ) link_text : Box < str > ,
55
+ pub ( crate ) diag_info : OwnedDiagnosticInfo ,
56
+ pub ( crate ) resolved : Vec < ( Res , Option < UrlFragment > ) > ,
50
57
}
51
58
52
59
fn filter_assoc_items_by_name_and_namespace < ' a > (
@@ -61,7 +68,7 @@ fn filter_assoc_items_by_name_and_namespace<'a>(
61
68
}
62
69
63
70
#[ derive( Copy , Clone , Debug , Hash , PartialEq ) ]
64
- enum Res {
71
+ pub ( crate ) enum Res {
65
72
Def ( DefKind , DefId ) ,
66
73
Primitive ( PrimitiveType ) ,
67
74
}
@@ -234,7 +241,7 @@ impl UrlFragment {
234
241
}
235
242
236
243
#[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
237
- struct ResolutionInfo {
244
+ pub ( crate ) struct ResolutionInfo {
238
245
item_id : DefId ,
239
246
module_id : DefId ,
240
247
dis : Option < Disambiguator > ,
@@ -243,18 +250,54 @@ struct ResolutionInfo {
243
250
}
244
251
245
252
#[ derive( Clone ) ]
246
- struct DiagnosticInfo < ' a > {
253
+ pub ( crate ) struct DiagnosticInfo < ' a > {
247
254
item : & ' a Item ,
248
255
dox : & ' a str ,
249
256
ori_link : & ' a str ,
250
257
link_range : MarkdownLinkRange ,
251
258
}
252
259
253
- struct LinkCollector < ' a , ' tcx > {
254
- cx : & ' a mut DocContext < ' tcx > ,
260
+ pub ( crate ) struct OwnedDiagnosticInfo {
261
+ item : Item ,
262
+ ori_link : String ,
263
+ link_range : MarkdownLinkRange ,
264
+ }
265
+
266
+ impl From < DiagnosticInfo < ' _ > > for OwnedDiagnosticInfo {
267
+ fn from ( f : DiagnosticInfo < ' _ > ) -> Self {
268
+ Self {
269
+ item : f. item . clone ( ) ,
270
+ ori_link : f. ori_link . to_string ( ) ,
271
+ link_range : f. link_range . clone ( ) ,
272
+ }
273
+ }
274
+ }
275
+
276
+ impl OwnedDiagnosticInfo {
277
+ pub ( crate ) fn into_info ( & self ) -> DiagnosticInfo < ' _ > {
278
+ DiagnosticInfo {
279
+ item : & self . item ,
280
+ ori_link : & self . ori_link ,
281
+ dox : "" ,
282
+ link_range : self . link_range . clone ( ) ,
283
+ }
284
+ }
285
+ }
286
+
287
+ pub ( crate ) struct LinkCollector < ' a , ' tcx > {
288
+ pub ( crate ) cx : & ' a mut DocContext < ' tcx > ,
255
289
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
256
290
/// The link will be `None` if it could not be resolved (i.e. the error was cached).
257
- visited_links : FxHashMap < ResolutionInfo , Option < ( Res , Option < UrlFragment > ) > > ,
291
+ pub ( crate ) visited_links : FxHashMap < ResolutionInfo , Option < ( Res , Option < UrlFragment > ) > > ,
292
+ /// These links are ambiguous. We need for the cache to have its paths filled. Unfortunately,
293
+ /// if we run the `LinkCollector` pass after `Cache::populate`, a lot of items that we need
294
+ /// to go through will be removed, making a lot of intra-doc links to not be inferred.
295
+ ///
296
+ /// So instead, we store the ambiguous links and we wait for cache paths to be filled before
297
+ /// inferring them (if possible).
298
+ ///
299
+ /// Key is `(item ID, path str)`.
300
+ pub ( crate ) ambiguous_links : FxHashMap < ( ItemId , String ) , AmbiguousLinks > ,
258
301
}
259
302
260
303
impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
@@ -993,14 +1036,17 @@ impl LinkCollector<'_, '_> {
993
1036
_ => find_nearest_parent_module ( self . cx . tcx , item_id) . unwrap ( ) ,
994
1037
} ;
995
1038
for md_link in preprocessed_markdown_links ( & doc) {
996
- let link = self . resolve_link ( & doc, item, item_id, module_id, & md_link) ;
997
- if let Some ( link) = link {
998
- self . cx . cache . intra_doc_links . entry ( item. item_id ) . or_default ( ) . insert ( link) ;
1039
+ if let Some ( link) = self . resolve_link ( & doc, item, item_id, module_id, & md_link) {
1040
+ self . save_link ( item. item_id , link) ;
999
1041
}
1000
1042
}
1001
1043
}
1002
1044
}
1003
1045
1046
+ pub ( crate ) fn save_link ( & mut self , item_id : ItemId , link : ItemLink ) {
1047
+ self . cx . cache . intra_doc_links . entry ( item_id) . or_default ( ) . insert ( link) ;
1048
+ }
1049
+
1004
1050
/// This is the entry point for resolving an intra-doc link.
1005
1051
///
1006
1052
/// FIXME(jynelson): this is way too many arguments
@@ -1024,7 +1070,7 @@ impl LinkCollector<'_, '_> {
1024
1070
pp_link. as_ref ( ) . map_err ( |err| err. report ( self . cx , diag_info. clone ( ) ) ) . ok ( ) ?;
1025
1071
let disambiguator = * disambiguator;
1026
1072
1027
- let ( mut res , fragment ) = self . resolve_with_disambiguator_cached (
1073
+ let mut resolved = self . resolve_with_disambiguator_cached (
1028
1074
ResolutionInfo {
1029
1075
item_id,
1030
1076
module_id,
@@ -1040,6 +1086,101 @@ impl LinkCollector<'_, '_> {
1040
1086
false ,
1041
1087
) ?;
1042
1088
1089
+ if resolved. len ( ) > 1 {
1090
+ let links = AmbiguousLinks {
1091
+ disambiguator,
1092
+ link_text : link_text. clone ( ) ,
1093
+ diag_info : diag_info. into ( ) ,
1094
+ resolved,
1095
+ } ;
1096
+ self . ambiguous_links . insert ( ( item. item_id , path_str. to_string ( ) ) , links) ;
1097
+ None
1098
+ } else if let Some ( ( res, fragment) ) = resolved. pop ( ) {
1099
+ self . compute_link ( res, fragment, path_str, disambiguator, diag_info, link_text)
1100
+ } else {
1101
+ None
1102
+ }
1103
+ }
1104
+
1105
+ /// Returns `true` if a link could be generated from the given intra-doc information.
1106
+ ///
1107
+ /// This is a very light version of `format::href_with_root_path` since we're only interested
1108
+ /// about whether we can generate a link to an item or not.
1109
+ ///
1110
+ /// * If `original_did` is local, then we check if the item is reexported or public.
1111
+ /// * If `original_did` is not local, then we check if the crate it comes from is a direct
1112
+ /// public dependency.
1113
+ fn validate_link ( & self , original_did : DefId ) -> bool {
1114
+ let tcx = self . cx . tcx ;
1115
+ let def_kind = tcx. def_kind ( original_did) ;
1116
+ let did = match def_kind {
1117
+ DefKind :: AssocTy | DefKind :: AssocFn | DefKind :: AssocConst | DefKind :: Variant => {
1118
+ // documented on their parent's page
1119
+ tcx. parent ( original_did)
1120
+ }
1121
+ // If this a constructor, we get the parent (either a struct or a variant) and then
1122
+ // generate the link for this item.
1123
+ DefKind :: Ctor ( ..) => return self . validate_link ( tcx. parent ( original_did) ) ,
1124
+ DefKind :: ExternCrate => {
1125
+ // Link to the crate itself, not the `extern crate` item.
1126
+ if let Some ( local_did) = original_did. as_local ( ) {
1127
+ tcx. extern_mod_stmt_cnum ( local_did) . unwrap_or ( LOCAL_CRATE ) . as_def_id ( )
1128
+ } else {
1129
+ original_did
1130
+ }
1131
+ }
1132
+ _ => original_did,
1133
+ } ;
1134
+
1135
+ let cache = & self . cx . cache ;
1136
+ if !original_did. is_local ( )
1137
+ && !cache. effective_visibilities . is_directly_public ( tcx, did)
1138
+ && !cache. document_private
1139
+ && !cache. primitive_locations . values ( ) . any ( |& id| id == did)
1140
+ {
1141
+ return false ;
1142
+ }
1143
+
1144
+ cache. paths . get ( & did) . is_some ( )
1145
+ || cache. external_paths . get ( & did) . is_some ( )
1146
+ || !did. is_local ( )
1147
+ }
1148
+
1149
+ pub ( crate ) fn resolve_ambiguities ( & mut self ) {
1150
+ let mut ambiguous_links = mem:: take ( & mut self . ambiguous_links ) ;
1151
+
1152
+ for ( ( item_id, path_str) , info) in ambiguous_links. iter_mut ( ) {
1153
+ info. resolved . retain ( |( res, _) | match res {
1154
+ Res :: Def ( _, def_id) => self . validate_link ( * def_id) ,
1155
+ // Primitive types are always valid.
1156
+ Res :: Primitive ( _) => true ,
1157
+ } ) ;
1158
+ if info. resolved . len ( ) == 1 {
1159
+ let ( res, fragment) = info. resolved . pop ( ) . unwrap ( ) ;
1160
+ let diag_info = info. diag_info . into_info ( ) ;
1161
+ if let Some ( link) = self . compute_link (
1162
+ res,
1163
+ fragment,
1164
+ path_str,
1165
+ info. disambiguator ,
1166
+ diag_info,
1167
+ & info. link_text ,
1168
+ ) {
1169
+ self . save_link ( * item_id, link) ;
1170
+ }
1171
+ }
1172
+ }
1173
+ }
1174
+
1175
+ fn compute_link (
1176
+ & mut self ,
1177
+ mut res : Res ,
1178
+ fragment : Option < UrlFragment > ,
1179
+ path_str : & str ,
1180
+ disambiguator : Option < Disambiguator > ,
1181
+ diag_info : DiagnosticInfo < ' _ > ,
1182
+ link_text : & Box < str > ,
1183
+ ) -> Option < ItemLink > {
1043
1184
// Check for a primitive which might conflict with a module
1044
1185
// Report the ambiguity and require that the user specify which one they meant.
1045
1186
// FIXME: could there ever be a primitive not in the type namespace?
@@ -1055,7 +1196,7 @@ impl LinkCollector<'_, '_> {
1055
1196
} else {
1056
1197
// `[char]` when a `char` module is in scope
1057
1198
let candidates = & [ ( res, res. def_id ( self . cx . tcx ) ) , ( prim, None ) ] ;
1058
- ambiguity_error ( self . cx , & diag_info, path_str, candidates) ;
1199
+ ambiguity_error ( self . cx , & diag_info, path_str, candidates, true ) ;
1059
1200
return None ;
1060
1201
}
1061
1202
}
@@ -1085,7 +1226,7 @@ impl LinkCollector<'_, '_> {
1085
1226
}
1086
1227
1087
1228
res. def_id ( self . cx . tcx ) . map ( |page_id| ItemLink {
1088
- link : Box :: < str > :: from ( & * ori_link . link ) ,
1229
+ link : Box :: < str > :: from ( & * diag_info . ori_link ) ,
1089
1230
link_text : link_text. clone ( ) ,
1090
1231
page_id,
1091
1232
fragment,
@@ -1107,7 +1248,7 @@ impl LinkCollector<'_, '_> {
1107
1248
1108
1249
let page_id = clean:: register_res ( self . cx , rustc_hir:: def:: Res :: Def ( kind, id) ) ;
1109
1250
Some ( ItemLink {
1110
- link : Box :: < str > :: from ( & * ori_link . link ) ,
1251
+ link : Box :: < str > :: from ( & * diag_info . ori_link ) ,
1111
1252
link_text : link_text. clone ( ) ,
1112
1253
page_id,
1113
1254
fragment,
@@ -1220,10 +1361,10 @@ impl LinkCollector<'_, '_> {
1220
1361
// If this call is intended to be recoverable, then pass true to silence.
1221
1362
// This is only recoverable when path is failed to resolved.
1222
1363
recoverable : bool ,
1223
- ) -> Option < ( Res , Option < UrlFragment > ) > {
1364
+ ) -> Option < Vec < ( Res , Option < UrlFragment > ) > > {
1224
1365
if let Some ( res) = self . visited_links . get ( & key) {
1225
1366
if res. is_some ( ) || cache_errors {
1226
- return res. clone ( ) ;
1367
+ return res. clone ( ) . map ( |r| vec ! [ r ] ) ;
1227
1368
}
1228
1369
}
1229
1370
@@ -1249,30 +1390,33 @@ impl LinkCollector<'_, '_> {
1249
1390
// won't emit an error. So at this point, we can just take the first candidate as it was
1250
1391
// the first retrieved and use it to generate the link.
1251
1392
if let [ candidate, _candidate2, ..] = * candidates
1252
- && !ambiguity_error ( self . cx , & diag, & key. path_str , & candidates)
1393
+ && !ambiguity_error ( self . cx , & diag, & key. path_str , & candidates, false )
1253
1394
{
1254
1395
candidates = vec ! [ candidate] ;
1255
1396
}
1256
1397
1257
- if let & [ ( res, def_id) ] = candidates. as_slice ( ) {
1398
+ let mut resolved = Vec :: with_capacity ( candidates. len ( ) ) ;
1399
+ for ( res, def_id) in candidates. as_slice ( ) {
1258
1400
let fragment = match ( & key. extra_fragment , def_id) {
1259
1401
( Some ( _) , Some ( def_id) ) => {
1260
- report_anchor_conflict ( self . cx , diag, def_id) ;
1402
+ report_anchor_conflict ( self . cx , diag, * def_id) ;
1261
1403
return None ;
1262
1404
}
1263
1405
( Some ( u_frag) , None ) => Some ( UrlFragment :: UserWritten ( u_frag. clone ( ) ) ) ,
1264
- ( None , Some ( def_id) ) => Some ( UrlFragment :: Item ( def_id) ) ,
1406
+ ( None , Some ( def_id) ) => Some ( UrlFragment :: Item ( * def_id) ) ,
1265
1407
( None , None ) => None ,
1266
1408
} ;
1267
- let r = Some ( ( res, fragment) ) ;
1268
- self . visited_links . insert ( key, r. clone ( ) ) ;
1269
- return r ;
1409
+ let r = ( res. clone ( ) , fragment. clone ( ) ) ;
1410
+ self . visited_links . insert ( key. clone ( ) , Some ( r. clone ( ) ) ) ;
1411
+ resolved . push ( r ) ;
1270
1412
}
1271
1413
1272
- if cache_errors {
1414
+ if resolved . is_empty ( ) && cache_errors {
1273
1415
self . visited_links . insert ( key, None ) ;
1416
+ None
1417
+ } else {
1418
+ Some ( resolved)
1274
1419
}
1275
- None
1276
1420
}
1277
1421
1278
1422
/// After parsing the disambiguator, resolve the main part of the link.
@@ -1429,7 +1573,7 @@ fn should_ignore_link(path_str: &str) -> bool {
1429
1573
1430
1574
#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
1431
1575
/// Disambiguators for a link.
1432
- enum Disambiguator {
1576
+ pub ( crate ) enum Disambiguator {
1433
1577
/// `prim@`
1434
1578
///
1435
1579
/// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
@@ -2046,6 +2190,7 @@ fn ambiguity_error(
2046
2190
diag_info : & DiagnosticInfo < ' _ > ,
2047
2191
path_str : & str ,
2048
2192
candidates : & [ ( Res , Option < DefId > ) ] ,
2193
+ emit_error : bool ,
2049
2194
) -> bool {
2050
2195
let mut descrs = FxHashSet :: default ( ) ;
2051
2196
let kinds = candidates
@@ -2062,6 +2207,9 @@ fn ambiguity_error(
2062
2207
// candidate and not show a warning.
2063
2208
return false ;
2064
2209
}
2210
+ if !emit_error {
2211
+ return true ;
2212
+ }
2065
2213
2066
2214
let mut msg = format ! ( "`{path_str}` is " ) ;
2067
2215
match kinds. as_slice ( ) {
0 commit comments