]> git.proxmox.com Git - rustc.git/blame - src/tools/tidy/src/error_codes_check.rs
New upstream version 1.49.0+dfsg1
[rustc.git] / src / tools / tidy / src / error_codes_check.rs
CommitLineData
e74abb32
XL
1//! Checks that all error codes have at least one test to prevent having error
2//! codes that are silently not thrown by the compiler anymore.
3
4use std::collections::HashMap;
5use std::ffi::OsStr;
60c5eb7d 6use std::fs::read_to_string;
e74abb32
XL
7use std::path::Path;
8
9// A few of those error codes can't be tested but all the others can and *should* be tested!
f035d41b 10const EXEMPTED_FROM_TEST: &[&str] = &[
3dfed10e
XL
11 "E0183", "E0227", "E0279", "E0280", "E0311", "E0313", "E0314", "E0315", "E0377", "E0461",
12 "E0462", "E0464", "E0465", "E0472", "E0473", "E0474", "E0475", "E0476", "E0479", "E0480",
13 "E0481", "E0482", "E0483", "E0484", "E0485", "E0486", "E0487", "E0488", "E0489", "E0514",
14 "E0519", "E0523", "E0553", "E0554", "E0570", "E0629", "E0630", "E0640", "E0717", "E0727",
15 "E0729",
e74abb32
XL
16];
17
f9f354fc 18// Some error codes don't have any tests apparently...
3dfed10e 19const IGNORE_EXPLANATION_CHECK: &[&str] = &["E0570", "E0601", "E0602", "E0639", "E0729"];
f9f354fc 20
60c5eb7d
XL
21fn check_error_code_explanation(
22 f: &str,
23 error_codes: &mut HashMap<String, bool>,
24 err_code: String,
f9f354fc
XL
25) -> bool {
26 let mut invalid_compile_fail_format = false;
27 let mut found_error_code = false;
28
60c5eb7d
XL
29 for line in f.lines() {
30 let s = line.trim();
f9f354fc
XL
31 if s.starts_with("```") {
32 if s.contains("compile_fail") && s.contains('E') {
33 if !found_error_code {
34 error_codes.insert(err_code.clone(), true);
35 found_error_code = true;
36 }
37 } else if s.contains("compile-fail") {
38 invalid_compile_fail_format = true;
39 }
60c5eb7d 40 } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
f9f354fc
XL
41 if !found_error_code {
42 error_codes.get_mut(&err_code).map(|x| *x = true);
43 found_error_code = true;
44 }
60c5eb7d
XL
45 }
46 }
f9f354fc
XL
47 invalid_compile_fail_format
48}
49
3dfed10e 50fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &str) -> bool {
f9f354fc
XL
51 for line in f.lines() {
52 let s = line.trim();
53 if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
54 return true;
55 }
56 if s.starts_with("```") {
57 if s.contains("compile_fail") && s.contains(err_code) {
58 return true;
3dfed10e 59 } else if s.contains('(') {
f9f354fc 60 // It's very likely that we can't actually make it fail compilation...
3dfed10e 61 return true;
f9f354fc
XL
62 }
63 }
64 }
3dfed10e 65 false
60c5eb7d
XL
66}
67
68macro_rules! some_or_continue {
dfeec247 69 ($e:expr) => {
60c5eb7d
XL
70 match $e {
71 Some(e) => e,
72 None => continue,
73 }
dfeec247 74 };
60c5eb7d
XL
75}
76
f9f354fc
XL
77fn extract_error_codes(
78 f: &str,
79 error_codes: &mut HashMap<String, bool>,
80 path: &Path,
81 errors: &mut Vec<String>,
82) {
e74abb32 83 let mut reached_no_explanation = false;
e74abb32
XL
84
85 for line in f.lines() {
86 let s = line.trim();
60c5eb7d 87 if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
e74abb32
XL
88 if let Some(err_code) = s.splitn(2, ':').next() {
89 let err_code = err_code.to_owned();
e74abb32 90 if !error_codes.contains_key(&err_code) {
60c5eb7d 91 error_codes.insert(err_code.clone(), false);
e74abb32 92 }
60c5eb7d 93 // Now we extract the tests from the markdown file!
dfeec247 94 let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1));
60c5eb7d 95 let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
f9f354fc
XL
96 let path = some_or_continue!(path.parent())
97 .join(md_file_name)
98 .canonicalize()
99 .expect("failed to canonicalize error explanation file path");
60c5eb7d
XL
100 match read_to_string(&path) {
101 Ok(content) => {
f9f354fc
XL
102 if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str())
103 && !check_if_error_code_is_test_in_explanation(&content, &err_code)
104 {
105 errors.push(format!(
106 "`{}` doesn't use its own error code in compile_fail example",
107 path.display(),
108 ));
109 }
110 if check_error_code_explanation(&content, error_codes, err_code) {
111 errors.push(format!(
112 "`{}` uses invalid tag `compile-fail` instead of `compile_fail`",
113 path.display(),
114 ));
115 }
60c5eb7d
XL
116 }
117 Err(e) => {
118 eprintln!("Couldn't read `{}`: {}", path.display(), e);
119 }
e74abb32
XL
120 }
121 }
e74abb32
XL
122 } else if reached_no_explanation && s.starts_with('E') {
123 if let Some(err_code) = s.splitn(2, ',').next() {
124 let err_code = err_code.to_owned();
dfeec247
XL
125 if !error_codes.contains_key(&err_code) {
126 // this check should *never* fail!
e74abb32
XL
127 error_codes.insert(err_code, false);
128 }
129 }
60c5eb7d
XL
130 } else if s == ";" {
131 reached_no_explanation = true;
e74abb32
XL
132 }
133 }
134}
135
136fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
137 for line in f.lines() {
138 let s = line.trim();
139 if s.starts_with("error[E") || s.starts_with("warning[E") {
140 if let Some(err_code) = s.splitn(2, ']').next() {
dfeec247 141 if let Some(err_code) = err_code.splitn(2, '[').nth(1) {
e74abb32
XL
142 let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
143 *nb = true;
144 }
145 }
146 }
147 }
148}
149
150pub fn check(path: &Path, bad: &mut bool) {
f9f354fc 151 let mut errors = Vec::new();
e74abb32
XL
152 println!("Checking which error codes lack tests...");
153 let mut error_codes: HashMap<String, bool> = HashMap::new();
dfeec247 154 super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| {
e74abb32
XL
155 let file_name = entry.file_name();
156 if file_name == "error_codes.rs" {
f9f354fc 157 extract_error_codes(contents, &mut error_codes, entry.path(), &mut errors);
e74abb32
XL
158 } else if entry.path().extension() == Some(OsStr::new("stderr")) {
159 extract_error_codes_from_tests(contents, &mut error_codes);
160 }
161 });
f9f354fc
XL
162 if errors.is_empty() {
163 println!("Found {} error codes", error_codes.len());
e74abb32 164
f9f354fc 165 for (err_code, nb) in &error_codes {
f035d41b 166 if !*nb && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
f9f354fc
XL
167 errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
168 }
e74abb32
XL
169 }
170 }
171 errors.sort();
172 for err in &errors {
173 eprintln!("{}", err);
174 }
175 println!("Found {} error codes with no tests", errors.len());
176 if !errors.is_empty() {
177 *bad = true;
178 }
179 println!("Done!");
180}