|
| 1 | +use std::collections::HashSet; |
| 2 | + |
| 3 | +use super::{Emit, TestCx, WillExecute}; |
| 4 | +use crate::errors; |
| 5 | +use crate::util::static_regex; |
| 6 | + |
| 7 | +impl 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| { |
| 30 | + line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string() |
| 31 | + }) |
| 32 | + .map(|line| str_to_mono_item(&line, true)) |
| 33 | + .collect(); |
| 34 | + |
| 35 | + let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None) |
| 36 | + .iter() |
| 37 | + .map(|e| str_to_mono_item(&e.msg[..], false)) |
| 38 | + .collect(); |
| 39 | + |
| 40 | + let mut missing = Vec::new(); |
| 41 | + let mut wrong_cgus = Vec::new(); |
| 42 | + |
| 43 | + for expected_item in &expected { |
| 44 | + let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name); |
| 45 | + |
| 46 | + if let Some(actual_item) = actual_item_with_same_name { |
| 47 | + if !expected_item.codegen_units.is_empty() && |
| 48 | + // Also check for codegen units |
| 49 | + expected_item.codegen_units != actual_item.codegen_units |
| 50 | + { |
| 51 | + wrong_cgus.push((expected_item.clone(), actual_item.clone())); |
| 52 | + } |
| 53 | + } else { |
| 54 | + missing.push(expected_item.string.clone()); |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + let unexpected: Vec<_> = actual |
| 59 | + .iter() |
| 60 | + .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name)) |
| 61 | + .map(|acgu| acgu.string.clone()) |
| 62 | + .collect(); |
| 63 | + |
| 64 | + if !missing.is_empty() { |
| 65 | + missing.sort(); |
| 66 | + |
| 67 | + println!("\nThese items should have been contained but were not:\n"); |
| 68 | + |
| 69 | + for item in &missing { |
| 70 | + println!("{}", item); |
| 71 | + } |
| 72 | + |
| 73 | + println!("\n"); |
| 74 | + } |
| 75 | + |
| 76 | + if !unexpected.is_empty() { |
| 77 | + let sorted = { |
| 78 | + let mut sorted = unexpected.clone(); |
| 79 | + sorted.sort(); |
| 80 | + sorted |
| 81 | + }; |
| 82 | + |
| 83 | + println!("\nThese items were contained but should not have been:\n"); |
| 84 | + |
| 85 | + for item in sorted { |
| 86 | + println!("{}", item); |
| 87 | + } |
| 88 | + |
| 89 | + println!("\n"); |
| 90 | + } |
| 91 | + |
| 92 | + if !wrong_cgus.is_empty() { |
| 93 | + wrong_cgus.sort_by_key(|pair| pair.0.name.clone()); |
| 94 | + println!("\nThe following items were assigned to wrong codegen units:\n"); |
| 95 | + |
| 96 | + for &(ref expected_item, ref actual_item) in &wrong_cgus { |
| 97 | + println!("{}", expected_item.name); |
| 98 | + println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units)); |
| 99 | + println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units)); |
| 100 | + println!(); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) { |
| 105 | + panic!(); |
| 106 | + } |
| 107 | + |
| 108 | + #[derive(Clone, Eq, PartialEq)] |
| 109 | + struct MonoItem { |
| 110 | + name: String, |
| 111 | + codegen_units: HashSet<String>, |
| 112 | + string: String, |
| 113 | + } |
| 114 | + |
| 115 | + // [MONO_ITEM] name [@@ (cgu)+] |
| 116 | + fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem { |
| 117 | + let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() }; |
| 118 | + |
| 119 | + let full_string = format!("{}{}", PREFIX, s); |
| 120 | + |
| 121 | + let parts: Vec<&str> = |
| 122 | + s.split(CGU_MARKER).map(str::trim).filter(|s| !s.is_empty()).collect(); |
| 123 | + |
| 124 | + let name = parts[0].trim(); |
| 125 | + |
| 126 | + let cgus = if parts.len() > 1 { |
| 127 | + let cgus_str = parts[1]; |
| 128 | + |
| 129 | + cgus_str |
| 130 | + .split(' ') |
| 131 | + .map(str::trim) |
| 132 | + .filter(|s| !s.is_empty()) |
| 133 | + .map(|s| { |
| 134 | + if cgu_has_crate_disambiguator { |
| 135 | + remove_crate_disambiguators_from_set_of_cgu_names(s) |
| 136 | + } else { |
| 137 | + s.to_string() |
| 138 | + } |
| 139 | + }) |
| 140 | + .collect() |
| 141 | + } else { |
| 142 | + HashSet::new() |
| 143 | + }; |
| 144 | + |
| 145 | + MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string } |
| 146 | + } |
| 147 | + |
| 148 | + fn codegen_units_to_str(cgus: &HashSet<String>) -> String { |
| 149 | + let mut cgus: Vec<_> = cgus.iter().collect(); |
| 150 | + cgus.sort(); |
| 151 | + |
| 152 | + let mut string = String::new(); |
| 153 | + for cgu in cgus { |
| 154 | + string.push_str(&cgu[..]); |
| 155 | + string.push(' '); |
| 156 | + } |
| 157 | + |
| 158 | + string |
| 159 | + } |
| 160 | + |
| 161 | + // Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or |
| 162 | + // the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>, |
| 163 | + // remove all crate-disambiguators. |
| 164 | + fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String { |
| 165 | + let Some(captures) = |
| 166 | + static_regex!(r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?") |
| 167 | + .captures(cgu) |
| 168 | + else { |
| 169 | + panic!("invalid cgu name encountered: {cgu}"); |
| 170 | + }; |
| 171 | + |
| 172 | + let mut new_name = cgu.to_owned(); |
| 173 | + |
| 174 | + if let Some(d2) = captures.name("d2") { |
| 175 | + new_name.replace_range(d2.start()..d2.end(), ""); |
| 176 | + } |
| 177 | + |
| 178 | + let d1 = captures.name("d1").unwrap(); |
| 179 | + new_name.replace_range(d1.start()..d1.end(), ""); |
| 180 | + |
| 181 | + new_name |
| 182 | + } |
| 183 | + |
| 184 | + // The name of merged CGUs is constructed as the names of the original |
| 185 | + // CGUs joined with "--". This function splits such composite CGU names |
| 186 | + // and handles each component individually. |
| 187 | + fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String { |
| 188 | + cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::<Vec<_>>().join("--") |
| 189 | + } |
| 190 | + } |
| 191 | +} |
0 commit comments