@@ -39,13 +39,16 @@ use crate::passes::Pass;
39
39
40
40
use super :: span_of_attrs;
41
41
42
+ mod early;
43
+ crate use early:: IntraLinkCrateLoader ;
44
+
42
45
crate const COLLECT_INTRA_DOC_LINKS : Pass = Pass {
43
46
name : "collect-intra-doc-links" ,
44
47
run : collect_intra_doc_links,
45
48
description : "resolves intra-doc links" ,
46
49
} ;
47
50
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 {
49
52
LinkCollector {
50
53
cx,
51
54
mod_ids : Vec :: new ( ) ,
@@ -892,6 +895,117 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
892
895
}
893
896
}
894
897
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
+
895
1009
impl LinkCollector < ' _ , ' _ > {
896
1010
/// This is the entry point for resolving an intra-doc link.
897
1011
///
@@ -907,64 +1021,36 @@ impl LinkCollector<'_, '_> {
907
1021
) -> Option < ItemLink > {
908
1022
trace ! ( "considering link '{}'" , ori_link. link) ;
909
1023
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
-
920
1024
let diag_info = DiagnosticInfo {
921
1025
item,
922
1026
dox,
923
1027
ori_link : & ori_link. link ,
924
1028
link_range : ori_link. range . clone ( ) ,
925
1029
} ;
926
1030
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 ;
955
1051
}
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;
968
1054
969
1055
// In order to correctly resolve intra-doc links we need to
970
1056
// pick a base AST node to work from. If the documentation for
@@ -1029,39 +1115,12 @@ impl LinkCollector<'_, '_> {
1029
1115
module_id = DefId { krate, index : CRATE_DEF_INDEX } ;
1030
1116
}
1031
1117
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
-
1059
1118
let ( mut res, mut fragment) = self . resolve_with_disambiguator_cached (
1060
1119
ResolutionInfo {
1061
1120
module_id,
1062
1121
dis : disambiguator,
1063
1122
path_str : path_str. to_owned ( ) ,
1064
- extra_fragment,
1123
+ extra_fragment : extra_fragment . map ( String :: from ) ,
1065
1124
} ,
1066
1125
diag_info. clone ( ) , // this struct should really be Copy, but Range is not :(
1067
1126
matches ! ( ori_link. kind, LinkType :: Reference | LinkType :: Shortcut ) ,
@@ -1438,7 +1497,7 @@ fn should_ignore_link(path_str: &str) -> bool {
1438
1497
1439
1498
#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
1440
1499
/// Disambiguators for a link.
1441
- crate enum Disambiguator {
1500
+ enum Disambiguator {
1442
1501
/// `prim@`
1443
1502
///
1444
1503
/// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
@@ -1467,7 +1526,7 @@ impl Disambiguator {
1467
1526
/// This returns `Ok(Some(...))` if a disambiguator was found,
1468
1527
/// `Ok(None)` if no disambiguator was found, or `Err(...)`
1469
1528
/// 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 > ) > {
1471
1530
use Disambiguator :: { Kind , Namespace as NS , Primitive } ;
1472
1531
1473
1532
if let Some ( idx) = link. find ( '@' ) {
0 commit comments