aboutsummaryrefslogtreecommitdiff
path: root/syn/tests/test_round_trip.rs
diff options
context:
space:
mode:
Diffstat (limited to 'syn/tests/test_round_trip.rs')
-rw-r--r--syn/tests/test_round_trip.rs151
1 files changed, 151 insertions, 0 deletions
diff --git a/syn/tests/test_round_trip.rs b/syn/tests/test_round_trip.rs
new file mode 100644
index 0000000..435bd6a
--- /dev/null
+++ b/syn/tests/test_round_trip.rs
@@ -0,0 +1,151 @@
+#![cfg(not(syn_disable_nightly_tests))]
+#![recursion_limit = "1024"]
+#![feature(rustc_private)]
+
+extern crate rustc_expand;
+extern crate rustc_parse as parse;
+extern crate rustc_span;
+extern crate syntax;
+
+mod features;
+
+use quote::quote;
+use rayon::iter::{IntoParallelIterator, ParallelIterator};
+use rustc_span::edition::Edition;
+use rustc_span::FileName;
+use syntax::ast;
+use syntax::errors::PResult;
+use syntax::sess::ParseSess;
+use syntax::source_map::FilePathMapping;
+use walkdir::{DirEntry, WalkDir};
+
+use std::fs::File;
+use std::io::Read;
+use std::panic;
+use std::process;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::time::Instant;
+
+#[macro_use]
+mod macros;
+
+#[allow(dead_code)]
+mod common;
+
+mod repo;
+
+use common::eq::SpanlessEq;
+
+#[test]
+fn test_round_trip() {
+ repo::clone_rust();
+ let abort_after = common::abort_after();
+ if abort_after == 0 {
+ panic!("Skipping all round_trip tests");
+ }
+
+ let failed = AtomicUsize::new(0);
+
+ WalkDir::new("tests/rust")
+ .sort_by(|a, b| a.file_name().cmp(b.file_name()))
+ .into_iter()
+ .filter_entry(repo::base_dir_filter)
+ .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
+ .unwrap()
+ .into_par_iter()
+ .for_each(|entry| {
+ let path = entry.path();
+ if path.is_dir() {
+ return;
+ }
+
+ let mut file = File::open(path).unwrap();
+ let mut content = String::new();
+ file.read_to_string(&mut content).unwrap();
+
+ let start = Instant::now();
+ let (krate, elapsed) = match syn::parse_file(&content) {
+ Ok(krate) => (krate, start.elapsed()),
+ Err(msg) => {
+ errorf!("=== {}: syn failed to parse\n{:?}\n", path.display(), msg);
+ let prev_failed = failed.fetch_add(1, Ordering::SeqCst);
+ if prev_failed + 1 >= abort_after {
+ process::exit(1);
+ }
+ return;
+ }
+ };
+ let back = quote!(#krate).to_string();
+
+ let equal = panic::catch_unwind(|| {
+ syntax::with_globals(Edition::Edition2018, || {
+ let sess = ParseSess::new(FilePathMapping::empty());
+ let before = match libsyntax_parse(content, &sess) {
+ Ok(before) => before,
+ Err(mut diagnostic) => {
+ diagnostic.cancel();
+ if diagnostic
+ .message()
+ .starts_with("file not found for module")
+ {
+ errorf!("=== {}: ignore\n", path.display());
+ } else {
+ errorf!(
+ "=== {}: ignore - libsyntax failed to parse original content: {}\n",
+ path.display(),
+ diagnostic.message()
+ );
+ }
+ return true;
+ }
+ };
+ let after = match libsyntax_parse(back, &sess) {
+ Ok(after) => after,
+ Err(mut diagnostic) => {
+ errorf!("=== {}: libsyntax failed to parse", path.display());
+ diagnostic.emit();
+ return false;
+ }
+ };
+
+ if SpanlessEq::eq(&before, &after) {
+ errorf!(
+ "=== {}: pass in {}ms\n",
+ path.display(),
+ elapsed.as_secs() * 1000
+ + u64::from(elapsed.subsec_nanos()) / 1_000_000
+ );
+ true
+ } else {
+ errorf!(
+ "=== {}: FAIL\nbefore: {:#?}\nafter: {:#?}\n",
+ path.display(),
+ before,
+ after,
+ );
+ false
+ }
+ })
+ });
+ match equal {
+ Err(_) => errorf!("=== {}: ignoring libsyntax panic\n", path.display()),
+ Ok(true) => {}
+ Ok(false) => {
+ let prev_failed = failed.fetch_add(1, Ordering::SeqCst);
+ if prev_failed + 1 >= abort_after {
+ process::exit(1);
+ }
+ }
+ }
+ });
+
+ let failed = failed.load(Ordering::SeqCst);
+ if failed > 0 {
+ panic!("{} failures", failed);
+ }
+}
+
+fn libsyntax_parse(content: String, sess: &ParseSess) -> PResult<ast::Crate> {
+ let name = FileName::Custom("test_round_trip".to_string());
+ parse::parse_crate_from_source_str(name, content, sess)
+}