@@ -64,6 +64,43 @@ impl Condition {
64
64
}
65
65
}
66
66
67
+ macro_rules! checked {
68
+ ( $path: expr, $l: expr) => {
69
+ let path = $path;
70
+ let l = $l;
71
+ #[ allow( unused_macros) ]
72
+ macro_rules! exit {
73
+ ( $fmt: expr $$( , $args: expr) * ) => { {
74
+ eprint!( "{}:{l}: " , path. display( ) ) ;
75
+ eprintln!( $fmt, $$( $args, ) * ) ;
76
+ #[ cfg( not( test) ) ]
77
+ std:: process:: exit( 1 ) ;
78
+ #[ cfg( test) ]
79
+ panic!( ) ;
80
+ } } ;
81
+ }
82
+ #[ allow( unused_macros) ]
83
+ macro_rules! check {
84
+ ( $cond: expr, $fmt: expr $$( , $args: expr) * ) => { {
85
+ if !$cond {
86
+ exit!( $fmt $$( , $args) * ) ;
87
+ }
88
+ } } ;
89
+ }
90
+ #[ allow( unused_macros) ]
91
+ macro_rules! unwrap {
92
+ ( $cond: expr, $fmt: expr $$( , $args: expr) * ) => { {
93
+ match $cond {
94
+ Some ( val) => val,
95
+ None => {
96
+ exit!( $fmt $$( , $args) * ) ;
97
+ }
98
+ }
99
+ } } ;
100
+ }
101
+ } ;
102
+ }
103
+
67
104
impl Comments {
68
105
pub ( crate ) fn parse_file ( path : & Path ) -> Self {
69
106
let content = std:: fs:: read_to_string ( path) . unwrap ( ) ;
@@ -72,14 +109,45 @@ impl Comments {
72
109
73
110
/// Parse comments in `content`.
74
111
/// `path` is only used to emit diagnostics if parsing fails.
75
- pub ( crate ) fn parse ( path : & Path , content : & str ) -> Self {
112
+ ///
113
+ /// This function will only parse `//@` and `//~` style comments
114
+ /// and ignore all others
115
+ fn parse_checked ( path : & Path , content : & str ) -> Self {
76
116
let mut this = Self :: default ( ) ;
77
- let error_pattern_regex =
78
- Regex :: new ( r"//(\[(?P<revision>[^\]]+)\])?~(?P<offset>\||[\^]+)? *(?P<level>ERROR|HELP|WARN|NOTE)?:?(?P<text>.*)" )
79
- . unwrap ( ) ;
80
117
81
118
// The line that a `|` will refer to
82
119
let mut fallthrough_to = None ;
120
+ for ( l, line) in content. lines ( ) . enumerate ( ) {
121
+ let l = l + 1 ; // enumerate starts at 0, but line numbers start at 1
122
+ if let Some ( ( _, command) ) = line. split_once ( "//@" ) {
123
+ let command = command. trim ( ) ;
124
+ if let Some ( ( command, args) ) = command. split_once ( ':' ) {
125
+ this. parse_command_with_args ( command, args, path, l) ;
126
+ } else if let Some ( ( command, _comments) ) = command. split_once ( ' ' ) {
127
+ this. parse_command ( command, path, l)
128
+ } else {
129
+ this. parse_command ( command, path, l)
130
+ }
131
+ } else if let Some ( ( _, pattern) ) = line. split_once ( "//~" ) {
132
+ this. parse_pattern ( pattern, & mut fallthrough_to, path, l)
133
+ } else if let Some ( ( _, pattern) ) = line. split_once ( "//[" ) {
134
+ this. parse_revisioned_pattern ( pattern, & mut fallthrough_to, path, l)
135
+ } else {
136
+ fallthrough_to = None ;
137
+ }
138
+ }
139
+ this
140
+ }
141
+
142
+ /// Parse comments in `content`.
143
+ /// `path` is only used to emit diagnostics if parsing fails.
144
+ pub ( crate ) fn parse ( path : & Path , content : & str ) -> Self {
145
+ let mut this = Self :: parse_checked ( path, content) ;
146
+ if content. contains ( "//@" ) {
147
+ // Migration mode: if new syntax is used, ignore all old syntax
148
+ return this;
149
+ }
150
+
83
151
for ( l, line) in content. lines ( ) . enumerate ( ) {
84
152
let l = l + 1 ; // enumerate starts at 0, but line numbers start at 1
85
153
if let Some ( revisions) = line. strip_prefix ( "// revisions:" ) {
@@ -140,36 +208,146 @@ impl Comments {
140
208
) ;
141
209
this. error_pattern = Some ( ( s. trim ( ) . to_string ( ) , l) ) ;
142
210
}
143
- if let Some ( captures ) = error_pattern_regex . captures ( line ) {
144
- // FIXME: check that the error happens on the marked line
145
- let matched = captures [ "text" ] . trim ( ) . to_string ( ) ;
211
+ }
212
+ this
213
+ }
146
214
147
- let revision = captures. name ( "revision" ) . map ( |rev| rev. as_str ( ) . to_string ( ) ) ;
215
+ fn parse_command_with_args ( & mut self , command : & str , args : & str , path : & Path , l : usize ) {
216
+ checked ! ( path, l) ;
217
+ match command {
218
+ "revisions" => {
219
+ check ! ( self . revisions. is_none( ) , "cannot specifiy revisions twice" ) ;
220
+ self . revisions = Some ( args. split_whitespace ( ) . map ( |s| s. to_string ( ) ) . collect ( ) ) ;
221
+ }
222
+ "compile-flags" => {
223
+ self . compile_flags . extend ( args. split_whitespace ( ) . map ( |s| s. to_string ( ) ) ) ;
224
+ }
225
+ "rustc-env" =>
226
+ for env in args. split_whitespace ( ) {
227
+ let ( k, v) = unwrap ! (
228
+ env. split_once( '=' ) ,
229
+ "environment variables must be key/value pairs separated by a `=`"
230
+ ) ;
231
+ self . env_vars . push ( ( k. to_string ( ) , v. to_string ( ) ) ) ;
232
+ } ,
233
+ "normalize-stderr-test" => {
234
+ let ( from, to) =
235
+ unwrap ! ( args. split_once( "->" ) , "normalize-stderr-test needs a `->`" ) ;
236
+ let from = from. trim ( ) . trim_matches ( '"' ) ;
237
+ let to = to. trim ( ) . trim_matches ( '"' ) ;
238
+ let from = unwrap ! ( Regex :: new( from) . ok( ) , "invalid regex" ) ;
239
+ self . normalize_stderr . push ( ( from, to. to_string ( ) ) ) ;
240
+ }
241
+ "error-pattern" => {
242
+ check ! (
243
+ self . error_pattern. is_none( ) ,
244
+ "cannot specifiy error_pattern twice, previous: {:?}" ,
245
+ self . error_pattern
246
+ ) ;
247
+ self . error_pattern = Some ( ( args. trim ( ) . to_string ( ) , l) ) ;
248
+ }
249
+ _ => exit ! ( "unknown command {command} with args {args}" ) ,
250
+ }
251
+ }
148
252
149
- let level = captures. name ( "level" ) . map ( |rev| rev. as_str ( ) . parse ( ) . unwrap ( ) ) ;
253
+ fn parse_command ( & mut self , command : & str , path : & Path , l : usize ) {
254
+ checked ! ( path, l) ;
150
255
151
- let match_line = match captures. name ( "offset" ) . map ( |rev| rev. as_str ( ) ) {
152
- Some ( "|" ) => fallthrough_to. expect ( "`//~|` pattern without preceding line" ) ,
153
- Some ( pat) => {
154
- debug_assert ! ( pat. chars( ) . all( |c| c == '^' ) ) ;
155
- l - pat. len ( )
156
- }
157
- None => l,
158
- } ;
159
-
160
- fallthrough_to = Some ( match_line) ;
161
-
162
- this. error_matches . push ( ErrorMatch {
163
- matched,
164
- revision,
165
- level,
166
- definition_line : l,
167
- line : match_line,
168
- } ) ;
169
- } else {
170
- fallthrough_to = None ;
171
- }
256
+ if let Some ( s) = command. strip_prefix ( "ignore-" ) {
257
+ self . ignore . push ( Condition :: parse ( s) ) ;
258
+ return ;
172
259
}
173
- this
260
+
261
+ if let Some ( s) = command. strip_prefix ( "only-" ) {
262
+ self . only . push ( Condition :: parse ( s) ) ;
263
+ return ;
264
+ }
265
+
266
+ if command. starts_with ( "stderr-per-bitwidth" ) {
267
+ check ! ( !self . stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice" ) ;
268
+ self . stderr_per_bitwidth = true ;
269
+ return ;
270
+ }
271
+
272
+ exit ! ( "unknown command {command}" ) ;
273
+ }
274
+
275
+ fn parse_pattern (
276
+ & mut self ,
277
+ pattern : & str ,
278
+ fallthrough_to : & mut Option < usize > ,
279
+ path : & Path ,
280
+ l : usize ,
281
+ ) {
282
+ self . parse_pattern_inner ( pattern, fallthrough_to, None , path, l)
283
+ }
284
+
285
+ fn parse_revisioned_pattern (
286
+ & mut self ,
287
+ pattern : & str ,
288
+ fallthrough_to : & mut Option < usize > ,
289
+ path : & Path ,
290
+ l : usize ,
291
+ ) {
292
+ checked ! ( path, l) ;
293
+ let ( revision, pattern) =
294
+ unwrap ! ( pattern. split_once( ']' ) , "`//[` without corresponding `]`" ) ;
295
+ if pattern. starts_with ( '~' ) {
296
+ self . parse_pattern_inner (
297
+ & pattern[ 1 ..] ,
298
+ fallthrough_to,
299
+ Some ( revision. to_owned ( ) ) ,
300
+ path,
301
+ l,
302
+ )
303
+ } else {
304
+ exit ! ( "revisioned pattern must have `~` following the `]`" ) ;
305
+ }
306
+ }
307
+
308
+ // parse something like (?P<offset>\||[\^]+)? *(?P<level>ERROR|HELP|WARN|NOTE)?:?(?P<text>.*)
309
+ fn parse_pattern_inner (
310
+ & mut self ,
311
+ pattern : & str ,
312
+ fallthrough_to : & mut Option < usize > ,
313
+ revision : Option < String > ,
314
+ path : & Path ,
315
+ l : usize ,
316
+ ) {
317
+ checked ! ( path, l) ;
318
+ // FIXME: check that the error happens on the marked line
319
+
320
+ let ( match_line, pattern) = match unwrap ! ( pattern. chars( ) . next( ) , "no pattern specified" ) {
321
+ '|' =>
322
+ ( * unwrap ! ( fallthrough_to, "`//~|` pattern without preceding line" ) , & pattern[ 1 ..] ) ,
323
+ '^' => {
324
+ let offset = pattern. chars ( ) . take_while ( |& c| c == '^' ) . count ( ) ;
325
+ ( l - offset, & pattern[ offset..] )
326
+ }
327
+ _ => ( l, pattern) ,
328
+ } ;
329
+
330
+ let ( level, pattern) = match pattern. trim_start ( ) . split_once ( |c| matches ! ( c, ':' | ' ' ) ) {
331
+ None => ( None , pattern) ,
332
+ Some ( ( level, pattern_without_level) ) =>
333
+ match level. parse ( ) . ok ( ) {
334
+ Some ( level) => ( Some ( level) , pattern_without_level) ,
335
+ None => ( None , pattern) ,
336
+ } ,
337
+ } ;
338
+
339
+ let matched = pattern. trim ( ) . to_string ( ) ;
340
+
341
+ check ! ( !matched. is_empty( ) , "no pattern specified" ) ;
342
+
343
+ * fallthrough_to = Some ( match_line) ;
344
+
345
+ self . error_matches . push ( ErrorMatch {
346
+ matched,
347
+ revision,
348
+ level,
349
+ definition_line : l,
350
+ line : match_line,
351
+ } ) ;
174
352
}
175
353
}
0 commit comments