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