@@ -8,7 +8,7 @@ use std::path::Path;
8
8
use std:: str:: FromStr ;
9
9
10
10
use once_cell:: sync:: Lazy ;
11
- use regex:: Regex ;
11
+ use regex:: { Captures , Regex } ;
12
12
use tracing:: * ;
13
13
14
14
#[ derive( Clone , Debug , PartialEq ) ]
@@ -64,13 +64,13 @@ enum WhichLine {
64
64
AdjustBackward ( usize ) ,
65
65
}
66
66
67
- /// Looks for either " //~| KIND MESSAGE" or " //~^^... KIND MESSAGE"
67
+ /// Looks for either ` //~| KIND MESSAGE` or ` //~^^... KIND MESSAGE`
68
68
/// The former is a "follow" that inherits its target from the preceding line;
69
69
/// the latter is an "adjusts" that goes that many lines up.
70
70
///
71
- /// Goal is to enable tests both like: //~^^^ ERROR go up three
72
- /// and also //~^ ERROR message one for the preceding line, and
73
- /// //~| ERROR message two for that same line.
71
+ /// Goal is to enable tests both like: ` //~^^^ ERROR` go up three
72
+ /// and also ` //~^` ERROR message one for the preceding line, and
73
+ /// ` //~|` ERROR message two for that same line.
74
74
///
75
75
/// If cfg is not None (i.e., in an incremental test), then we look
76
76
/// for `//[X]~` instead, where `X` is the current `cfg`.
@@ -90,7 +90,7 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
90
90
rdr. lines ( )
91
91
. enumerate ( )
92
92
. filter_map ( |( line_num, line) | {
93
- parse_expected ( last_nonfollow_error, line_num + 1 , & line. unwrap ( ) , cfg) . map (
93
+ try_parse_error_comment ( last_nonfollow_error, line_num + 1 , & line. unwrap ( ) , cfg) . map (
94
94
|( which, error) | {
95
95
match which {
96
96
FollowPrevious ( _) => { }
@@ -104,56 +104,92 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
104
104
. collect ( )
105
105
}
106
106
107
- fn parse_expected (
107
+ /// Parses an error pattern from a line, if a pattern exists on that line.
108
+ fn try_parse_error_comment (
108
109
last_nonfollow_error : Option < usize > ,
109
110
line_num : usize ,
110
111
line : & str ,
111
112
cfg : Option < & str > ,
112
113
) -> Option < ( WhichLine , Error ) > {
113
- // Matches comments like:
114
- // //~
115
- // //~|
116
- // //~^
117
- // //~^^^^^
118
- // //[cfg1]~
119
- // //[cfg1,cfg2]~^^
120
- static RE : Lazy < Regex > =
121
- Lazy :: new ( || Regex :: new ( r"//(?:\[(?P<cfgs>[\w,]+)])?~(?P<adjust>\||\^*)" ) . unwrap ( ) ) ;
122
-
123
- let captures = RE . captures ( line) ?;
124
-
125
- match ( cfg, captures. name ( "cfgs" ) ) {
126
- // Only error messages that contain our `cfg` between the square brackets apply to us.
127
- ( Some ( cfg) , Some ( filter) ) if !filter. as_str ( ) . split ( ',' ) . any ( |s| s == cfg) => return None ,
128
- ( Some ( _) , Some ( _) ) => { }
129
-
130
- ( None , Some ( _) ) => panic ! ( "Only tests with revisions should use `//[X]~`" ) ,
131
-
132
- // If an error has no list of revisions, it applies to all revisions.
133
- ( Some ( _) , None ) | ( None , None ) => { }
114
+ let mut line = line. trim_start ( ) ;
115
+
116
+ // compiletest style revisions are `[revs]~`
117
+ static COMPILETEST_REVISION : Lazy < Regex > =
118
+ Lazy :: new ( || Regex :: new ( r"//\[(?P<revs>[\w,]+)\]~" ) . unwrap ( ) ) ;
119
+
120
+ // compiletest style revisions are `~[revs]`
121
+ static UI_TEST_REVISION : Lazy < Regex > =
122
+ Lazy :: new ( || Regex :: new ( r"//~\[(?P<revs>[\w,]+)\]" ) . unwrap ( ) ) ;
123
+
124
+ let check_valid_rev = |captures : & Captures < ' _ > | {
125
+ let revs = captures. name ( "revs" ) . unwrap_or_else ( || {
126
+ panic ! ( "expected comment {} parsed as compiletest to have a revs group" , line)
127
+ } ) ;
128
+ match cfg {
129
+ // If the comment has revisions, only emit an expected error if one of the specified
130
+ // revisions is the current revision.
131
+ Some ( current_rev) => {
132
+ revs. as_str ( ) . split ( ',' ) . position ( |rev| rev == current_rev) . is_some ( )
133
+ }
134
+ None => {
135
+ panic ! ( "Only tests with revisions should use revisioned error patterns //~[rev]" )
136
+ }
137
+ }
138
+ } ;
139
+
140
+ // Check for the different types of revisions.
141
+ // If neither of the revision styles match, it's a normal error pattern which must start with a //~
142
+ // Note that error pattern comments may start anywhere within a line, such as on the same line as code.
143
+ if let Some ( captures) = COMPILETEST_REVISION . captures ( line) {
144
+ if !check_valid_rev ( & captures) {
145
+ // Comment doesn't have a revision for the current revision.
146
+ return None ;
147
+ }
148
+ // Remove the matched revisions and trailing ~ from the line.
149
+ line = & line[ captures. get ( 0 ) . unwrap ( ) . end ( ) ..] ;
150
+ } else if let Some ( captures) = UI_TEST_REVISION . captures ( line) {
151
+ if !check_valid_rev ( & captures) {
152
+ // Comment doesn't have a revision for the current revision.
153
+ return None ;
154
+ }
155
+ // Remove the matched ~ and revisions from the line.
156
+ line = & line[ captures. get ( 0 ) . unwrap ( ) . end ( ) ..] ;
157
+ } else {
158
+ // Errors without revisions start with a //~ so find where that starts
159
+ line = line. find ( "//~" ) . map ( |idx| & line[ idx + 3 ..] ) ?;
134
160
}
135
161
136
- let ( follow, adjusts) = match & captures[ "adjust" ] {
137
- "|" => ( true , 0 ) ,
138
- circumflexes => ( false , circumflexes. len ( ) ) ,
139
- } ;
162
+ // At this point, if the comment has revisions, they've been verified to be correct for the
163
+ // current checking revision. Those revisions have been stripped if applicable, and the leading
164
+ // ~ for non-revisioned comments has been removed.
165
+
166
+ // Parse adjustments:
167
+ // - | = "same line as previous error"
168
+ // - ^ = "applies to the previous line" (may be repeated indefinitely)
169
+ // Only one type of adjustment may exist per error pattern.
140
170
141
- // Get the part of the comment after the sigil (e.g. `~^^` or ~|).
142
- let whole_match = captures. get ( 0 ) . unwrap ( ) ;
143
- let ( _, mut msg) = line. split_at ( whole_match. end ( ) ) ;
171
+ let ( follow, adjusts) = if line. starts_with ( '|' ) {
172
+ line = & line[ 1 ..] ;
173
+ ( true , 0 )
174
+ } else {
175
+ let adjust_count = line. chars ( ) . take_while ( |& c| c == '^' ) . count ( ) ;
176
+ line = & line[ adjust_count..] ;
177
+ ( false , adjust_count)
178
+ } ;
144
179
145
- let first_word = msg. split_whitespace ( ) . next ( ) . expect ( "Encountered unexpected empty comment" ) ;
180
+ line = line. trim_start ( ) ;
181
+ let first_word = line. split_whitespace ( ) . next ( ) . expect ( "Encountered unexpected empty comment" ) ;
146
182
147
183
// If we find `//~ ERROR foo` or something like that, skip the first word.
184
+ // The `FromStr` impl for ErrorKind accepts a trailing `:` too.
148
185
let kind = first_word. parse :: < ErrorKind > ( ) . ok ( ) ;
149
186
if kind. is_some ( ) {
150
- msg = & msg . trim_start ( ) . split_at ( first_word. len ( ) ) . 1 ;
187
+ line = & line . trim_start ( ) . split_at ( first_word. len ( ) ) . 1 ;
151
188
}
152
189
153
- let msg = msg . trim ( ) . to_owned ( ) ;
190
+ let line = line . trim ( ) . to_owned ( ) ;
154
191
155
192
let ( which, line_num) = if follow {
156
- assert_eq ! ( adjusts, 0 , "use either //~| or //~^, not both." ) ;
157
193
let line_num = last_nonfollow_error. expect (
158
194
"encountered //~| without \
159
195
preceding //~^ line.",
@@ -165,13 +201,6 @@ fn parse_expected(
165
201
( which, line_num)
166
202
} ;
167
203
168
- debug ! (
169
- "line={} tag={:?} which={:?} kind={:?} msg={:?}" ,
170
- line_num,
171
- whole_match. as_str( ) ,
172
- which,
173
- kind,
174
- msg
175
- ) ;
176
- Some ( ( which, Error { line_num, kind, msg } ) )
204
+ debug ! ( "line={} which={:?} kind={:?} line={:?}" , line_num, which, kind, line) ;
205
+ Some ( ( which, Error { line_num, kind, msg : line } ) )
177
206
}
0 commit comments