1
1
//! Checks that all error codes have at least one test to prevent having error
2
2
//! codes that are silently not thrown by the compiler anymore.
3
3
4
+ use std:: collections:: hash_map:: Entry ;
4
5
use std:: collections:: HashMap ;
5
6
use std:: ffi:: OsStr ;
6
7
use std:: fs:: read_to_string;
7
8
use std:: path:: Path ;
8
9
10
+ use regex:: Regex ;
11
+
9
12
// A few of those error codes can't be tested but all the others can and *should* be tested!
10
13
const EXEMPTED_FROM_TEST : & [ & str ] = & [
11
14
"E0227" , "E0279" , "E0280" , "E0313" , "E0314" , "E0315" , "E0377" , "E0461" , "E0462" , "E0464" ,
@@ -17,9 +20,16 @@ const EXEMPTED_FROM_TEST: &[&str] = &[
17
20
// Some error codes don't have any tests apparently...
18
21
const IGNORE_EXPLANATION_CHECK : & [ & str ] = & [ "E0570" , "E0601" , "E0602" , "E0729" ] ;
19
22
23
+ #[ derive( Default , Debug ) ]
24
+ struct ErrorCodeStatus {
25
+ has_test : bool ,
26
+ has_explanation : bool ,
27
+ is_used : bool ,
28
+ }
29
+
20
30
fn check_error_code_explanation (
21
31
f : & str ,
22
- error_codes : & mut HashMap < String , bool > ,
32
+ error_codes : & mut HashMap < String , ErrorCodeStatus > ,
23
33
err_code : String ,
24
34
) -> bool {
25
35
let mut invalid_compile_fail_format = false ;
@@ -30,15 +40,15 @@ fn check_error_code_explanation(
30
40
if s. starts_with ( "```" ) {
31
41
if s. contains ( "compile_fail" ) && s. contains ( 'E' ) {
32
42
if !found_error_code {
33
- error_codes. insert ( err_code. clone ( ) , true ) ;
43
+ error_codes. get_mut ( & err_code) . map ( |x| x . has_test = true ) ;
34
44
found_error_code = true ;
35
45
}
36
46
} else if s. contains ( "compile-fail" ) {
37
47
invalid_compile_fail_format = true ;
38
48
}
39
49
} else if s. starts_with ( "#### Note: this error code is no longer emitted by the compiler" ) {
40
50
if !found_error_code {
41
- error_codes. get_mut ( & err_code) . map ( |x| * x = true ) ;
51
+ error_codes. get_mut ( & err_code) . map ( |x| x . has_test = true ) ;
42
52
found_error_code = true ;
43
53
}
44
54
}
@@ -77,7 +87,7 @@ macro_rules! some_or_continue {
77
87
78
88
fn extract_error_codes (
79
89
f : & str ,
80
- error_codes : & mut HashMap < String , bool > ,
90
+ error_codes : & mut HashMap < String , ErrorCodeStatus > ,
81
91
path : & Path ,
82
92
errors : & mut Vec < String > ,
83
93
) {
@@ -90,14 +100,26 @@ fn extract_error_codes(
90
100
. split_once ( ':' )
91
101
. expect (
92
102
format ! (
93
- "Expected a line with the format `E0xxx: include_str!(\" ..\" )`, but got {} without a `:` delimiter" ,
103
+ "Expected a line with the format `E0xxx: include_str!(\" ..\" )`, but got {} \
104
+ without a `:` delimiter",
94
105
s,
95
- ) . as_str ( )
106
+ )
107
+ . as_str ( ) ,
96
108
)
97
109
. 0
98
110
. to_owned ( ) ;
99
- if !error_codes. contains_key ( & err_code) {
100
- error_codes. insert ( err_code. clone ( ) , false ) ;
111
+ match error_codes. entry ( err_code. clone ( ) ) {
112
+ Entry :: Occupied ( mut e) => {
113
+ let mut entry = e. get_mut ( ) ;
114
+ entry. has_explanation = true
115
+ }
116
+ Entry :: Vacant ( e) => {
117
+ e. insert ( ErrorCodeStatus {
118
+ has_test : false ,
119
+ is_used : false ,
120
+ has_explanation : true ,
121
+ } ) ;
122
+ }
101
123
}
102
124
// Now we extract the tests from the markdown file!
103
125
let md_file_name = match s. split_once ( "include_str!(\" " ) {
@@ -145,15 +167,15 @@ fn extract_error_codes(
145
167
. to_string ( ) ;
146
168
if !error_codes. contains_key ( & err_code) {
147
169
// this check should *never* fail!
148
- error_codes. insert ( err_code, false ) ;
170
+ error_codes. insert ( err_code, ErrorCodeStatus :: default ( ) ) ;
149
171
}
150
172
} else if s == ";" {
151
173
reached_no_explanation = true ;
152
174
}
153
175
}
154
176
}
155
177
156
- fn extract_error_codes_from_tests ( f : & str , error_codes : & mut HashMap < String , bool > ) {
178
+ fn extract_error_codes_from_tests ( f : & str , error_codes : & mut HashMap < String , ErrorCodeStatus > ) {
157
179
for line in f. lines ( ) {
158
180
let s = line. trim ( ) ;
159
181
if s. starts_with ( "error[E" ) || s. starts_with ( "warning[E" ) {
@@ -164,8 +186,48 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, boo
164
186
Some ( ( _, err_code) ) => err_code,
165
187
} ,
166
188
} ;
167
- let nb = error_codes. entry ( err_code. to_owned ( ) ) . or_insert ( false ) ;
168
- * nb = true ;
189
+ match error_codes. entry ( err_code. to_owned ( ) ) {
190
+ Entry :: Occupied ( mut e) => {
191
+ let mut entry = e. get_mut ( ) ;
192
+ entry. has_test = true
193
+ }
194
+ Entry :: Vacant ( e) => {
195
+ e. insert ( ErrorCodeStatus {
196
+ has_test : true ,
197
+ is_used : false ,
198
+ has_explanation : false ,
199
+ } ) ;
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ fn extract_error_codes_from_source (
207
+ f : & str ,
208
+ error_codes : & mut HashMap < String , ErrorCodeStatus > ,
209
+ regex : & Regex ,
210
+ ) {
211
+ for line in f. lines ( ) {
212
+ if line. trim_start ( ) . starts_with ( "//" ) {
213
+ continue ;
214
+ }
215
+ for cap in regex. captures_iter ( line) {
216
+ if let Some ( error_code) = cap. get ( 1 ) {
217
+ match error_codes. entry ( error_code. as_str ( ) . to_owned ( ) ) {
218
+ Entry :: Occupied ( mut e) => {
219
+ let mut entry = e. get_mut ( ) ;
220
+ entry. is_used = true
221
+ }
222
+ Entry :: Vacant ( e) => {
223
+ e. insert ( ErrorCodeStatus {
224
+ has_test : false ,
225
+ is_used : true ,
226
+ has_explanation : false ,
227
+ } ) ;
228
+ }
229
+ }
230
+ }
169
231
}
170
232
}
171
233
}
@@ -174,8 +236,17 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
174
236
let mut errors = Vec :: new ( ) ;
175
237
let mut found_explanations = 0 ;
176
238
let mut found_tests = 0 ;
239
+ let mut error_codes: HashMap < String , ErrorCodeStatus > = HashMap :: new ( ) ;
240
+ // We want error codes which match the following cases:
241
+ //
242
+ // * foo(a, E0111, a)
243
+ // * foo(a, E0111)
244
+ // * foo(E0111, a)
245
+ // * #[error = "E0111"]
246
+ let regex = Regex :: new ( r#"[(,"\s](E\d{4})[,)"]"# ) . unwrap ( ) ;
247
+
177
248
println ! ( "Checking which error codes lack tests..." ) ;
178
- let mut error_codes : HashMap < String , bool > = HashMap :: new ( ) ;
249
+
179
250
for path in paths {
180
251
super :: walk ( path, & mut |path| super :: filter_dirs ( path) , & mut |entry, contents| {
181
252
let file_name = entry. file_name ( ) ;
@@ -185,6 +256,11 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
185
256
} else if entry. path ( ) . extension ( ) == Some ( OsStr :: new ( "stderr" ) ) {
186
257
extract_error_codes_from_tests ( contents, & mut error_codes) ;
187
258
found_tests += 1 ;
259
+ } else if entry. path ( ) . extension ( ) == Some ( OsStr :: new ( "rs" ) ) {
260
+ let path = entry. path ( ) . to_string_lossy ( ) ;
261
+ if [ "src/test/" , "src/doc/" , "src/tools/" ] . iter ( ) . all ( |c| !path. contains ( c) ) {
262
+ extract_error_codes_from_source ( contents, & mut error_codes, & regex) ;
263
+ }
188
264
}
189
265
} ) ;
190
266
}
@@ -199,15 +275,22 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
199
275
if errors. is_empty ( ) {
200
276
println ! ( "Found {} error codes" , error_codes. len( ) ) ;
201
277
202
- for ( err_code, nb ) in & error_codes {
203
- if !* nb && !EXEMPTED_FROM_TEST . contains ( & err_code. as_str ( ) ) {
278
+ for ( err_code, error_status ) in & error_codes {
279
+ if !error_status . has_test && !EXEMPTED_FROM_TEST . contains ( & err_code. as_str ( ) ) {
204
280
errors. push ( format ! ( "Error code {} needs to have at least one UI test!" , err_code) ) ;
205
- } else if * nb && EXEMPTED_FROM_TEST . contains ( & err_code. as_str ( ) ) {
281
+ } else if error_status . has_test && EXEMPTED_FROM_TEST . contains ( & err_code. as_str ( ) ) {
206
282
errors. push ( format ! (
207
283
"Error code {} has a UI test, it shouldn't be listed into EXEMPTED_FROM_TEST!" ,
208
284
err_code
209
285
) ) ;
210
286
}
287
+ if !error_status. is_used && !error_status. has_explanation {
288
+ errors. push ( format ! (
289
+ "Error code {} isn't used and doesn't have an error explanation, it should be \
290
+ commented in error_codes.rs file",
291
+ err_code
292
+ ) ) ;
293
+ }
211
294
}
212
295
}
213
296
errors. sort ( ) ;
0 commit comments