1use std::collections::HashSet;
2use std::env;
3use std::fs::File;
4use std::io::BufReader;
5use std::io::prelude::*;
6use std::process::Command;
7
8use camino::{Utf8Path, Utf8PathBuf};
9use semver::Version;
10use tracing::*;
11
12use crate::common::{CodegenBackend, Config, Debugger, FailMode, PassMode, RunFailMode, TestMode};
13use crate::debuggers::{extract_cdb_version, extract_gdb_version};
14use crate::directives::auxiliary::{AuxProps, parse_and_update_aux};
15use crate::directives::directive_names::{
16 KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES,
17};
18use crate::directives::needs::CachedNeedsConditions;
19use crate::errors::ErrorKind;
20use crate::executor::{CollectedTestDesc, ShouldPanic};
21use crate::help;
22use crate::util::static_regex;
23
24pub(crate) mod auxiliary;
25mod cfg;
26mod directive_names;
27mod needs;
28#[cfg(test)]
29mod tests;
30
31pub struct DirectivesCache {
32 needs: CachedNeedsConditions,
33}
34
35impl DirectivesCache {
36 pub fn load(config: &Config) -> Self {
37 Self { needs: CachedNeedsConditions::load(config) }
38 }
39}
40
41#[derive(Default)]
44pub struct EarlyProps {
45 pub(crate) aux: AuxProps,
49 pub revisions: Vec<String>,
50}
51
52impl EarlyProps {
53 pub fn from_file(config: &Config, testfile: &Utf8Path) -> Self {
54 let file = File::open(testfile.as_std_path()).expect("open test file to parse earlyprops");
55 Self::from_reader(config, testfile, file)
56 }
57
58 pub fn from_reader<R: Read>(config: &Config, testfile: &Utf8Path, rdr: R) -> Self {
59 let mut props = EarlyProps::default();
60 let mut poisoned = false;
61 iter_directives(
62 config.mode,
63 &mut poisoned,
64 testfile,
65 rdr,
66 &mut |DirectiveLine { line_number, raw_directive: ln, .. }| {
67 parse_and_update_aux(config, ln, testfile, line_number, &mut props.aux);
68 config.parse_and_update_revisions(testfile, line_number, ln, &mut props.revisions);
69 },
70 );
71
72 if poisoned {
73 eprintln!("errors encountered during EarlyProps parsing: {}", testfile);
74 panic!("errors encountered during EarlyProps parsing");
75 }
76
77 props
78 }
79}
80
81#[derive(Clone, Debug)]
82pub struct TestProps {
83 pub error_patterns: Vec<String>,
85 pub regex_error_patterns: Vec<String>,
87 pub compile_flags: Vec<String>,
89 pub run_flags: Vec<String>,
91 pub doc_flags: Vec<String>,
93 pub pp_exact: Option<Utf8PathBuf>,
96 pub(crate) aux: AuxProps,
98 pub rustc_env: Vec<(String, String)>,
100 pub unset_rustc_env: Vec<String>,
103 pub exec_env: Vec<(String, String)>,
105 pub unset_exec_env: Vec<String>,
108 pub build_aux_docs: bool,
110 pub unique_doc_out_dir: bool,
113 pub force_host: bool,
115 pub check_stdout: bool,
117 pub check_run_results: bool,
119 pub dont_check_compiler_stdout: bool,
121 pub dont_check_compiler_stderr: bool,
123 pub no_prefer_dynamic: bool,
129 pub pretty_mode: String,
131 pub pretty_compare_only: bool,
133 pub forbid_output: Vec<String>,
135 pub revisions: Vec<String>,
137 pub incremental_dir: Option<Utf8PathBuf>,
142 pub incremental: bool,
157 pub known_bug: bool,
163 pass_mode: Option<PassMode>,
165 ignore_pass: bool,
167 pub fail_mode: Option<FailMode>,
169 pub check_test_line_numbers_match: bool,
171 pub normalize_stdout: Vec<(String, String)>,
173 pub normalize_stderr: Vec<(String, String)>,
174 pub failure_status: Option<i32>,
175 pub dont_check_failure_status: bool,
177 pub run_rustfix: bool,
180 pub rustfix_only_machine_applicable: bool,
182 pub assembly_output: Option<String>,
183 pub should_ice: bool,
185 pub stderr_per_bitwidth: bool,
187 pub mir_unit_test: Option<String>,
189 pub remap_src_base: bool,
192 pub llvm_cov_flags: Vec<String>,
195 pub filecheck_flags: Vec<String>,
197 pub no_auto_check_cfg: bool,
199 pub has_enzyme: bool,
201 pub add_core_stubs: bool,
204 pub dont_require_annotations: HashSet<ErrorKind>,
206 pub disable_gdb_pretty_printers: bool,
208 pub compare_output_by_lines: bool,
210}
211
212mod directives {
213 pub const ERROR_PATTERN: &'static str = "error-pattern";
214 pub const REGEX_ERROR_PATTERN: &'static str = "regex-error-pattern";
215 pub const COMPILE_FLAGS: &'static str = "compile-flags";
216 pub const RUN_FLAGS: &'static str = "run-flags";
217 pub const DOC_FLAGS: &'static str = "doc-flags";
218 pub const SHOULD_ICE: &'static str = "should-ice";
219 pub const BUILD_AUX_DOCS: &'static str = "build-aux-docs";
220 pub const UNIQUE_DOC_OUT_DIR: &'static str = "unique-doc-out-dir";
221 pub const FORCE_HOST: &'static str = "force-host";
222 pub const CHECK_STDOUT: &'static str = "check-stdout";
223 pub const CHECK_RUN_RESULTS: &'static str = "check-run-results";
224 pub const DONT_CHECK_COMPILER_STDOUT: &'static str = "dont-check-compiler-stdout";
225 pub const DONT_CHECK_COMPILER_STDERR: &'static str = "dont-check-compiler-stderr";
226 pub const DONT_REQUIRE_ANNOTATIONS: &'static str = "dont-require-annotations";
227 pub const NO_PREFER_DYNAMIC: &'static str = "no-prefer-dynamic";
228 pub const PRETTY_MODE: &'static str = "pretty-mode";
229 pub const PRETTY_COMPARE_ONLY: &'static str = "pretty-compare-only";
230 pub const AUX_BIN: &'static str = "aux-bin";
231 pub const AUX_BUILD: &'static str = "aux-build";
232 pub const AUX_CRATE: &'static str = "aux-crate";
233 pub const PROC_MACRO: &'static str = "proc-macro";
234 pub const AUX_CODEGEN_BACKEND: &'static str = "aux-codegen-backend";
235 pub const EXEC_ENV: &'static str = "exec-env";
236 pub const RUSTC_ENV: &'static str = "rustc-env";
237 pub const UNSET_EXEC_ENV: &'static str = "unset-exec-env";
238 pub const UNSET_RUSTC_ENV: &'static str = "unset-rustc-env";
239 pub const FORBID_OUTPUT: &'static str = "forbid-output";
240 pub const CHECK_TEST_LINE_NUMBERS_MATCH: &'static str = "check-test-line-numbers-match";
241 pub const IGNORE_PASS: &'static str = "ignore-pass";
242 pub const FAILURE_STATUS: &'static str = "failure-status";
243 pub const DONT_CHECK_FAILURE_STATUS: &'static str = "dont-check-failure-status";
244 pub const RUN_RUSTFIX: &'static str = "run-rustfix";
245 pub const RUSTFIX_ONLY_MACHINE_APPLICABLE: &'static str = "rustfix-only-machine-applicable";
246 pub const ASSEMBLY_OUTPUT: &'static str = "assembly-output";
247 pub const STDERR_PER_BITWIDTH: &'static str = "stderr-per-bitwidth";
248 pub const INCREMENTAL: &'static str = "incremental";
249 pub const KNOWN_BUG: &'static str = "known-bug";
250 pub const TEST_MIR_PASS: &'static str = "test-mir-pass";
251 pub const REMAP_SRC_BASE: &'static str = "remap-src-base";
252 pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags";
253 pub const FILECHECK_FLAGS: &'static str = "filecheck-flags";
254 pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg";
255 pub const ADD_CORE_STUBS: &'static str = "add-core-stubs";
256 pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags";
258 pub const DISABLE_GDB_PRETTY_PRINTERS: &'static str = "disable-gdb-pretty-printers";
259 pub const COMPARE_OUTPUT_BY_LINES: &'static str = "compare-output-by-lines";
260}
261
262impl TestProps {
263 pub fn new() -> Self {
264 TestProps {
265 error_patterns: vec![],
266 regex_error_patterns: vec![],
267 compile_flags: vec![],
268 run_flags: vec![],
269 doc_flags: vec![],
270 pp_exact: None,
271 aux: Default::default(),
272 revisions: vec![],
273 rustc_env: vec![
274 ("RUSTC_ICE".to_string(), "0".to_string()),
275 ("RUST_BACKTRACE".to_string(), "short".to_string()),
276 ],
277 unset_rustc_env: vec![("RUSTC_LOG_COLOR".to_string())],
278 exec_env: vec![],
279 unset_exec_env: vec![],
280 build_aux_docs: false,
281 unique_doc_out_dir: false,
282 force_host: false,
283 check_stdout: false,
284 check_run_results: false,
285 dont_check_compiler_stdout: false,
286 dont_check_compiler_stderr: false,
287 no_prefer_dynamic: false,
288 pretty_mode: "normal".to_string(),
289 pretty_compare_only: false,
290 forbid_output: vec![],
291 incremental_dir: None,
292 incremental: false,
293 known_bug: false,
294 pass_mode: None,
295 fail_mode: None,
296 ignore_pass: false,
297 check_test_line_numbers_match: false,
298 normalize_stdout: vec![],
299 normalize_stderr: vec![],
300 failure_status: None,
301 dont_check_failure_status: false,
302 run_rustfix: false,
303 rustfix_only_machine_applicable: false,
304 assembly_output: None,
305 should_ice: false,
306 stderr_per_bitwidth: false,
307 mir_unit_test: None,
308 remap_src_base: false,
309 llvm_cov_flags: vec![],
310 filecheck_flags: vec![],
311 no_auto_check_cfg: false,
312 has_enzyme: false,
313 add_core_stubs: false,
314 dont_require_annotations: Default::default(),
315 disable_gdb_pretty_printers: false,
316 compare_output_by_lines: false,
317 }
318 }
319
320 pub fn from_aux_file(
321 &self,
322 testfile: &Utf8Path,
323 revision: Option<&str>,
324 config: &Config,
325 ) -> Self {
326 let mut props = TestProps::new();
327
328 props.incremental_dir = self.incremental_dir.clone();
330 props.ignore_pass = true;
331 props.load_from(testfile, revision, config);
332
333 props
334 }
335
336 pub fn from_file(testfile: &Utf8Path, revision: Option<&str>, config: &Config) -> Self {
337 let mut props = TestProps::new();
338 props.load_from(testfile, revision, config);
339 props.exec_env.push(("RUSTC".to_string(), config.rustc_path.to_string()));
340
341 match (props.pass_mode, props.fail_mode) {
342 (None, None) if config.mode == TestMode::Ui => props.fail_mode = Some(FailMode::Check),
343 (Some(_), Some(_)) => panic!("cannot use a *-fail and *-pass mode together"),
344 _ => {}
345 }
346
347 props
348 }
349
350 fn load_from(&mut self, testfile: &Utf8Path, test_revision: Option<&str>, config: &Config) {
355 let mut has_edition = false;
356 if !testfile.is_dir() {
357 let file = File::open(testfile.as_std_path()).unwrap();
358
359 let mut poisoned = false;
360
361 iter_directives(
362 config.mode,
363 &mut poisoned,
364 testfile,
365 file,
366 &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| {
367 if !directive.applies_to_test_revision(test_revision) {
368 return;
369 }
370
371 use directives::*;
372
373 config.push_name_value_directive(
374 ln,
375 ERROR_PATTERN,
376 testfile,
377 line_number,
378 &mut self.error_patterns,
379 |r| r,
380 );
381 config.push_name_value_directive(
382 ln,
383 REGEX_ERROR_PATTERN,
384 testfile,
385 line_number,
386 &mut self.regex_error_patterns,
387 |r| r,
388 );
389
390 config.push_name_value_directive(
391 ln,
392 DOC_FLAGS,
393 testfile,
394 line_number,
395 &mut self.doc_flags,
396 |r| r,
397 );
398
399 fn split_flags(flags: &str) -> Vec<String> {
400 flags
403 .split('\'')
404 .enumerate()
405 .flat_map(|(i, f)| {
406 if i % 2 == 1 { vec![f] } else { f.split_whitespace().collect() }
407 })
408 .map(move |s| s.to_owned())
409 .collect::<Vec<_>>()
410 }
411
412 if let Some(flags) =
413 config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile, line_number)
414 {
415 let flags = split_flags(&flags);
416 for flag in &flags {
417 if flag == "--edition" || flag.starts_with("--edition=") {
418 panic!("you must use `//@ edition` to configure the edition");
419 }
420 }
421 self.compile_flags.extend(flags);
422 }
423 if config
424 .parse_name_value_directive(
425 ln,
426 INCORRECT_COMPILER_FLAGS,
427 testfile,
428 line_number,
429 )
430 .is_some()
431 {
432 panic!("`compiler-flags` directive should be spelled `compile-flags`");
433 }
434
435 if let Some(edition) = config.parse_edition(ln, testfile, line_number) {
436 self.compile_flags.insert(0, format!("--edition={}", edition.trim()));
439 has_edition = true;
440 }
441
442 config.parse_and_update_revisions(
443 testfile,
444 line_number,
445 ln,
446 &mut self.revisions,
447 );
448
449 if let Some(flags) =
450 config.parse_name_value_directive(ln, RUN_FLAGS, testfile, line_number)
451 {
452 self.run_flags.extend(split_flags(&flags));
453 }
454
455 if self.pp_exact.is_none() {
456 self.pp_exact = config.parse_pp_exact(ln, testfile, line_number);
457 }
458
459 config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice);
460 config.set_name_directive(ln, BUILD_AUX_DOCS, &mut self.build_aux_docs);
461 config.set_name_directive(ln, UNIQUE_DOC_OUT_DIR, &mut self.unique_doc_out_dir);
462
463 config.set_name_directive(ln, FORCE_HOST, &mut self.force_host);
464 config.set_name_directive(ln, CHECK_STDOUT, &mut self.check_stdout);
465 config.set_name_directive(ln, CHECK_RUN_RESULTS, &mut self.check_run_results);
466 config.set_name_directive(
467 ln,
468 DONT_CHECK_COMPILER_STDOUT,
469 &mut self.dont_check_compiler_stdout,
470 );
471 config.set_name_directive(
472 ln,
473 DONT_CHECK_COMPILER_STDERR,
474 &mut self.dont_check_compiler_stderr,
475 );
476 config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic);
477
478 if let Some(m) =
479 config.parse_name_value_directive(ln, PRETTY_MODE, testfile, line_number)
480 {
481 self.pretty_mode = m;
482 }
483
484 config.set_name_directive(
485 ln,
486 PRETTY_COMPARE_ONLY,
487 &mut self.pretty_compare_only,
488 );
489
490 parse_and_update_aux(config, ln, testfile, line_number, &mut self.aux);
492
493 config.push_name_value_directive(
494 ln,
495 EXEC_ENV,
496 testfile,
497 line_number,
498 &mut self.exec_env,
499 Config::parse_env,
500 );
501 config.push_name_value_directive(
502 ln,
503 UNSET_EXEC_ENV,
504 testfile,
505 line_number,
506 &mut self.unset_exec_env,
507 |r| r.trim().to_owned(),
508 );
509 config.push_name_value_directive(
510 ln,
511 RUSTC_ENV,
512 testfile,
513 line_number,
514 &mut self.rustc_env,
515 Config::parse_env,
516 );
517 config.push_name_value_directive(
518 ln,
519 UNSET_RUSTC_ENV,
520 testfile,
521 line_number,
522 &mut self.unset_rustc_env,
523 |r| r.trim().to_owned(),
524 );
525 config.push_name_value_directive(
526 ln,
527 FORBID_OUTPUT,
528 testfile,
529 line_number,
530 &mut self.forbid_output,
531 |r| r,
532 );
533 config.set_name_directive(
534 ln,
535 CHECK_TEST_LINE_NUMBERS_MATCH,
536 &mut self.check_test_line_numbers_match,
537 );
538
539 self.update_pass_mode(ln, test_revision, config);
540 self.update_fail_mode(ln, config);
541
542 config.set_name_directive(ln, IGNORE_PASS, &mut self.ignore_pass);
543
544 if let Some(NormalizeRule { kind, regex, replacement }) =
545 config.parse_custom_normalization(ln)
546 {
547 let rule_tuple = (regex, replacement);
548 match kind {
549 NormalizeKind::Stdout => self.normalize_stdout.push(rule_tuple),
550 NormalizeKind::Stderr => self.normalize_stderr.push(rule_tuple),
551 NormalizeKind::Stderr32bit => {
552 if config.target_cfg().pointer_width == 32 {
553 self.normalize_stderr.push(rule_tuple);
554 }
555 }
556 NormalizeKind::Stderr64bit => {
557 if config.target_cfg().pointer_width == 64 {
558 self.normalize_stderr.push(rule_tuple);
559 }
560 }
561 }
562 }
563
564 if let Some(code) = config
565 .parse_name_value_directive(ln, FAILURE_STATUS, testfile, line_number)
566 .and_then(|code| code.trim().parse::<i32>().ok())
567 {
568 self.failure_status = Some(code);
569 }
570
571 config.set_name_directive(
572 ln,
573 DONT_CHECK_FAILURE_STATUS,
574 &mut self.dont_check_failure_status,
575 );
576
577 config.set_name_directive(ln, RUN_RUSTFIX, &mut self.run_rustfix);
578 config.set_name_directive(
579 ln,
580 RUSTFIX_ONLY_MACHINE_APPLICABLE,
581 &mut self.rustfix_only_machine_applicable,
582 );
583 config.set_name_value_directive(
584 ln,
585 ASSEMBLY_OUTPUT,
586 testfile,
587 line_number,
588 &mut self.assembly_output,
589 |r| r.trim().to_string(),
590 );
591 config.set_name_directive(
592 ln,
593 STDERR_PER_BITWIDTH,
594 &mut self.stderr_per_bitwidth,
595 );
596 config.set_name_directive(ln, INCREMENTAL, &mut self.incremental);
597
598 if let Some(known_bug) =
601 config.parse_name_value_directive(ln, KNOWN_BUG, testfile, line_number)
602 {
603 let known_bug = known_bug.trim();
604 if known_bug == "unknown"
605 || known_bug.split(',').all(|issue_ref| {
606 issue_ref
607 .trim()
608 .split_once('#')
609 .filter(|(_, number)| {
610 number.chars().all(|digit| digit.is_numeric())
611 })
612 .is_some()
613 })
614 {
615 self.known_bug = true;
616 } else {
617 panic!(
618 "Invalid known-bug value: {known_bug}\nIt requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
619 );
620 }
621 } else if config.parse_name_directive(ln, KNOWN_BUG) {
622 panic!(
623 "Invalid known-bug attribute, requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
624 );
625 }
626
627 config.set_name_value_directive(
628 ln,
629 TEST_MIR_PASS,
630 testfile,
631 line_number,
632 &mut self.mir_unit_test,
633 |s| s.trim().to_string(),
634 );
635 config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base);
636
637 if let Some(flags) =
638 config.parse_name_value_directive(ln, LLVM_COV_FLAGS, testfile, line_number)
639 {
640 self.llvm_cov_flags.extend(split_flags(&flags));
641 }
642
643 if let Some(flags) = config.parse_name_value_directive(
644 ln,
645 FILECHECK_FLAGS,
646 testfile,
647 line_number,
648 ) {
649 self.filecheck_flags.extend(split_flags(&flags));
650 }
651
652 config.set_name_directive(ln, NO_AUTO_CHECK_CFG, &mut self.no_auto_check_cfg);
653
654 self.update_add_core_stubs(ln, config);
655
656 if let Some(err_kind) = config.parse_name_value_directive(
657 ln,
658 DONT_REQUIRE_ANNOTATIONS,
659 testfile,
660 line_number,
661 ) {
662 self.dont_require_annotations
663 .insert(ErrorKind::expect_from_user_str(err_kind.trim()));
664 }
665
666 config.set_name_directive(
667 ln,
668 DISABLE_GDB_PRETTY_PRINTERS,
669 &mut self.disable_gdb_pretty_printers,
670 );
671 config.set_name_directive(
672 ln,
673 COMPARE_OUTPUT_BY_LINES,
674 &mut self.compare_output_by_lines,
675 );
676 },
677 );
678
679 if poisoned {
680 eprintln!("errors encountered during TestProps parsing: {}", testfile);
681 panic!("errors encountered during TestProps parsing");
682 }
683 }
684
685 if self.should_ice {
686 self.failure_status = Some(101);
687 }
688
689 if config.mode == TestMode::Incremental {
690 self.incremental = true;
691 }
692
693 if config.mode == TestMode::Crashes {
694 self.rustc_env = vec![
698 ("RUST_BACKTRACE".to_string(), "0".to_string()),
699 ("RUSTC_ICE".to_string(), "0".to_string()),
700 ];
701 }
702
703 for key in &["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] {
704 if let Ok(val) = env::var(key) {
705 if !self.exec_env.iter().any(|&(ref x, _)| x == key) {
706 self.exec_env.push(((*key).to_owned(), val))
707 }
708 }
709 }
710
711 if let (Some(edition), false) = (&config.edition, has_edition) {
712 self.compile_flags.insert(0, format!("--edition={}", edition));
715 }
716 }
717
718 fn update_fail_mode(&mut self, ln: &str, config: &Config) {
719 let check_ui = |mode: &str| {
720 if config.mode != TestMode::Ui && config.mode != TestMode::Crashes {
722 panic!("`{}-fail` directive is only supported in UI tests", mode);
723 }
724 };
725 if config.mode == TestMode::Ui && config.parse_name_directive(ln, "compile-fail") {
726 panic!("`compile-fail` directive is useless in UI tests");
727 }
728 let fail_mode = if config.parse_name_directive(ln, "check-fail") {
729 check_ui("check");
730 Some(FailMode::Check)
731 } else if config.parse_name_directive(ln, "build-fail") {
732 check_ui("build");
733 Some(FailMode::Build)
734 } else if config.parse_name_directive(ln, "run-fail") {
735 check_ui("run");
736 Some(FailMode::Run(RunFailMode::Fail))
737 } else if config.parse_name_directive(ln, "run-crash") {
738 check_ui("run");
739 Some(FailMode::Run(RunFailMode::Crash))
740 } else if config.parse_name_directive(ln, "run-fail-or-crash") {
741 check_ui("run");
742 Some(FailMode::Run(RunFailMode::FailOrCrash))
743 } else {
744 None
745 };
746 match (self.fail_mode, fail_mode) {
747 (None, Some(_)) => self.fail_mode = fail_mode,
748 (Some(_), Some(_)) => panic!("multiple `*-fail` directives in a single test"),
749 (_, None) => {}
750 }
751 }
752
753 fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) {
754 let check_no_run = |s| match (config.mode, s) {
755 (TestMode::Ui, _) => (),
756 (TestMode::Crashes, _) => (),
757 (TestMode::Codegen, "build-pass") => (),
758 (TestMode::Incremental, _) => {
759 if revision.is_some() && !self.revisions.iter().all(|r| r.starts_with("cfail")) {
760 panic!("`{s}` directive is only supported in `cfail` incremental tests")
761 }
762 }
763 (mode, _) => panic!("`{s}` directive is not supported in `{mode}` tests"),
764 };
765 let pass_mode = if config.parse_name_directive(ln, "check-pass") {
766 check_no_run("check-pass");
767 Some(PassMode::Check)
768 } else if config.parse_name_directive(ln, "build-pass") {
769 check_no_run("build-pass");
770 Some(PassMode::Build)
771 } else if config.parse_name_directive(ln, "run-pass") {
772 check_no_run("run-pass");
773 Some(PassMode::Run)
774 } else {
775 None
776 };
777 match (self.pass_mode, pass_mode) {
778 (None, Some(_)) => self.pass_mode = pass_mode,
779 (Some(_), Some(_)) => panic!("multiple `*-pass` directives in a single test"),
780 (_, None) => {}
781 }
782 }
783
784 pub fn pass_mode(&self, config: &Config) -> Option<PassMode> {
785 if !self.ignore_pass && self.fail_mode.is_none() {
786 if let mode @ Some(_) = config.force_pass_mode {
787 return mode;
788 }
789 }
790 self.pass_mode
791 }
792
793 pub fn local_pass_mode(&self) -> Option<PassMode> {
795 self.pass_mode
796 }
797
798 pub fn update_add_core_stubs(&mut self, ln: &str, config: &Config) {
799 let add_core_stubs = config.parse_name_directive(ln, directives::ADD_CORE_STUBS);
800 if add_core_stubs {
801 if !matches!(config.mode, TestMode::Ui | TestMode::Codegen | TestMode::Assembly) {
802 panic!(
803 "`add-core-stubs` is currently only supported for ui, codegen and assembly test modes"
804 );
805 }
806
807 if self.local_pass_mode().is_some_and(|pm| pm == PassMode::Run) {
810 panic!("`add-core-stubs` cannot be used to run the test binary");
813 }
814
815 self.add_core_stubs = add_core_stubs;
816 }
817 }
818}
819
820fn line_directive<'line>(
823 line_number: usize,
824 original_line: &'line str,
825) -> Option<DirectiveLine<'line>> {
826 let after_comment =
828 original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start();
829
830 let revision;
831 let raw_directive;
832
833 if let Some(after_open_bracket) = after_comment.strip_prefix('[') {
834 let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else {
836 panic!(
837 "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`"
838 )
839 };
840
841 revision = Some(line_revision);
842 raw_directive = after_close_bracket.trim_start();
843 } else {
844 revision = None;
845 raw_directive = after_comment;
846 };
847
848 Some(DirectiveLine { line_number, revision, raw_directive })
849}
850
851struct DirectiveLine<'ln> {
865 line_number: usize,
866 revision: Option<&'ln str>,
870 raw_directive: &'ln str,
876}
877
878impl<'ln> DirectiveLine<'ln> {
879 fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool {
880 self.revision.is_none() || self.revision == test_revision
881 }
882}
883
884pub(crate) struct CheckDirectiveResult<'ln> {
885 is_known_directive: bool,
886 trailing_directive: Option<&'ln str>,
887}
888
889pub(crate) fn check_directive<'a>(
890 directive_ln: &'a str,
891 mode: TestMode,
892) -> CheckDirectiveResult<'a> {
893 let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, ""));
894
895 let is_known_directive = KNOWN_DIRECTIVE_NAMES.contains(&directive_name)
896 || match mode {
897 TestMode::Rustdoc => KNOWN_HTMLDOCCK_DIRECTIVE_NAMES.contains(&directive_name),
898 TestMode::RustdocJson => KNOWN_JSONDOCCK_DIRECTIVE_NAMES.contains(&directive_name),
899 _ => false,
900 };
901
902 let trailing = post.trim().split_once(' ').map(|(pre, _)| pre).unwrap_or(post);
903 let trailing_directive = {
904 directive_ln.get(directive_name.len()..).is_some_and(|s| s.starts_with(' '))
906 && KNOWN_DIRECTIVE_NAMES.contains(&trailing)
908 }
909 .then_some(trailing);
910
911 CheckDirectiveResult { is_known_directive, trailing_directive }
912}
913
914const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@";
915
916fn iter_directives(
917 mode: TestMode,
918 poisoned: &mut bool,
919 testfile: &Utf8Path,
920 rdr: impl Read,
921 it: &mut dyn FnMut(DirectiveLine<'_>),
922) {
923 if testfile.is_dir() {
924 return;
925 }
926
927 if mode == TestMode::CoverageRun {
932 let extra_directives: &[&str] = &[
933 "needs-profiler-runtime",
934 "ignore-cross-compile",
938 ];
939 for raw_directive in extra_directives {
941 it(DirectiveLine { line_number: 0, revision: None, raw_directive });
942 }
943 }
944
945 let mut rdr = BufReader::with_capacity(1024, rdr);
946 let mut ln = String::new();
947 let mut line_number = 0;
948
949 loop {
950 line_number += 1;
951 ln.clear();
952 if rdr.read_line(&mut ln).unwrap() == 0 {
953 break;
954 }
955 let ln = ln.trim();
956
957 let Some(directive_line) = line_directive(line_number, ln) else {
958 continue;
959 };
960
961 if testfile.extension() == Some("rs") {
963 let CheckDirectiveResult { is_known_directive, trailing_directive } =
964 check_directive(directive_line.raw_directive, mode);
965
966 if !is_known_directive {
967 *poisoned = true;
968
969 error!(
970 "{testfile}:{line_number}: detected unknown compiletest test directive `{}`",
971 directive_line.raw_directive,
972 );
973
974 return;
975 }
976
977 if let Some(trailing_directive) = &trailing_directive {
978 *poisoned = true;
979
980 error!(
981 "{testfile}:{line_number}: detected trailing compiletest test directive `{}`",
982 trailing_directive,
983 );
984 help!("put the trailing directive in its own line: `//@ {}`", trailing_directive);
985
986 return;
987 }
988 }
989
990 it(directive_line);
991 }
992}
993
994impl Config {
995 fn parse_and_update_revisions(
996 &self,
997 testfile: &Utf8Path,
998 line_number: usize,
999 line: &str,
1000 existing: &mut Vec<String>,
1001 ) {
1002 const FORBIDDEN_REVISION_NAMES: [&str; 2] = [
1003 "true", "false",
1007 ];
1008
1009 const FILECHECK_FORBIDDEN_REVISION_NAMES: [&str; 9] =
1010 ["CHECK", "COM", "NEXT", "SAME", "EMPTY", "NOT", "COUNT", "DAG", "LABEL"];
1011
1012 if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile, line_number)
1013 {
1014 if self.mode == TestMode::RunMake {
1015 panic!("`run-make` tests do not support revisions: {}", testfile);
1016 }
1017
1018 let mut duplicates: HashSet<_> = existing.iter().cloned().collect();
1019 for revision in raw.split_whitespace() {
1020 if !duplicates.insert(revision.to_string()) {
1021 panic!("duplicate revision: `{}` in line `{}`: {}", revision, raw, testfile);
1022 }
1023
1024 if FORBIDDEN_REVISION_NAMES.contains(&revision) {
1025 panic!(
1026 "revision name `{revision}` is not permitted: `{}` in line `{}`: {}",
1027 revision, raw, testfile
1028 );
1029 }
1030
1031 if matches!(self.mode, TestMode::Assembly | TestMode::Codegen | TestMode::MirOpt)
1032 && FILECHECK_FORBIDDEN_REVISION_NAMES.contains(&revision)
1033 {
1034 panic!(
1035 "revision name `{revision}` is not permitted in a test suite that uses \
1036 `FileCheck` annotations as it is confusing when used as custom `FileCheck` \
1037 prefix: `{revision}` in line `{}`: {}",
1038 raw, testfile
1039 );
1040 }
1041
1042 existing.push(revision.to_string());
1043 }
1044 }
1045 }
1046
1047 fn parse_env(nv: String) -> (String, String) {
1048 let (name, value) = nv.split_once('=').unwrap_or((&nv, ""));
1052 let name = name.trim();
1055 (name.to_owned(), value.to_owned())
1056 }
1057
1058 fn parse_pp_exact(
1059 &self,
1060 line: &str,
1061 testfile: &Utf8Path,
1062 line_number: usize,
1063 ) -> Option<Utf8PathBuf> {
1064 if let Some(s) = self.parse_name_value_directive(line, "pp-exact", testfile, line_number) {
1065 Some(Utf8PathBuf::from(&s))
1066 } else if self.parse_name_directive(line, "pp-exact") {
1067 testfile.file_name().map(Utf8PathBuf::from)
1068 } else {
1069 None
1070 }
1071 }
1072
1073 fn parse_custom_normalization(&self, raw_directive: &str) -> Option<NormalizeRule> {
1074 let (directive_name, raw_value) = raw_directive.split_once(':')?;
1077
1078 let kind = match directive_name {
1079 "normalize-stdout" => NormalizeKind::Stdout,
1080 "normalize-stderr" => NormalizeKind::Stderr,
1081 "normalize-stderr-32bit" => NormalizeKind::Stderr32bit,
1082 "normalize-stderr-64bit" => NormalizeKind::Stderr64bit,
1083 _ => return None,
1084 };
1085
1086 let Some((regex, replacement)) = parse_normalize_rule(raw_value) else {
1087 error!("couldn't parse custom normalization rule: `{raw_directive}`");
1088 help!("expected syntax is: `{directive_name}: \"REGEX\" -> \"REPLACEMENT\"`");
1089 panic!("invalid normalization rule detected");
1090 };
1091 Some(NormalizeRule { kind, regex, replacement })
1092 }
1093
1094 fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
1095 line.starts_with(directive)
1098 && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':'))
1099 }
1100
1101 fn parse_negative_name_directive(&self, line: &str, directive: &str) -> bool {
1102 line.starts_with("no-") && self.parse_name_directive(&line[3..], directive)
1103 }
1104
1105 pub fn parse_name_value_directive(
1106 &self,
1107 line: &str,
1108 directive: &str,
1109 testfile: &Utf8Path,
1110 line_number: usize,
1111 ) -> Option<String> {
1112 let colon = directive.len();
1113 if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
1114 let value = line[(colon + 1)..].to_owned();
1115 debug!("{}: {}", directive, value);
1116 let value = expand_variables(value, self);
1117 if value.is_empty() {
1118 error!("{testfile}:{line_number}: empty value for directive `{directive}`");
1119 help!("expected syntax is: `{directive}: value`");
1120 panic!("empty directive value detected");
1121 }
1122 Some(value)
1123 } else {
1124 None
1125 }
1126 }
1127
1128 fn parse_edition(&self, line: &str, testfile: &Utf8Path, line_number: usize) -> Option<String> {
1129 self.parse_name_value_directive(line, "edition", testfile, line_number)
1130 }
1131
1132 fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) {
1133 match value {
1134 true => {
1135 if self.parse_negative_name_directive(line, directive) {
1136 *value = false;
1137 }
1138 }
1139 false => {
1140 if self.parse_name_directive(line, directive) {
1141 *value = true;
1142 }
1143 }
1144 }
1145 }
1146
1147 fn set_name_value_directive<T>(
1148 &self,
1149 line: &str,
1150 directive: &str,
1151 testfile: &Utf8Path,
1152 line_number: usize,
1153 value: &mut Option<T>,
1154 parse: impl FnOnce(String) -> T,
1155 ) {
1156 if value.is_none() {
1157 *value =
1158 self.parse_name_value_directive(line, directive, testfile, line_number).map(parse);
1159 }
1160 }
1161
1162 fn push_name_value_directive<T>(
1163 &self,
1164 line: &str,
1165 directive: &str,
1166 testfile: &Utf8Path,
1167 line_number: usize,
1168 values: &mut Vec<T>,
1169 parse: impl FnOnce(String) -> T,
1170 ) {
1171 if let Some(value) =
1172 self.parse_name_value_directive(line, directive, testfile, line_number).map(parse)
1173 {
1174 values.push(value);
1175 }
1176 }
1177}
1178
1179fn expand_variables(mut value: String, config: &Config) -> String {
1181 const CWD: &str = "{{cwd}}";
1182 const SRC_BASE: &str = "{{src-base}}";
1183 const TEST_SUITE_BUILD_BASE: &str = "{{build-base}}";
1184 const RUST_SRC_BASE: &str = "{{rust-src-base}}";
1185 const SYSROOT_BASE: &str = "{{sysroot-base}}";
1186 const TARGET_LINKER: &str = "{{target-linker}}";
1187 const TARGET: &str = "{{target}}";
1188
1189 if value.contains(CWD) {
1190 let cwd = env::current_dir().unwrap();
1191 value = value.replace(CWD, &cwd.to_str().unwrap());
1192 }
1193
1194 if value.contains(SRC_BASE) {
1195 value = value.replace(SRC_BASE, &config.src_test_suite_root.as_str());
1196 }
1197
1198 if value.contains(TEST_SUITE_BUILD_BASE) {
1199 value = value.replace(TEST_SUITE_BUILD_BASE, &config.build_test_suite_root.as_str());
1200 }
1201
1202 if value.contains(SYSROOT_BASE) {
1203 value = value.replace(SYSROOT_BASE, &config.sysroot_base.as_str());
1204 }
1205
1206 if value.contains(TARGET_LINKER) {
1207 value = value.replace(TARGET_LINKER, config.target_linker.as_deref().unwrap_or(""));
1208 }
1209
1210 if value.contains(TARGET) {
1211 value = value.replace(TARGET, &config.target);
1212 }
1213
1214 if value.contains(RUST_SRC_BASE) {
1215 let src_base = config.sysroot_base.join("lib/rustlib/src/rust");
1216 src_base.try_exists().expect(&*format!("{} should exists", src_base));
1217 let src_base = src_base.read_link_utf8().unwrap_or(src_base);
1218 value = value.replace(RUST_SRC_BASE, &src_base.as_str());
1219 }
1220
1221 value
1222}
1223
1224struct NormalizeRule {
1225 kind: NormalizeKind,
1226 regex: String,
1227 replacement: String,
1228}
1229
1230enum NormalizeKind {
1231 Stdout,
1232 Stderr,
1233 Stderr32bit,
1234 Stderr64bit,
1235}
1236
1237fn parse_normalize_rule(raw_value: &str) -> Option<(String, String)> {
1242 let captures = static_regex!(
1244 r#"(?x) # (verbose mode regex)
1245 ^
1246 \s* # (leading whitespace)
1247 "(?<regex>[^"]*)" # "REGEX"
1248 \s+->\s+ # ->
1249 "(?<replacement>[^"]*)" # "REPLACEMENT"
1250 $
1251 "#
1252 )
1253 .captures(raw_value)?;
1254 let regex = captures["regex"].to_owned();
1255 let replacement = captures["replacement"].to_owned();
1256 let replacement = replacement.replace("\\n", "\n");
1260 Some((regex, replacement))
1261}
1262
1263pub fn extract_llvm_version(version: &str) -> Version {
1273 let version = version.trim();
1276 let uninterested = |c: char| !c.is_ascii_digit() && c != '.';
1277 let version_without_suffix = match version.split_once(uninterested) {
1278 Some((prefix, _suffix)) => prefix,
1279 None => version,
1280 };
1281
1282 let components: Vec<u64> = version_without_suffix
1283 .split('.')
1284 .map(|s| s.parse().expect("llvm version component should consist of only digits"))
1285 .collect();
1286
1287 match &components[..] {
1288 [major] => Version::new(*major, 0, 0),
1289 [major, minor] => Version::new(*major, *minor, 0),
1290 [major, minor, patch] => Version::new(*major, *minor, *patch),
1291 _ => panic!("malformed llvm version string, expected only 1-3 components: {version}"),
1292 }
1293}
1294
1295pub fn extract_llvm_version_from_binary(binary_path: &str) -> Option<Version> {
1296 let output = Command::new(binary_path).arg("--version").output().ok()?;
1297 if !output.status.success() {
1298 return None;
1299 }
1300 let version = String::from_utf8(output.stdout).ok()?;
1301 for line in version.lines() {
1302 if let Some(version) = line.split("LLVM version ").nth(1) {
1303 return Some(extract_llvm_version(version));
1304 }
1305 }
1306 None
1307}
1308
1309pub fn llvm_has_libzstd(config: &Config) -> bool {
1313 fn is_zstd_in_config(llvm_bin_dir: &Utf8Path) -> Option<()> {
1321 let llvm_config_path = llvm_bin_dir.join("llvm-config");
1322 let output = Command::new(llvm_config_path).arg("--system-libs").output().ok()?;
1323 assert!(output.status.success(), "running llvm-config --system-libs failed");
1324
1325 let libs = String::from_utf8(output.stdout).ok()?;
1326 for lib in libs.split_whitespace() {
1327 if lib.ends_with("libzstd.a") && Utf8Path::new(lib).exists() {
1328 return Some(());
1329 }
1330 }
1331
1332 None
1333 }
1334
1335 #[cfg(unix)]
1345 fn is_lld_built_with_zstd(llvm_bin_dir: &Utf8Path) -> Option<()> {
1346 let lld_path = llvm_bin_dir.join("lld");
1347 if lld_path.exists() {
1348 let lld_symlink_path = llvm_bin_dir.join("ld.lld");
1351 if !lld_symlink_path.exists() {
1352 std::os::unix::fs::symlink(lld_path, &lld_symlink_path).ok()?;
1353 }
1354
1355 let output = Command::new(&lld_symlink_path)
1358 .arg("--compress-debug-sections=zstd")
1359 .output()
1360 .ok()?;
1361 assert!(!output.status.success());
1362
1363 let stderr = String::from_utf8(output.stderr).ok()?;
1366 let zstd_available = !stderr.contains("LLVM was not built with LLVM_ENABLE_ZSTD");
1367
1368 std::fs::remove_file(lld_symlink_path).ok()?;
1371
1372 if zstd_available {
1373 return Some(());
1374 }
1375 }
1376
1377 None
1378 }
1379
1380 #[cfg(not(unix))]
1381 fn is_lld_built_with_zstd(_llvm_bin_dir: &Utf8Path) -> Option<()> {
1382 None
1383 }
1384
1385 if let Some(llvm_bin_dir) = &config.llvm_bin_dir {
1386 if is_zstd_in_config(llvm_bin_dir).is_some() {
1388 return true;
1389 }
1390
1391 if config.target == "x86_64-unknown-linux-gnu"
1399 && config.host == config.target
1400 && is_lld_built_with_zstd(llvm_bin_dir).is_some()
1401 {
1402 return true;
1403 }
1404 }
1405
1406 false
1408}
1409
1410fn extract_version_range<'a, F, VersionTy: Clone>(
1416 line: &'a str,
1417 parse: F,
1418) -> Option<(VersionTy, VersionTy)>
1419where
1420 F: Fn(&'a str) -> Option<VersionTy>,
1421{
1422 let mut splits = line.splitn(2, "- ").map(str::trim);
1423 let min = splits.next().unwrap();
1424 if min.ends_with('-') {
1425 return None;
1426 }
1427
1428 let max = splits.next();
1429
1430 if min.is_empty() {
1431 return None;
1432 }
1433
1434 let min = parse(min)?;
1435 let max = match max {
1436 Some("") => return None,
1437 Some(max) => parse(max)?,
1438 _ => min.clone(),
1439 };
1440
1441 Some((min, max))
1442}
1443
1444pub(crate) fn make_test_description<R: Read>(
1445 config: &Config,
1446 cache: &DirectivesCache,
1447 name: String,
1448 path: &Utf8Path,
1449 src: R,
1450 test_revision: Option<&str>,
1451 poisoned: &mut bool,
1452) -> CollectedTestDesc {
1453 let mut ignore = false;
1454 let mut ignore_message = None;
1455 let mut should_fail = false;
1456
1457 let mut local_poisoned = false;
1458
1459 iter_directives(
1461 config.mode,
1462 &mut local_poisoned,
1463 path,
1464 src,
1465 &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| {
1466 if !directive.applies_to_test_revision(test_revision) {
1467 return;
1468 }
1469
1470 macro_rules! decision {
1471 ($e:expr) => {
1472 match $e {
1473 IgnoreDecision::Ignore { reason } => {
1474 ignore = true;
1475 ignore_message = Some(reason.into());
1476 }
1477 IgnoreDecision::Error { message } => {
1478 error!("{path}:{line_number}: {message}");
1479 *poisoned = true;
1480 return;
1481 }
1482 IgnoreDecision::Continue => {}
1483 }
1484 };
1485 }
1486
1487 decision!(cfg::handle_ignore(config, ln));
1488 decision!(cfg::handle_only(config, ln));
1489 decision!(needs::handle_needs(&cache.needs, config, ln));
1490 decision!(ignore_llvm(config, path, ln, line_number));
1491 decision!(ignore_backends(config, path, ln, line_number));
1492 decision!(needs_backends(config, path, ln, line_number));
1493 decision!(ignore_cdb(config, ln));
1494 decision!(ignore_gdb(config, ln));
1495 decision!(ignore_lldb(config, ln));
1496
1497 if config.target == "wasm32-unknown-unknown"
1498 && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS)
1499 {
1500 decision!(IgnoreDecision::Ignore {
1501 reason: "ignored on WASM as the run results cannot be checked there".into(),
1502 });
1503 }
1504
1505 should_fail |= config.parse_name_directive(ln, "should-fail");
1506 },
1507 );
1508
1509 if local_poisoned {
1510 eprintln!("errors encountered when trying to make test description: {}", path);
1511 panic!("errors encountered when trying to make test description");
1512 }
1513
1514 let should_panic = match config.mode {
1518 TestMode::Pretty => ShouldPanic::No,
1519 _ if should_fail => ShouldPanic::Yes,
1520 _ => ShouldPanic::No,
1521 };
1522
1523 CollectedTestDesc { name, ignore, ignore_message, should_panic }
1524}
1525
1526fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision {
1527 if config.debugger != Some(Debugger::Cdb) {
1528 return IgnoreDecision::Continue;
1529 }
1530
1531 if let Some(actual_version) = config.cdb_version {
1532 if let Some(rest) = line.strip_prefix("min-cdb-version:").map(str::trim) {
1533 let min_version = extract_cdb_version(rest).unwrap_or_else(|| {
1534 panic!("couldn't parse version range: {:?}", rest);
1535 });
1536
1537 if actual_version < min_version {
1540 return IgnoreDecision::Ignore {
1541 reason: format!("ignored when the CDB version is lower than {rest}"),
1542 };
1543 }
1544 }
1545 }
1546 IgnoreDecision::Continue
1547}
1548
1549fn ignore_gdb(config: &Config, line: &str) -> IgnoreDecision {
1550 if config.debugger != Some(Debugger::Gdb) {
1551 return IgnoreDecision::Continue;
1552 }
1553
1554 if let Some(actual_version) = config.gdb_version {
1555 if let Some(rest) = line.strip_prefix("min-gdb-version:").map(str::trim) {
1556 let (start_ver, end_ver) = extract_version_range(rest, extract_gdb_version)
1557 .unwrap_or_else(|| {
1558 panic!("couldn't parse version range: {:?}", rest);
1559 });
1560
1561 if start_ver != end_ver {
1562 panic!("Expected single GDB version")
1563 }
1564 if actual_version < start_ver {
1567 return IgnoreDecision::Ignore {
1568 reason: format!("ignored when the GDB version is lower than {rest}"),
1569 };
1570 }
1571 } else if let Some(rest) = line.strip_prefix("ignore-gdb-version:").map(str::trim) {
1572 let (min_version, max_version) = extract_version_range(rest, extract_gdb_version)
1573 .unwrap_or_else(|| {
1574 panic!("couldn't parse version range: {:?}", rest);
1575 });
1576
1577 if max_version < min_version {
1578 panic!("Malformed GDB version range: max < min")
1579 }
1580
1581 if actual_version >= min_version && actual_version <= max_version {
1582 if min_version == max_version {
1583 return IgnoreDecision::Ignore {
1584 reason: format!("ignored when the GDB version is {rest}"),
1585 };
1586 } else {
1587 return IgnoreDecision::Ignore {
1588 reason: format!("ignored when the GDB version is between {rest}"),
1589 };
1590 }
1591 }
1592 }
1593 }
1594 IgnoreDecision::Continue
1595}
1596
1597fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision {
1598 if config.debugger != Some(Debugger::Lldb) {
1599 return IgnoreDecision::Continue;
1600 }
1601
1602 if let Some(actual_version) = config.lldb_version {
1603 if let Some(rest) = line.strip_prefix("min-lldb-version:").map(str::trim) {
1604 let min_version = rest.parse().unwrap_or_else(|e| {
1605 panic!("Unexpected format of LLDB version string: {}\n{:?}", rest, e);
1606 });
1607 if actual_version < min_version {
1610 return IgnoreDecision::Ignore {
1611 reason: format!("ignored when the LLDB version is {rest}"),
1612 };
1613 }
1614 }
1615 }
1616 IgnoreDecision::Continue
1617}
1618
1619fn ignore_backends(
1620 config: &Config,
1621 path: &Utf8Path,
1622 line: &str,
1623 line_number: usize,
1624) -> IgnoreDecision {
1625 if let Some(backends_to_ignore) =
1626 config.parse_name_value_directive(line, "ignore-backends", path, line_number)
1627 {
1628 for backend in backends_to_ignore.split_whitespace().map(|backend| {
1629 match CodegenBackend::try_from(backend) {
1630 Ok(backend) => backend,
1631 Err(error) => {
1632 panic!("Invalid ignore-backends value `{backend}` in `{path}`: {error}")
1633 }
1634 }
1635 }) {
1636 if config.default_codegen_backend == backend {
1637 return IgnoreDecision::Ignore {
1638 reason: format!("{} backend is marked as ignore", backend.as_str()),
1639 };
1640 }
1641 }
1642 }
1643 IgnoreDecision::Continue
1644}
1645
1646fn needs_backends(
1647 config: &Config,
1648 path: &Utf8Path,
1649 line: &str,
1650 line_number: usize,
1651) -> IgnoreDecision {
1652 if let Some(needed_backends) =
1653 config.parse_name_value_directive(line, "needs-backends", path, line_number)
1654 {
1655 if !needed_backends
1656 .split_whitespace()
1657 .map(|backend| match CodegenBackend::try_from(backend) {
1658 Ok(backend) => backend,
1659 Err(error) => {
1660 panic!("Invalid needs-backends value `{backend}` in `{path}`: {error}")
1661 }
1662 })
1663 .any(|backend| config.default_codegen_backend == backend)
1664 {
1665 return IgnoreDecision::Ignore {
1666 reason: format!(
1667 "{} backend is not part of required backends",
1668 config.default_codegen_backend.as_str()
1669 ),
1670 };
1671 }
1672 }
1673 IgnoreDecision::Continue
1674}
1675
1676fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) -> IgnoreDecision {
1677 if let Some(needed_components) =
1678 config.parse_name_value_directive(line, "needs-llvm-components", path, line_number)
1679 {
1680 let components: HashSet<_> = config.llvm_components.split_whitespace().collect();
1681 if let Some(missing_component) = needed_components
1682 .split_whitespace()
1683 .find(|needed_component| !components.contains(needed_component))
1684 {
1685 if env::var_os("COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS").is_some() {
1686 panic!(
1687 "missing LLVM component {}, and COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS is set: {}",
1688 missing_component, path
1689 );
1690 }
1691 return IgnoreDecision::Ignore {
1692 reason: format!("ignored when the {missing_component} LLVM component is missing"),
1693 };
1694 }
1695 }
1696 if let Some(actual_version) = &config.llvm_version {
1697 if let Some(version_string) =
1700 config.parse_name_value_directive(line, "min-llvm-version", path, line_number)
1701 {
1702 let min_version = extract_llvm_version(&version_string);
1703 if *actual_version < min_version {
1705 return IgnoreDecision::Ignore {
1706 reason: format!(
1707 "ignored when the LLVM version {actual_version} is older than {min_version}"
1708 ),
1709 };
1710 }
1711 } else if let Some(version_string) =
1712 config.parse_name_value_directive(line, "max-llvm-major-version", path, line_number)
1713 {
1714 let max_version = extract_llvm_version(&version_string);
1715 if actual_version.major > max_version.major {
1717 return IgnoreDecision::Ignore {
1718 reason: format!(
1719 "ignored when the LLVM version ({actual_version}) is newer than major\
1720 version {}",
1721 max_version.major
1722 ),
1723 };
1724 }
1725 } else if let Some(version_string) =
1726 config.parse_name_value_directive(line, "min-system-llvm-version", path, line_number)
1727 {
1728 let min_version = extract_llvm_version(&version_string);
1729 if config.system_llvm && *actual_version < min_version {
1732 return IgnoreDecision::Ignore {
1733 reason: format!(
1734 "ignored when the system LLVM version {actual_version} is older than {min_version}"
1735 ),
1736 };
1737 }
1738 } else if let Some(version_range) =
1739 config.parse_name_value_directive(line, "ignore-llvm-version", path, line_number)
1740 {
1741 let (v_min, v_max) =
1743 extract_version_range(&version_range, |s| Some(extract_llvm_version(s)))
1744 .unwrap_or_else(|| {
1745 panic!("couldn't parse version range: \"{version_range}\"");
1746 });
1747 if v_max < v_min {
1748 panic!("malformed LLVM version range where {v_max} < {v_min}")
1749 }
1750 if *actual_version >= v_min && *actual_version <= v_max {
1752 if v_min == v_max {
1753 return IgnoreDecision::Ignore {
1754 reason: format!("ignored when the LLVM version is {actual_version}"),
1755 };
1756 } else {
1757 return IgnoreDecision::Ignore {
1758 reason: format!(
1759 "ignored when the LLVM version is between {v_min} and {v_max}"
1760 ),
1761 };
1762 }
1763 }
1764 } else if let Some(version_string) =
1765 config.parse_name_value_directive(line, "exact-llvm-major-version", path, line_number)
1766 {
1767 let version = extract_llvm_version(&version_string);
1769 if actual_version.major != version.major {
1770 return IgnoreDecision::Ignore {
1771 reason: format!(
1772 "ignored when the actual LLVM major version is {}, but the test only targets major version {}",
1773 actual_version.major, version.major
1774 ),
1775 };
1776 }
1777 }
1778 }
1779 IgnoreDecision::Continue
1780}
1781
1782enum IgnoreDecision {
1783 Ignore { reason: String },
1784 Continue,
1785 Error { message: String },
1786}