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