@@ -14,7 +14,7 @@ use rustc_data_structures::intern::Interned;
1414use rustc_errors:: { Applicability , Diag , DiagMessage } ;
1515use rustc_hir:: def:: Namespace :: * ;
1616use 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 } ;
1818use rustc_hir:: { Mutability , Safety } ;
1919use rustc_middle:: ty:: { Ty , TyCtxt } ;
2020use rustc_middle:: { bug, span_bug, ty} ;
@@ -30,23 +30,30 @@ use smallvec::{SmallVec, smallvec};
3030use tracing:: { debug, info, instrument, trace} ;
3131
3232use 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 } ;
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 ;
3837use crate :: visit:: DocVisitor ;
3938
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+ } ;
4848 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 > ) > ,
5057}
5158
5259fn filter_assoc_items_by_name_and_namespace < ' a > (
@@ -61,7 +68,7 @@ fn filter_assoc_items_by_name_and_namespace<'a>(
6168}
6269
6370#[ derive( Copy , Clone , Debug , Hash , PartialEq ) ]
64- enum Res {
71+ pub ( crate ) enum Res {
6572 Def ( DefKind , DefId ) ,
6673 Primitive ( PrimitiveType ) ,
6774}
@@ -233,7 +240,7 @@ impl UrlFragment {
233240}
234241
235242#[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
236- struct ResolutionInfo {
243+ pub ( crate ) struct ResolutionInfo {
237244 item_id : DefId ,
238245 module_id : DefId ,
239246 dis : Option < Disambiguator > ,
@@ -242,18 +249,54 @@ struct ResolutionInfo {
242249}
243250
244251#[ derive( Clone ) ]
245- struct DiagnosticInfo < ' a > {
252+ pub ( crate ) struct DiagnosticInfo < ' a > {
246253 item : & ' a Item ,
247254 dox : & ' a str ,
248255 ori_link : & ' a str ,
249256 link_range : MarkdownLinkRange ,
250257}
251258
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 > ,
254288 /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
255289 /// 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 > ,
257300}
258301
259302impl < ' a , ' tcx > LinkCollector < ' a , ' tcx > {
@@ -981,14 +1024,17 @@ impl LinkCollector<'_, '_> {
9811024 _ => find_nearest_parent_module ( self . cx . tcx , item_id) . unwrap ( ) ,
9821025 } ;
9831026 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) ;
9871029 }
9881030 }
9891031 }
9901032 }
9911033
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+
9921038 /// This is the entry point for resolving an intra-doc link.
9931039 ///
9941040 /// FIXME(jynelson): this is way too many arguments
@@ -1012,7 +1058,7 @@ impl LinkCollector<'_, '_> {
10121058 pp_link. as_ref ( ) . map_err ( |err| err. report ( self . cx , diag_info. clone ( ) ) ) . ok ( ) ?;
10131059 let disambiguator = * disambiguator;
10141060
1015- let ( mut res , fragment ) = self . resolve_with_disambiguator_cached (
1061+ let mut resolved = self . resolve_with_disambiguator_cached (
10161062 ResolutionInfo {
10171063 item_id,
10181064 module_id,
@@ -1028,6 +1074,75 @@ impl LinkCollector<'_, '_> {
10281074 false ,
10291075 ) ?;
10301076
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 > {
10311146 // Check for a primitive which might conflict with a module
10321147 // Report the ambiguity and require that the user specify which one they meant.
10331148 // FIXME: could there ever be a primitive not in the type namespace?
@@ -1073,7 +1188,7 @@ impl LinkCollector<'_, '_> {
10731188 }
10741189
10751190 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 ) ,
10771192 link_text : link_text. clone ( ) ,
10781193 page_id,
10791194 fragment,
@@ -1095,7 +1210,7 @@ impl LinkCollector<'_, '_> {
10951210
10961211 let page_id = clean:: register_res ( self . cx , rustc_hir:: def:: Res :: Def ( kind, id) ) ;
10971212 Some ( ItemLink {
1098- link : Box :: < str > :: from ( & * ori_link . link ) ,
1213+ link : Box :: < str > :: from ( & * diag_info . ori_link ) ,
10991214 link_text : link_text. clone ( ) ,
11001215 page_id,
11011216 fragment,
@@ -1208,10 +1323,10 @@ impl LinkCollector<'_, '_> {
12081323 // If this call is intended to be recoverable, then pass true to silence.
12091324 // This is only recoverable when path is failed to resolved.
12101325 recoverable : bool ,
1211- ) -> Option < ( Res , Option < UrlFragment > ) > {
1326+ ) -> Option < Vec < ( Res , Option < UrlFragment > ) > > {
12121327 if let Some ( res) = self . visited_links . get ( & key) {
12131328 if res. is_some ( ) || cache_errors {
1214- return res. clone ( ) ;
1329+ return res. clone ( ) . map ( |r| vec ! [ r ] ) ;
12151330 }
12161331 }
12171332
@@ -1242,25 +1357,28 @@ impl LinkCollector<'_, '_> {
12421357 candidates = vec ! [ candidate] ;
12431358 }
12441359
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 ( ) {
12461362 let fragment = match ( & key. extra_fragment , def_id) {
12471363 ( Some ( _) , Some ( def_id) ) => {
1248- report_anchor_conflict ( self . cx , diag, def_id) ;
1364+ report_anchor_conflict ( self . cx , diag, * def_id) ;
12491365 return None ;
12501366 }
12511367 ( 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) ) ,
12531369 ( None , None ) => None ,
12541370 } ;
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 ) ;
12581374 }
12591375
1260- if cache_errors {
1376+ if resolved . is_empty ( ) && cache_errors {
12611377 self . visited_links . insert ( key, None ) ;
1378+ None
1379+ } else {
1380+ Some ( resolved)
12621381 }
1263- None
12641382 }
12651383
12661384 /// After parsing the disambiguator, resolve the main part of the link.
@@ -1416,7 +1534,7 @@ fn should_ignore_link(path_str: &str) -> bool {
14161534
14171535#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
14181536/// Disambiguators for a link.
1419- enum Disambiguator {
1537+ pub ( crate ) enum Disambiguator {
14201538 /// `prim@`
14211539 ///
14221540 /// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
0 commit comments