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 use crate::common::eq::SpanlessEq;
13 use quote::quote;
14 use rayon::iter::{IntoParallelIterator, ParallelIterator};
15 use rustc_ast::ast::{
16     AngleBracketedArg, AngleBracketedArgs, Crate, GenericArg, GenericParamKind, Generics,
17     WhereClause,
18 };
19 use rustc_ast::mut_visit::{self, MutVisitor};
20 use rustc_errors::PResult;
21 use rustc_session::parse::ParseSess;
22 use rustc_span::source_map::FilePathMapping;
23 use rustc_span::FileName;
24 use std::fs;
25 use std::panic;
26 use std::path::Path;
27 use std::process;
28 use std::sync::atomic::{AtomicUsize, Ordering};
29 use std::time::Instant;
30 use walkdir::{DirEntry, WalkDir};
31 
32 #[macro_use]
33 mod macros;
34 
35 #[allow(dead_code)]
36 mod common;
37 
38 mod repo;
39 
40 #[test]
test_round_trip()41 fn test_round_trip() {
42     common::rayon_init();
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                 test(path, &failed, abort_after);
62             }
63         });
64 
65     let failed = failed.load(Ordering::SeqCst);
66     if failed > 0 {
67         panic!("{} failures", failed);
68     }
69 }
70 
test(path: &Path, failed: &AtomicUsize, abort_after: usize)71 fn test(path: &Path, failed: &AtomicUsize, abort_after: usize) {
72     let content = fs::read_to_string(path).unwrap();
73 
74     let start = Instant::now();
75     let (krate, elapsed) = match syn::parse_file(&content) {
76         Ok(krate) => (krate, start.elapsed()),
77         Err(msg) => {
78             errorf!("=== {}: syn failed to parse\n{:?}\n", path.display(), msg);
79             let prev_failed = failed.fetch_add(1, Ordering::SeqCst);
80             if prev_failed + 1 >= abort_after {
81                 process::exit(1);
82             }
83             return;
84         }
85     };
86     let back = quote!(#krate).to_string();
87     let edition = repo::edition(path).parse().unwrap();
88 
89     rustc_span::create_session_if_not_set_then(edition, |_| {
90         let equal = match panic::catch_unwind(|| {
91             let sess = ParseSess::new(FilePathMapping::empty());
92             let before = match librustc_parse(content, &sess) {
93                 Ok(before) => before,
94                 Err(mut diagnostic) => {
95                     diagnostic.cancel();
96                     if diagnostic
97                         .message()
98                         .starts_with("file not found for module")
99                     {
100                         errorf!("=== {}: ignore\n", path.display());
101                     } else {
102                         errorf!(
103                             "=== {}: ignore - librustc failed to parse original content: {}\n",
104                             path.display(),
105                             diagnostic.message(),
106                         );
107                     }
108                     return Err(true);
109                 }
110             };
111             let after = match librustc_parse(back, &sess) {
112                 Ok(after) => after,
113                 Err(mut diagnostic) => {
114                     errorf!("=== {}: librustc failed to parse", path.display());
115                     diagnostic.emit();
116                     return Err(false);
117                 }
118             };
119             Ok((before, after))
120         }) {
121             Err(_) => {
122                 errorf!("=== {}: ignoring librustc panic\n", path.display());
123                 true
124             }
125             Ok(Err(equal)) => equal,
126             Ok(Ok((mut before, mut after))) => {
127                 normalize(&mut before);
128                 normalize(&mut after);
129                 if SpanlessEq::eq(&before, &after) {
130                     errorf!(
131                         "=== {}: pass in {}ms\n",
132                         path.display(),
133                         elapsed.as_secs() * 1000 + u64::from(elapsed.subsec_nanos()) / 1_000_000
134                     );
135                     true
136                 } else {
137                     errorf!(
138                         "=== {}: FAIL\nbefore: {:#?}\nafter: {:#?}\n",
139                         path.display(),
140                         before,
141                         after,
142                     );
143                     false
144                 }
145             }
146         };
147         if !equal {
148             let prev_failed = failed.fetch_add(1, Ordering::SeqCst);
149             if prev_failed + 1 >= abort_after {
150                 process::exit(1);
151             }
152         }
153     });
154 }
155 
librustc_parse(content: String, sess: &ParseSess) -> PResult<Crate>156 fn librustc_parse(content: String, sess: &ParseSess) -> PResult<Crate> {
157     static COUNTER: AtomicUsize = AtomicUsize::new(0);
158     let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
159     let name = FileName::Custom(format!("test_round_trip{}", counter));
160     parse::parse_crate_from_source_str(name, content, sess)
161 }
162 
normalize(krate: &mut Crate)163 fn normalize(krate: &mut Crate) {
164     struct NormalizeVisitor;
165 
166     impl MutVisitor for NormalizeVisitor {
167         fn visit_angle_bracketed_parameter_data(&mut self, e: &mut AngleBracketedArgs) {
168             #[derive(Ord, PartialOrd, Eq, PartialEq)]
169             enum Group {
170                 Lifetimes,
171                 TypesAndConsts,
172                 Constraints,
173             }
174             e.args.sort_by_key(|arg| match arg {
175                 AngleBracketedArg::Arg(arg) => match arg {
176                     GenericArg::Lifetime(_) => Group::Lifetimes,
177                     GenericArg::Type(_) | GenericArg::Const(_) => Group::TypesAndConsts,
178                 },
179                 AngleBracketedArg::Constraint(_) => Group::Constraints,
180             });
181             mut_visit::noop_visit_angle_bracketed_parameter_data(e, self);
182         }
183 
184         fn visit_generics(&mut self, e: &mut Generics) {
185             #[derive(Ord, PartialOrd, Eq, PartialEq)]
186             enum Group {
187                 Lifetimes,
188                 TypesAndConsts,
189             }
190             e.params.sort_by_key(|param| match param.kind {
191                 GenericParamKind::Lifetime => Group::Lifetimes,
192                 GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
193                     Group::TypesAndConsts
194                 }
195             });
196             mut_visit::noop_visit_generics(e, self);
197         }
198 
199         fn visit_where_clause(&mut self, e: &mut WhereClause) {
200             if e.predicates.is_empty() {
201                 e.has_where_token = false;
202             }
203         }
204     }
205 
206     NormalizeVisitor.visit_crate(krate);
207 }
208