]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | use std::collections::HashSet; |
a7813a04 XL |
2 | use std::env; |
3 | use std::fs::File; | |
a7813a04 | 4 | use std::io::prelude::*; |
94b46f34 | 5 | use std::io::BufReader; |
a7813a04 | 6 | use std::path::{Path, PathBuf}; |
2b03887a | 7 | use std::process::Command; |
a7813a04 | 8 | |
3dfed10e | 9 | use tracing::*; |
48663c56 | 10 | |
2b03887a | 11 | use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PassMode}; |
dfeec247 | 12 | use crate::util; |
1b1a35ee | 13 | use crate::{extract_cdb_version, extract_gdb_version}; |
c30ab7b3 | 14 | |
416331ca XL |
15 | #[cfg(test)] |
16 | mod tests; | |
17 | ||
0bf4aa26 XL |
18 | /// The result of parse_cfg_name_directive. |
19 | #[derive(Clone, Copy, PartialEq, Debug)] | |
20 | enum ParsedNameDirective { | |
21 | /// No match. | |
22 | NoMatch, | |
23 | /// Match. | |
24 | Match, | |
0bf4aa26 XL |
25 | } |
26 | ||
a7813a04 XL |
27 | /// Properties which must be known very early, before actually running |
28 | /// the test. | |
74b04a01 | 29 | #[derive(Default)] |
a7813a04 | 30 | pub struct EarlyProps { |
8bb4bdeb | 31 | pub aux: Vec<String>, |
60c5eb7d | 32 | pub aux_crate: Vec<(String, String)>, |
ff7c6d11 | 33 | pub revisions: Vec<String>, |
a7813a04 XL |
34 | } |
35 | ||
36 | impl EarlyProps { | |
37 | pub fn from_file(config: &Config, testfile: &Path) -> Self { | |
136023e0 | 38 | let file = File::open(testfile).expect("open test file to parse earlyprops"); |
74b04a01 XL |
39 | Self::from_reader(config, testfile, file) |
40 | } | |
a7813a04 | 41 | |
74b04a01 XL |
42 | pub fn from_reader<R: Read>(config: &Config, testfile: &Path, rdr: R) -> Self { |
43 | let mut props = EarlyProps::default(); | |
136023e0 | 44 | iter_header(testfile, rdr, &mut |_, ln| { |
5099ac24 FG |
45 | config.push_name_value_directive(ln, directives::AUX_BUILD, &mut props.aux, |r| { |
46 | r.trim().to_string() | |
47 | }); | |
48 | config.push_name_value_directive( | |
49 | ln, | |
50 | directives::AUX_CRATE, | |
51 | &mut props.aux_crate, | |
52 | Config::parse_aux_crate, | |
53 | ); | |
cdc7bbd5 | 54 | config.parse_and_update_revisions(ln, &mut props.revisions); |
a7813a04 | 55 | }); |
a7813a04 | 56 | return props; |
a7813a04 XL |
57 | } |
58 | } | |
59 | ||
60 | #[derive(Clone, Debug)] | |
61 | pub struct TestProps { | |
62 | // Lines that should be expected, in order, on standard out | |
5bcae85e | 63 | pub error_patterns: Vec<String>, |
064997fb FG |
64 | // Regexes that should be expected, in order, on standard out |
65 | pub regex_error_patterns: Vec<String>, | |
a7813a04 XL |
66 | // Extra flags to pass to the compiler |
67 | pub compile_flags: Vec<String>, | |
68 | // Extra flags to pass when the compiled code is run (such as --bench) | |
69 | pub run_flags: Option<String>, | |
70 | // If present, the name of a file that this test should match when | |
71 | // pretty-printed | |
72 | pub pp_exact: Option<PathBuf>, | |
73 | // Other crates that should be compiled (typically from the same | |
74 | // directory as the test, but for backwards compatibility reasons | |
75 | // we also check the auxiliary directory) | |
5bcae85e | 76 | pub aux_builds: Vec<String>, |
60c5eb7d XL |
77 | // Similar to `aux_builds`, but a list of NAME=somelib.rs of dependencies |
78 | // to build and pass with the `--extern` flag. | |
79 | pub aux_crates: Vec<(String, String)>, | |
a7813a04 | 80 | // Environment settings to use for compiling |
5bcae85e | 81 | pub rustc_env: Vec<(String, String)>, |
48663c56 XL |
82 | // Environment variables to unset prior to compiling. |
83 | // Variables are unset before applying 'rustc_env'. | |
84 | pub unset_rustc_env: Vec<String>, | |
a7813a04 | 85 | // Environment settings to use during execution |
5bcae85e | 86 | pub exec_env: Vec<(String, String)>, |
a7813a04 XL |
87 | // Build documentation for all specified aux-builds as well |
88 | pub build_aux_docs: bool, | |
89 | // Flag to force a crate to be built with the host architecture | |
90 | pub force_host: bool, | |
91 | // Check stdout for error-pattern output as well as stderr | |
92 | pub check_stdout: bool, | |
e1599b0c XL |
93 | // Check stdout & stderr for output of run-pass test |
94 | pub check_run_results: bool, | |
0bf4aa26 XL |
95 | // For UI tests, allows compiler to generate arbitrary output to stdout |
96 | pub dont_check_compiler_stdout: bool, | |
97 | // For UI tests, allows compiler to generate arbitrary output to stderr | |
98 | pub dont_check_compiler_stderr: bool, | |
a7813a04 | 99 | // Don't force a --crate-type=dylib flag on the command line |
48663c56 XL |
100 | // |
101 | // Set this for example if you have an auxiliary test file that contains | |
102 | // a proc-macro and needs `#![crate_type = "proc-macro"]`. This ensures | |
103 | // that the aux file is compiled as a `proc-macro` and not as a `dylib`. | |
a7813a04 | 104 | pub no_prefer_dynamic: bool, |
94222f64 | 105 | // Run -Zunpretty expanded when running pretty printing tests |
a7813a04 XL |
106 | pub pretty_expanded: bool, |
107 | // Which pretty mode are we testing with, default to 'normal' | |
108 | pub pretty_mode: String, | |
109 | // Only compare pretty output and don't try compiling | |
110 | pub pretty_compare_only: bool, | |
111 | // Patterns which must not appear in the output of a cfail test. | |
112 | pub forbid_output: Vec<String>, | |
113 | // Revisions to test for incremental compilation. | |
114 | pub revisions: Vec<String>, | |
115 | // Directory (if any) to use for incremental compilation. This is | |
116 | // not set by end-users; rather it is set by the incremental | |
117 | // testing harness and used when generating compilation | |
118 | // arguments. (In particular, it propagates to the aux-builds.) | |
119 | pub incremental_dir: Option<PathBuf>, | |
c295e0f8 XL |
120 | // If `true`, this test will use incremental compilation. |
121 | // | |
122 | // This can be set manually with the `incremental` header, or implicitly | |
123 | // by being a part of an incremental mode test. Using the `incremental` | |
124 | // header should be avoided if possible; using an incremental mode test is | |
125 | // preferred. Incremental mode tests support multiple passes, which can | |
126 | // verify that the incremental cache can be loaded properly after being | |
127 | // created. Just setting the header will only verify the behavior with | |
128 | // creating an incremental cache, but doesn't check that it is created | |
129 | // correctly. | |
130 | // | |
131 | // Compiletest will create the incremental directory, and ensure it is | |
132 | // empty before the test starts. Incremental mode tests will reuse the | |
133 | // incremental directory between passes in the same test. | |
134 | pub incremental: bool, | |
5099ac24 FG |
135 | // If `true`, this test is a known bug. |
136 | // | |
137 | // When set, some requirements are relaxed. Currently, this only means no | |
138 | // error annotations are needed, but this may be updated in the future to | |
139 | // include other relaxations. | |
140 | pub known_bug: bool, | |
dc9dc135 XL |
141 | // How far should the test proceed while still passing. |
142 | pass_mode: Option<PassMode>, | |
143 | // Ignore `--pass` overrides from the command line for this test. | |
144 | ignore_pass: bool, | |
dfeec247 XL |
145 | // How far this test should proceed to start failing. |
146 | pub fail_mode: Option<FailMode>, | |
8bb4bdeb XL |
147 | // rustdoc will test the output of the `--test` option |
148 | pub check_test_line_numbers_match: bool, | |
041b39d2 XL |
149 | // customized normalization rules |
150 | pub normalize_stdout: Vec<(String, String)>, | |
151 | pub normalize_stderr: Vec<(String, String)>, | |
0531ce1d | 152 | pub failure_status: i32, |
532ac7d7 XL |
153 | // Whether or not `rustfix` should apply the `CodeSuggestion`s of this test and compile the |
154 | // resulting Rust code. | |
83c7162d | 155 | pub run_rustfix: bool, |
532ac7d7 | 156 | // If true, `rustfix` will only apply `MachineApplicable` suggestions. |
b7449926 | 157 | pub rustfix_only_machine_applicable: bool, |
532ac7d7 | 158 | pub assembly_output: Option<String>, |
60c5eb7d XL |
159 | // If true, the test is expected to ICE |
160 | pub should_ice: bool, | |
6a06907d XL |
161 | // If true, the stderr is expected to be different across bit-widths. |
162 | pub stderr_per_bitwidth: bool, | |
04454e1e FG |
163 | // The MIR opt to unit test, if any |
164 | pub mir_unit_test: Option<String>, | |
f25598a0 FG |
165 | // Whether to tell `rustc` to remap the "src base" directory to a fake |
166 | // directory. | |
167 | pub remap_src_base: bool, | |
a7813a04 XL |
168 | } |
169 | ||
5099ac24 FG |
170 | mod directives { |
171 | pub const ERROR_PATTERN: &'static str = "error-pattern"; | |
064997fb | 172 | pub const REGEX_ERROR_PATTERN: &'static str = "regex-error-pattern"; |
5099ac24 FG |
173 | pub const COMPILE_FLAGS: &'static str = "compile-flags"; |
174 | pub const RUN_FLAGS: &'static str = "run-flags"; | |
175 | pub const SHOULD_ICE: &'static str = "should-ice"; | |
176 | pub const BUILD_AUX_DOCS: &'static str = "build-aux-docs"; | |
177 | pub const FORCE_HOST: &'static str = "force-host"; | |
178 | pub const CHECK_STDOUT: &'static str = "check-stdout"; | |
179 | pub const CHECK_RUN_RESULTS: &'static str = "check-run-results"; | |
180 | pub const DONT_CHECK_COMPILER_STDOUT: &'static str = "dont-check-compiler-stdout"; | |
181 | pub const DONT_CHECK_COMPILER_STDERR: &'static str = "dont-check-compiler-stderr"; | |
182 | pub const NO_PREFER_DYNAMIC: &'static str = "no-prefer-dynamic"; | |
183 | pub const PRETTY_EXPANDED: &'static str = "pretty-expanded"; | |
184 | pub const PRETTY_MODE: &'static str = "pretty-mode"; | |
185 | pub const PRETTY_COMPARE_ONLY: &'static str = "pretty-compare-only"; | |
186 | pub const AUX_BUILD: &'static str = "aux-build"; | |
187 | pub const AUX_CRATE: &'static str = "aux-crate"; | |
188 | pub const EXEC_ENV: &'static str = "exec-env"; | |
189 | pub const RUSTC_ENV: &'static str = "rustc-env"; | |
190 | pub const UNSET_RUSTC_ENV: &'static str = "unset-rustc-env"; | |
191 | pub const FORBID_OUTPUT: &'static str = "forbid-output"; | |
192 | pub const CHECK_TEST_LINE_NUMBERS_MATCH: &'static str = "check-test-line-numbers-match"; | |
193 | pub const IGNORE_PASS: &'static str = "ignore-pass"; | |
194 | pub const FAILURE_STATUS: &'static str = "failure-status"; | |
195 | pub const RUN_RUSTFIX: &'static str = "run-rustfix"; | |
196 | pub const RUSTFIX_ONLY_MACHINE_APPLICABLE: &'static str = "rustfix-only-machine-applicable"; | |
197 | pub const ASSEMBLY_OUTPUT: &'static str = "assembly-output"; | |
198 | pub const STDERR_PER_BITWIDTH: &'static str = "stderr-per-bitwidth"; | |
199 | pub const INCREMENTAL: &'static str = "incremental"; | |
200 | pub const KNOWN_BUG: &'static str = "known-bug"; | |
04454e1e | 201 | pub const MIR_UNIT_TEST: &'static str = "unit-test"; |
f25598a0 | 202 | pub const REMAP_SRC_BASE: &'static str = "remap-src-base"; |
04454e1e FG |
203 | // This isn't a real directive, just one that is probably mistyped often |
204 | pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags"; | |
5099ac24 FG |
205 | } |
206 | ||
a7813a04 XL |
207 | impl TestProps { |
208 | pub fn new() -> Self { | |
a7813a04 | 209 | TestProps { |
9e0c209e | 210 | error_patterns: vec![], |
064997fb | 211 | regex_error_patterns: vec![], |
a7813a04 | 212 | compile_flags: vec![], |
9e0c209e SL |
213 | run_flags: None, |
214 | pp_exact: None, | |
215 | aux_builds: vec![], | |
60c5eb7d | 216 | aux_crates: vec![], |
a7813a04 XL |
217 | revisions: vec![], |
218 | rustc_env: vec![], | |
48663c56 | 219 | unset_rustc_env: vec![], |
9e0c209e | 220 | exec_env: vec![], |
9e0c209e SL |
221 | build_aux_docs: false, |
222 | force_host: false, | |
223 | check_stdout: false, | |
e1599b0c | 224 | check_run_results: false, |
0bf4aa26 XL |
225 | dont_check_compiler_stdout: false, |
226 | dont_check_compiler_stderr: false, | |
9e0c209e SL |
227 | no_prefer_dynamic: false, |
228 | pretty_expanded: false, | |
041b39d2 | 229 | pretty_mode: "normal".to_string(), |
9e0c209e SL |
230 | pretty_compare_only: false, |
231 | forbid_output: vec![], | |
a7813a04 | 232 | incremental_dir: None, |
c295e0f8 | 233 | incremental: false, |
5099ac24 | 234 | known_bug: false, |
dc9dc135 | 235 | pass_mode: None, |
dfeec247 | 236 | fail_mode: None, |
dc9dc135 | 237 | ignore_pass: false, |
8bb4bdeb | 238 | check_test_line_numbers_match: false, |
041b39d2 XL |
239 | normalize_stdout: vec![], |
240 | normalize_stderr: vec![], | |
8faf50e0 | 241 | failure_status: -1, |
83c7162d | 242 | run_rustfix: false, |
b7449926 | 243 | rustfix_only_machine_applicable: false, |
532ac7d7 | 244 | assembly_output: None, |
60c5eb7d | 245 | should_ice: false, |
6a06907d | 246 | stderr_per_bitwidth: false, |
04454e1e | 247 | mir_unit_test: None, |
f25598a0 | 248 | remap_src_base: false, |
a7813a04 XL |
249 | } |
250 | } | |
251 | ||
94b46f34 | 252 | pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>, config: &Config) -> Self { |
a7813a04 XL |
253 | let mut props = TestProps::new(); |
254 | ||
255 | // copy over select properties to the aux build: | |
256 | props.incremental_dir = self.incremental_dir.clone(); | |
064997fb | 257 | props.ignore_pass = true; |
7cac9316 | 258 | props.load_from(testfile, cfg, config); |
a7813a04 XL |
259 | |
260 | props | |
261 | } | |
262 | ||
abe05a73 | 263 | pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self { |
a7813a04 | 264 | let mut props = TestProps::new(); |
abe05a73 | 265 | props.load_from(testfile, cfg, config); |
dfeec247 XL |
266 | |
267 | match (props.pass_mode, props.fail_mode) { | |
487cf647 | 268 | (None, None) if config.mode == Mode::Ui => props.fail_mode = Some(FailMode::Check), |
dfeec247 | 269 | (Some(_), Some(_)) => panic!("cannot use a *-fail and *-pass mode together"), |
487cf647 | 270 | _ => {} |
dfeec247 XL |
271 | } |
272 | ||
a7813a04 XL |
273 | props |
274 | } | |
275 | ||
9fa01778 | 276 | /// Loads properties from `testfile` into `props`. If a property is |
a7813a04 XL |
277 | /// tied to a particular revision `foo` (indicated by writing |
278 | /// `//[foo]`), then the property is ignored unless `cfg` is | |
279 | /// `Some("foo")`. | |
94b46f34 | 280 | fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) { |
f25598a0 FG |
281 | // Mode-dependent defaults. |
282 | self.remap_src_base = config.mode == Mode::Ui && !config.suite.contains("rustdoc"); | |
283 | ||
3c0e092e | 284 | let mut has_edition = false; |
74b04a01 XL |
285 | if !testfile.is_dir() { |
286 | let file = File::open(testfile).unwrap(); | |
a7813a04 | 287 | |
136023e0 XL |
288 | iter_header(testfile, file, &mut |revision, ln| { |
289 | if revision.is_some() && revision != cfg { | |
290 | return; | |
291 | } | |
292 | ||
5099ac24 | 293 | use directives::*; |
a7813a04 | 294 | |
5099ac24 FG |
295 | config.push_name_value_directive( |
296 | ln, | |
297 | ERROR_PATTERN, | |
298 | &mut self.error_patterns, | |
299 | |r| r, | |
300 | ); | |
064997fb FG |
301 | config.push_name_value_directive( |
302 | ln, | |
303 | REGEX_ERROR_PATTERN, | |
304 | &mut self.regex_error_patterns, | |
305 | |r| r, | |
306 | ); | |
5099ac24 FG |
307 | |
308 | if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) { | |
74b04a01 XL |
309 | self.compile_flags.extend(flags.split_whitespace().map(|s| s.to_owned())); |
310 | } | |
04454e1e FG |
311 | if config.parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS).is_some() { |
312 | panic!("`compiler-flags` directive should be spelled `compile-flags`"); | |
313 | } | |
8faf50e0 | 314 | |
74b04a01 | 315 | if let Some(edition) = config.parse_edition(ln) { |
064997fb | 316 | self.compile_flags.push(format!("--edition={}", edition.trim())); |
3c0e092e | 317 | has_edition = true; |
74b04a01 | 318 | } |
a7813a04 | 319 | |
cdc7bbd5 | 320 | config.parse_and_update_revisions(ln, &mut self.revisions); |
a7813a04 | 321 | |
5099ac24 | 322 | config.set_name_value_directive(ln, RUN_FLAGS, &mut self.run_flags, |r| r); |
a7813a04 | 323 | |
74b04a01 XL |
324 | if self.pp_exact.is_none() { |
325 | self.pp_exact = config.parse_pp_exact(ln, testfile); | |
326 | } | |
60c5eb7d | 327 | |
5099ac24 FG |
328 | config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice); |
329 | config.set_name_directive(ln, BUILD_AUX_DOCS, &mut self.build_aux_docs); | |
330 | config.set_name_directive(ln, FORCE_HOST, &mut self.force_host); | |
331 | config.set_name_directive(ln, CHECK_STDOUT, &mut self.check_stdout); | |
332 | config.set_name_directive(ln, CHECK_RUN_RESULTS, &mut self.check_run_results); | |
333 | config.set_name_directive( | |
334 | ln, | |
335 | DONT_CHECK_COMPILER_STDOUT, | |
336 | &mut self.dont_check_compiler_stdout, | |
337 | ); | |
338 | config.set_name_directive( | |
339 | ln, | |
340 | DONT_CHECK_COMPILER_STDERR, | |
341 | &mut self.dont_check_compiler_stderr, | |
342 | ); | |
343 | config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic); | |
344 | config.set_name_directive(ln, PRETTY_EXPANDED, &mut self.pretty_expanded); | |
345 | ||
346 | if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE) { | |
74b04a01 XL |
347 | self.pretty_mode = m; |
348 | } | |
a7813a04 | 349 | |
5099ac24 FG |
350 | config.set_name_directive(ln, PRETTY_COMPARE_ONLY, &mut self.pretty_compare_only); |
351 | config.push_name_value_directive(ln, AUX_BUILD, &mut self.aux_builds, |r| { | |
352 | r.trim().to_string() | |
353 | }); | |
354 | config.push_name_value_directive( | |
355 | ln, | |
356 | AUX_CRATE, | |
357 | &mut self.aux_crates, | |
358 | Config::parse_aux_crate, | |
359 | ); | |
360 | config.push_name_value_directive( | |
361 | ln, | |
362 | EXEC_ENV, | |
363 | &mut self.exec_env, | |
364 | Config::parse_env, | |
365 | ); | |
366 | config.push_name_value_directive( | |
367 | ln, | |
368 | RUSTC_ENV, | |
369 | &mut self.rustc_env, | |
370 | Config::parse_env, | |
371 | ); | |
372 | config.push_name_value_directive( | |
373 | ln, | |
374 | UNSET_RUSTC_ENV, | |
375 | &mut self.unset_rustc_env, | |
376 | |r| r, | |
377 | ); | |
378 | config.push_name_value_directive(ln, FORBID_OUTPUT, &mut self.forbid_output, |r| r); | |
379 | config.set_name_directive( | |
380 | ln, | |
381 | CHECK_TEST_LINE_NUMBERS_MATCH, | |
382 | &mut self.check_test_line_numbers_match, | |
383 | ); | |
ff7c6d11 | 384 | |
74b04a01 XL |
385 | self.update_pass_mode(ln, cfg, config); |
386 | self.update_fail_mode(ln, config); | |
83c7162d | 387 | |
5099ac24 | 388 | config.set_name_directive(ln, IGNORE_PASS, &mut self.ignore_pass); |
0531ce1d | 389 | |
74b04a01 XL |
390 | if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stdout") { |
391 | self.normalize_stdout.push(rule); | |
392 | } | |
393 | if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stderr") { | |
394 | self.normalize_stderr.push(rule); | |
395 | } | |
83c7162d | 396 | |
5099ac24 FG |
397 | if let Some(code) = config |
398 | .parse_name_value_directive(ln, FAILURE_STATUS) | |
399 | .and_then(|code| code.trim().parse::<i32>().ok()) | |
400 | { | |
74b04a01 XL |
401 | self.failure_status = code; |
402 | } | |
b7449926 | 403 | |
5099ac24 FG |
404 | config.set_name_directive(ln, RUN_RUSTFIX, &mut self.run_rustfix); |
405 | config.set_name_directive( | |
406 | ln, | |
407 | RUSTFIX_ONLY_MACHINE_APPLICABLE, | |
408 | &mut self.rustfix_only_machine_applicable, | |
409 | ); | |
410 | config.set_name_value_directive( | |
411 | ln, | |
412 | ASSEMBLY_OUTPUT, | |
413 | &mut self.assembly_output, | |
414 | |r| r.trim().to_string(), | |
415 | ); | |
416 | config.set_name_directive(ln, STDERR_PER_BITWIDTH, &mut self.stderr_per_bitwidth); | |
417 | config.set_name_directive(ln, INCREMENTAL, &mut self.incremental); | |
064997fb FG |
418 | |
419 | // Unlike the other `name_value_directive`s this needs to be handled manually, | |
420 | // because it sets a `bool` flag. | |
421 | if let Some(known_bug) = config.parse_name_value_directive(ln, KNOWN_BUG) { | |
422 | let known_bug = known_bug.trim(); | |
423 | if known_bug == "unknown" | |
424 | || known_bug.split(',').all(|issue_ref| { | |
425 | issue_ref | |
426 | .trim() | |
427 | .split_once('#') | |
428 | .filter(|(_, number)| { | |
429 | number.chars().all(|digit| digit.is_numeric()) | |
430 | }) | |
431 | .is_some() | |
432 | }) | |
433 | { | |
434 | self.known_bug = true; | |
435 | } else { | |
436 | panic!( | |
f25598a0 | 437 | "Invalid known-bug value: {known_bug}\nIt requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`." |
064997fb FG |
438 | ); |
439 | } | |
f25598a0 FG |
440 | } else if config.parse_name_directive(ln, KNOWN_BUG) { |
441 | panic!( | |
442 | "Invalid known-bug attribute, requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`." | |
443 | ); | |
064997fb | 444 | } |
f25598a0 | 445 | |
04454e1e FG |
446 | config.set_name_value_directive(ln, MIR_UNIT_TEST, &mut self.mir_unit_test, |s| { |
447 | s.trim().to_string() | |
448 | }); | |
f25598a0 | 449 | config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base); |
74b04a01 XL |
450 | }); |
451 | } | |
a7813a04 | 452 | |
74d20737 | 453 | if self.failure_status == -1 { |
5869c6ff | 454 | self.failure_status = 1; |
74d20737 | 455 | } |
60c5eb7d XL |
456 | if self.should_ice { |
457 | self.failure_status = 101; | |
458 | } | |
74d20737 | 459 | |
c295e0f8 XL |
460 | if config.mode == Mode::Incremental { |
461 | self.incremental = true; | |
462 | } | |
463 | ||
041b39d2 XL |
464 | for key in &["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] { |
465 | if let Ok(val) = env::var(key) { | |
466 | if self.exec_env.iter().find(|&&(ref x, _)| x == key).is_none() { | |
467 | self.exec_env.push(((*key).to_owned(), val)) | |
5bcae85e | 468 | } |
a7813a04 XL |
469 | } |
470 | } | |
3c0e092e XL |
471 | |
472 | if let (Some(edition), false) = (&config.edition, has_edition) { | |
473 | self.compile_flags.push(format!("--edition={}", edition)); | |
474 | } | |
a7813a04 | 475 | } |
dc9dc135 | 476 | |
dfeec247 XL |
477 | fn update_fail_mode(&mut self, ln: &str, config: &Config) { |
478 | let check_ui = |mode: &str| { | |
479 | if config.mode != Mode::Ui { | |
480 | panic!("`{}-fail` header is only supported in UI tests", mode); | |
481 | } | |
482 | }; | |
74b04a01 XL |
483 | if config.mode == Mode::Ui && config.parse_name_directive(ln, "compile-fail") { |
484 | panic!("`compile-fail` header is useless in UI tests"); | |
485 | } | |
dfeec247 XL |
486 | let fail_mode = if config.parse_name_directive(ln, "check-fail") { |
487 | check_ui("check"); | |
488 | Some(FailMode::Check) | |
489 | } else if config.parse_name_directive(ln, "build-fail") { | |
490 | check_ui("build"); | |
491 | Some(FailMode::Build) | |
492 | } else if config.parse_name_directive(ln, "run-fail") { | |
493 | check_ui("run"); | |
494 | Some(FailMode::Run) | |
495 | } else { | |
496 | None | |
497 | }; | |
498 | match (self.fail_mode, fail_mode) { | |
499 | (None, Some(_)) => self.fail_mode = fail_mode, | |
500 | (Some(_), Some(_)) => panic!("multiple `*-fail` headers in a single test"), | |
501 | (_, None) => {} | |
502 | } | |
503 | } | |
504 | ||
dc9dc135 XL |
505 | fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) { |
506 | let check_no_run = |s| { | |
507 | if config.mode != Mode::Ui && config.mode != Mode::Incremental { | |
508 | panic!("`{}` header is only supported in UI and incremental tests", s); | |
509 | } | |
dfeec247 XL |
510 | if config.mode == Mode::Incremental |
511 | && !revision.map_or(false, |r| r.starts_with("cfail")) | |
512 | && !self.revisions.iter().all(|r| r.starts_with("cfail")) | |
513 | { | |
dc9dc135 XL |
514 | panic!("`{}` header is only supported in `cfail` incremental tests", s); |
515 | } | |
516 | }; | |
517 | let pass_mode = if config.parse_name_directive(ln, "check-pass") { | |
518 | check_no_run("check-pass"); | |
519 | Some(PassMode::Check) | |
520 | } else if config.parse_name_directive(ln, "build-pass") { | |
521 | check_no_run("build-pass"); | |
522 | Some(PassMode::Build) | |
dc9dc135 | 523 | } else if config.parse_name_directive(ln, "run-pass") { |
416331ca | 524 | if config.mode != Mode::Ui { |
dc9dc135 XL |
525 | panic!("`run-pass` header is only supported in UI tests") |
526 | } | |
527 | Some(PassMode::Run) | |
528 | } else { | |
529 | None | |
530 | }; | |
531 | match (self.pass_mode, pass_mode) { | |
532 | (None, Some(_)) => self.pass_mode = pass_mode, | |
533 | (Some(_), Some(_)) => panic!("multiple `*-pass` headers in a single test"), | |
534 | (_, None) => {} | |
535 | } | |
536 | } | |
537 | ||
538 | pub fn pass_mode(&self, config: &Config) -> Option<PassMode> { | |
487cf647 FG |
539 | if !self.ignore_pass && self.fail_mode.is_none() { |
540 | if let mode @ Some(_) = config.force_pass_mode { | |
dc9dc135 XL |
541 | return mode; |
542 | } | |
543 | } | |
544 | self.pass_mode | |
545 | } | |
e1599b0c XL |
546 | |
547 | // does not consider CLI override for pass mode | |
548 | pub fn local_pass_mode(&self) -> Option<PassMode> { | |
549 | self.pass_mode | |
550 | } | |
a7813a04 XL |
551 | } |
552 | ||
064997fb FG |
553 | pub fn line_directive<'line>( |
554 | comment: &str, | |
555 | ln: &'line str, | |
556 | ) -> Option<(Option<&'line str>, &'line str)> { | |
557 | if ln.starts_with(comment) { | |
558 | let ln = ln[comment.len()..].trim_start(); | |
559 | if ln.starts_with('[') { | |
560 | // A comment like `//[foo]` is specific to revision `foo` | |
561 | if let Some(close_brace) = ln.find(']') { | |
562 | let lncfg = &ln[1..close_brace]; | |
563 | ||
564 | Some((Some(lncfg), ln[(close_brace + 1)..].trim_start())) | |
565 | } else { | |
566 | panic!("malformed condition directive: expected `{}[foo]`, found `{}`", comment, ln) | |
567 | } | |
568 | } else { | |
569 | Some((None, ln)) | |
570 | } | |
571 | } else { | |
572 | None | |
573 | } | |
574 | } | |
575 | ||
136023e0 | 576 | fn iter_header<R: Read>(testfile: &Path, rdr: R, it: &mut dyn FnMut(Option<&str>, &str)) { |
a7813a04 | 577 | if testfile.is_dir() { |
5bcae85e | 578 | return; |
a7813a04 | 579 | } |
83c7162d | 580 | |
136023e0 | 581 | let comment = if testfile.extension().map(|e| e == "rs") == Some(true) { "//" } else { "#" }; |
83c7162d | 582 | |
74b04a01 XL |
583 | let mut rdr = BufReader::new(rdr); |
584 | let mut ln = String::new(); | |
585 | ||
586 | loop { | |
587 | ln.clear(); | |
588 | if rdr.read_line(&mut ln).unwrap() == 0 { | |
589 | break; | |
590 | } | |
83c7162d | 591 | |
a7813a04 XL |
592 | // Assume that any directives will be found before the first |
593 | // module or function. This doesn't seem to be an optimization | |
594 | // with a warm page cache. Maybe with a cold one. | |
a7813a04 XL |
595 | let ln = ln.trim(); |
596 | if ln.starts_with("fn") || ln.starts_with("mod") { | |
597 | return; | |
064997fb FG |
598 | } else if let Some((lncfg, ln)) = line_directive(comment, ln) { |
599 | it(lncfg, ln); | |
a7813a04 XL |
600 | } |
601 | } | |
a7813a04 XL |
602 | } |
603 | ||
7cac9316 | 604 | impl Config { |
5099ac24 FG |
605 | fn parse_aux_crate(r: String) -> (String, String) { |
606 | let mut parts = r.trim().splitn(2, '='); | |
607 | ( | |
608 | parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(), | |
609 | parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(), | |
610 | ) | |
7cac9316 | 611 | } |
a7813a04 | 612 | |
cdc7bbd5 XL |
613 | fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) { |
614 | if let Some(raw) = self.parse_name_value_directive(line, "revisions") { | |
615 | let mut duplicates: HashSet<_> = existing.iter().cloned().collect(); | |
616 | for revision in raw.split_whitespace().map(|r| r.to_string()) { | |
617 | if !duplicates.insert(revision.clone()) { | |
618 | panic!("Duplicate revision: `{}` in line `{}`", revision, raw); | |
619 | } | |
620 | existing.push(revision); | |
621 | } | |
622 | } | |
7cac9316 | 623 | } |
a7813a04 | 624 | |
5099ac24 FG |
625 | fn parse_env(nv: String) -> (String, String) { |
626 | // nv is either FOO or FOO=BAR | |
627 | let mut strs: Vec<String> = nv.splitn(2, '=').map(str::to_owned).collect(); | |
a7813a04 | 628 | |
5099ac24 FG |
629 | match strs.len() { |
630 | 1 => (strs.pop().unwrap(), String::new()), | |
631 | 2 => { | |
632 | let end = strs.pop().unwrap(); | |
633 | (strs.pop().unwrap(), end) | |
5bcae85e | 634 | } |
5099ac24 FG |
635 | n => panic!("Expected 1 or 2 strings, not {}", n), |
636 | } | |
7cac9316 | 637 | } |
a7813a04 | 638 | |
7cac9316 XL |
639 | fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option<PathBuf> { |
640 | if let Some(s) = self.parse_name_value_directive(line, "pp-exact") { | |
641 | Some(PathBuf::from(&s)) | |
041b39d2 XL |
642 | } else if self.parse_name_directive(line, "pp-exact") { |
643 | testfile.file_name().map(PathBuf::from) | |
a7813a04 | 644 | } else { |
041b39d2 XL |
645 | None |
646 | } | |
647 | } | |
648 | ||
649 | fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> { | |
0bf4aa26 | 650 | if self.parse_cfg_name_directive(line, prefix) == ParsedNameDirective::Match { |
0731742a XL |
651 | let from = parse_normalization_string(&mut line)?; |
652 | let to = parse_normalization_string(&mut line)?; | |
041b39d2 XL |
653 | Some((from, to)) |
654 | } else { | |
655 | None | |
656 | } | |
657 | } | |
658 | ||
9fa01778 XL |
659 | fn parse_needs_matching_clang(&self, line: &str) -> bool { |
660 | self.parse_name_directive(line, "needs-matching-clang") | |
661 | } | |
662 | ||
48663c56 XL |
663 | fn parse_needs_profiler_support(&self, line: &str) -> bool { |
664 | self.parse_name_directive(line, "needs-profiler-support") | |
665 | } | |
666 | ||
0731742a | 667 | /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86` |
0bf4aa26 XL |
668 | /// or `normalize-stderr-32bit`. |
669 | fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective { | |
dfeec247 XL |
670 | if !line.as_bytes().starts_with(prefix.as_bytes()) { |
671 | return ParsedNameDirective::NoMatch; | |
672 | } | |
673 | if line.as_bytes().get(prefix.len()) != Some(&b'-') { | |
674 | return ParsedNameDirective::NoMatch; | |
a7813a04 | 675 | } |
dfeec247 XL |
676 | |
677 | let name = line[prefix.len() + 1..].split(&[':', ' '][..]).next().unwrap(); | |
678 | ||
f2b60f7d FG |
679 | let matches_pointer_width = || { |
680 | name.strip_suffix("bit") | |
681 | .and_then(|width| width.parse::<u32>().ok()) | |
682 | .map(|width| self.get_pointer_width() == width) | |
683 | .unwrap_or(false) | |
684 | }; | |
685 | ||
686 | // If something is ignored for emscripten, it likely also needs to be | |
687 | // ignored for wasm32-unknown-unknown. | |
688 | // `wasm32-bare` is an alias to refer to just wasm32-unknown-unknown | |
689 | // (in contrast to `wasm32` which also matches non-bare targets like | |
690 | // asmjs-unknown-emscripten). | |
691 | let matches_wasm32_alias = || { | |
692 | self.target == "wasm32-unknown-unknown" && matches!(name, "emscripten" | "wasm32-bare") | |
693 | }; | |
694 | ||
dfeec247 | 695 | let is_match = name == "test" || |
3dfed10e | 696 | self.target == name || // triple |
f2b60f7d FG |
697 | self.matches_os(name) || |
698 | self.matches_env(name) || | |
699 | self.matches_abi(name) || | |
700 | self.matches_family(name) || | |
3dfed10e | 701 | self.target.ends_with(name) || // target and env |
f2b60f7d FG |
702 | self.matches_arch(name) || |
703 | matches_wasm32_alias() || | |
704 | matches_pointer_width() || | |
dfeec247 | 705 | name == self.stage_id.split('-').next().unwrap() || // stage |
17df50a5 | 706 | name == self.channel || // channel |
dfeec247 | 707 | (self.target != self.host && name == "cross-compile") || |
f2b60f7d | 708 | (name == "endian-big" && self.is_big_endian()) || |
f035d41b | 709 | (self.remote_test_client.is_some() && name == "remote") || |
dfeec247 | 710 | match self.compare_mode { |
dfeec247 | 711 | Some(CompareMode::Polonius) => name == "compare-mode-polonius", |
f035d41b | 712 | Some(CompareMode::Chalk) => name == "compare-mode-chalk", |
fc512014 XL |
713 | Some(CompareMode::SplitDwarf) => name == "compare-mode-split-dwarf", |
714 | Some(CompareMode::SplitDwarfSingle) => name == "compare-mode-split-dwarf-single", | |
dfeec247 XL |
715 | None => false, |
716 | } || | |
717 | (cfg!(debug_assertions) && name == "debug") || | |
718 | match self.debugger { | |
719 | Some(Debugger::Cdb) => name == "cdb", | |
720 | Some(Debugger::Gdb) => name == "gdb", | |
721 | Some(Debugger::Lldb) => name == "lldb", | |
722 | None => false, | |
723 | }; | |
724 | ||
725 | if is_match { ParsedNameDirective::Match } else { ParsedNameDirective::NoMatch } | |
a7813a04 | 726 | } |
a7813a04 | 727 | |
2c00a5a8 XL |
728 | fn has_cfg_prefix(&self, line: &str, prefix: &str) -> bool { |
729 | // returns whether this line contains this prefix or not. For prefix | |
730 | // "ignore", returns true if line says "ignore-x86_64", "ignore-arch", | |
83c7162d | 731 | // "ignore-android" etc. |
2c00a5a8 XL |
732 | line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') |
733 | } | |
734 | ||
7cac9316 XL |
735 | fn parse_name_directive(&self, line: &str, directive: &str) -> bool { |
736 | // Ensure the directive is a whole word. Do not match "ignore-x86" when | |
737 | // the line says "ignore-x86_64". | |
dfeec247 | 738 | line.starts_with(directive) |
3dfed10e | 739 | && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':')) |
7cac9316 | 740 | } |
a7813a04 | 741 | |
f25598a0 FG |
742 | fn parse_negative_name_directive(&self, line: &str, directive: &str) -> bool { |
743 | line.starts_with("no-") && self.parse_name_directive(&line[3..], directive) | |
744 | } | |
745 | ||
7cac9316 XL |
746 | pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> { |
747 | let colon = directive.len(); | |
748 | if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { | |
94b46f34 | 749 | let value = line[(colon + 1)..].to_owned(); |
7cac9316 XL |
750 | debug!("{}: {}", directive, value); |
751 | Some(expand_variables(value, self)) | |
752 | } else { | |
753 | None | |
754 | } | |
a7813a04 | 755 | } |
abe05a73 XL |
756 | |
757 | pub fn find_rust_src_root(&self) -> Option<PathBuf> { | |
758 | let mut path = self.src_base.clone(); | |
759 | let path_postfix = Path::new("src/etc/lldb_batchmode.py"); | |
760 | ||
761 | while path.pop() { | |
762 | if path.join(&path_postfix).is_file() { | |
763 | return Some(path); | |
764 | } | |
765 | } | |
766 | ||
767 | None | |
768 | } | |
83c7162d | 769 | |
5099ac24 FG |
770 | fn parse_edition(&self, line: &str) -> Option<String> { |
771 | self.parse_name_value_directive(line, "edition") | |
83c7162d | 772 | } |
8faf50e0 | 773 | |
5099ac24 | 774 | fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) { |
f25598a0 FG |
775 | match value { |
776 | true => { | |
777 | if self.parse_negative_name_directive(line, directive) { | |
778 | *value = false; | |
779 | } | |
780 | } | |
781 | false => { | |
782 | if self.parse_name_directive(line, directive) { | |
783 | *value = true; | |
784 | } | |
785 | } | |
5099ac24 | 786 | } |
b7449926 XL |
787 | } |
788 | ||
5099ac24 FG |
789 | fn set_name_value_directive<T>( |
790 | &self, | |
791 | line: &str, | |
792 | directive: &str, | |
793 | value: &mut Option<T>, | |
794 | parse: impl FnOnce(String) -> T, | |
795 | ) { | |
796 | if value.is_none() { | |
797 | *value = self.parse_name_value_directive(line, directive).map(parse); | |
798 | } | |
8faf50e0 | 799 | } |
c295e0f8 | 800 | |
5099ac24 FG |
801 | fn push_name_value_directive<T>( |
802 | &self, | |
803 | line: &str, | |
804 | directive: &str, | |
805 | values: &mut Vec<T>, | |
806 | parse: impl FnOnce(String) -> T, | |
807 | ) { | |
808 | if let Some(value) = self.parse_name_value_directive(line, directive).map(parse) { | |
809 | values.push(value); | |
810 | } | |
c295e0f8 | 811 | } |
a7813a04 XL |
812 | } |
813 | ||
7cac9316 | 814 | fn expand_variables(mut value: String, config: &Config) -> String { |
3dfed10e XL |
815 | const CWD: &str = "{{cwd}}"; |
816 | const SRC_BASE: &str = "{{src-base}}"; | |
817 | const BUILD_BASE: &str = "{{build-base}}"; | |
7cac9316 XL |
818 | |
819 | if value.contains(CWD) { | |
820 | let cwd = env::current_dir().unwrap(); | |
821 | value = value.replace(CWD, &cwd.to_string_lossy()); | |
822 | } | |
823 | ||
824 | if value.contains(SRC_BASE) { | |
825 | value = value.replace(SRC_BASE, &config.src_base.to_string_lossy()); | |
826 | } | |
827 | ||
828 | if value.contains(BUILD_BASE) { | |
829 | value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy()); | |
830 | } | |
831 | ||
832 | value | |
833 | } | |
041b39d2 XL |
834 | |
835 | /// Finds the next quoted string `"..."` in `line`, and extract the content from it. Move the `line` | |
836 | /// variable after the end of the quoted string. | |
837 | /// | |
838 | /// # Examples | |
839 | /// | |
840 | /// ``` | |
841 | /// let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\"."; | |
842 | /// let first = parse_normalization_string(&mut s); | |
843 | /// assert_eq!(first, Some("something (32 bits)".to_owned())); | |
844 | /// assert_eq!(s, " -> \"something ($WORD bits)\"."); | |
845 | /// ``` | |
846 | fn parse_normalization_string(line: &mut &str) -> Option<String> { | |
847 | // FIXME support escapes in strings. | |
0731742a XL |
848 | let begin = line.find('"')? + 1; |
849 | let end = line[begin..].find('"')? + begin; | |
041b39d2 | 850 | let result = line[begin..end].to_owned(); |
94b46f34 | 851 | *line = &line[end + 1..]; |
041b39d2 XL |
852 | Some(result) |
853 | } | |
3dfed10e XL |
854 | |
855 | pub fn extract_llvm_version(version: &str) -> Option<u32> { | |
cdc7bbd5 XL |
856 | let pat = |c: char| !c.is_ascii_digit() && c != '.'; |
857 | let version_without_suffix = match version.find(pat) { | |
858 | Some(pos) => &version[..pos], | |
859 | None => version, | |
860 | }; | |
3dfed10e XL |
861 | let components: Vec<u32> = version_without_suffix |
862 | .split('.') | |
863 | .map(|s| s.parse().expect("Malformed version component")) | |
864 | .collect(); | |
865 | let version = match *components { | |
866 | [a] => a * 10_000, | |
867 | [a, b] => a * 10_000 + b * 100, | |
868 | [a, b, c] => a * 10_000 + b * 100 + c, | |
869 | _ => panic!("Malformed version"), | |
870 | }; | |
871 | Some(version) | |
872 | } | |
873 | ||
2b03887a FG |
874 | pub fn extract_llvm_version_from_binary(binary_path: &str) -> Option<u32> { |
875 | let output = Command::new(binary_path).arg("--version").output().ok()?; | |
876 | if !output.status.success() { | |
877 | return None; | |
878 | } | |
879 | let version = String::from_utf8(output.stdout).ok()?; | |
880 | for line in version.lines() { | |
881 | if let Some(version) = line.split("LLVM version ").skip(1).next() { | |
882 | return extract_llvm_version(version); | |
883 | } | |
884 | } | |
885 | None | |
886 | } | |
887 | ||
136023e0 XL |
888 | /// Takes a directive of the form "<version1> [- <version2>]", |
889 | /// returns the numeric representation of <version1> and <version2> as | |
890 | /// tuple: (<version1> as u32, <version2> as u32) | |
891 | /// | |
892 | /// If the <version2> part is omitted, the second component of the tuple | |
893 | /// is the same as <version1>. | |
3dfed10e XL |
894 | fn extract_version_range<F>(line: &str, parse: F) -> Option<(u32, u32)> |
895 | where | |
896 | F: Fn(&str) -> Option<u32>, | |
897 | { | |
898 | let mut splits = line.splitn(2, "- ").map(str::trim); | |
899 | let min = splits.next().unwrap(); | |
900 | if min.ends_with('-') { | |
901 | return None; | |
902 | } | |
903 | ||
904 | let max = splits.next(); | |
905 | ||
906 | if min.is_empty() { | |
907 | return None; | |
908 | } | |
909 | ||
910 | let min = parse(min)?; | |
911 | let max = match max { | |
912 | Some(max) if max.is_empty() => return None, | |
913 | Some(max) => parse(max)?, | |
914 | _ => min, | |
915 | }; | |
916 | ||
917 | Some((min, max)) | |
918 | } | |
136023e0 XL |
919 | |
920 | pub fn make_test_description<R: Read>( | |
921 | config: &Config, | |
922 | name: test::TestName, | |
923 | path: &Path, | |
924 | src: R, | |
925 | cfg: Option<&str>, | |
926 | ) -> test::TestDesc { | |
927 | let mut ignore = false; | |
04454e1e | 928 | let ignore_message = None; |
136023e0 XL |
929 | let mut should_fail = false; |
930 | ||
931 | let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some(); | |
932 | let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(); | |
f2b60f7d | 933 | let has_asm_support = config.has_asm_support(); |
136023e0 | 934 | let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target); |
064997fb | 935 | let has_cfi = util::CFI_SUPPORTED_TARGETS.contains(&&*config.target); |
f25598a0 | 936 | let has_kcfi = util::KCFI_SUPPORTED_TARGETS.contains(&&*config.target); |
136023e0 XL |
937 | let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); |
938 | let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); | |
939 | let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target); | |
940 | let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target); | |
5099ac24 | 941 | let has_memtag = util::MEMTAG_SUPPORTED_TARGETS.contains(&&*config.target); |
064997fb | 942 | let has_shadow_call_stack = util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(&&*config.target); |
f2b60f7d FG |
943 | |
944 | // For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find | |
945 | // whether `rust-lld` is present in the compiler under test. | |
946 | // | |
947 | // The --compile-lib-path is the path to host shared libraries, but depends on the OS. For | |
948 | // example: | |
949 | // - on linux, it can be <sysroot>/lib | |
950 | // - on windows, it can be <sysroot>/bin | |
951 | // | |
952 | // However, `rust-lld` is only located under the lib path, so we look for it there. | |
136023e0 XL |
953 | let has_rust_lld = config |
954 | .compile_lib_path | |
f2b60f7d FG |
955 | .parent() |
956 | .expect("couldn't traverse to the parent of the specified --compile-lib-path") | |
957 | .join("lib") | |
136023e0 XL |
958 | .join("rustlib") |
959 | .join(&config.target) | |
960 | .join("bin") | |
f2b60f7d | 961 | .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" }) |
136023e0 | 962 | .exists(); |
f2b60f7d | 963 | |
136023e0 XL |
964 | iter_header(path, src, &mut |revision, ln| { |
965 | if revision.is_some() && revision != cfg { | |
966 | return; | |
967 | } | |
968 | ignore = match config.parse_cfg_name_directive(ln, "ignore") { | |
969 | ParsedNameDirective::Match => true, | |
970 | ParsedNameDirective::NoMatch => ignore, | |
971 | }; | |
972 | if config.has_cfg_prefix(ln, "only") { | |
973 | ignore = match config.parse_cfg_name_directive(ln, "only") { | |
974 | ParsedNameDirective::Match => ignore, | |
975 | ParsedNameDirective::NoMatch => true, | |
976 | }; | |
977 | } | |
978 | ignore |= ignore_llvm(config, ln); | |
979 | ignore |= | |
980 | config.run_clang_based_tests_with.is_none() && config.parse_needs_matching_clang(ln); | |
981 | ignore |= !has_asm_support && config.parse_name_directive(ln, "needs-asm-support"); | |
982 | ignore |= !rustc_has_profiler_support && config.parse_needs_profiler_support(ln); | |
983 | ignore |= !config.run_enabled() && config.parse_name_directive(ln, "needs-run-enabled"); | |
984 | ignore |= !rustc_has_sanitizer_support | |
985 | && config.parse_name_directive(ln, "needs-sanitizer-support"); | |
986 | ignore |= !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address"); | |
064997fb | 987 | ignore |= !has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi"); |
f25598a0 | 988 | ignore |= !has_kcfi && config.parse_name_directive(ln, "needs-sanitizer-kcfi"); |
136023e0 XL |
989 | ignore |= !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak"); |
990 | ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory"); | |
991 | ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread"); | |
992 | ignore |= !has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress"); | |
5099ac24 | 993 | ignore |= !has_memtag && config.parse_name_directive(ln, "needs-sanitizer-memtag"); |
064997fb FG |
994 | ignore |= !has_shadow_call_stack |
995 | && config.parse_name_directive(ln, "needs-sanitizer-shadow-call-stack"); | |
2b03887a | 996 | ignore |= !config.can_unwind() && config.parse_name_directive(ln, "needs-unwind"); |
5099ac24 FG |
997 | ignore |= config.target == "wasm32-unknown-unknown" |
998 | && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS); | |
136023e0 XL |
999 | ignore |= config.debugger == Some(Debugger::Cdb) && ignore_cdb(config, ln); |
1000 | ignore |= config.debugger == Some(Debugger::Gdb) && ignore_gdb(config, ln); | |
1001 | ignore |= config.debugger == Some(Debugger::Lldb) && ignore_lldb(config, ln); | |
1002 | ignore |= !has_rust_lld && config.parse_name_directive(ln, "needs-rust-lld"); | |
1003 | should_fail |= config.parse_name_directive(ln, "should-fail"); | |
1004 | }); | |
1005 | ||
1006 | // The `should-fail` annotation doesn't apply to pretty tests, | |
1007 | // since we run the pretty printer across all tests by default. | |
1008 | // If desired, we could add a `should-fail-pretty` annotation. | |
1009 | let should_panic = match config.mode { | |
1010 | crate::common::Pretty => test::ShouldPanic::No, | |
1011 | _ if should_fail => test::ShouldPanic::Yes, | |
1012 | _ => test::ShouldPanic::No, | |
1013 | }; | |
1014 | ||
1015 | test::TestDesc { | |
1016 | name, | |
1017 | ignore, | |
5e7ed085 | 1018 | ignore_message, |
136023e0 | 1019 | should_panic, |
136023e0 XL |
1020 | compile_fail: false, |
1021 | no_run: false, | |
1022 | test_type: test::TestType::Unknown, | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | fn ignore_cdb(config: &Config, line: &str) -> bool { | |
1027 | if let Some(actual_version) = config.cdb_version { | |
1028 | if let Some(min_version) = line.strip_prefix("min-cdb-version:").map(str::trim) { | |
1029 | let min_version = extract_cdb_version(min_version).unwrap_or_else(|| { | |
1030 | panic!("couldn't parse version range: {:?}", min_version); | |
1031 | }); | |
1032 | ||
1033 | // Ignore if actual version is smaller than the minimum | |
1034 | // required version | |
1035 | return actual_version < min_version; | |
1036 | } | |
1037 | } | |
1038 | false | |
1039 | } | |
1040 | ||
1041 | fn ignore_gdb(config: &Config, line: &str) -> bool { | |
1042 | if let Some(actual_version) = config.gdb_version { | |
1043 | if let Some(rest) = line.strip_prefix("min-gdb-version:").map(str::trim) { | |
1044 | let (start_ver, end_ver) = extract_version_range(rest, extract_gdb_version) | |
1045 | .unwrap_or_else(|| { | |
1046 | panic!("couldn't parse version range: {:?}", rest); | |
1047 | }); | |
1048 | ||
1049 | if start_ver != end_ver { | |
1050 | panic!("Expected single GDB version") | |
1051 | } | |
1052 | // Ignore if actual version is smaller than the minimum | |
1053 | // required version | |
1054 | return actual_version < start_ver; | |
1055 | } else if let Some(rest) = line.strip_prefix("ignore-gdb-version:").map(str::trim) { | |
1056 | let (min_version, max_version) = extract_version_range(rest, extract_gdb_version) | |
1057 | .unwrap_or_else(|| { | |
1058 | panic!("couldn't parse version range: {:?}", rest); | |
1059 | }); | |
1060 | ||
1061 | if max_version < min_version { | |
1062 | panic!("Malformed GDB version range: max < min") | |
1063 | } | |
1064 | ||
1065 | return actual_version >= min_version && actual_version <= max_version; | |
1066 | } | |
1067 | } | |
1068 | false | |
1069 | } | |
1070 | ||
1071 | fn ignore_lldb(config: &Config, line: &str) -> bool { | |
1072 | if let Some(actual_version) = config.lldb_version { | |
1073 | if let Some(min_version) = line.strip_prefix("min-lldb-version:").map(str::trim) { | |
1074 | let min_version = min_version.parse().unwrap_or_else(|e| { | |
1075 | panic!("Unexpected format of LLDB version string: {}\n{:?}", min_version, e); | |
1076 | }); | |
1077 | // Ignore if actual version is smaller the minimum required | |
1078 | // version | |
1079 | actual_version < min_version | |
1080 | } else { | |
1081 | line.starts_with("rust-lldb") && !config.lldb_native_rust | |
1082 | } | |
1083 | } else { | |
1084 | false | |
1085 | } | |
1086 | } | |
1087 | ||
1088 | fn ignore_llvm(config: &Config, line: &str) -> bool { | |
1089 | if config.system_llvm && line.starts_with("no-system-llvm") { | |
1090 | return true; | |
1091 | } | |
1092 | if let Some(needed_components) = | |
1093 | config.parse_name_value_directive(line, "needs-llvm-components") | |
1094 | { | |
1095 | let components: HashSet<_> = config.llvm_components.split_whitespace().collect(); | |
1096 | if let Some(missing_component) = needed_components | |
1097 | .split_whitespace() | |
1098 | .find(|needed_component| !components.contains(needed_component)) | |
1099 | { | |
1100 | if env::var_os("COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS").is_some() { | |
1101 | panic!("missing LLVM component: {}", missing_component); | |
1102 | } | |
1103 | return true; | |
1104 | } | |
1105 | } | |
1106 | if let Some(actual_version) = config.llvm_version { | |
1107 | if let Some(rest) = line.strip_prefix("min-llvm-version:").map(str::trim) { | |
1108 | let min_version = extract_llvm_version(rest).unwrap(); | |
1109 | // Ignore if actual version is smaller the minimum required | |
1110 | // version | |
1111 | actual_version < min_version | |
1112 | } else if let Some(rest) = line.strip_prefix("min-system-llvm-version:").map(str::trim) { | |
1113 | let min_version = extract_llvm_version(rest).unwrap(); | |
1114 | // Ignore if using system LLVM and actual version | |
1115 | // is smaller the minimum required version | |
1116 | config.system_llvm && actual_version < min_version | |
1117 | } else if let Some(rest) = line.strip_prefix("ignore-llvm-version:").map(str::trim) { | |
1118 | // Syntax is: "ignore-llvm-version: <version1> [- <version2>]" | |
1119 | let (v_min, v_max) = | |
1120 | extract_version_range(rest, extract_llvm_version).unwrap_or_else(|| { | |
1121 | panic!("couldn't parse version range: {:?}", rest); | |
1122 | }); | |
1123 | if v_max < v_min { | |
1124 | panic!("Malformed LLVM version range: max < min") | |
1125 | } | |
1126 | // Ignore if version lies inside of range. | |
1127 | actual_version >= v_min && actual_version <= v_max | |
1128 | } else { | |
1129 | false | |
1130 | } | |
1131 | } else { | |
1132 | false | |
1133 | } | |
1134 | } |