compiletest/runtest/
codegen_units.rs

1use 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        // Some MonoItems can contain {closure@/path/to/checkout/tests/codgen-units/test.rs}
23        // To prevent the current dir from leaking, we just replace the entire path to the test
24        // file with TEST_PATH.
25        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                   // Also check for codegen units
47                   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        // [MONO_ITEM] name [@@ (cgu)+]
122        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        // Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or
168        // the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>,
169        // remove all crate-disambiguators.
170        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        // The name of merged CGUs is constructed as the names of the original
191        // CGUs joined with "--". This function splits such composite CGU names
192        // and handles each component individually.
193        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}