]> git.proxmox.com Git - rustc.git/blob - src/tools/compiletest/src/runtest/debugger.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / src / tools / compiletest / src / runtest / debugger.rs
1 use crate::common::Config;
2 use crate::header::line_directive;
3 use crate::runtest::ProcRes;
4
5 use std::fmt::Write;
6 use std::fs::File;
7 use std::io::{BufRead, BufReader};
8 use std::path::{Path, PathBuf};
9
10 /// Representation of information to invoke a debugger and check its output
11 pub(super) struct DebuggerCommands {
12 /// Commands for the debuuger
13 pub commands: Vec<String>,
14 /// Lines to insert breakpoints at
15 pub breakpoint_lines: Vec<usize>,
16 /// Contains the source line number to check and the line itself
17 check_lines: Vec<(usize, String)>,
18 /// Source file name
19 file: PathBuf,
20 }
21
22 impl DebuggerCommands {
23 pub fn parse_from(
24 file: &Path,
25 config: &Config,
26 debugger_prefixes: &[&str],
27 rev: Option<&str>,
28 ) -> Result<Self, String> {
29 let directives = debugger_prefixes
30 .iter()
31 .map(|prefix| (format!("{prefix}-command"), format!("{prefix}-check")))
32 .collect::<Vec<_>>();
33
34 let mut breakpoint_lines = vec![];
35 let mut commands = vec![];
36 let mut check_lines = vec![];
37 let mut counter = 0;
38 let reader = BufReader::new(File::open(file).unwrap());
39 for (line_no, line) in reader.lines().enumerate() {
40 counter += 1;
41 let line = line.map_err(|e| format!("Error while parsing debugger commands: {}", e))?;
42 let (lnrev, line) = line_directive("//", &line).unwrap_or((None, &line));
43
44 // Skip any revision specific directive that doesn't match the current
45 // revision being tested
46 if lnrev.is_some() && lnrev != rev {
47 continue;
48 }
49
50 if line.contains("#break") {
51 breakpoint_lines.push(counter);
52 }
53
54 for &(ref command_directive, ref check_directive) in &directives {
55 config
56 .parse_name_value_directive(&line, command_directive)
57 .map(|cmd| commands.push(cmd));
58
59 config
60 .parse_name_value_directive(&line, check_directive)
61 .map(|cmd| check_lines.push((line_no, cmd)));
62 }
63 }
64
65 Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_owned() })
66 }
67
68 /// Given debugger output and lines to check, ensure that every line is
69 /// contained in the debugger output. The check lines need to be found in
70 /// order, but there can be extra lines between.
71 pub fn check_output(&self, debugger_run_result: &ProcRes) -> Result<(), String> {
72 // (src_lineno, ck_line) that we did find
73 let mut found = vec![];
74 // (src_lineno, ck_line) that we couldn't find
75 let mut missing = vec![];
76 // We can find our any current match anywhere after our last match
77 let mut last_idx = 0;
78 let dbg_lines: Vec<&str> = debugger_run_result.stdout.lines().collect();
79
80 for (src_lineno, ck_line) in &self.check_lines {
81 if let Some(offset) = dbg_lines
82 .iter()
83 .skip(last_idx)
84 .position(|out_line| check_single_line(out_line, &ck_line))
85 {
86 last_idx += offset;
87 found.push((src_lineno, dbg_lines[last_idx]));
88 } else {
89 missing.push((src_lineno, ck_line));
90 }
91 }
92
93 if missing.is_empty() {
94 Ok(())
95 } else {
96 let fname = self.file.file_name().unwrap().to_string_lossy();
97 let mut msg = format!(
98 "check directive(s) from `{}` not found in debugger output. errors:",
99 self.file.display()
100 );
101
102 for (src_lineno, err_line) in missing {
103 write!(msg, "\n ({fname}:{num}) `{err_line}`", num = src_lineno + 1).unwrap();
104 }
105
106 if !found.is_empty() {
107 let init = "\nthe following subset of check directive(s) was found successfully:";
108 msg.push_str(init);
109 for (src_lineno, found_line) in found {
110 write!(msg, "\n ({fname}:{num}) `{found_line}`", num = src_lineno + 1)
111 .unwrap();
112 }
113 }
114
115 Err(msg)
116 }
117 }
118 }
119
120 /// Check that the pattern in `check_line` applies to `line`. Returns `true` if they do match.
121 fn check_single_line(line: &str, check_line: &str) -> bool {
122 // Allow check lines to leave parts unspecified (e.g., uninitialized
123 // bits in the wrong case of an enum) with the notation "[...]".
124 let line = line.trim();
125 let check_line = check_line.trim();
126 let can_start_anywhere = check_line.starts_with("[...]");
127 let can_end_anywhere = check_line.ends_with("[...]");
128
129 let check_fragments: Vec<&str> =
130 check_line.split("[...]").filter(|frag| !frag.is_empty()).collect();
131 if check_fragments.is_empty() {
132 return true;
133 }
134
135 let (mut rest, first_fragment) = if can_start_anywhere {
136 let Some(pos) = line.find(check_fragments[0]) else {
137 return false;
138 };
139 (&line[pos + check_fragments[0].len()..], 1)
140 } else {
141 (line, 0)
142 };
143
144 for current_fragment in &check_fragments[first_fragment..] {
145 let Some(pos) = rest.find(current_fragment) else {
146 return false;
147 };
148 rest = &rest[pos + current_fragment.len()..];
149 }
150
151 if !can_end_anywhere && !rest.is_empty() { false } else { true }
152 }