]> git.proxmox.com Git - rustc.git/blame - src/tools/compiletest/src/header.rs
New upstream version 1.68.2+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 6use std::path::{Path, PathBuf};
2b03887a 7use std::process::Command;
a7813a04 8
3dfed10e 9use tracing::*;
48663c56 10
2b03887a 11use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PassMode};
dfeec247 12use crate::util;
1b1a35ee 13use crate::{extract_cdb_version, extract_gdb_version};
c30ab7b3 14
416331ca
XL
15#[cfg(test)]
16mod tests;
17
0bf4aa26
XL
18/// The result of parse_cfg_name_directive.
19#[derive(Clone, Copy, PartialEq, Debug)]
20enum 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 30pub 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
36impl 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)]
61pub 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
170mod 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
207impl 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
553pub 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 576fn 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 604impl 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 814fn 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/// ```
846fn 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
855pub 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
874pub 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
894fn extract_version_range<F>(line: &str, parse: F) -> Option<(u32, u32)>
895where
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
920pub 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
1026fn 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
1041fn 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
1071fn 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
1088fn 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}