@@ -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
}
@@ -233,7 +240,7 @@ impl UrlFragment {
233
240
}
234
241
235
242
#[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
236
- struct ResolutionInfo {
243
+ pub ( crate ) struct ResolutionInfo {
237
244
item_id : DefId ,
238
245
module_id : DefId ,
239
246
dis : Option < Disambiguator > ,
@@ -242,18 +249,54 @@ struct ResolutionInfo {
242
249
}
243
250
244
251
#[ derive( Clone ) ]
245
- struct DiagnosticInfo < ' a > {
252
+ pub ( crate ) struct DiagnosticInfo < ' a > {
246
253
item : & ' a Item ,
247
254
dox : & ' a str ,
248
255
ori_link : & ' a str ,
249
256
link_range : MarkdownLinkRange ,
250
257
}
251
258
252
- struct LinkCollector < ' a , ' tcx > {
253
- cx : & ' a mut DocContext < ' tcx > ,
259
+ pub ( crate ) struct OwnedDiagnosticInfo {
260
+ item : Item ,
261
+ ori_link : String ,
262
+ link_range : MarkdownLinkRange ,
263
+ }
264
+
265
+ impl From < DiagnosticInfo < ' _ > > for OwnedDiagnosticInfo {
266
+ fn from ( f : DiagnosticInfo < ' _ > ) -> Self {
267
+ Self {
268
+ item : f. item . clone ( ) ,
269
+ ori_link : f. ori_link . to_string ( ) ,
270
+ link_range : f. link_range . clone ( ) ,
271
+ }
272
+ }
273
+ }
274
+
275
+ impl OwnedDiagnosticInfo {
276
+ pub ( crate ) fn into_info ( & self ) -> DiagnosticInfo < ' _ > {
277
+ DiagnosticInfo {
278
+ item : & self . item ,
279
+ ori_link : & self . ori_link ,
280
+ dox : "" ,
281
+ link_range : self . link_range . clone ( ) ,
282
+ }
283
+ }
284
+ }
285
+
286
+ pub ( crate ) struct LinkCollector < ' a , ' tcx > {
287
+ pub ( crate ) cx : & ' a mut DocContext < ' tcx > ,
254
288
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
255
289
/// The link will be `None` if it could not be resolved (i.e. the error was cached).
256
- visited_links : FxHashMap < ResolutionInfo , Option < ( Res , Option < UrlFragment > ) > > ,
290
+ pub ( crate ) visited_links : FxHashMap < ResolutionInfo , Option < ( Res , Option < UrlFragment > ) > > ,
291
+ /// These links are ambiguous. We need for the cache to have its paths filled. Unfortunately,
292
+ /// if we run the `LinkCollector` pass after `Cache::populate`, a lot of items that we need
293
+ /// to go through will be removed, making a lot of intra-doc links to not be inferred.
294
+ ///
295
+ /// So instead, we store the ambiguous links and we wait for cache paths to be filled before
296
+ /// inferring them (if possible).
297
+ ///
298
+ /// Key is `(item ID, path str)`.
299
+ pub ( crate ) ambiguous_links : FxHashMap < ( ItemId , String ) , AmbiguousLinks > ,
257
300
}
258
301
259
302
impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
@@ -981,14 +1024,17 @@ impl LinkCollector<'_, '_> {
981
1024
_ => find_nearest_parent_module ( self . cx . tcx , item_id) . unwrap ( ) ,
982
1025
} ;
983
1026
for md_link in preprocessed_markdown_links ( & doc) {
984
- let link = self . resolve_link ( & doc, item, item_id, module_id, & md_link) ;
985
- if let Some ( link) = link {
986
- self . cx . cache . intra_doc_links . entry ( item. item_id ) . or_default ( ) . insert ( link) ;
1027
+ if let Some ( link) = self . resolve_link ( & doc, item, item_id, module_id, & md_link) {
1028
+ self . save_link ( item. item_id , link) ;
987
1029
}
988
1030
}
989
1031
}
990
1032
}
991
1033
1034
+ pub ( crate ) fn save_link ( & mut self , item_id : ItemId , link : ItemLink ) {
1035
+ self . cx . cache . intra_doc_links . entry ( item_id) . or_default ( ) . insert ( link) ;
1036
+ }
1037
+
992
1038
/// This is the entry point for resolving an intra-doc link.
993
1039
///
994
1040
/// FIXME(jynelson): this is way too many arguments
@@ -1012,7 +1058,7 @@ impl LinkCollector<'_, '_> {
1012
1058
pp_link. as_ref ( ) . map_err ( |err| err. report ( self . cx , diag_info. clone ( ) ) ) . ok ( ) ?;
1013
1059
let disambiguator = * disambiguator;
1014
1060
1015
- let ( mut res , fragment ) = self . resolve_with_disambiguator_cached (
1061
+ let mut resolved = self . resolve_with_disambiguator_cached (
1016
1062
ResolutionInfo {
1017
1063
item_id,
1018
1064
module_id,
@@ -1028,6 +1074,75 @@ impl LinkCollector<'_, '_> {
1028
1074
false ,
1029
1075
) ?;
1030
1076
1077
+ if resolved. len ( ) > 1 {
1078
+ let links = AmbiguousLinks {
1079
+ disambiguator,
1080
+ link_text : link_text. clone ( ) ,
1081
+ diag_info : diag_info. into ( ) ,
1082
+ resolved,
1083
+ } ;
1084
+ self . ambiguous_links . insert ( ( item. item_id , path_str. to_string ( ) ) , links) ;
1085
+ None
1086
+ } else if let Some ( ( res, fragment) ) = resolved. pop ( ) {
1087
+ self . compute_link ( res, fragment, path_str, disambiguator, diag_info, link_text)
1088
+ } else {
1089
+ None
1090
+ }
1091
+ }
1092
+
1093
+ /// Returns `true` if a link could be generated from the given intra-doc information.
1094
+ ///
1095
+ /// This is a very light version of `format::href_with_root_path` since we're only interested
1096
+ /// about whether we can generate a link to an item or not.
1097
+ ///
1098
+ /// * If `original_did` is local, then we check if the item is reexported or public.
1099
+ /// * If `original_did` is not local, then we check if the crate it comes from is a direct
1100
+ /// public dependency.
1101
+ pub ( crate ) fn validate_link ( & self , original_did : DefId ) -> bool {
1102
+ let tcx = self . cx . tcx ;
1103
+ let def_kind = tcx. def_kind ( original_did) ;
1104
+ let did = match def_kind {
1105
+ DefKind :: AssocTy | DefKind :: AssocFn | DefKind :: AssocConst | DefKind :: Variant => {
1106
+ // documented on their parent's page
1107
+ tcx. parent ( original_did)
1108
+ }
1109
+ // If this a constructor, we get the parent (either a struct or a variant) and then
1110
+ // generate the link for this item.
1111
+ DefKind :: Ctor ( ..) => return self . validate_link ( tcx. parent ( original_did) ) ,
1112
+ DefKind :: ExternCrate => {
1113
+ // Link to the crate itself, not the `extern crate` item.
1114
+ if let Some ( local_did) = original_did. as_local ( ) {
1115
+ tcx. extern_mod_stmt_cnum ( local_did) . unwrap_or ( LOCAL_CRATE ) . as_def_id ( )
1116
+ } else {
1117
+ original_did
1118
+ }
1119
+ }
1120
+ _ => original_did,
1121
+ } ;
1122
+
1123
+ let cache = & self . cx . cache ;
1124
+ if !original_did. is_local ( )
1125
+ && !cache. effective_visibilities . is_directly_public ( tcx, did)
1126
+ && !cache. document_private
1127
+ && !cache. primitive_locations . values ( ) . any ( |& id| id == did)
1128
+ {
1129
+ return false ;
1130
+ }
1131
+
1132
+ cache. paths . get ( & did) . is_some ( )
1133
+ || cache. external_paths . get ( & did) . is_some ( )
1134
+ || !did. is_local ( )
1135
+ }
1136
+
1137
+ pub ( crate ) fn compute_link (
1138
+ & mut self ,
1139
+ mut res : Res ,
1140
+ fragment : Option < UrlFragment > ,
1141
+ path_str : & str ,
1142
+ disambiguator : Option < Disambiguator > ,
1143
+ diag_info : DiagnosticInfo < ' _ > ,
1144
+ link_text : & Box < str > ,
1145
+ ) -> Option < ItemLink > {
1031
1146
// Check for a primitive which might conflict with a module
1032
1147
// Report the ambiguity and require that the user specify which one they meant.
1033
1148
// FIXME: could there ever be a primitive not in the type namespace?
@@ -1073,7 +1188,7 @@ impl LinkCollector<'_, '_> {
1073
1188
}
1074
1189
1075
1190
res. def_id ( self . cx . tcx ) . map ( |page_id| ItemLink {
1076
- link : Box :: < str > :: from ( & * ori_link . link ) ,
1191
+ link : Box :: < str > :: from ( & * diag_info . ori_link ) ,
1077
1192
link_text : link_text. clone ( ) ,
1078
1193
page_id,
1079
1194
fragment,
@@ -1095,7 +1210,7 @@ impl LinkCollector<'_, '_> {
1095
1210
1096
1211
let page_id = clean:: register_res ( self . cx , rustc_hir:: def:: Res :: Def ( kind, id) ) ;
1097
1212
Some ( ItemLink {
1098
- link : Box :: < str > :: from ( & * ori_link . link ) ,
1213
+ link : Box :: < str > :: from ( & * diag_info . ori_link ) ,
1099
1214
link_text : link_text. clone ( ) ,
1100
1215
page_id,
1101
1216
fragment,
@@ -1208,10 +1323,10 @@ impl LinkCollector<'_, '_> {
1208
1323
// If this call is intended to be recoverable, then pass true to silence.
1209
1324
// This is only recoverable when path is failed to resolved.
1210
1325
recoverable : bool ,
1211
- ) -> Option < ( Res , Option < UrlFragment > ) > {
1326
+ ) -> Option < Vec < ( Res , Option < UrlFragment > ) > > {
1212
1327
if let Some ( res) = self . visited_links . get ( & key) {
1213
1328
if res. is_some ( ) || cache_errors {
1214
- return res. clone ( ) ;
1329
+ return res. clone ( ) . map ( |r| vec ! [ r ] ) ;
1215
1330
}
1216
1331
}
1217
1332
@@ -1242,25 +1357,28 @@ impl LinkCollector<'_, '_> {
1242
1357
candidates = vec ! [ candidate] ;
1243
1358
}
1244
1359
1245
- if let & [ ( res, def_id) ] = candidates. as_slice ( ) {
1360
+ let mut resolved = Vec :: with_capacity ( candidates. len ( ) ) ;
1361
+ for ( res, def_id) in candidates. as_slice ( ) {
1246
1362
let fragment = match ( & key. extra_fragment , def_id) {
1247
1363
( Some ( _) , Some ( def_id) ) => {
1248
- report_anchor_conflict ( self . cx , diag, def_id) ;
1364
+ report_anchor_conflict ( self . cx , diag, * def_id) ;
1249
1365
return None ;
1250
1366
}
1251
1367
( Some ( u_frag) , None ) => Some ( UrlFragment :: UserWritten ( u_frag. clone ( ) ) ) ,
1252
- ( None , Some ( def_id) ) => Some ( UrlFragment :: Item ( def_id) ) ,
1368
+ ( None , Some ( def_id) ) => Some ( UrlFragment :: Item ( * def_id) ) ,
1253
1369
( None , None ) => None ,
1254
1370
} ;
1255
- let r = Some ( ( res, fragment) ) ;
1256
- self . visited_links . insert ( key, r. clone ( ) ) ;
1257
- return r ;
1371
+ let r = ( res. clone ( ) , fragment. clone ( ) ) ;
1372
+ self . visited_links . insert ( key. clone ( ) , Some ( r. clone ( ) ) ) ;
1373
+ resolved . push ( r ) ;
1258
1374
}
1259
1375
1260
- if cache_errors {
1376
+ if resolved . is_empty ( ) && cache_errors {
1261
1377
self . visited_links . insert ( key, None ) ;
1378
+ None
1379
+ } else {
1380
+ Some ( resolved)
1262
1381
}
1263
- None
1264
1382
}
1265
1383
1266
1384
/// After parsing the disambiguator, resolve the main part of the link.
@@ -1416,7 +1534,7 @@ fn should_ignore_link(path_str: &str) -> bool {
1416
1534
1417
1535
#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
1418
1536
/// Disambiguators for a link.
1419
- enum Disambiguator {
1537
+ pub ( crate ) enum Disambiguator {
1420
1538
/// `prim@`
1421
1539
///
1422
1540
/// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
0 commit comments