| 
 | 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