@@ -39,13 +39,16 @@ use crate::passes::Pass;
3939
4040use super :: span_of_attrs;
4141
42+ mod early;
43+ crate use early:: IntraLinkCrateLoader ;
44+
4245crate const COLLECT_INTRA_DOC_LINKS : Pass = Pass {
4346 name : "collect-intra-doc-links" ,
4447 run : collect_intra_doc_links,
4548 description : "resolves intra-doc links" ,
4649} ;
4750
48- crate fn collect_intra_doc_links ( krate : Crate , cx : & mut DocContext < ' _ > ) -> Crate {
51+ fn collect_intra_doc_links ( krate : Crate , cx : & mut DocContext < ' _ > ) -> Crate {
4952 LinkCollector {
5053 cx,
5154 mod_ids : Vec :: new ( ) ,
@@ -892,6 +895,117 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
892895 }
893896}
894897
898+ enum PreprocessingError < ' a > {
899+ Anchor ( AnchorFailure ) ,
900+ Disambiguator ( Range < usize > , String ) ,
901+ Resolution ( ResolutionFailure < ' a > , String , Option < Disambiguator > ) ,
902+ }
903+
904+ impl From < AnchorFailure > for PreprocessingError < ' _ > {
905+ fn from ( err : AnchorFailure ) -> Self {
906+ Self :: Anchor ( err)
907+ }
908+ }
909+
910+ struct PreprocessingInfo {
911+ path_str : String ,
912+ disambiguator : Option < Disambiguator > ,
913+ extra_fragment : Option < String > ,
914+ link_text : String ,
915+ }
916+
917+ /// Returns:
918+ /// - `None` if the link should be ignored.
919+ /// - `Some(Err)` if the link should emit an error
920+ /// - `Some(Ok)` if the link is valid
921+ ///
922+ /// `link_buffer` is needed for lifetime reasons; it will always be overwritten and the contents ignored.
923+ fn preprocess_link < ' a > (
924+ ori_link : & ' a MarkdownLink ,
925+ ) -> Option < Result < PreprocessingInfo , PreprocessingError < ' a > > > {
926+ // [] is mostly likely not supposed to be a link
927+ if ori_link. link . is_empty ( ) {
928+ return None ;
929+ }
930+
931+ // Bail early for real links.
932+ if ori_link. link . contains ( '/' ) {
933+ return None ;
934+ }
935+
936+ let stripped = ori_link. link . replace ( "`" , "" ) ;
937+ let mut parts = stripped. split ( '#' ) ;
938+
939+ let link = parts. next ( ) . unwrap ( ) ;
940+ if link. trim ( ) . is_empty ( ) {
941+ // This is an anchor to an element of the current page, nothing to do in here!
942+ return None ;
943+ }
944+ let extra_fragment = parts. next ( ) ;
945+ if parts. next ( ) . is_some ( ) {
946+ // A valid link can't have multiple #'s
947+ return Some ( Err ( AnchorFailure :: MultipleAnchors . into ( ) ) ) ;
948+ }
949+
950+ // Parse and strip the disambiguator from the link, if present.
951+ let ( path_str, disambiguator) = match Disambiguator :: from_str ( & link) {
952+ Ok ( Some ( ( d, path) ) ) => ( path. trim ( ) , Some ( d) ) ,
953+ Ok ( None ) => ( link. trim ( ) , None ) ,
954+ Err ( ( err_msg, relative_range) ) => {
955+ // Only report error if we would not have ignored this link. See issue #83859.
956+ if !should_ignore_link_with_disambiguators ( link) {
957+ let no_backticks_range = range_between_backticks ( & ori_link) ;
958+ let disambiguator_range = ( no_backticks_range. start + relative_range. start )
959+ ..( no_backticks_range. start + relative_range. end ) ;
960+ return Some ( Err ( PreprocessingError :: Disambiguator ( disambiguator_range, err_msg) ) ) ;
961+ } else {
962+ return None ;
963+ }
964+ }
965+ } ;
966+
967+ if should_ignore_link ( path_str) {
968+ return None ;
969+ }
970+
971+ // We stripped `()` and `!` when parsing the disambiguator.
972+ // Add them back to be displayed, but not prefix disambiguators.
973+ let link_text =
974+ disambiguator. map ( |d| d. display_for ( path_str) ) . unwrap_or_else ( || path_str. to_owned ( ) ) ;
975+
976+ // Strip generics from the path.
977+ let path_str = if path_str. contains ( [ '<' , '>' ] . as_slice ( ) ) {
978+ match strip_generics_from_path ( & path_str) {
979+ Ok ( path) => path,
980+ Err ( err_kind) => {
981+ debug ! ( "link has malformed generics: {}" , path_str) ;
982+ return Some ( Err ( PreprocessingError :: Resolution (
983+ err_kind,
984+ path_str. to_owned ( ) ,
985+ disambiguator,
986+ ) ) ) ;
987+ }
988+ }
989+ } else {
990+ path_str. to_owned ( )
991+ } ;
992+
993+ // Sanity check to make sure we don't have any angle brackets after stripping generics.
994+ assert ! ( !path_str. contains( [ '<' , '>' ] . as_slice( ) ) ) ;
995+
996+ // The link is not an intra-doc link if it still contains spaces after stripping generics.
997+ if path_str. contains ( ' ' ) {
998+ return None ;
999+ }
1000+
1001+ Some ( Ok ( PreprocessingInfo {
1002+ path_str,
1003+ disambiguator,
1004+ extra_fragment : extra_fragment. map ( String :: from) ,
1005+ link_text,
1006+ } ) )
1007+ }
1008+
8951009impl LinkCollector < ' _ , ' _ > {
8961010 /// This is the entry point for resolving an intra-doc link.
8971011 ///
@@ -907,64 +1021,36 @@ impl LinkCollector<'_, '_> {
9071021 ) -> Option < ItemLink > {
9081022 trace ! ( "considering link '{}'" , ori_link. link) ;
9091023
910- // Bail early for real links.
911- if ori_link. link . contains ( '/' ) {
912- return None ;
913- }
914-
915- // [] is mostly likely not supposed to be a link
916- if ori_link. link . is_empty ( ) {
917- return None ;
918- }
919-
9201024 let diag_info = DiagnosticInfo {
9211025 item,
9221026 dox,
9231027 ori_link : & ori_link. link ,
9241028 link_range : ori_link. range . clone ( ) ,
9251029 } ;
9261030
927- let link = ori_link. link . replace ( "`" , "" ) ;
928- let no_backticks_range = range_between_backticks ( & ori_link) ;
929- let parts = link. split ( '#' ) . collect :: < Vec < _ > > ( ) ;
930- let ( link, extra_fragment) = if parts. len ( ) > 2 {
931- // A valid link can't have multiple #'s
932- anchor_failure ( self . cx , diag_info, AnchorFailure :: MultipleAnchors ) ;
933- return None ;
934- } else if parts. len ( ) == 2 {
935- if parts[ 0 ] . trim ( ) . is_empty ( ) {
936- // This is an anchor to an element of the current page, nothing to do in here!
937- return None ;
938- }
939- ( parts[ 0 ] , Some ( parts[ 1 ] . to_owned ( ) ) )
940- } else {
941- ( parts[ 0 ] , None )
942- } ;
943-
944- // Parse and strip the disambiguator from the link, if present.
945- let ( mut path_str, disambiguator) = match Disambiguator :: from_str ( & link) {
946- Ok ( Some ( ( d, path) ) ) => ( path. trim ( ) , Some ( d) ) ,
947- Ok ( None ) => ( link. trim ( ) , None ) ,
948- Err ( ( err_msg, relative_range) ) => {
949- if !should_ignore_link_with_disambiguators ( link) {
950- // Only report error if we would not have ignored this link.
951- // See issue #83859.
952- let disambiguator_range = ( no_backticks_range. start + relative_range. start )
953- ..( no_backticks_range. start + relative_range. end ) ;
954- disambiguator_error ( self . cx , diag_info, disambiguator_range, & err_msg) ;
1031+ let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } =
1032+ match preprocess_link ( & ori_link) ? {
1033+ Ok ( x) => x,
1034+ Err ( err) => {
1035+ match err {
1036+ PreprocessingError :: Anchor ( err) => anchor_failure ( self . cx , diag_info, err) ,
1037+ PreprocessingError :: Disambiguator ( range, msg) => {
1038+ disambiguator_error ( self . cx , diag_info, range, & msg)
1039+ }
1040+ PreprocessingError :: Resolution ( err, path_str, disambiguator) => {
1041+ resolution_failure (
1042+ self ,
1043+ diag_info,
1044+ & path_str,
1045+ disambiguator,
1046+ smallvec ! [ err] ,
1047+ ) ;
1048+ }
1049+ }
1050+ return None ;
9551051 }
956- return None ;
957- }
958- } ;
959-
960- if should_ignore_link ( path_str) {
961- return None ;
962- }
963-
964- // We stripped `()` and `!` when parsing the disambiguator.
965- // Add them back to be displayed, but not prefix disambiguators.
966- let link_text =
967- disambiguator. map ( |d| d. display_for ( path_str) ) . unwrap_or_else ( || path_str. to_owned ( ) ) ;
1052+ } ;
1053+ let mut path_str = & * path_str;
9681054
9691055 // In order to correctly resolve intra-doc links we need to
9701056 // pick a base AST node to work from. If the documentation for
@@ -1029,39 +1115,12 @@ impl LinkCollector<'_, '_> {
10291115 module_id = DefId { krate, index : CRATE_DEF_INDEX } ;
10301116 }
10311117
1032- // Strip generics from the path.
1033- let stripped_path_string;
1034- if path_str. contains ( [ '<' , '>' ] . as_slice ( ) ) {
1035- stripped_path_string = match strip_generics_from_path ( path_str) {
1036- Ok ( path) => path,
1037- Err ( err_kind) => {
1038- debug ! ( "link has malformed generics: {}" , path_str) ;
1039- resolution_failure (
1040- self ,
1041- diag_info,
1042- path_str,
1043- disambiguator,
1044- smallvec ! [ err_kind] ,
1045- ) ;
1046- return None ;
1047- }
1048- } ;
1049- path_str = & stripped_path_string;
1050- }
1051- // Sanity check to make sure we don't have any angle brackets after stripping generics.
1052- assert ! ( !path_str. contains( [ '<' , '>' ] . as_slice( ) ) ) ;
1053-
1054- // The link is not an intra-doc link if it still contains spaces after stripping generics.
1055- if path_str. contains ( ' ' ) {
1056- return None ;
1057- }
1058-
10591118 let ( mut res, mut fragment) = self . resolve_with_disambiguator_cached (
10601119 ResolutionInfo {
10611120 module_id,
10621121 dis : disambiguator,
10631122 path_str : path_str. to_owned ( ) ,
1064- extra_fragment,
1123+ extra_fragment : extra_fragment . map ( String :: from ) ,
10651124 } ,
10661125 diag_info. clone ( ) , // this struct should really be Copy, but Range is not :(
10671126 matches ! ( ori_link. kind, LinkType :: Reference | LinkType :: Shortcut ) ,
@@ -1438,7 +1497,7 @@ fn should_ignore_link(path_str: &str) -> bool {
14381497
14391498#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
14401499/// Disambiguators for a link.
1441- crate enum Disambiguator {
1500+ enum Disambiguator {
14421501 /// `prim@`
14431502 ///
14441503 /// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
@@ -1467,7 +1526,7 @@ impl Disambiguator {
14671526 /// This returns `Ok(Some(...))` if a disambiguator was found,
14681527 /// `Ok(None)` if no disambiguator was found, or `Err(...)`
14691528 /// if there was a problem with the disambiguator.
1470- crate fn from_str ( link : & str ) -> Result < Option < ( Self , & str ) > , ( String , Range < usize > ) > {
1529+ fn from_str ( link : & str ) -> Result < Option < ( Self , & str ) > , ( String , Range < usize > ) > {
14711530 use Disambiguator :: { Kind , Namespace as NS , Primitive } ;
14721531
14731532 if let Some ( idx) = link. find ( '@' ) {
0 commit comments