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