]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/header.rs
Rollup merge of #62063 - ecstatic-morse:dataflow-backward-order, r=nagisa
[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, PassMode};
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, Debug)]
294 pub struct TestProps {
295     // Lines that should be expected, in order, on standard out
296     pub error_patterns: Vec<String>,
297     // Extra flags to pass to the compiler
298     pub compile_flags: Vec<String>,
299     // Extra flags to pass when the compiled code is run (such as --bench)
300     pub run_flags: Option<String>,
301     // If present, the name of a file that this test should match when
302     // pretty-printed
303     pub pp_exact: Option<PathBuf>,
304     // Other crates that should be compiled (typically from the same
305     // directory as the test, but for backwards compatibility reasons
306     // we also check the auxiliary directory)
307     pub aux_builds: Vec<String>,
308     // A list of crates to pass '--extern-private name:PATH' flags for
309     // This should be a subset of 'aux_build'
310     // FIXME: Replace this with a better solution: https://github.com/rust-lang/rust/pull/54020
311     pub extern_private: Vec<String>,
312     // Environment settings to use for compiling
313     pub rustc_env: Vec<(String, String)>,
314     // Environment variables to unset prior to compiling.
315     // Variables are unset before applying 'rustc_env'.
316     pub unset_rustc_env: Vec<String>,
317     // Environment settings to use during execution
318     pub exec_env: Vec<(String, String)>,
319     // Lines to check if they appear in the expected debugger output
320     pub check_lines: Vec<String>,
321     // Build documentation for all specified aux-builds as well
322     pub build_aux_docs: bool,
323     // Flag to force a crate to be built with the host architecture
324     pub force_host: bool,
325     // Check stdout for error-pattern output as well as stderr
326     pub check_stdout: bool,
327     // For UI tests, allows compiler to generate arbitrary output to stdout
328     pub dont_check_compiler_stdout: bool,
329     // For UI tests, allows compiler to generate arbitrary output to stderr
330     pub dont_check_compiler_stderr: bool,
331     // Don't force a --crate-type=dylib flag on the command line
332     //
333     // Set this for example if you have an auxiliary test file that contains
334     // a proc-macro and needs `#![crate_type = "proc-macro"]`. This ensures
335     // that the aux file is compiled as a `proc-macro` and not as a `dylib`.
336     pub no_prefer_dynamic: bool,
337     // Run --pretty expanded when running pretty printing tests
338     pub pretty_expanded: bool,
339     // Which pretty mode are we testing with, default to 'normal'
340     pub pretty_mode: String,
341     // Only compare pretty output and don't try compiling
342     pub pretty_compare_only: bool,
343     // Patterns which must not appear in the output of a cfail test.
344     pub forbid_output: Vec<String>,
345     // Revisions to test for incremental compilation.
346     pub revisions: Vec<String>,
347     // Directory (if any) to use for incremental compilation.  This is
348     // not set by end-users; rather it is set by the incremental
349     // testing harness and used when generating compilation
350     // arguments. (In particular, it propagates to the aux-builds.)
351     pub incremental_dir: Option<PathBuf>,
352     // How far should the test proceed while still passing.
353     pass_mode: Option<PassMode>,
354     // Ignore `--pass` overrides from the command line for this test.
355     ignore_pass: bool,
356     // rustdoc will test the output of the `--test` option
357     pub check_test_line_numbers_match: bool,
358     // Do not pass `-Z ui-testing` to UI tests
359     pub disable_ui_testing_normalization: bool,
360     // customized normalization rules
361     pub normalize_stdout: Vec<(String, String)>,
362     pub normalize_stderr: Vec<(String, String)>,
363     pub failure_status: i32,
364     // Whether or not `rustfix` should apply the `CodeSuggestion`s of this test and compile the
365     // resulting Rust code.
366     pub run_rustfix: bool,
367     // If true, `rustfix` will only apply `MachineApplicable` suggestions.
368     pub rustfix_only_machine_applicable: bool,
369     pub assembly_output: Option<String>,
370 }
371
372 impl TestProps {
373     pub fn new() -> Self {
374         TestProps {
375             error_patterns: vec![],
376             compile_flags: vec![],
377             run_flags: None,
378             pp_exact: None,
379             aux_builds: vec![],
380             extern_private: vec![],
381             revisions: vec![],
382             rustc_env: vec![],
383             unset_rustc_env: vec![],
384             exec_env: vec![],
385             check_lines: vec![],
386             build_aux_docs: false,
387             force_host: false,
388             check_stdout: false,
389             dont_check_compiler_stdout: false,
390             dont_check_compiler_stderr: false,
391             no_prefer_dynamic: false,
392             pretty_expanded: false,
393             pretty_mode: "normal".to_string(),
394             pretty_compare_only: false,
395             forbid_output: vec![],
396             incremental_dir: None,
397             pass_mode: None,
398             ignore_pass: false,
399             check_test_line_numbers_match: false,
400             disable_ui_testing_normalization: false,
401             normalize_stdout: vec![],
402             normalize_stderr: vec![],
403             failure_status: -1,
404             run_rustfix: false,
405             rustfix_only_machine_applicable: false,
406             assembly_output: None,
407         }
408     }
409
410     pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
411         let mut props = TestProps::new();
412
413         // copy over select properties to the aux build:
414         props.incremental_dir = self.incremental_dir.clone();
415         props.load_from(testfile, cfg, config);
416
417         props
418     }
419
420     pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
421         let mut props = TestProps::new();
422         props.load_from(testfile, cfg, config);
423         props
424     }
425
426     /// Loads properties from `testfile` into `props`. If a property is
427     /// tied to a particular revision `foo` (indicated by writing
428     /// `//[foo]`), then the property is ignored unless `cfg` is
429     /// `Some("foo")`.
430     fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) {
431         iter_header(testfile, cfg, &mut |ln| {
432             if let Some(ep) = config.parse_error_pattern(ln) {
433                 self.error_patterns.push(ep);
434             }
435
436             if let Some(flags) = config.parse_compile_flags(ln) {
437                 self.compile_flags
438                     .extend(flags.split_whitespace().map(|s| s.to_owned()));
439             }
440
441             if let Some(edition) = config.parse_edition(ln) {
442                 self.compile_flags.push(format!("--edition={}", edition));
443             }
444
445             if let Some(r) = config.parse_revisions(ln) {
446                 self.revisions.extend(r);
447             }
448
449             if self.run_flags.is_none() {
450                 self.run_flags = config.parse_run_flags(ln);
451             }
452
453             if self.pp_exact.is_none() {
454                 self.pp_exact = config.parse_pp_exact(ln, testfile);
455             }
456
457             if !self.build_aux_docs {
458                 self.build_aux_docs = config.parse_build_aux_docs(ln);
459             }
460
461             if !self.force_host {
462                 self.force_host = config.parse_force_host(ln);
463             }
464
465             if !self.check_stdout {
466                 self.check_stdout = config.parse_check_stdout(ln);
467             }
468
469             if !self.dont_check_compiler_stdout {
470                 self.dont_check_compiler_stdout = config.parse_dont_check_compiler_stdout(ln);
471             }
472
473             if !self.dont_check_compiler_stderr {
474                 self.dont_check_compiler_stderr = config.parse_dont_check_compiler_stderr(ln);
475             }
476
477             if !self.no_prefer_dynamic {
478                 self.no_prefer_dynamic = config.parse_no_prefer_dynamic(ln);
479             }
480
481             if !self.pretty_expanded {
482                 self.pretty_expanded = config.parse_pretty_expanded(ln);
483             }
484
485             if let Some(m) = config.parse_pretty_mode(ln) {
486                 self.pretty_mode = m;
487             }
488
489             if !self.pretty_compare_only {
490                 self.pretty_compare_only = config.parse_pretty_compare_only(ln);
491             }
492
493             if let Some(ab) = config.parse_aux_build(ln) {
494                 self.aux_builds.push(ab);
495             }
496
497             if let Some(ep) = config.parse_extern_private(ln) {
498                 self.extern_private.push(ep);
499             }
500
501             if let Some(ee) = config.parse_env(ln, "exec-env") {
502                 self.exec_env.push(ee);
503             }
504
505             if let Some(ee) = config.parse_env(ln, "rustc-env") {
506                 self.rustc_env.push(ee);
507             }
508
509             if let Some(ev) = config.parse_name_value_directive(ln, "unset-rustc-env") {
510                 self.unset_rustc_env.push(ev);
511             }
512
513             if let Some(cl) = config.parse_check_line(ln) {
514                 self.check_lines.push(cl);
515             }
516
517             if let Some(of) = config.parse_forbid_output(ln) {
518                 self.forbid_output.push(of);
519             }
520
521             if !self.check_test_line_numbers_match {
522                 self.check_test_line_numbers_match = config.parse_check_test_line_numbers_match(ln);
523             }
524
525             self.update_pass_mode(ln, cfg, config);
526
527             if !self.ignore_pass {
528                 self.ignore_pass = config.parse_ignore_pass(ln);
529             }
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     pub fn pass_mode(&self, config: &Config) -> Option<PassMode> {
613         if !self.ignore_pass {
614             if let (mode @ Some(_), Some(_)) = (config.force_pass_mode, self.pass_mode) {
615                 return mode;
616             }
617         }
618         self.pass_mode
619     }
620 }
621
622 fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut dyn FnMut(&str)) {
623     if testfile.is_dir() {
624         return;
625     }
626
627     let comment = if testfile.to_string_lossy().ends_with(".rs") {
628         "//"
629     } else {
630         "#"
631     };
632
633     // FIXME: would be nice to allow some whitespace between comment and brace :)
634     // It took me like 2 days to debug why compile-flags weren’t taken into account for my test :)
635     let comment_with_brace = comment.to_string() + "[";
636
637     let rdr = BufReader::new(File::open(testfile).unwrap());
638     for ln in rdr.lines() {
639         // Assume that any directives will be found before the first
640         // module or function. This doesn't seem to be an optimization
641         // with a warm page cache. Maybe with a cold one.
642         let ln = ln.unwrap();
643         let ln = ln.trim();
644         if ln.starts_with("fn") || ln.starts_with("mod") {
645             return;
646         } else if ln.starts_with(&comment_with_brace) {
647             // A comment like `//[foo]` is specific to revision `foo`
648             if let Some(close_brace) = ln.find(']') {
649                 let open_brace = ln.find('[').unwrap();
650                 let lncfg = &ln[open_brace + 1 .. close_brace];
651                 let matches = match cfg {
652                     Some(s) => s == &lncfg[..],
653                     None => false,
654                 };
655                 if matches {
656                     it(ln[(close_brace + 1)..].trim_start());
657                 }
658             } else {
659                 panic!("malformed condition directive: expected `{}foo]`, found `{}`",
660                         comment_with_brace, ln)
661             }
662         } else if ln.starts_with(comment) {
663             it(ln[comment.len() ..].trim_start());
664         }
665     }
666     return;
667 }
668
669 impl Config {
670     fn parse_error_pattern(&self, line: &str) -> Option<String> {
671         self.parse_name_value_directive(line, "error-pattern")
672     }
673
674     fn parse_forbid_output(&self, line: &str) -> Option<String> {
675         self.parse_name_value_directive(line, "forbid-output")
676     }
677
678     fn parse_aux_build(&self, line: &str) -> Option<String> {
679         self.parse_name_value_directive(line, "aux-build")
680             .map(|r| r.trim().to_string())
681     }
682
683     fn parse_extern_private(&self, line: &str) -> Option<String> {
684         self.parse_name_value_directive(line, "extern-private")
685     }
686
687     fn parse_compile_flags(&self, line: &str) -> Option<String> {
688         self.parse_name_value_directive(line, "compile-flags")
689     }
690
691     fn parse_revisions(&self, line: &str) -> Option<Vec<String>> {
692         self.parse_name_value_directive(line, "revisions")
693             .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
694     }
695
696     fn parse_run_flags(&self, line: &str) -> Option<String> {
697         self.parse_name_value_directive(line, "run-flags")
698     }
699
700     fn parse_check_line(&self, line: &str) -> Option<String> {
701         self.parse_name_value_directive(line, "check")
702     }
703
704     fn parse_force_host(&self, line: &str) -> bool {
705         self.parse_name_directive(line, "force-host")
706     }
707
708     fn parse_build_aux_docs(&self, line: &str) -> bool {
709         self.parse_name_directive(line, "build-aux-docs")
710     }
711
712     fn parse_check_stdout(&self, line: &str) -> bool {
713         self.parse_name_directive(line, "check-stdout")
714     }
715
716     fn parse_dont_check_compiler_stdout(&self, line: &str) -> bool {
717         self.parse_name_directive(line, "dont-check-compiler-stdout")
718     }
719
720     fn parse_dont_check_compiler_stderr(&self, line: &str) -> bool {
721         self.parse_name_directive(line, "dont-check-compiler-stderr")
722     }
723
724     fn parse_no_prefer_dynamic(&self, line: &str) -> bool {
725         self.parse_name_directive(line, "no-prefer-dynamic")
726     }
727
728     fn parse_pretty_expanded(&self, line: &str) -> bool {
729         self.parse_name_directive(line, "pretty-expanded")
730     }
731
732     fn parse_pretty_mode(&self, line: &str) -> Option<String> {
733         self.parse_name_value_directive(line, "pretty-mode")
734     }
735
736     fn parse_pretty_compare_only(&self, line: &str) -> bool {
737         self.parse_name_directive(line, "pretty-compare-only")
738     }
739
740     fn parse_failure_status(&self, line: &str) -> Option<i32> {
741         match self.parse_name_value_directive(line, "failure-status") {
742             Some(code) => code.trim().parse::<i32>().ok(),
743             _ => None,
744         }
745     }
746
747     fn parse_disable_ui_testing_normalization(&self, line: &str) -> bool {
748         self.parse_name_directive(line, "disable-ui-testing-normalization")
749     }
750
751     fn parse_check_test_line_numbers_match(&self, line: &str) -> bool {
752         self.parse_name_directive(line, "check-test-line-numbers-match")
753     }
754
755     fn parse_ignore_pass(&self, line: &str) -> bool {
756         self.parse_name_directive(line, "ignore-pass")
757     }
758
759     fn parse_assembly_output(&self, line: &str) -> Option<String> {
760         self.parse_name_value_directive(line, "assembly-output")
761             .map(|r| r.trim().to_string())
762     }
763
764     fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> {
765         self.parse_name_value_directive(line, name).map(|nv| {
766             // nv is either FOO or FOO=BAR
767             let mut strs: Vec<String> = nv.splitn(2, '=').map(str::to_owned).collect();
768
769             match strs.len() {
770                 1 => (strs.pop().unwrap(), String::new()),
771                 2 => {
772                     let end = strs.pop().unwrap();
773                     (strs.pop().unwrap(), end)
774                 }
775                 n => panic!("Expected 1 or 2 strings, not {}", n),
776             }
777         })
778     }
779
780     fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option<PathBuf> {
781         if let Some(s) = self.parse_name_value_directive(line, "pp-exact") {
782             Some(PathBuf::from(&s))
783         } else if self.parse_name_directive(line, "pp-exact") {
784             testfile.file_name().map(PathBuf::from)
785         } else {
786             None
787         }
788     }
789
790     fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> {
791         if self.parse_cfg_name_directive(line, prefix) == ParsedNameDirective::Match {
792             let from = parse_normalization_string(&mut line)?;
793             let to = parse_normalization_string(&mut line)?;
794             Some((from, to))
795         } else {
796             None
797         }
798     }
799
800     fn parse_needs_matching_clang(&self, line: &str) -> bool {
801         self.parse_name_directive(line, "needs-matching-clang")
802     }
803
804     fn parse_needs_profiler_support(&self, line: &str) -> bool {
805         self.parse_name_directive(line, "needs-profiler-support")
806     }
807
808     fn parse_needs_sanitizer_support(&self, line: &str) -> bool {
809         self.parse_name_directive(line, "needs-sanitizer-support")
810     }
811
812     /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
813     /// or `normalize-stderr-32bit`.
814     fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective {
815         if line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') {
816             let name = line[prefix.len() + 1..]
817                 .split(&[':', ' '][..])
818                 .next()
819                 .unwrap();
820
821             if name == "test" ||
822                 util::matches_os(&self.target, name) ||             // target
823                 name == util::get_arch(&self.target) ||             // architecture
824                 name == util::get_pointer_width(&self.target) ||    // pointer width
825                 name == self.stage_id.split('-').next().unwrap() || // stage
826                 Some(name) == util::get_env(&self.target) ||        // env
827                 (self.target != self.host && name == "cross-compile") ||
828                 match self.compare_mode {
829                     Some(CompareMode::Nll) => name == "compare-mode-nll",
830                     Some(CompareMode::Polonius) => name == "compare-mode-polonius",
831                     None => false,
832                 } ||
833                 (cfg!(debug_assertions) && name == "debug") {
834                 ParsedNameDirective::Match
835             } else {
836                 match self.mode {
837                     common::DebugInfoGdbLldb => {
838                         if name == "gdb" {
839                             ParsedNameDirective::MatchGdb
840                         } else if name == "lldb" {
841                             ParsedNameDirective::MatchLldb
842                         } else {
843                             ParsedNameDirective::NoMatch
844                         }
845                     },
846                     common::DebugInfoCdb => if name == "cdb" {
847                         ParsedNameDirective::Match
848                     } else {
849                         ParsedNameDirective::NoMatch
850                     },
851                     common::DebugInfoGdb => if name == "gdb" {
852                         ParsedNameDirective::Match
853                     } else {
854                         ParsedNameDirective::NoMatch
855                     },
856                     common::DebugInfoLldb => if name == "lldb" {
857                         ParsedNameDirective::Match
858                     } else {
859                         ParsedNameDirective::NoMatch
860                     },
861                     common::Pretty => if name == "pretty" {
862                         ParsedNameDirective::Match
863                     } else {
864                         ParsedNameDirective::NoMatch
865                     },
866                     _ => ParsedNameDirective::NoMatch,
867                 }
868             }
869         } else {
870             ParsedNameDirective::NoMatch
871         }
872     }
873
874     fn has_cfg_prefix(&self, line: &str, prefix: &str) -> bool {
875         // returns whether this line contains this prefix or not. For prefix
876         // "ignore", returns true if line says "ignore-x86_64", "ignore-arch",
877         // "ignore-android" etc.
878         line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-')
879     }
880
881     fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
882         // Ensure the directive is a whole word. Do not match "ignore-x86" when
883         // the line says "ignore-x86_64".
884         line.starts_with(directive) && match line.as_bytes().get(directive.len()) {
885             None | Some(&b' ') | Some(&b':') => true,
886             _ => false,
887         }
888     }
889
890     pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> {
891         let colon = directive.len();
892         if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
893             let value = line[(colon + 1)..].to_owned();
894             debug!("{}: {}", directive, value);
895             Some(expand_variables(value, self))
896         } else {
897             None
898         }
899     }
900
901     pub fn find_rust_src_root(&self) -> Option<PathBuf> {
902         let mut path = self.src_base.clone();
903         let path_postfix = Path::new("src/etc/lldb_batchmode.py");
904
905         while path.pop() {
906             if path.join(&path_postfix).is_file() {
907                 return Some(path);
908             }
909         }
910
911         None
912     }
913
914     fn parse_run_rustfix(&self, line: &str) -> bool {
915         self.parse_name_directive(line, "run-rustfix")
916     }
917
918     fn parse_rustfix_only_machine_applicable(&self, line: &str) -> bool {
919         self.parse_name_directive(line, "rustfix-only-machine-applicable")
920     }
921
922     fn parse_edition(&self, line: &str) -> Option<String> {
923         self.parse_name_value_directive(line, "edition")
924     }
925 }
926
927 pub fn lldb_version_to_int(version_string: &str) -> isize {
928     let error_string = format!(
929         "Encountered LLDB version string with unexpected format: {}",
930         version_string
931     );
932     version_string.parse().expect(&error_string)
933 }
934
935 fn expand_variables(mut value: String, config: &Config) -> String {
936     const CWD: &'static str = "{{cwd}}";
937     const SRC_BASE: &'static str = "{{src-base}}";
938     const BUILD_BASE: &'static str = "{{build-base}}";
939
940     if value.contains(CWD) {
941         let cwd = env::current_dir().unwrap();
942         value = value.replace(CWD, &cwd.to_string_lossy());
943     }
944
945     if value.contains(SRC_BASE) {
946         value = value.replace(SRC_BASE, &config.src_base.to_string_lossy());
947     }
948
949     if value.contains(BUILD_BASE) {
950         value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy());
951     }
952
953     value
954 }
955
956 /// Finds the next quoted string `"..."` in `line`, and extract the content from it. Move the `line`
957 /// variable after the end of the quoted string.
958 ///
959 /// # Examples
960 ///
961 /// ```
962 /// let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\".";
963 /// let first = parse_normalization_string(&mut s);
964 /// assert_eq!(first, Some("something (32 bits)".to_owned()));
965 /// assert_eq!(s, " -> \"something ($WORD bits)\".");
966 /// ```
967 fn parse_normalization_string(line: &mut &str) -> Option<String> {
968     // FIXME support escapes in strings.
969     let begin = line.find('"')? + 1;
970     let end = line[begin..].find('"')? + begin;
971     let result = line[begin..end].to_owned();
972     *line = &line[end + 1..];
973     Some(result)
974 }
975
976 #[test]
977 fn test_parse_normalization_string() {
978     let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\".";
979     let first = parse_normalization_string(&mut s);
980     assert_eq!(first, Some("something (32 bits)".to_owned()));
981     assert_eq!(s, " -> \"something ($WORD bits)\".");
982
983     // Nothing to normalize (No quotes)
984     let mut s = "normalize-stderr-32bit: something (32 bits) -> something ($WORD bits).";
985     let first = parse_normalization_string(&mut s);
986     assert_eq!(first, None);
987     assert_eq!(s, r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."#);
988
989     // Nothing to normalize (Only a single quote)
990     let mut s = "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits).";
991     let first = parse_normalization_string(&mut s);
992     assert_eq!(first, None);
993     assert_eq!(s, "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits).");
994
995     // Nothing to normalize (Three quotes)
996     let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits).";
997     let first = parse_normalization_string(&mut s);
998     assert_eq!(first, Some("something (32 bits)".to_owned()));
999     assert_eq!(s, " -> \"something ($WORD bits).");
1000 }