@@ -23,6 +23,7 @@ use smallvec::{smallvec, SmallVec};
23
23
24
24
use std:: borrow:: Cow ;
25
25
use std:: cell:: Cell ;
26
+ use std:: mem;
26
27
use std:: ops:: Range ;
27
28
28
29
use crate :: clean:: * ;
@@ -65,10 +66,53 @@ enum ResolutionFailure<'a> {
65
66
NotResolved { module_id : DefId , partial_res : Option < Res > , unresolved : Cow < ' a , str > } ,
66
67
/// should not ever happen
67
68
NoParentItem ,
69
+ /// This link has malformed generic parameters; e.g., the angle brackets are unbalanced.
70
+ MalformedGenerics ( MalformedGenerics ) ,
68
71
/// used to communicate that this should be ignored, but shouldn't be reported to the user
69
72
Dummy ,
70
73
}
71
74
75
+ #[ derive( Debug ) ]
76
+ enum MalformedGenerics {
77
+ /// This link has unbalanced angle brackets.
78
+ ///
79
+ /// For example, `Vec<T` should trigger this, as should `Vec<T>>`.
80
+ UnbalancedAngleBrackets ,
81
+ /// The generics are not attached to a type.
82
+ ///
83
+ /// For example, `<T>` should trigger this.
84
+ ///
85
+ /// This is detected by checking if the path is empty after the generics are stripped.
86
+ MissingType ,
87
+ /// The link uses fully-qualified syntax, which is currently unsupported.
88
+ ///
89
+ /// For example, `<Vec as IntoIterator>::into_iter` should trigger this.
90
+ ///
91
+ /// This is detected by checking if ` as ` (the keyword `as` with spaces around it) is inside
92
+ /// angle brackets.
93
+ HasFullyQualifiedSyntax ,
94
+ /// The link has an invalid path separator.
95
+ ///
96
+ /// For example, `Vec:<T>:new()` should trigger this. Note that `Vec:new()` will **not**
97
+ /// trigger this because it has no generics and thus [`strip_generics_from_path`] will not be
98
+ /// called.
99
+ ///
100
+ /// Note that this will also **not** be triggered if the invalid path separator is inside angle
101
+ /// brackets because rustdoc mostly ignores what's inside angle brackets (except for
102
+ /// [`HasFullyQualifiedSyntax`](MalformedGenerics::HasFullyQualifiedSyntax)).
103
+ ///
104
+ /// This is detected by checking if there is a colon followed by a non-colon in the link.
105
+ InvalidPathSeparator ,
106
+ /// The link has too many angle brackets.
107
+ ///
108
+ /// For example, `Vec<<T>>` should trigger this.
109
+ TooManyAngleBrackets ,
110
+ /// The link has empty angle brackets.
111
+ ///
112
+ /// For example, `Vec<>` should trigger this.
113
+ EmptyAngleBrackets ,
114
+ }
115
+
72
116
impl ResolutionFailure < ' a > {
73
117
// This resolved fully (not just partially) but is erroneous for some other reason
74
118
fn full_res ( & self ) -> Option < Res > {
@@ -912,6 +956,7 @@ impl LinkCollector<'_, '_> {
912
956
let link_text;
913
957
let mut path_str;
914
958
let disambiguator;
959
+ let stripped_path_string;
915
960
let ( mut res, mut fragment) = {
916
961
path_str = if let Ok ( ( d, path) ) = Disambiguator :: from_str ( & link) {
917
962
disambiguator = Some ( d) ;
@@ -922,7 +967,7 @@ impl LinkCollector<'_, '_> {
922
967
}
923
968
. trim ( ) ;
924
969
925
- if path_str. contains ( |ch : char | !( ch. is_alphanumeric ( ) || ch == ':' || ch == '_' ) ) {
970
+ if path_str. contains ( |ch : char | !( ch. is_alphanumeric ( ) || ":_<>, " . contains ( ch ) ) ) {
926
971
return None ;
927
972
}
928
973
@@ -985,6 +1030,36 @@ impl LinkCollector<'_, '_> {
985
1030
module_id = DefId { krate, index : CRATE_DEF_INDEX } ;
986
1031
}
987
1032
1033
+ // Strip generics from the path.
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
+ & item,
1042
+ path_str,
1043
+ disambiguator,
1044
+ dox,
1045
+ link_range,
1046
+ smallvec ! [ err_kind] ,
1047
+ ) ;
1048
+ return None ;
1049
+ }
1050
+ } ;
1051
+ path_str = & stripped_path_string;
1052
+ }
1053
+
1054
+ // Sanity check to make sure we don't have any angle brackets after stripping generics.
1055
+ assert ! ( !path_str. contains( [ '<' , '>' ] . as_slice( ) ) ) ;
1056
+
1057
+ // The link is not an intra-doc link if it still contains commas or spaces after
1058
+ // stripping generics.
1059
+ if path_str. contains ( [ ',' , ' ' ] . as_slice ( ) ) {
1060
+ return None ;
1061
+ }
1062
+
988
1063
match self . resolve_with_disambiguator (
989
1064
disambiguator,
990
1065
item,
@@ -1718,6 +1793,27 @@ fn resolution_failure(
1718
1793
diag. level = rustc_errors:: Level :: Bug ;
1719
1794
"all intra doc links should have a parent item" . to_owned ( )
1720
1795
}
1796
+ ResolutionFailure :: MalformedGenerics ( variant) => match variant {
1797
+ MalformedGenerics :: UnbalancedAngleBrackets => {
1798
+ String :: from ( "unbalanced angle brackets" )
1799
+ }
1800
+ MalformedGenerics :: MissingType => {
1801
+ String :: from ( "missing type for generic parameters" )
1802
+ }
1803
+ MalformedGenerics :: HasFullyQualifiedSyntax => {
1804
+ diag. note ( "see https://github.com/rust-lang/rust/issues/74563 for more information" ) ;
1805
+ String :: from ( "fully-qualified syntax is unsupported" )
1806
+ }
1807
+ MalformedGenerics :: InvalidPathSeparator => {
1808
+ String :: from ( "has invalid path separator" )
1809
+ }
1810
+ MalformedGenerics :: TooManyAngleBrackets => {
1811
+ String :: from ( "too many angle brackets" )
1812
+ }
1813
+ MalformedGenerics :: EmptyAngleBrackets => {
1814
+ String :: from ( "empty angle brackets" )
1815
+ }
1816
+ } ,
1721
1817
} ;
1722
1818
if let Some ( span) = sp {
1723
1819
diag. span_label ( span, & note) ;
@@ -1908,3 +2004,108 @@ fn is_primitive(path_str: &str, ns: Namespace) -> Option<(&'static str, Res)> {
1908
2004
fn primitive_impl ( cx : & DocContext < ' _ > , path_str : & str ) -> Option < & ' static SmallVec < [ DefId ; 4 ] > > {
1909
2005
Some ( PrimitiveType :: from_symbol ( Symbol :: intern ( path_str) ) ?. impls ( cx. tcx ) )
1910
2006
}
2007
+
2008
+ fn strip_generics_from_path ( path_str : & str ) -> Result < String , ResolutionFailure < ' static > > {
2009
+ let mut stripped_segments = vec ! [ ] ;
2010
+ let mut path = path_str. chars ( ) . peekable ( ) ;
2011
+ let mut segment = Vec :: new ( ) ;
2012
+
2013
+ while let Some ( chr) = path. next ( ) {
2014
+ match chr {
2015
+ ':' => {
2016
+ if path. next_if_eq ( & ':' ) . is_some ( ) {
2017
+ let stripped_segment =
2018
+ strip_generics_from_path_segment ( mem:: take ( & mut segment) ) ?;
2019
+ if !stripped_segment. is_empty ( ) {
2020
+ stripped_segments. push ( stripped_segment) ;
2021
+ }
2022
+ } else {
2023
+ return Err ( ResolutionFailure :: MalformedGenerics (
2024
+ MalformedGenerics :: InvalidPathSeparator ,
2025
+ ) ) ;
2026
+ }
2027
+ }
2028
+ '<' => {
2029
+ segment. push ( chr) ;
2030
+
2031
+ match path. peek ( ) {
2032
+ Some ( '<' ) => {
2033
+ return Err ( ResolutionFailure :: MalformedGenerics (
2034
+ MalformedGenerics :: TooManyAngleBrackets ,
2035
+ ) ) ;
2036
+ }
2037
+ Some ( '>' ) => {
2038
+ return Err ( ResolutionFailure :: MalformedGenerics (
2039
+ MalformedGenerics :: EmptyAngleBrackets ,
2040
+ ) ) ;
2041
+ }
2042
+ Some ( _) => {
2043
+ segment. push ( path. next ( ) . unwrap ( ) ) ;
2044
+
2045
+ while let Some ( chr) = path. next_if ( |c| * c != '>' ) {
2046
+ segment. push ( chr) ;
2047
+ }
2048
+ }
2049
+ None => break ,
2050
+ }
2051
+ }
2052
+ _ => segment. push ( chr) ,
2053
+ }
2054
+ debug ! ( "raw segment: {:?}" , segment) ;
2055
+ }
2056
+
2057
+ if !segment. is_empty ( ) {
2058
+ let stripped_segment = strip_generics_from_path_segment ( segment) ?;
2059
+ if !stripped_segment. is_empty ( ) {
2060
+ stripped_segments. push ( stripped_segment) ;
2061
+ }
2062
+ }
2063
+
2064
+ debug ! ( "path_str: {:?}\n stripped segments: {:?}" , path_str, & stripped_segments) ;
2065
+
2066
+ let stripped_path = stripped_segments. join ( "::" ) ;
2067
+
2068
+ if !stripped_path. is_empty ( ) {
2069
+ Ok ( stripped_path)
2070
+ } else {
2071
+ Err ( ResolutionFailure :: MalformedGenerics ( MalformedGenerics :: MissingType ) )
2072
+ }
2073
+ }
2074
+
2075
+ fn strip_generics_from_path_segment (
2076
+ segment : Vec < char > ,
2077
+ ) -> Result < String , ResolutionFailure < ' static > > {
2078
+ let mut stripped_segment = String :: new ( ) ;
2079
+ let mut param_depth = 0 ;
2080
+
2081
+ let mut latest_generics_chunk = String :: new ( ) ;
2082
+
2083
+ for c in segment {
2084
+ if c == '<' {
2085
+ param_depth += 1 ;
2086
+ latest_generics_chunk. clear ( ) ;
2087
+ } else if c == '>' {
2088
+ param_depth -= 1 ;
2089
+ if latest_generics_chunk. contains ( " as " ) {
2090
+ // The segment tries to use fully-qualified syntax, which is currently unsupported.
2091
+ // Give a helpful error message instead of completely ignoring the angle brackets.
2092
+ return Err ( ResolutionFailure :: MalformedGenerics (
2093
+ MalformedGenerics :: HasFullyQualifiedSyntax ,
2094
+ ) ) ;
2095
+ }
2096
+ } else {
2097
+ if param_depth == 0 {
2098
+ stripped_segment. push ( c) ;
2099
+ } else {
2100
+ latest_generics_chunk. push ( c) ;
2101
+ }
2102
+ }
2103
+ }
2104
+
2105
+ if param_depth == 0 {
2106
+ Ok ( stripped_segment)
2107
+ } else {
2108
+ // The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>`
2109
+ Err ( ResolutionFailure :: MalformedGenerics ( MalformedGenerics :: UnbalancedAngleBrackets ) )
2110
+ }
2111
+ }
0 commit comments