• 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 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