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