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