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