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