diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:24 +0000 |
commit | 023939b627b7dc93b01471f7d41fb8553ddb4ffa (patch) | |
tree | 60fc59477c605c72b0a1051409062ddecc43f877 /src/tools/compiletest | |
parent | Adding debian version 1.72.1+dfsg1-1. (diff) | |
download | rustc-023939b627b7dc93b01471f7d41fb8553ddb4ffa.tar.xz rustc-023939b627b7dc93b01471f7d41fb8553ddb4ffa.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/compiletest')
-rw-r--r-- | src/tools/compiletest/Cargo.toml | 2 | ||||
-rw-r--r-- | src/tools/compiletest/src/header.rs | 47 | ||||
-rw-r--r-- | src/tools/compiletest/src/header/cfg.rs | 4 | ||||
-rw-r--r-- | src/tools/compiletest/src/lib.rs | 2 | ||||
-rw-r--r-- | src/tools/compiletest/src/runtest.rs | 153 | ||||
-rw-r--r-- | src/tools/compiletest/src/runtest/debugger.rs | 148 | ||||
-rw-r--r-- | src/tools/compiletest/src/runtest/tests.rs | 8 |
7 files changed, 213 insertions, 151 deletions
diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index d2f258320..ff1d5cecb 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -29,7 +29,7 @@ anyhow = "1" libc = "0.2" [target.'cfg(windows)'.dependencies] -miow = "0.5" +miow = "0.6" [target.'cfg(windows)'.dependencies.windows] version = "0.48.0" diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index c835962ad..269d93843 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -6,7 +6,6 @@ use std::io::BufReader; use std::path::{Path, PathBuf}; use std::process::Command; -use build_helper::ci::CiEnv; use tracing::*; use crate::common::{Config, Debugger, FailMode, Mode, PassMode}; @@ -232,7 +231,7 @@ impl TestProps { aux_builds: vec![], aux_crates: vec![], revisions: vec![], - rustc_env: vec![], + rustc_env: vec![("RUSTC_ICE".to_string(), "0".to_string())], unset_rustc_env: vec![], exec_env: vec![], unset_exec_env: vec![], @@ -298,13 +297,6 @@ impl TestProps { /// `//[foo]`), then the property is ignored unless `cfg` is /// `Some("foo")`. fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) { - // In CI, we've sometimes encountered non-determinism related to truncating very long paths. - // Set a consistent (short) prefix to avoid issues, but only in CI to avoid regressing the - // contributor experience. - if CiEnv::is_ci() { - self.remap_src_base = config.mode == Mode::Ui && !config.suite.contains("rustdoc"); - } - let mut has_edition = false; if !testfile.is_dir() { let file = File::open(testfile).unwrap(); @@ -541,16 +533,15 @@ impl TestProps { } fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) { - let check_no_run = |s| { - if config.mode != Mode::Ui && config.mode != Mode::Incremental { - panic!("`{}` header is only supported in UI and incremental tests", s); - } - if config.mode == Mode::Incremental - && !revision.map_or(false, |r| r.starts_with("cfail")) - && !self.revisions.iter().all(|r| r.starts_with("cfail")) - { - panic!("`{}` header is only supported in `cfail` incremental tests", s); + let check_no_run = |s| match (config.mode, s) { + (Mode::Ui, _) => (), + (Mode::Codegen, "build-pass") => (), + (Mode::Incremental, _) => { + if revision.is_some() && !self.revisions.iter().all(|r| r.starts_with("cfail")) { + panic!("`{s}` header is only supported in `cfail` incremental tests") + } } + (mode, _) => panic!("`{s}` header is not supported in `{mode}` tests"), }; let pass_mode = if config.parse_name_directive(ln, "check-pass") { check_no_run("check-pass"); @@ -559,9 +550,7 @@ impl TestProps { check_no_run("build-pass"); Some(PassMode::Build) } else if config.parse_name_directive(ln, "run-pass") { - if config.mode != Mode::Ui { - panic!("`run-pass` header is only supported in UI tests") - } + check_no_run("run-pass"); Some(PassMode::Run) } else { None @@ -588,21 +577,25 @@ impl TestProps { } } +/// Extract a `(Option<line_config>, directive)` directive from a line if comment is present. pub fn line_directive<'line>( comment: &str, ln: &'line str, ) -> Option<(Option<&'line str>, &'line str)> { + let ln = ln.trim_start(); if ln.starts_with(comment) { let ln = ln[comment.len()..].trim_start(); if ln.starts_with('[') { // A comment like `//[foo]` is specific to revision `foo` - if let Some(close_brace) = ln.find(']') { - let lncfg = &ln[1..close_brace]; + let Some(close_brace) = ln.find(']') else { + panic!( + "malformed condition directive: expected `{}[foo]`, found `{}`", + comment, ln + ); + }; - Some((Some(lncfg), ln[(close_brace + 1)..].trim_start())) - } else { - panic!("malformed condition directive: expected `{}[foo]`, found `{}`", comment, ln) - } + let lncfg = &ln[1..close_brace]; + Some((Some(lncfg), ln[(close_brace + 1)..].trim_start())) } else { Some((None, ln)) } diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs index 86a749b93..77c2866b3 100644 --- a/src/tools/compiletest/src/header/cfg.rs +++ b/src/tools/compiletest/src/header/cfg.rs @@ -112,7 +112,7 @@ pub(super) fn parse_cfg_name_directive<'a>( (config.target == "wasm32-unknown-unknown").then_some("emscripten"), ], allowed_names: &target_cfgs.all_oses, - message: "when the operative system is {name}" + message: "when the operating system is {name}" } condition! { name: &target_cfg.env, @@ -122,7 +122,7 @@ pub(super) fn parse_cfg_name_directive<'a>( condition! { name: &target_cfg.os_and_env(), allowed_names: &target_cfgs.all_oses_and_envs, - message: "when the operative system and target environment are {name}" + message: "when the operating system and target environment are {name}" } condition! { name: &target_cfg.abi, diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index fc48d0159..1a765477f 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -1119,7 +1119,7 @@ fn check_overlapping_tests(found_paths: &BTreeSet<PathBuf>) { for path in found_paths { for ancestor in path.ancestors().skip(1) { if found_paths.contains(ancestor) { - collisions.push((path, ancestor.clone())); + collisions.push((path, ancestor)); } } } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 672779325..4ef79af31 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -18,6 +18,7 @@ use crate::ColorConfig; use regex::{Captures, Regex}; use rustfix::{apply_suggestions, get_suggestions_from_json, Filter}; +use std::borrow::Cow; use std::collections::hash_map::DefaultHasher; use std::collections::{HashMap, HashSet}; use std::env; @@ -41,7 +42,7 @@ use crate::extract_gdb_version; use crate::is_android_gdb_target; mod debugger; -use debugger::{check_debugger_output, DebuggerCommands}; +use debugger::DebuggerCommands; #[cfg(test)] mod tests; @@ -664,6 +665,7 @@ impl<'test> TestCx<'test> { fn normalize_coverage_output(&self, coverage: &str) -> Result<String, String> { let normalized = self.normalize_output(coverage, &[]); + let normalized = Self::anonymize_coverage_line_numbers(&normalized); let mut lines = normalized.lines().collect::<Vec<_>>(); @@ -674,6 +676,21 @@ impl<'test> TestCx<'test> { Ok(joined_lines) } + /// Replace line numbers in coverage reports with the placeholder `LL`, + /// so that the tests are less sensitive to lines being added/removed. + fn anonymize_coverage_line_numbers(coverage: &str) -> Cow<'_, str> { + // The coverage reporter prints line numbers at the start of a line. + // They are truncated or left-padded to occupy exactly 5 columns. + // (`LineNumberColumnWidth` in `SourceCoverageViewText.cpp`.) + // A pipe character `|` appears immediately after the final digit. + // + // Line numbers that appear inside expansion/instantiation subviews + // have an additional prefix of ` |` for each nesting level. + static LINE_NUMBER_RE: Lazy<Regex> = + Lazy::new(|| Regex::new(r"(?m:^)(?<prefix>(?: \|)*) *[0-9]+\|").unwrap()); + LINE_NUMBER_RE.replace_all(coverage, "$prefix LL|") + } + /// Coverage reports can describe multiple source files, separated by /// blank lines. The order of these files is unpredictable (since it /// depends on implementation details), so we need to sort the file @@ -868,6 +885,8 @@ impl<'test> TestCx<'test> { .args(&["--target", &self.config.target]) .arg("-L") .arg(&aux_dir) + .arg("-A") + .arg("internal_features") .args(&self.props.compile_flags) .envs(self.props.rustc_env.clone()); self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags); @@ -936,7 +955,9 @@ impl<'test> TestCx<'test> { .arg("-L") .arg(&self.config.build_base) .arg("-L") - .arg(aux_dir); + .arg(aux_dir) + .arg("-A") + .arg("internal_features"); self.set_revision_flags(&mut rustc); self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags); rustc.args(&self.props.compile_flags); @@ -997,16 +1018,13 @@ impl<'test> TestCx<'test> { }; // Parse debugger commands etc from test files - let DebuggerCommands { commands, check_lines, breakpoint_lines, .. } = - match DebuggerCommands::parse_from( - &self.testpaths.file, - self.config, - prefixes, - self.revision, - ) { - Ok(cmds) => cmds, - Err(e) => self.fatal(&e), - }; + let dbg_cmds = DebuggerCommands::parse_from( + &self.testpaths.file, + self.config, + prefixes, + self.revision, + ) + .unwrap_or_else(|e| self.fatal(&e)); // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands let mut script_str = String::with_capacity(2048); @@ -1023,12 +1041,12 @@ impl<'test> TestCx<'test> { // Set breakpoints on every line that contains the string "#break" let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); - for line in &breakpoint_lines { + for line in &dbg_cmds.breakpoint_lines { script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line)); } // Append the other `cdb-command:`s - for line in &commands { + for line in &dbg_cmds.commands { script_str.push_str(line); script_str.push_str("\n"); } @@ -1058,7 +1076,7 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec("Error while running CDB", &debugger_run_result); } - if let Err(e) = check_debugger_output(&debugger_run_result, &check_lines) { + if let Err(e) = dbg_cmds.check_output(&debugger_run_result) { self.fatal_proc_rec(&e, &debugger_run_result); } } @@ -1088,17 +1106,14 @@ impl<'test> TestCx<'test> { PREFIXES }; - let DebuggerCommands { commands, check_lines, breakpoint_lines } = - match DebuggerCommands::parse_from( - &self.testpaths.file, - self.config, - prefixes, - self.revision, - ) { - Ok(cmds) => cmds, - Err(e) => self.fatal(&e), - }; - let mut cmds = commands.join("\n"); + let dbg_cmds = DebuggerCommands::parse_from( + &self.testpaths.file, + self.config, + prefixes, + self.revision, + ) + .unwrap_or_else(|e| self.fatal(&e)); + let mut cmds = dbg_cmds.commands.join("\n"); // compile test file (it should have 'compile-flags:-g' in the header) let should_run = self.run_if_enabled(); @@ -1132,13 +1147,14 @@ impl<'test> TestCx<'test> { ./{}/stage2/lib/rustlib/{}/lib/\n", self.config.host, self.config.target )); - for line in &breakpoint_lines { + for line in &dbg_cmds.breakpoint_lines { script_str.push_str( - &format!( + format!( "break {:?}:{}\n", self.testpaths.file.file_name().unwrap().to_string_lossy(), *line - )[..], + ) + .as_str(), ); } script_str.push_str(&cmds); @@ -1279,7 +1295,7 @@ impl<'test> TestCx<'test> { } // Add line breakpoints - for line in &breakpoint_lines { + for line in &dbg_cmds.breakpoint_lines { script_str.push_str(&format!( "break '{}':{}\n", self.testpaths.file.file_name().unwrap().to_string_lossy(), @@ -1315,7 +1331,7 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec("gdb failed to execute", &debugger_run_result); } - if let Err(e) = check_debugger_output(&debugger_run_result, &check_lines) { + if let Err(e) = dbg_cmds.check_output(&debugger_run_result) { self.fatal_proc_rec(&e, &debugger_run_result); } } @@ -1372,16 +1388,13 @@ impl<'test> TestCx<'test> { }; // Parse debugger commands etc from test files - let DebuggerCommands { commands, check_lines, breakpoint_lines, .. } = - match DebuggerCommands::parse_from( - &self.testpaths.file, - self.config, - prefixes, - self.revision, - ) { - Ok(cmds) => cmds, - Err(e) => self.fatal(&e), - }; + let dbg_cmds = DebuggerCommands::parse_from( + &self.testpaths.file, + self.config, + prefixes, + self.revision, + ) + .unwrap_or_else(|e| self.fatal(&e)); // Write debugger script: // We don't want to hang when calling `quit` while the process is still running @@ -1430,7 +1443,7 @@ impl<'test> TestCx<'test> { // Set breakpoints on every line that contains the string "#break" let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); - for line in &breakpoint_lines { + for line in &dbg_cmds.breakpoint_lines { script_str.push_str(&format!( "breakpoint set --file '{}' --line {}\n", source_file_name, line @@ -1438,7 +1451,7 @@ impl<'test> TestCx<'test> { } // Append the other commands - for line in &commands { + for line in &dbg_cmds.commands { script_str.push_str(line); script_str.push_str("\n"); } @@ -1458,7 +1471,7 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec("Error while running LLDB", &debugger_run_result); } - if let Err(e) = check_debugger_output(&debugger_run_result, &check_lines) { + if let Err(e) = dbg_cmds.check_output(&debugger_run_result) { self.fatal_proc_rec(&e, &debugger_run_result); } } @@ -1649,7 +1662,7 @@ impl<'test> TestCx<'test> { if self.props.known_bug { if !expected_errors.is_empty() { self.fatal_proc_rec( - "`known_bug` tests should not have an expected errors", + "`known_bug` tests should not have an expected error", proc_res, ); } @@ -1875,6 +1888,8 @@ impl<'test> TestCx<'test> { .arg("--deny") .arg("warnings") .arg(&self.testpaths.file) + .arg("-A") + .arg("internal_features") .args(&self.props.compile_flags); if self.config.mode == RustdocJson { @@ -1941,7 +1956,8 @@ impl<'test> TestCx<'test> { let mut test_client = Command::new(self.config.remote_test_client.as_ref().unwrap()); test_client - .args(&["run", &support_libs.len().to_string(), &prog]) + .args(&["run", &support_libs.len().to_string()]) + .arg(&prog) .args(support_libs) .args(args); @@ -2338,6 +2354,14 @@ impl<'test> TestCx<'test> { // Hide line numbers to reduce churn rustc.arg("-Zui-testing"); rustc.arg("-Zdeduplicate-diagnostics=no"); + // #[cfg(not(bootstrap)] unconditionally pass flag after beta bump + // since `ui-fulldeps --stage=1` builds using the stage 0 compiler, + // which doesn't have this flag. + if !(self.config.stage_id.starts_with("stage1-") + && self.config.suite == "ui-fulldeps") + { + rustc.arg("-Zwrite-long-types-to-disk=no"); + } // FIXME: use this for other modes too, for perf? rustc.arg("-Cstrip=debuginfo"); } @@ -2459,6 +2483,14 @@ impl<'test> TestCx<'test> { rustc.args(&["-A", "unused"]); } + // #[cfg(not(bootstrap)] unconditionally pass flag after beta bump + // since `ui-fulldeps --stage=1` builds using the stage 0 compiler, + // which doesn't have this lint. + if !(self.config.stage_id.starts_with("stage1-") && self.config.suite == "ui-fulldeps") { + // Allow tests to use internal features. + rustc.args(&["-A", "internal_features"]); + } + if self.props.force_host { self.maybe_add_external_args(&mut rustc, &self.config.host_rustcflags); if !is_rustdoc { @@ -2516,7 +2548,7 @@ impl<'test> TestCx<'test> { // If this is emscripten, then run tests under nodejs if self.config.target.contains("emscripten") { if let Some(ref p) = self.config.nodejs { - args.push(p.clone()); + args.push(p.into()); } else { self.fatal("emscripten target requested and no NodeJS binary found (--nodejs)"); } @@ -2524,7 +2556,7 @@ impl<'test> TestCx<'test> { // shim } else if self.config.target.contains("wasm32") { if let Some(ref p) = self.config.nodejs { - args.push(p.clone()); + args.push(p.into()); } else { self.fatal("wasm32 target requested and no NodeJS binary found (--nodejs)"); } @@ -2536,13 +2568,12 @@ impl<'test> TestCx<'test> { .unwrap() // chop off `ui` .parent() .unwrap(); // chop off `tests` - args.push(src.join("src/etc/wasm32-shim.js").display().to_string()); + args.push(src.join("src/etc/wasm32-shim.js").into_os_string()); } let exe_file = self.make_exe_name(); - // FIXME (#9639): This needs to handle non-utf8 paths - args.push(exe_file.to_str().unwrap().to_owned()); + args.push(exe_file.into_os_string()); // Add the arguments in the run_flags directive args.extend(self.split_maybe_args(&self.props.run_flags)); @@ -2551,12 +2582,16 @@ impl<'test> TestCx<'test> { ProcArgs { prog, args } } - fn split_maybe_args(&self, argstr: &Option<String>) -> Vec<String> { + fn split_maybe_args(&self, argstr: &Option<String>) -> Vec<OsString> { match *argstr { Some(ref s) => s .split(' ') .filter_map(|s| { - if s.chars().all(|c| c.is_whitespace()) { None } else { Some(s.to_owned()) } + if s.chars().all(|c| c.is_whitespace()) { + None + } else { + Some(OsString::from(s)) + } }) .collect(), None => Vec::new(), @@ -2760,6 +2795,10 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec("compilation failed!", &proc_res); } + if let Some(PassMode::Build) = self.pass_mode() { + return; + } + let output_path = self.output_base_name().with_extension("ll"); let proc_res = self.verify_with_filecheck(&output_path); if !proc_res.status.success() { @@ -4153,8 +4192,8 @@ impl<'test> TestCx<'test> { # Match paths that don't include spaces. (?:\\[\pL\pN\.\-_']+)+\.\pL+ | - # If the path starts with a well-known root, then allow spaces. - \$(?:DIR|SRC_DIR|TEST_BUILD_DIR|BUILD_DIR|LIB_DIR)(?:\\[\pL\pN\.\-_' ]+)+ + # If the path starts with a well-known root, then allow spaces and no file extension. + \$(?:DIR|SRC_DIR|TEST_BUILD_DIR|BUILD_DIR|LIB_DIR)(?:\\[\pL\pN\.\-_'\ ]+)+ )"#, ) .unwrap() @@ -4359,8 +4398,8 @@ impl<'test> TestCx<'test> { } struct ProcArgs { - prog: String, - args: Vec<String>, + prog: OsString, + args: Vec<OsString>, } pub struct ProcRes { diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs index 379ff0bab..eebe5f358 100644 --- a/src/tools/compiletest/src/runtest/debugger.rs +++ b/src/tools/compiletest/src/runtest/debugger.rs @@ -2,18 +2,25 @@ use crate::common::Config; use crate::header::line_directive; use crate::runtest::ProcRes; +use std::fmt::Write; use std::fs::File; use std::io::{BufRead, BufReader}; -use std::path::Path; +use std::path::{Path, PathBuf}; +/// Representation of information to invoke a debugger and check its output pub(super) struct DebuggerCommands { + /// Commands for the debuuger pub commands: Vec<String>, - pub check_lines: Vec<String>, + /// Lines to insert breakpoints at pub breakpoint_lines: Vec<usize>, + /// Contains the source line number to check and the line itself + check_lines: Vec<(usize, String)>, + /// Source file name + file: PathBuf, } impl DebuggerCommands { - pub(super) fn parse_from( + pub fn parse_from( file: &Path, config: &Config, debugger_prefixes: &[&str], @@ -21,7 +28,7 @@ impl DebuggerCommands { ) -> Result<Self, String> { let directives = debugger_prefixes .iter() - .map(|prefix| (format!("{}-command", prefix), format!("{}-check", prefix))) + .map(|prefix| (format!("{prefix}-command"), format!("{prefix}-check"))) .collect::<Vec<_>>(); let mut breakpoint_lines = vec![]; @@ -29,63 +36,88 @@ impl DebuggerCommands { let mut check_lines = vec![]; let mut counter = 0; let reader = BufReader::new(File::open(file).unwrap()); - for line in reader.lines() { + for (line_no, line) in reader.lines().enumerate() { counter += 1; - match line { - Ok(line) => { - let (lnrev, line) = line_directive("//", &line).unwrap_or((None, &line)); - - // Skip any revision specific directive that doesn't match the current - // revision being tested - if lnrev.is_some() && lnrev != rev { - continue; - } - - if line.contains("#break") { - breakpoint_lines.push(counter); - } - - for &(ref command_directive, ref check_directive) in &directives { - config - .parse_name_value_directive(&line, command_directive) - .map(|cmd| commands.push(cmd)); - - config - .parse_name_value_directive(&line, check_directive) - .map(|cmd| check_lines.push(cmd)); - } - } - Err(e) => return Err(format!("Error while parsing debugger commands: {}", e)), + let line = line.map_err(|e| format!("Error while parsing debugger commands: {}", e))?; + let (lnrev, line) = line_directive("//", &line).unwrap_or((None, &line)); + + // Skip any revision specific directive that doesn't match the current + // revision being tested + if lnrev.is_some() && lnrev != rev { + continue; + } + + if line.contains("#break") { + breakpoint_lines.push(counter); + } + + for &(ref command_directive, ref check_directive) in &directives { + config + .parse_name_value_directive(&line, command_directive) + .map(|cmd| commands.push(cmd)); + + config + .parse_name_value_directive(&line, check_directive) + .map(|cmd| check_lines.push((line_no, cmd))); } } - Ok(Self { commands, check_lines, breakpoint_lines }) + Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_owned() }) } -} -pub(super) fn check_debugger_output( - debugger_run_result: &ProcRes, - check_lines: &[String], -) -> Result<(), String> { - let num_check_lines = check_lines.len(); - - let mut check_line_index = 0; - for line in debugger_run_result.stdout.lines() { - if check_line_index >= num_check_lines { - break; + /// Given debugger output and lines to check, ensure that every line is + /// contained in the debugger output. The check lines need to be found in + /// order, but there can be extra lines between. + pub fn check_output(&self, debugger_run_result: &ProcRes) -> Result<(), String> { + // (src_lineno, ck_line) that we did find + let mut found = vec![]; + // (src_lineno, ck_line) that we couldn't find + let mut missing = vec![]; + // We can find our any current match anywhere after our last match + let mut last_idx = 0; + let dbg_lines: Vec<&str> = debugger_run_result.stdout.lines().collect(); + + for (src_lineno, ck_line) in &self.check_lines { + if let Some(offset) = dbg_lines + .iter() + .skip(last_idx) + .position(|out_line| check_single_line(out_line, &ck_line)) + { + last_idx += offset; + found.push((src_lineno, dbg_lines[last_idx])); + } else { + missing.push((src_lineno, ck_line)); + } } - if check_single_line(line, &(check_lines[check_line_index])[..]) { - check_line_index += 1; + if missing.is_empty() { + Ok(()) + } else { + let fname = self.file.file_name().unwrap().to_string_lossy(); + let mut msg = format!( + "check directive(s) from `{}` not found in debugger output. errors:", + self.file.display() + ); + + for (src_lineno, err_line) in missing { + write!(msg, "\n ({fname}:{num}) `{err_line}`", num = src_lineno + 1).unwrap(); + } + + if !found.is_empty() { + let init = "\nthe following subset of check directive(s) was found successfully:"; + msg.push_str(init); + for (src_lineno, found_line) in found { + write!(msg, "\n ({fname}:{num}) `{found_line}`", num = src_lineno + 1) + .unwrap(); + } + } + + Err(msg) } } - if check_line_index != num_check_lines && num_check_lines > 0 { - Err(format!("line not found in debugger output: {}", check_lines[check_line_index])) - } else { - Ok(()) - } } +/// Check that the pattern in `check_line` applies to `line`. Returns `true` if they do match. fn check_single_line(line: &str, check_line: &str) -> bool { // Allow check lines to leave parts unspecified (e.g., uninitialized // bits in the wrong case of an enum) with the notation "[...]". @@ -101,21 +133,19 @@ fn check_single_line(line: &str, check_line: &str) -> bool { } let (mut rest, first_fragment) = if can_start_anywhere { - match line.find(check_fragments[0]) { - Some(pos) => (&line[pos + check_fragments[0].len()..], 1), - None => return false, - } + let Some(pos) = line.find(check_fragments[0]) else { + return false; + }; + (&line[pos + check_fragments[0].len()..], 1) } else { (line, 0) }; for current_fragment in &check_fragments[first_fragment..] { - match rest.find(current_fragment) { - Some(pos) => { - rest = &rest[pos + current_fragment.len()..]; - } - None => return false, - } + let Some(pos) = rest.find(current_fragment) else { + return false; + }; + rest = &rest[pos + current_fragment.len()..]; } if !can_end_anywhere && !rest.is_empty() { false } else { true } diff --git a/src/tools/compiletest/src/runtest/tests.rs b/src/tools/compiletest/src/runtest/tests.rs index 511051111..fb3dd326a 100644 --- a/src/tools/compiletest/src/runtest/tests.rs +++ b/src/tools/compiletest/src/runtest/tests.rs @@ -8,8 +8,8 @@ fn normalize_platform_differences() { "$BUILD_DIR/../parser.rs" ); assert_eq!( - TestCx::normalize_platform_differences(r"$DIR\bar.rs hello\nworld"), - r"$DIR/bar.rs hello\nworld" + TestCx::normalize_platform_differences(r"$DIR\bar.rs: hello\nworld"), + r"$DIR/bar.rs: hello\nworld" ); assert_eq!( TestCx::normalize_platform_differences(r"either bar\baz.rs or bar\baz\mod.rs"), @@ -27,8 +27,8 @@ fn normalize_platform_differences() { ); assert_eq!(TestCx::normalize_platform_differences(r"$DIR\foo.rs:12:11"), "$DIR/foo.rs:12:11",); assert_eq!( - TestCx::normalize_platform_differences(r"$DIR\path with spaces 'n' quotes"), - "$DIR/path with spaces 'n' quotes", + TestCx::normalize_platform_differences(r"$DIR\path with\spaces 'n' quotes"), + "$DIR/path with/spaces 'n' quotes", ); assert_eq!( TestCx::normalize_platform_differences(r"$DIR\file_with\no_extension"), |