1 #![cfg(not(syn_disable_nightly_tests))] 2 #![recursion_limit = "1024"] 3 #![feature(rustc_private)] 4 5 extern crate rustc_ast; 6 extern crate rustc_errors; 7 extern crate rustc_expand; 8 extern crate rustc_parse as parse; 9 extern crate rustc_session; 10 extern crate rustc_span; 11 12 mod features; 13 14 use quote::quote; 15 use rayon::iter::{IntoParallelIterator, ParallelIterator}; 16 use rustc_ast::ast; 17 use rustc_errors::PResult; 18 use rustc_session::parse::ParseSess; 19 use rustc_span::edition::Edition; 20 use rustc_span::source_map::FilePathMapping; 21 use rustc_span::FileName; 22 use walkdir::{DirEntry, WalkDir}; 23 24 use std::fs::File; 25 use std::io::Read; 26 use std::panic; 27 use std::process; 28 use std::sync::atomic::{AtomicUsize, Ordering}; 29 use std::time::Instant; 30 31 #[macro_use] 32 mod macros; 33 34 #[allow(dead_code)] 35 mod common; 36 37 mod repo; 38 39 use common::eq::SpanlessEq; 40 41 #[test] 42 fn test_round_trip() { 43 repo::clone_rust(); 44 let abort_after = common::abort_after(); 45 if abort_after == 0 { 46 panic!("Skipping all round_trip tests"); 47 } 48 49 let failed = AtomicUsize::new(0); 50 51 WalkDir::new("tests/rust") 52 .sort_by(|a, b| a.file_name().cmp(b.file_name())) 53 .into_iter() 54 .filter_entry(repo::base_dir_filter) 55 .collect::<Result<Vec<DirEntry>, walkdir::Error>>() 56 .unwrap() 57 .into_par_iter() 58 .for_each(|entry| { 59 let path = entry.path(); 60 if path.is_dir() { 61 return; 62 } 63 64 let mut file = File::open(path).unwrap(); 65 let mut content = String::new(); 66 file.read_to_string(&mut content).unwrap(); 67 68 let start = Instant::now(); 69 let (krate, elapsed) = match syn::parse_file(&content) { 70 Ok(krate) => (krate, start.elapsed()), 71 Err(msg) => { 72 errorf!("=== {}: syn failed to parse\n{:?}\n", path.display(), msg); 73 let prev_failed = failed.fetch_add(1, Ordering::SeqCst); 74 if prev_failed + 1 >= abort_after { 75 process::exit(1); 76 } 77 return; 78 } 79 }; 80 let back = quote!(#krate).to_string(); 81 82 let equal = panic::catch_unwind(|| { 83 rustc_ast::with_globals(Edition::Edition2018, || { 84 let sess = ParseSess::new(FilePathMapping::empty()); 85 let before = match libsyntax_parse(content, &sess) { 86 Ok(before) => before, 87 Err(mut diagnostic) => { 88 diagnostic.cancel(); 89 if diagnostic 90 .message() 91 .starts_with("file not found for module") 92 { 93 errorf!("=== {}: ignore\n", path.display()); 94 } else { 95 errorf!( 96 "=== {}: ignore - libsyntax failed to parse original content: {}\n", 97 path.display(), 98 diagnostic.message() 99 ); 100 } 101 return true; 102 } 103 }; 104 let after = match libsyntax_parse(back, &sess) { 105 Ok(after) => after, 106 Err(mut diagnostic) => { 107 errorf!("=== {}: libsyntax failed to parse", path.display()); 108 diagnostic.emit(); 109 return false; 110 } 111 }; 112 113 if SpanlessEq::eq(&before, &after) { 114 errorf!( 115 "=== {}: pass in {}ms\n", 116 path.display(), 117 elapsed.as_secs() * 1000 118 + u64::from(elapsed.subsec_nanos()) / 1_000_000 119 ); 120 true 121 } else { 122 errorf!( 123 "=== {}: FAIL\nbefore: {:#?}\nafter: {:#?}\n", 124 path.display(), 125 before, 126 after, 127 ); 128 false 129 } 130 }) 131 }); 132 match equal { 133 Err(_) => errorf!("=== {}: ignoring libsyntax panic\n", path.display()), 134 Ok(true) => {} 135 Ok(false) => { 136 let prev_failed = failed.fetch_add(1, Ordering::SeqCst); 137 if prev_failed + 1 >= abort_after { 138 process::exit(1); 139 } 140 } 141 } 142 }); 143 144 let failed = failed.load(Ordering::SeqCst); 145 if failed > 0 { 146 panic!("{} failures", failed); 147 } 148 } 149 150 fn libsyntax_parse(content: String, sess: &ParseSess) -> PResult<ast::Crate> { 151 let name = FileName::Custom("test_round_trip".to_string()); 152 parse::parse_crate_from_source_str(name, content, sess) 153 } 154