7
7
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
+ use self :: WhichLine :: * ;
10
11
11
12
use std:: ascii:: AsciiExt ;
12
13
use std:: io:: { BufferedReader , File } ;
@@ -18,28 +19,74 @@ pub struct ExpectedError {
18
19
pub msg : String ,
19
20
}
20
21
21
- pub static EXPECTED_PATTERN : & ' static str = r"//~(?P<adjusts>\^*)\s*(?P<kind>\S*)\s*(?P<msg>.*)" ;
22
+ /// Looks for either "//~| KIND MESSAGE" or "//~^^... KIND MESSAGE"
23
+ /// The former is a "follow" that inherits its target from the preceding line;
24
+ /// the latter is an "adjusts" that goes that many lines up.
25
+ ///
26
+ /// Goal is to enable tests both like: //~^^^ ERROR go up three
27
+ /// and also //~^ ERROR message one for the preceding line, and
28
+ /// //~| ERROR message two for that same line.
29
+
30
+ pub static EXPECTED_PATTERN : & ' static str =
31
+ r"//~(?P<follow>\|)?(?P<adjusts>\^*)\s*(?P<kind>\S*)\s*(?P<msg>.*)" ;
32
+
33
+ #[ deriving( PartialEq , Show ) ]
34
+ enum WhichLine { ThisLine , FollowPrevious ( uint ) , AdjustBackward ( uint ) }
22
35
23
36
// Load any test directives embedded in the file
24
37
pub fn load_errors ( re : & Regex , testfile : & Path ) -> Vec < ExpectedError > {
25
38
let mut rdr = BufferedReader :: new ( File :: open ( testfile) . unwrap ( ) ) ;
26
39
40
+ // `last_nonfollow_error` tracks the most recently seen
41
+ // line with an error template that did not use the
42
+ // follow-syntax, "//~| ...".
43
+ //
44
+ // (pnkfelix could not find an easy way to compose Iterator::scan
45
+ // and Iterator::filter_map to pass along this information into
46
+ // `parse_expected`. So instead I am storing that state here and
47
+ // updating it in the map callback below.)
48
+ let mut last_nonfollow_error = None ;
49
+
27
50
rdr. lines ( ) . enumerate ( ) . filter_map ( |( line_no, ln) | {
28
- parse_expected ( line_no + 1 , ln. unwrap ( ) . as_slice ( ) , re)
51
+ parse_expected ( last_nonfollow_error,
52
+ line_no + 1 ,
53
+ ln. unwrap ( ) . as_slice ( ) , re)
54
+ . map ( |( which, error) | {
55
+ match which {
56
+ FollowPrevious ( _) => { }
57
+ _ => last_nonfollow_error = Some ( error. line ) ,
58
+ }
59
+ error
60
+ } )
29
61
} ) . collect ( )
30
62
}
31
63
32
- fn parse_expected ( line_num : uint , line : & str , re : & Regex ) -> Option < ExpectedError > {
64
+ fn parse_expected ( last_nonfollow_error : Option < uint > ,
65
+ line_num : uint ,
66
+ line : & str ,
67
+ re : & Regex ) -> Option < ( WhichLine , ExpectedError ) > {
33
68
re. captures ( line) . and_then ( |caps| {
34
69
let adjusts = caps. name ( "adjusts" ) . len ( ) ;
35
70
let kind = caps. name ( "kind" ) . to_ascii_lower ( ) ;
36
71
let msg = caps. name ( "msg" ) . trim ( ) . to_string ( ) ;
72
+ let follow = caps. name ( "follow" ) . len ( ) > 0 ;
73
+
74
+ let ( which, line) = if follow {
75
+ assert ! ( adjusts == 0 , "use either //~| or //~^, not both." ) ;
76
+ let line = last_nonfollow_error. unwrap_or_else ( || {
77
+ panic ! ( "encountered //~| without preceding //~^ line." )
78
+ } ) ;
79
+ ( FollowPrevious ( line) , line)
80
+ } else {
81
+ let which =
82
+ if adjusts > 0 { AdjustBackward ( adjusts) } else { ThisLine } ;
83
+ let line = line_num - adjusts;
84
+ ( which, line)
85
+ } ;
37
86
38
- debug ! ( "line={} kind={} msg={}" , line_num, kind, msg) ;
39
- Some ( ExpectedError {
40
- line : line_num - adjusts,
41
- kind : kind,
42
- msg : msg,
43
- } )
87
+ debug ! ( "line={} which={} kind={} msg={}" , line_num, which, kind, msg) ;
88
+ Some ( ( which, ExpectedError { line : line,
89
+ kind : kind,
90
+ msg : msg, } ) )
44
91
} )
45
92
}
0 commit comments