diff options
Diffstat (limited to 'vendor/syn/tests/test_round_trip.rs')
-rw-r--r-- | vendor/syn/tests/test_round_trip.rs | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/vendor/syn/tests/test_round_trip.rs b/vendor/syn/tests/test_round_trip.rs new file mode 100644 index 0000000..a673fff --- /dev/null +++ b/vendor/syn/tests/test_round_trip.rs @@ -0,0 +1,239 @@ +#![cfg(not(syn_disable_nightly_tests))] +#![cfg(not(miri))] +#![recursion_limit = "1024"] +#![feature(rustc_private)] +#![allow( + clippy::blocks_in_conditions, + clippy::manual_assert, + clippy::manual_let_else, + clippy::match_like_matches_macro, + clippy::uninlined_format_args +)] + +extern crate rustc_ast; +extern crate rustc_ast_pretty; +extern crate rustc_data_structures; +extern crate rustc_driver; +extern crate rustc_error_messages; +extern crate rustc_errors; +extern crate rustc_expand; +extern crate rustc_parse as parse; +extern crate rustc_session; +extern crate rustc_span; + +use crate::common::eq::SpanlessEq; +use quote::quote; +use rustc_ast::ast::{ + AngleBracketedArg, AngleBracketedArgs, Crate, GenericArg, GenericParamKind, Generics, + WhereClause, +}; +use rustc_ast::mut_visit::{self, MutVisitor}; +use rustc_ast_pretty::pprust; +use rustc_error_messages::{DiagnosticMessage, LazyFallbackBundle}; +use rustc_errors::{translation, Diagnostic, PResult}; +use rustc_session::parse::ParseSess; +use rustc_span::source_map::FilePathMapping; +use rustc_span::FileName; +use std::borrow::Cow; +use std::fs; +use std::panic; +use std::path::Path; +use std::process; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::Instant; + +#[macro_use] +mod macros; + +#[allow(dead_code)] +mod common; + +mod repo; + +#[test] +fn test_round_trip() { + common::rayon_init(); + repo::clone_rust(); + let abort_after = common::abort_after(); + if abort_after == 0 { + panic!("Skipping all round_trip tests"); + } + + let failed = AtomicUsize::new(0); + + repo::for_each_rust_file(|path| test(path, &failed, abort_after)); + + let failed = failed.load(Ordering::Relaxed); + if failed > 0 { + panic!("{} failures", failed); + } +} + +fn test(path: &Path, failed: &AtomicUsize, abort_after: usize) { + let content = fs::read_to_string(path).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::Relaxed); + if prev_failed + 1 >= abort_after { + process::exit(1); + } + return; + } + }; + let back = quote!(#krate).to_string(); + let edition = repo::edition(path).parse().unwrap(); + + rustc_span::create_session_if_not_set_then(edition, |_| { + let equal = match panic::catch_unwind(|| { + let locale_resources = rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(); + let file_path_mapping = FilePathMapping::empty(); + let sess = ParseSess::new(locale_resources, file_path_mapping); + let before = match librustc_parse(content, &sess) { + Ok(before) => before, + Err(diagnostic) => { + errorf!( + "=== {}: ignore - librustc failed to parse original content: {}\n", + path.display(), + translate_message(&diagnostic), + ); + diagnostic.cancel(); + return Err(true); + } + }; + let after = match librustc_parse(back, &sess) { + Ok(after) => after, + Err(mut diagnostic) => { + errorf!("=== {}: librustc failed to parse", path.display()); + diagnostic.emit(); + return Err(false); + } + }; + Ok((before, after)) + }) { + Err(_) => { + errorf!("=== {}: ignoring librustc panic\n", path.display()); + true + } + Ok(Err(equal)) => equal, + Ok(Ok((mut before, mut after))) => { + normalize(&mut before); + normalize(&mut after); + 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\n{}\n!=\n{}\n", + path.display(), + pprust::crate_to_string_for_macros(&before), + pprust::crate_to_string_for_macros(&after), + ); + false + } + } + }; + if !equal { + let prev_failed = failed.fetch_add(1, Ordering::Relaxed); + if prev_failed + 1 >= abort_after { + process::exit(1); + } + } + }); +} + +fn librustc_parse(content: String, sess: &ParseSess) -> PResult<Crate> { + static COUNTER: AtomicUsize = AtomicUsize::new(0); + let counter = COUNTER.fetch_add(1, Ordering::Relaxed); + let name = FileName::Custom(format!("test_round_trip{}", counter)); + parse::parse_crate_from_source_str(name, content, sess) +} + +fn translate_message(diagnostic: &Diagnostic) -> Cow<'static, str> { + thread_local! { + static FLUENT_BUNDLE: LazyFallbackBundle = { + let locale_resources = rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(); + let with_directionality_markers = false; + rustc_error_messages::fallback_fluent_bundle(locale_resources, with_directionality_markers) + }; + } + + let message = &diagnostic.messages[0].0; + let args = translation::to_fluent_args(diagnostic.args()); + + let (identifier, attr) = match message { + DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => return msg.clone(), + DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), + }; + + FLUENT_BUNDLE.with(|fluent_bundle| { + let message = fluent_bundle + .get_message(identifier) + .expect("missing diagnostic in fluent bundle"); + let value = match attr { + Some(attr) => message + .get_attribute(attr) + .expect("missing attribute in fluent message") + .value(), + None => message.value().expect("missing value in fluent message"), + }; + + let mut err = Vec::new(); + let translated = fluent_bundle.format_pattern(value, Some(&args), &mut err); + assert!(err.is_empty()); + Cow::Owned(translated.into_owned()) + }) +} + +fn normalize(krate: &mut Crate) { + struct NormalizeVisitor; + + impl MutVisitor for NormalizeVisitor { + fn visit_angle_bracketed_parameter_data(&mut self, e: &mut AngleBracketedArgs) { + #[derive(Ord, PartialOrd, Eq, PartialEq)] + enum Group { + Lifetimes, + TypesAndConsts, + Constraints, + } + e.args.sort_by_key(|arg| match arg { + AngleBracketedArg::Arg(arg) => match arg { + GenericArg::Lifetime(_) => Group::Lifetimes, + GenericArg::Type(_) | GenericArg::Const(_) => Group::TypesAndConsts, + }, + AngleBracketedArg::Constraint(_) => Group::Constraints, + }); + mut_visit::noop_visit_angle_bracketed_parameter_data(e, self); + } + + fn visit_generics(&mut self, e: &mut Generics) { + #[derive(Ord, PartialOrd, Eq, PartialEq)] + enum Group { + Lifetimes, + TypesAndConsts, + } + e.params.sort_by_key(|param| match param.kind { + GenericParamKind::Lifetime => Group::Lifetimes, + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { + Group::TypesAndConsts + } + }); + mut_visit::noop_visit_generics(e, self); + } + + fn visit_where_clause(&mut self, e: &mut WhereClause) { + if e.predicates.is_empty() { + e.has_where_token = false; + } + } + } + + NormalizeVisitor.visit_crate(krate); +} |