]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/header.rs
Auto merge of #62012 - wesleywiser:const_prop_use_ecx, r=oli-obk
[rust.git] / src / tools / compiletest / src / header.rs
1 use std::env;
2 use std::fs::File;
3 use std::io::prelude::*;
4 use std::io::BufReader;
5 use std::path::{Path, PathBuf};
6
7 use log::*;
8
9 use crate::common::{self, CompareMode, Config, Mode};
10 use crate::util;
11
12 use crate::extract_gdb_version;
13
14 /// Whether to ignore the test.
15 #[derive(Clone, Copy, PartialEq, Debug)]
16 pub enum Ignore {
17     /// Runs it.
18     Run,
19     /// Ignore it totally.
20     Ignore,
21     /// Ignore only the gdb test, but run the lldb test.
22     IgnoreGdb,
23     /// Ignore only the lldb test, but run the gdb test.
24     IgnoreLldb,
25 }
26
27 impl Ignore {
28     pub fn can_run_gdb(&self) -> bool {
29         *self == Ignore::Run || *self == Ignore::IgnoreLldb
30     }
31
32     pub fn can_run_lldb(&self) -> bool {
33         *self == Ignore::Run || *self == Ignore::IgnoreGdb
34     }
35
36     pub fn no_gdb(&self) -> Ignore {
37         match *self {
38             Ignore::Run => Ignore::IgnoreGdb,
39             Ignore::IgnoreGdb => Ignore::IgnoreGdb,
40             _ => Ignore::Ignore,
41         }
42     }
43
44     pub fn no_lldb(&self) -> Ignore {
45         match *self {
46             Ignore::Run => Ignore::IgnoreLldb,
47             Ignore::IgnoreLldb => Ignore::IgnoreLldb,
48             _ => Ignore::Ignore,
49         }
50     }
51 }
52
53 /// The result of parse_cfg_name_directive.
54 #[derive(Clone, Copy, PartialEq, Debug)]
55 enum ParsedNameDirective {
56     /// No match.
57     NoMatch,
58     /// Match.
59     Match,
60     /// Mode was DebugInfoGdbLldb and this matched gdb.
61     MatchGdb,
62     /// Mode was DebugInfoGdbLldb and this matched lldb.
63     MatchLldb,
64 }
65
66 /// Properties which must be known very early, before actually running
67 /// the test.
68 pub struct EarlyProps {
69     pub ignore: Ignore,
70     pub should_fail: bool,
71     pub aux: Vec<String>,
72     pub revisions: Vec<String>,
73 }
74
75 impl EarlyProps {
76     pub fn from_file(config: &Config, testfile: &Path) -> Self {
77         let mut props = EarlyProps {
78             ignore: Ignore::Run,
79             should_fail: false,
80             aux: Vec::new(),
81             revisions: vec![],
82         };
83
84         if config.mode == common::DebugInfoGdbLldb {
85             if config.lldb_python_dir.is_none() {
86                 props.ignore = props.ignore.no_lldb();
87             }
88             if config.gdb_version.is_none() {
89                 props.ignore = props.ignore.no_gdb();
90             }
91         } else if config.mode == common::DebugInfoCdb {
92             if config.cdb.is_none() {
93                 props.ignore = Ignore::Ignore;
94             }
95         }
96
97         let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
98         let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some();
99
100         iter_header(testfile, None, &mut |ln| {
101             // we should check if any only-<platform> exists and if it exists
102             // and does not matches the current platform, skip the test
103             if props.ignore != Ignore::Ignore {
104                 props.ignore = match config.parse_cfg_name_directive(ln, "ignore") {
105                     ParsedNameDirective::Match => Ignore::Ignore,
106                     ParsedNameDirective::NoMatch => props.ignore,
107                     ParsedNameDirective::MatchGdb => props.ignore.no_gdb(),
108                     ParsedNameDirective::MatchLldb => props.ignore.no_lldb(),
109                 };
110
111                 if config.has_cfg_prefix(ln, "only") {
112                     props.ignore = match config.parse_cfg_name_directive(ln, "only") {
113                         ParsedNameDirective::Match => props.ignore,
114                         ParsedNameDirective::NoMatch => Ignore::Ignore,
115                         ParsedNameDirective::MatchLldb => props.ignore.no_gdb(),
116                         ParsedNameDirective::MatchGdb => props.ignore.no_lldb(),
117                     };
118                 }
119
120                 if ignore_llvm(config, ln) {
121                     props.ignore = Ignore::Ignore;
122                 }
123
124                 if config.run_clang_based_tests_with.is_none() &&
125                    config.parse_needs_matching_clang(ln) {
126                     props.ignore = Ignore::Ignore;
127                 }
128
129                 if !rustc_has_profiler_support &&
130                    config.parse_needs_profiler_support(ln) {
131                     props.ignore = Ignore::Ignore;
132                 }
133
134                 if !rustc_has_sanitizer_support &&
135                    config.parse_needs_sanitizer_support(ln) {
136                     props.ignore = Ignore::Ignore;
137                 }
138             }
139
140             if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoGdbLldb) &&
141                 props.ignore.can_run_gdb() && ignore_gdb(config, ln) {
142                 props.ignore = props.ignore.no_gdb();
143             }
144
145             if (config.mode == common::DebugInfoLldb || config.mode == common::DebugInfoGdbLldb) &&
146                 props.ignore.can_run_lldb() && ignore_lldb(config, ln) {
147                 props.ignore = props.ignore.no_lldb();
148             }
149
150             if let Some(s) = config.parse_aux_build(ln) {
151                 props.aux.push(s);
152             }
153
154             if let Some(r) = config.parse_revisions(ln) {
155                 props.revisions.extend(r);
156             }
157
158             props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail");
159         });
160
161         return props;
162
163         fn ignore_gdb(config: &Config, line: &str) -> bool {
164             if let Some(actual_version) = config.gdb_version {
165                 if line.starts_with("min-gdb-version") {
166                     let (start_ver, end_ver) = extract_gdb_version_range(line);
167
168                     if start_ver != end_ver {
169                         panic!("Expected single GDB version")
170                     }
171                     // Ignore if actual version is smaller the minimum required
172                     // version
173                     actual_version < start_ver
174                 } else if line.starts_with("ignore-gdb-version") {
175                     let (min_version, max_version) = extract_gdb_version_range(line);
176
177                     if max_version < min_version {
178                         panic!("Malformed GDB version range: max < min")
179                     }
180
181                     actual_version >= min_version && actual_version <= max_version
182                 } else {
183                     false
184                 }
185             } else {
186                 false
187             }
188         }
189
190         // Takes a directive of the form "ignore-gdb-version <version1> [- <version2>]",
191         // returns the numeric representation of <version1> and <version2> as
192         // tuple: (<version1> as u32, <version2> as u32)
193         // If the <version2> part is omitted, the second component of the tuple
194         // is the same as <version1>.
195         fn extract_gdb_version_range(line: &str) -> (u32, u32) {
196             const ERROR_MESSAGE: &'static str = "Malformed GDB version directive";
197
198             let range_components = line.split(&[' ', '-'][..])
199                                        .filter(|word| !word.is_empty())
200                                        .map(extract_gdb_version)
201                                        .skip_while(Option::is_none)
202                                        .take(3) // 3 or more = invalid, so take at most 3.
203                                        .collect::<Vec<Option<u32>>>();
204
205             match range_components.len() {
206                 1 => {
207                     let v = range_components[0].unwrap();
208                     (v, v)
209                 }
210                 2 => {
211                     let v_min = range_components[0].unwrap();
212                     let v_max = range_components[1].expect(ERROR_MESSAGE);
213                     (v_min, v_max)
214                 }
215                 _ => panic!(ERROR_MESSAGE),
216             }
217         }
218
219         fn ignore_lldb(config: &Config, line: &str) -> bool {
220             if let Some(ref actual_version) = config.lldb_version {
221                 if line.starts_with("min-lldb-version") {
222                     let min_version = line.trim_end()
223                         .rsplit(' ')
224                         .next()
225                         .expect("Malformed lldb version directive");
226                     // Ignore if actual version is smaller the minimum required
227                     // version
228                     lldb_version_to_int(actual_version) < lldb_version_to_int(min_version)
229                 } else if line.starts_with("rust-lldb") && !config.lldb_native_rust {
230                     true
231                 } else {
232                     false
233                 }
234             } else {
235                 false
236             }
237         }
238
239         fn ignore_llvm(config: &Config, line: &str) -> bool {
240             if config.system_llvm && line.starts_with("no-system-llvm") {
241                 return true;
242             }
243             if let Some(ref actual_version) = config.llvm_version {
244                 if line.starts_with("min-llvm-version") {
245                     let min_version = line.trim_end()
246                         .rsplit(' ')
247                         .next()
248                         .expect("Malformed llvm version directive");
249                     // Ignore if actual version is smaller the minimum required
250                     // version
251                     &actual_version[..] < min_version
252                 } else if line.starts_with("min-system-llvm-version") {
253                     let min_version = line.trim_end()
254                         .rsplit(' ')
255                         .next()
256                         .expect("Malformed llvm version directive");
257                     // Ignore if using system LLVM and actual version
258                     // is smaller the minimum required version
259                     config.system_llvm && &actual_version[..] < min_version
260                 } else if line.starts_with("ignore-llvm-version") {
261                     // Syntax is: "ignore-llvm-version <version1> [- <version2>]"
262                     let range_components = line.split(' ')
263                         .skip(1) // Skip the directive.
264                         .map(|s| s.trim())
265                         .filter(|word| !word.is_empty() && word != &"-")
266                         .take(3) // 3 or more = invalid, so take at most 3.
267                         .collect::<Vec<&str>>();
268                     match range_components.len() {
269                         1 => {
270                             &actual_version[..] == range_components[0]
271                         }
272                         2 => {
273                             let v_min = range_components[0];
274                             let v_max = range_components[1];
275                             if v_max < v_min {
276                                 panic!("Malformed LLVM version range: max < min")
277                             }
278                             // Ignore if version lies inside of range.
279                             &actual_version[..] >= v_min && &actual_version[..] <= v_max
280                         }
281                         _ => panic!("Malformed LLVM version directive"),
282                     }
283                 } else {
284                     false
285                 }
286             } else {
287                 false
288             }
289         }
290     }
291 }
292
293 #[derive(Clone, Copy, PartialEq, Debug)]
294 pub enum PassMode {
295     Check,
296     Build,
297     Run,
298 }
299
300 #[derive(Clone, Debug)]
301 pub struct TestProps {
302     // Lines that should be expected, in order, on standard out
303     pub error_patterns: Vec<String>,
304     // Extra flags to pass to the compiler
305     pub compile_flags: Vec<String>,
306     // Extra flags to pass when the compiled code is run (such as --bench)
307     pub run_flags: Option<String>,
308     // If present, the name of a file that this test should match when
309     // pretty-printed
310     pub pp_exact: Option<PathBuf>,
311     // Other crates that should be compiled (typically from the same
312     // directory as the test, but for backwards compatibility reasons
313     // we also check the auxiliary directory)
314     pub aux_builds: Vec<String>,
315     // A list of crates to pass '--extern-private name:PATH' flags for
316     // This should be a subset of 'aux_build'
317     // FIXME: Replace this with a better solution: https://github.com/rust-lang/rust/pull/54020
318     pub extern_private: Vec<String>,
319     // Environment settings to use for compiling
320     pub rustc_env: Vec<(String, String)>,
321     // Environment variables to unset prior to compiling.
322     // Variables are unset before applying 'rustc_env'.
323     pub unset_rustc_env: Vec<String>,
324     // Environment settings to use during execution
325     pub exec_env: Vec<(String, String)>,
326     // Lines to check if they appear in the expected debugger output
327     pub check_lines: Vec<String>,
328     // Build documentation for all specified aux-builds as well
329     pub build_aux_docs: bool,
330     // Flag to force a crate to be built with the host architecture
331     pub force_host: bool,
332     // Check stdout for error-pattern output as well as stderr
333     pub check_stdout: bool,
334     // For UI tests, allows compiler to generate arbitrary output to stdout
335     pub dont_check_compiler_stdout: bool,
336     // For UI tests, allows compiler to generate arbitrary output to stderr
337     pub dont_check_compiler_stderr: bool,
338     // Don't force a --crate-type=dylib flag on the command line
339     //
340     // Set this for example if you have an auxiliary test file that contains
341     // a proc-macro and needs `#![crate_type = "proc-macro"]`. This ensures
342     // that the aux file is compiled as a `proc-macro` and not as a `dylib`.
343     pub no_prefer_dynamic: bool,
344     // Run --pretty expanded when running pretty printing tests
345     pub pretty_expanded: bool,
346     // Which pretty mode are we testing with, default to 'normal'
347     pub pretty_mode: String,
348     // Only compare pretty output and don't try compiling
349     pub pretty_compare_only: bool,
350     // Patterns which must not appear in the output of a cfail test.
351     pub forbid_output: Vec<String>,
352     // Revisions to test for incremental compilation.
353     pub revisions: Vec<String>,
354     // Directory (if any) to use for incremental compilation.  This is
355     // not set by end-users; rather it is set by the incremental
356     // testing harness and used when generating compilation
357     // arguments. (In particular, it propagates to the aux-builds.)
358     pub incremental_dir: Option<PathBuf>,
359     // How far should the test proceed while still passing.
360     pub pass_mode: Option<PassMode>,
361     // rustdoc will test the output of the `--test` option
362     pub check_test_line_numbers_match: bool,
363     // Do not pass `-Z ui-testing` to UI tests
364     pub disable_ui_testing_normalization: bool,
365     // customized normalization rules
366     pub normalize_stdout: Vec<(String, String)>,
367     pub normalize_stderr: Vec<(String, String)>,
368     pub failure_status: i32,
369     // Whether or not `rustfix` should apply the `CodeSuggestion`s of this test and compile the
370     // resulting Rust code.
371     pub run_rustfix: bool,
372     // If true, `rustfix` will only apply `MachineApplicable` suggestions.
373     pub rustfix_only_machine_applicable: bool,
374     pub assembly_output: Option<String>,
375 }
376
377 impl TestProps {
378     pub fn new() -> Self {
379         TestProps {
380             error_patterns: vec![],
381             compile_flags: vec![],
382             run_flags: None,
383             pp_exact: None,
384             aux_builds: vec![],
385             extern_private: vec![],
386             revisions: vec![],
387             rustc_env: vec![],
388             unset_rustc_env: vec![],
389             exec_env: vec![],
390             check_lines: vec![],
391             build_aux_docs: false,
392             force_host: false,
393             check_stdout: false,
394             dont_check_compiler_stdout: false,
395             dont_check_compiler_stderr: false,
396             no_prefer_dynamic: false,
397             pretty_expanded: false,
398             pretty_mode: "normal".to_string(),
399             pretty_compare_only: false,
400             forbid_output: vec![],
401             incremental_dir: None,
402             pass_mode: None,
403             check_test_line_numbers_match: false,
404             disable_ui_testing_normalization: false,
405             normalize_stdout: vec![],
406             normalize_stderr: vec![],
407             failure_status: -1,
408             run_rustfix: false,
409             rustfix_only_machine_applicable: false,
410             assembly_output: None,
411         }
412     }
413
414     pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
415         let mut props = TestProps::new();
416
417         // copy over select properties to the aux build:
418         props.incremental_dir = self.incremental_dir.clone();
419         props.load_from(testfile, cfg, config);
420
421         props
422     }
423
424     pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
425         let mut props = TestProps::new();
426         props.load_from(testfile, cfg, config);
427         props
428     }
429
430     /// Loads properties from `testfile` into `props`. If a property is
431     /// tied to a particular revision `foo` (indicated by writing
432     /// `//[foo]`), then the property is ignored unless `cfg` is
433     /// `Some("foo")`.
434     fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) {
435         iter_header(testfile, cfg, &mut |ln| {
436             if let Some(ep) = config.parse_error_pattern(ln) {
437                 self.error_patterns.push(ep);
438             }
439
440             if let Some(flags) = config.parse_compile_flags(ln) {
441                 self.compile_flags
442                     .extend(flags.split_whitespace().map(|s| s.to_owned()));
443             }
444
445             if let Some(edition) = config.parse_edition(ln) {
446                 self.compile_flags.push(format!("--edition={}", edition));
447             }
448
449             if let Some(r) = config.parse_revisions(ln) {
450                 self.revisions.extend(r);
451             }
452
453             if self.run_flags.is_none() {
454                 self.run_flags = config.parse_run_flags(ln);
455             }
456
457             if self.pp_exact.is_none() {
458                 self.pp_exact = config.parse_pp_exact(ln, testfile);
459             }
460
461             if !self.build_aux_docs {
462                 self.build_aux_docs = config.parse_build_aux_docs(ln);
463             }
464
465             if !self.force_host {
466                 self.force_host = config.parse_force_host(ln);
467             }
468
469             if !self.check_stdout {
470                 self.check_stdout = config.parse_check_stdout(ln);
471             }
472
473             if !self.dont_check_compiler_stdout {
474                 self.dont_check_compiler_stdout = config.parse_dont_check_compiler_stdout(ln);
475             }
476
477             if !self.dont_check_compiler_stderr {
478                 self.dont_check_compiler_stderr = config.parse_dont_check_compiler_stderr(ln);
479             }
480
481             if !self.no_prefer_dynamic {
482                 self.no_prefer_dynamic = config.parse_no_prefer_dynamic(ln);
483             }
484
485             if !self.pretty_expanded {
486                 self.pretty_expanded = config.parse_pretty_expanded(ln);
487             }
488
489             if let Some(m) = config.parse_pretty_mode(ln) {
490                 self.pretty_mode = m;
491             }
492
493             if !self.pretty_compare_only {
494                 self.pretty_compare_only = config.parse_pretty_compare_only(ln);
495             }
496
497             if let Some(ab) = config.parse_aux_build(ln) {
498                 self.aux_builds.push(ab);
499             }
500
501             if let Some(ep) = config.parse_extern_private(ln) {
502                 self.extern_private.push(ep);
503             }
504
505             if let Some(ee) = config.parse_env(ln, "exec-env") {
506                 self.exec_env.push(ee);
507             }
508
509             if let Some(ee) = config.parse_env(ln, "rustc-env") {
510                 self.rustc_env.push(ee);
511             }
512
513             if let Some(ev) = config.parse_name_value_directive(ln, "unset-rustc-env") {
514                 self.unset_rustc_env.push(ev);
515             }
516
517             if let Some(cl) = config.parse_check_line(ln) {
518                 self.check_lines.push(cl);
519             }
520
521             if let Some(of) = config.parse_forbid_output(ln) {
522                 self.forbid_output.push(of);
523             }
524
525             if !self.check_test_line_numbers_match {
526                 self.check_test_line_numbers_match = config.parse_check_test_line_numbers_match(ln);
527             }
528
529             self.update_pass_mode(ln, cfg, config);
530
531             if !self.disable_ui_testing_normalization {
532                 self.disable_ui_testing_normalization =
533                     config.parse_disable_ui_testing_normalization(ln);
534             }
535
536             if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stdout") {
537                 self.normalize_stdout.push(rule);
538             }
539             if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stderr") {
540                 self.normalize_stderr.push(rule);
541             }
542
543             if let Some(code) = config.parse_failure_status(ln) {
544                 self.failure_status = code;
545             }
546
547             if !self.run_rustfix {
548                 self.run_rustfix = config.parse_run_rustfix(ln);
549             }
550
551             if !self.rustfix_only_machine_applicable {
552                 self.rustfix_only_machine_applicable =
553                     config.parse_rustfix_only_machine_applicable(ln);
554             }
555
556             if self.assembly_output.is_none() {
557                 self.assembly_output = config.parse_assembly_output(ln);
558             }
559         });
560
561         if self.failure_status == -1 {
562             self.failure_status = match config.mode {
563                 Mode::RunFail => 101,
564                 _ => 1,
565             };
566         }
567
568         for key in &["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] {
569             if let Ok(val) = env::var(key) {
570                 if self.exec_env.iter().find(|&&(ref x, _)| x == key).is_none() {
571                     self.exec_env.push(((*key).to_owned(), val))
572                 }
573             }
574         }
575     }
576
577     fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) {
578         let check_no_run = |s| {
579             if config.mode != Mode::Ui && config.mode != Mode::Incremental {
580                 panic!("`{}` header is only supported in UI and incremental tests", s);
581             }
582             if config.mode == Mode::Incremental &&
583                 !revision.map_or(false, |r| r.starts_with("cfail")) &&
584                 !self.revisions.iter().all(|r| r.starts_with("cfail")) {
585                 panic!("`{}` header is only supported in `cfail` incremental tests", s);
586             }
587         };
588         let pass_mode = if config.parse_name_directive(ln, "check-pass") {
589             check_no_run("check-pass");
590             Some(PassMode::Check)
591         } else if config.parse_name_directive(ln, "build-pass") {
592             check_no_run("build-pass");
593             Some(PassMode::Build)
594         } else if config.parse_name_directive(ln, "compile-pass") /* compatibility */ {
595             check_no_run("compile-pass");
596             Some(PassMode::Build)
597         } else if config.parse_name_directive(ln, "run-pass") {
598             if config.mode != Mode::Ui && config.mode != Mode::RunPass /* compatibility */ {
599                 panic!("`run-pass` header is only supported in UI tests")
600             }
601             Some(PassMode::Run)
602         } else {
603             None
604         };
605         match (self.pass_mode, pass_mode) {
606             (None, Some(_)) => self.pass_mode = pass_mode,
607             (Some(_), Some(_)) => panic!("multiple `*-pass` headers in a single test"),
608             (_, None) => {}
609         }
610     }
611 }
612
613 fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut dyn FnMut(&str)) {
614     if testfile.is_dir() {
615         return;
616     }
617
618     let comment = if testfile.to_string_lossy().ends_with(".rs") {
619         "//"
620     } else {
621         "#"
622     };
623
624     // FIXME: would be nice to allow some whitespace between comment and brace :)
625     // It took me like 2 days to debug why compile-flags weren’t taken into account for my test :)
626     let comment_with_brace = comment.to_string() + "[";
627
628     let rdr = BufReader::new(File::open(testfile).unwrap());
629     for ln in rdr.lines() {
630         // Assume that any directives will be found before the first
631         // module or function. This doesn't seem to be an optimization
632         // with a warm page cache. Maybe with a cold one.
633         let ln = ln.unwrap();
634         let ln = ln.trim();
635         if ln.starts_with("fn") || ln.starts_with("mod") {
636             return;
637         } else if ln.starts_with(&comment_with_brace) {
638             // A comment like `//[foo]` is specific to revision `foo`
639             if let Some(close_brace) = ln.find(']') {
640                 let open_brace = ln.find('[').unwrap();
641                 let lncfg = &ln[open_brace + 1 .. close_brace];
642                 let matches = match cfg {
643                     Some(s) => s == &lncfg[..],
644                     None => false,
645                 };
646                 if matches {
647                     it(ln[(close_brace + 1)..].trim_start());
648                 }
649             } else {
650                 panic!("malformed condition directive: expected `{}foo]`, found `{}`",
651                         comment_with_brace, ln)
652             }
653         } else if ln.starts_with(comment) {
654             it(ln[comment.len() ..].trim_start());
655         }
656     }
657     return;
658 }
659
660 impl Config {
661     fn parse_error_pattern(&self, line: &str) -> Option<String> {
662         self.parse_name_value_directive(line, "error-pattern")
663     }
664
665     fn parse_forbid_output(&self, line: &str) -> Option<String> {
666         self.parse_name_value_directive(line, "forbid-output")
667     }
668
669     fn parse_aux_build(&self, line: &str) -> Option<String> {
670         self.parse_name_value_directive(line, "aux-build")
671             .map(|r| r.trim().to_string())
672     }
673
674     fn parse_extern_private(&self, line: &str) -> Option<String> {
675         self.parse_name_value_directive(line, "extern-private")
676     }
677
678     fn parse_compile_flags(&self, line: &str) -> Option<String> {
679         self.parse_name_value_directive(line, "compile-flags")
680     }
681
682     fn parse_revisions(&self, line: &str) -> Option<Vec<String>> {
683         self.parse_name_value_directive(line, "revisions")
684             .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
685     }
686
687     fn parse_run_flags(&self, line: &str) -> Option<String> {
688         self.parse_name_value_directive(line, "run-flags")
689     }
690
691     fn parse_check_line(&self, line: &str) -> Option<String> {
692         self.parse_name_value_directive(line, "check")
693     }
694
695     fn parse_force_host(&self, line: &str) -> bool {
696         self.parse_name_directive(line, "force-host")
697     }
698
699     fn parse_build_aux_docs(&self, line: &str) -> bool {
700         self.parse_name_directive(line, "build-aux-docs")
701     }
702
703     fn parse_check_stdout(&self, line: &str) -> bool {
704         self.parse_name_directive(line, "check-stdout")
705     }
706
707     fn parse_dont_check_compiler_stdout(&self, line: &str) -> bool {
708         self.parse_name_directive(line, "dont-check-compiler-stdout")
709     }
710
711     fn parse_dont_check_compiler_stderr(&self, line: &str) -> bool {
712         self.parse_name_directive(line, "dont-check-compiler-stderr")
713     }
714
715     fn parse_no_prefer_dynamic(&self, line: &str) -> bool {
716         self.parse_name_directive(line, "no-prefer-dynamic")
717     }
718
719     fn parse_pretty_expanded(&self, line: &str) -> bool {
720         self.parse_name_directive(line, "pretty-expanded")
721     }
722
723     fn parse_pretty_mode(&self, line: &str) -> Option<String> {
724         self.parse_name_value_directive(line, "pretty-mode")
725     }
726
727     fn parse_pretty_compare_only(&self, line: &str) -> bool {
728         self.parse_name_directive(line, "pretty-compare-only")
729     }
730
731     fn parse_failure_status(&self, line: &str) -> Option<i32> {
732         match self.parse_name_value_directive(line, "failure-status") {
733             Some(code) => code.trim().parse::<i32>().ok(),
734             _ => None,
735         }
736     }
737
738     fn parse_disable_ui_testing_normalization(&self, line: &str) -> bool {
739         self.parse_name_directive(line, "disable-ui-testing-normalization")
740     }
741
742     fn parse_check_test_line_numbers_match(&self, line: &str) -> bool {
743         self.parse_name_directive(line, "check-test-line-numbers-match")
744     }
745
746     fn parse_assembly_output(&self, line: &str) -> Option<String> {
747         self.parse_name_value_directive(line, "assembly-output")
748             .map(|r| r.trim().to_string())
749     }
750
751     fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> {
752         self.parse_name_value_directive(line, name).map(|nv| {
753             // nv is either FOO or FOO=BAR
754             let mut strs: Vec<String> = nv.splitn(2, '=').map(str::to_owned).collect();
755
756             match strs.len() {
757                 1 => (strs.pop().unwrap(), String::new()),
758                 2 => {
759                     let end = strs.pop().unwrap();
760                     (strs.pop().unwrap(), end)
761                 }
762                 n => panic!("Expected 1 or 2 strings, not {}", n),
763             }
764         })
765     }
766
767     fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option<PathBuf> {
768         if let Some(s) = self.parse_name_value_directive(line, "pp-exact") {
769             Some(PathBuf::from(&s))
770         } else if self.parse_name_directive(line, "pp-exact") {
771             testfile.file_name().map(PathBuf::from)
772         } else {
773             None
774         }
775     }
776
777     fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> {
778         if self.parse_cfg_name_directive(line, prefix) == ParsedNameDirective::Match {
779             let from = parse_normalization_string(&mut line)?;
780             let to = parse_normalization_string(&mut line)?;
781             Some((from, to))
782         } else {
783             None
784         }
785     }
786
787     fn parse_needs_matching_clang(&self, line: &str) -> bool {
788         self.parse_name_directive(line, "needs-matching-clang")
789     }
790
791     fn parse_needs_profiler_support(&self, line: &str) -> bool {
792         self.parse_name_directive(line, "needs-profiler-support")
793     }
794
795     fn parse_needs_sanitizer_support(&self, line: &str) -> bool {
796         self.parse_name_directive(line, "needs-sanitizer-support")
797     }
798
799     /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
800     /// or `normalize-stderr-32bit`.
801     fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective {
802         if line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') {
803             let name = line[prefix.len() + 1..]
804                 .split(&[':', ' '][..])
805                 .next()
806                 .unwrap();
807
808             if name == "test" ||
809                 util::matches_os(&self.target, name) ||             // target
810                 name == util::get_arch(&self.target) ||             // architecture
811                 name == util::get_pointer_width(&self.target) ||    // pointer width
812                 name == self.stage_id.split('-').next().unwrap() || // stage
813                 Some(name) == util::get_env(&self.target) ||        // env
814                 (self.target != self.host && name == "cross-compile") ||
815                 match self.compare_mode {
816                     Some(CompareMode::Nll) => name == "compare-mode-nll",
817                     Some(CompareMode::Polonius) => name == "compare-mode-polonius",
818                     None => false,
819                 } ||
820                 (cfg!(debug_assertions) && name == "debug") {
821                 ParsedNameDirective::Match
822             } else {
823                 match self.mode {
824                     common::DebugInfoGdbLldb => {
825                         if name == "gdb" {
826                             ParsedNameDirective::MatchGdb
827                         } else if name == "lldb" {
828                             ParsedNameDirective::MatchLldb
829                         } else {
830                             ParsedNameDirective::NoMatch
831                         }
832                     },
833                     common::DebugInfoCdb => if name == "cdb" {
834                         ParsedNameDirective::Match
835                     } else {
836                         ParsedNameDirective::NoMatch
837                     },
838                     common::DebugInfoGdb => if name == "gdb" {
839                         ParsedNameDirective::Match
840                     } else {
841                         ParsedNameDirective::NoMatch
842                     },
843                     common::DebugInfoLldb => if name == "lldb" {
844                         ParsedNameDirective::Match
845                     } else {
846                         ParsedNameDirective::NoMatch
847                     },
848                     common::Pretty => if name == "pretty" {
849                         ParsedNameDirective::Match
850                     } else {
851                         ParsedNameDirective::NoMatch
852                     },
853                     _ => ParsedNameDirective::NoMatch,
854                 }
855             }
856         } else {
857             ParsedNameDirective::NoMatch
858         }
859     }
860
861     fn has_cfg_prefix(&self, line: &str, prefix: &str) -> bool {
862         // returns whether this line contains this prefix or not. For prefix
863         // "ignore", returns true if line says "ignore-x86_64", "ignore-arch",
864         // "ignore-android" etc.
865         line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-')
866     }
867
868     fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
869         // Ensure the directive is a whole word. Do not match "ignore-x86" when
870         // the line says "ignore-x86_64".
871         line.starts_with(directive) && match line.as_bytes().get(directive.len()) {
872             None | Some(&b' ') | Some(&b':') => true,
873             _ => false,
874         }
875     }
876
877     pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> {
878         let colon = directive.len();
879         if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
880             let value = line[(colon + 1)..].to_owned();
881             debug!("{}: {}", directive, value);
882             Some(expand_variables(value, self))
883         } else {
884             None
885         }
886     }
887
888     pub fn find_rust_src_root(&self) -> Option<PathBuf> {
889         let mut path = self.src_base.clone();
890         let path_postfix = Path::new("src/etc/lldb_batchmode.py");
891
892         while path.pop() {
893             if path.join(&path_postfix).is_file() {
894                 return Some(path);
895             }
896         }
897
898         None
899     }
900
901     fn parse_run_rustfix(&self, line: &str) -> bool {
902         self.parse_name_directive(line, "run-rustfix")
903     }
904
905     fn parse_rustfix_only_machine_applicable(&self, line: &str) -> bool {
906         self.parse_name_directive(line, "rustfix-only-machine-applicable")
907     }
908
909     fn parse_edition(&self, line: &str) -> Option<String> {
910         self.parse_name_value_directive(line, "edition")
911     }
912 }
913
914 pub fn lldb_version_to_int(version_string: &str) -> isize {
915     let error_string = format!(
916         "Encountered LLDB version string with unexpected format: {}",
917         version_string
918     );
919     version_string.parse().expect(&error_string)
920 }
921
922 fn expand_variables(mut value: String, config: &Config) -> String {
923     const CWD: &'static str = "{{cwd}}";
924     const SRC_BASE: &'static str = "{{src-base}}";
925     const BUILD_BASE: &'static str = "{{build-base}}";
926
927     if value.contains(CWD) {
928         let cwd = env::current_dir().unwrap();
929         value = value.replace(CWD, &cwd.to_string_lossy());
930     }
931
932     if value.contains(SRC_BASE) {
933         value = value.replace(SRC_BASE, &config.src_base.to_string_lossy());
934     }
935
936     if value.contains(BUILD_BASE) {
937         value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy());
938     }
939
940     value
941 }
942
943 /// Finds the next quoted string `"..."` in `line`, and extract the content from it. Move the `line`
944 /// variable after the end of the quoted string.
945 ///
946 /// # Examples
947 ///
948 /// ```
949 /// let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\".";
950 /// let first = parse_normalization_string(&mut s);
951 /// assert_eq!(first, Some("something (32 bits)".to_owned()));
952 /// assert_eq!(s, " -> \"something ($WORD bits)\".");
953 /// ```
954 fn parse_normalization_string(line: &mut &str) -> Option<String> {
955     // FIXME support escapes in strings.
956     let begin = line.find('"')? + 1;
957     let end = line[begin..].find('"')? + begin;
958     let result = line[begin..end].to_owned();
959     *line = &line[end + 1..];
960     Some(result)
961 }
962
963 #[test]
964 fn test_parse_normalization_string() {
965     let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\".";
966     let first = parse_normalization_string(&mut s);
967     assert_eq!(first, Some("something (32 bits)".to_owned()));
968     assert_eq!(s, " -> \"something ($WORD bits)\".");
969
970     // Nothing to normalize (No quotes)
971     let mut s = "normalize-stderr-32bit: something (32 bits) -> something ($WORD bits).";
972     let first = parse_normalization_string(&mut s);
973     assert_eq!(first, None);
974     assert_eq!(s, r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."#);
975
976     // Nothing to normalize (Only a single quote)
977     let mut s = "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits).";
978     let first = parse_normalization_string(&mut s);
979     assert_eq!(first, None);
980     assert_eq!(s, "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits).");
981
982     // Nothing to normalize (Three quotes)
983     let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits).";
984     let first = parse_normalization_string(&mut s);
985     assert_eq!(first, Some("something (32 bits)".to_owned()));
986     assert_eq!(s, " -> \"something ($WORD bits).");
987 }