@@ -8,7 +8,7 @@ use std::path::Path;
88use std:: str:: FromStr ;
99
1010use once_cell:: sync:: Lazy ;
11- use regex:: Regex ;
11+ use regex:: { Captures , Regex } ;
1212use tracing:: * ;
1313
1414#[ derive( Clone , Debug , PartialEq ) ]
@@ -64,13 +64,13 @@ enum WhichLine {
6464 AdjustBackward ( usize ) ,
6565}
6666
67- /// Looks for either " //~| KIND MESSAGE" or " //~^^... KIND MESSAGE"
67+ /// Looks for either ` //~| KIND MESSAGE` or ` //~^^... KIND MESSAGE`
6868/// The former is a "follow" that inherits its target from the preceding line;
6969/// the latter is an "adjusts" that goes that many lines up.
7070///
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.
7474///
7575/// If cfg is not None (i.e., in an incremental test), then we look
7676/// for `//[X]~` instead, where `X` is the current `cfg`.
@@ -90,7 +90,7 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
9090 rdr. lines ( )
9191 . enumerate ( )
9292 . 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 (
9494 |( which, error) | {
9595 match which {
9696 FollowPrevious ( _) => { }
@@ -104,56 +104,92 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
104104 . collect ( )
105105}
106106
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 (
108109 last_nonfollow_error : Option < usize > ,
109110 line_num : usize ,
110111 line : & str ,
111112 cfg : Option < & str > ,
112113) -> 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 ..] ) ?;
134160 }
135161
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.
140170
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+ } ;
144179
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" ) ;
146182
147183 // If we find `//~ ERROR foo` or something like that, skip the first word.
184+ // The `FromStr` impl for ErrorKind accepts a trailing `:` too.
148185 let kind = first_word. parse :: < ErrorKind > ( ) . ok ( ) ;
149186 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 ;
151188 }
152189
153- let msg = msg . trim ( ) . to_owned ( ) ;
190+ let line = line . trim ( ) . to_owned ( ) ;
154191
155192 let ( which, line_num) = if follow {
156- assert_eq ! ( adjusts, 0 , "use either //~| or //~^, not both." ) ;
157193 let line_num = last_nonfollow_error. expect (
158194 "encountered //~| without \
159195 preceding //~^ line.",
@@ -165,13 +201,6 @@ fn parse_expected(
165201 ( which, line_num)
166202 } ;
167203
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 } ) )
177206}
0 commit comments