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