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]
test_round_trip()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 
libsyntax_parse(content: String, sess: &ParseSess) -> PResult<ast::Crate>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