compiletest/runtest/
codegen_units.rs1use std::collections::HashSet;
2
3use super::{Emit, TestCx, WillExecute};
4use crate::util::static_regex;
5use crate::{errors, fatal};
6
7impl TestCx<'_> {
8 pub(super) fn run_codegen_units_test(&self) {
9 assert!(self.revision.is_none(), "revisions not relevant here");
10
11 let proc_res = self.compile_test(WillExecute::No, Emit::None);
12
13 if !proc_res.status.success() {
14 self.fatal_proc_rec("compilation failed!", &proc_res);
15 }
16
17 self.check_no_compiler_crash(&proc_res, self.props.should_ice);
18
19 const PREFIX: &str = "MONO_ITEM ";
20 const CGU_MARKER: &str = "@@";
21
22 let actual: Vec<MonoItem> = proc_res
26 .stdout
27 .lines()
28 .filter(|line| line.starts_with(PREFIX))
29 .map(|line| line.replace(&self.testpaths.file.as_str(), "TEST_PATH").to_string())
30 .map(|line| str_to_mono_item(&line, true))
31 .collect();
32
33 let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None)
34 .iter()
35 .map(|e| str_to_mono_item(&e.msg[..], false))
36 .collect();
37
38 let mut missing = Vec::new();
39 let mut wrong_cgus = Vec::new();
40
41 for expected_item in &expected {
42 let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name);
43
44 if let Some(actual_item) = actual_item_with_same_name {
45 if !expected_item.codegen_units.is_empty() &&
46 expected_item.codegen_units != actual_item.codegen_units
48 {
49 wrong_cgus.push((expected_item.clone(), actual_item.clone()));
50 }
51 } else {
52 missing.push(expected_item.string.clone());
53 }
54 }
55
56 let unexpected: Vec<_> = actual
57 .iter()
58 .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
59 .map(|acgu| acgu.string.clone())
60 .collect();
61
62 if !missing.is_empty() {
63 missing.sort();
64
65 writeln!(self.stdout, "\nThese items should have been contained but were not:\n");
66
67 for item in &missing {
68 writeln!(self.stdout, "{}", item);
69 }
70
71 writeln!(self.stdout, "\n");
72 }
73
74 if !unexpected.is_empty() {
75 let sorted = {
76 let mut sorted = unexpected.clone();
77 sorted.sort();
78 sorted
79 };
80
81 writeln!(self.stdout, "\nThese items were contained but should not have been:\n");
82
83 for item in sorted {
84 writeln!(self.stdout, "{}", item);
85 }
86
87 writeln!(self.stdout, "\n");
88 }
89
90 if !wrong_cgus.is_empty() {
91 wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
92 writeln!(self.stdout, "\nThe following items were assigned to wrong codegen units:\n");
93
94 for &(ref expected_item, ref actual_item) in &wrong_cgus {
95 writeln!(self.stdout, "{}", expected_item.name);
96 writeln!(
97 self.stdout,
98 " expected: {}",
99 codegen_units_to_str(&expected_item.codegen_units)
100 );
101 writeln!(
102 self.stdout,
103 " actual: {}",
104 codegen_units_to_str(&actual_item.codegen_units)
105 );
106 writeln!(self.stdout);
107 }
108 }
109
110 if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) {
111 fatal!("!(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty())");
112 }
113
114 #[derive(Clone, Eq, PartialEq)]
115 struct MonoItem {
116 name: String,
117 codegen_units: HashSet<String>,
118 string: String,
119 }
120
121 fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem {
123 let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() };
124
125 let full_string = format!("{}{}", PREFIX, s);
126
127 let parts: Vec<&str> =
128 s.split(CGU_MARKER).map(str::trim).filter(|s| !s.is_empty()).collect();
129
130 let name = parts[0].trim();
131
132 let cgus = if parts.len() > 1 {
133 let cgus_str = parts[1];
134
135 cgus_str
136 .split(' ')
137 .map(str::trim)
138 .filter(|s| !s.is_empty())
139 .map(|s| {
140 if cgu_has_crate_disambiguator {
141 remove_crate_disambiguators_from_set_of_cgu_names(s)
142 } else {
143 s.to_string()
144 }
145 })
146 .collect()
147 } else {
148 HashSet::new()
149 };
150
151 MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string }
152 }
153
154 fn codegen_units_to_str(cgus: &HashSet<String>) -> String {
155 let mut cgus: Vec<_> = cgus.iter().collect();
156 cgus.sort();
157
158 let mut string = String::new();
159 for cgu in cgus {
160 string.push_str(&cgu[..]);
161 string.push(' ');
162 }
163
164 string
165 }
166
167 fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String {
171 let Some(captures) =
172 static_regex!(r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?")
173 .captures(cgu)
174 else {
175 panic!("invalid cgu name encountered: {cgu}");
176 };
177
178 let mut new_name = cgu.to_owned();
179
180 if let Some(d2) = captures.name("d2") {
181 new_name.replace_range(d2.start()..d2.end(), "");
182 }
183
184 let d1 = captures.name("d1").unwrap();
185 new_name.replace_range(d1.start()..d1.end(), "");
186
187 new_name
188 }
189
190 fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String {
194 cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::<Vec<_>>().join("--")
195 }
196 }
197}