use crate::common::eq::SpanlessEq;
use quote::quote;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
-use rustc_ast::ast;
+use rustc_ast::ast::{
+ AngleBracketedArg, AngleBracketedArgs, Crate, GenericArg, GenericParamKind, Generics,
+};
+use rustc_ast::mut_visit::{self, MutVisitor};
use rustc_errors::PResult;
use rustc_session::parse::ParseSess;
use rustc_span::source_map::FilePathMapping;
use rustc_span::FileName;
-use std::fs::File;
-use std::io::Read;
+use std::fs;
use std::panic;
+use std::path::Path;
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Instant;
.into_par_iter()
.for_each(|entry| {
let path = entry.path();
- if path.is_dir() {
- return;
+ if !path.is_dir() {
+ test(path, &failed, abort_after);
}
+ });
- 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 edition = repo::edition(path).parse().unwrap();
-
- let equal = panic::catch_unwind(|| {
- rustc_span::with_session_globals(edition, || {
- let sess = ParseSess::new(FilePathMapping::empty());
- let before = match librustc_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 - librustc failed to parse original content: {}\n",
- path.display(),
- diagnostic.message()
- );
- }
- return true;
- }
- };
- let after = match librustc_parse(back, &sess) {
- Ok(after) => after,
- Err(mut diagnostic) => {
- errorf!("=== {}: librustc 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
+ let failed = failed.load(Ordering::SeqCst);
+ 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::SeqCst);
+ 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::with_session_globals(edition, || {
+ let equal = match panic::catch_unwind(|| {
+ let sess = ParseSess::new(FilePathMapping::empty());
+ let before = match librustc_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!(
- "=== {}: FAIL\nbefore: {:#?}\nafter: {:#?}\n",
+ "=== {}: ignore - librustc failed to parse original content: {}\n",
path.display(),
- before,
- after,
+ diagnostic.message(),
);
- false
- }
- })
- });
- match equal {
- Err(_) => errorf!("=== {}: ignoring librustc 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);
}
+ 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
}
- });
-
- let failed = failed.load(Ordering::SeqCst);
- if failed > 0 {
- panic!("{} failures", failed);
- }
+ 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\nbefore: {:#?}\nafter: {:#?}\n",
+ path.display(),
+ before,
+ after,
+ );
+ false
+ }
+ }
+ };
+ if !equal {
+ let prev_failed = failed.fetch_add(1, Ordering::SeqCst);
+ if prev_failed + 1 >= abort_after {
+ process::exit(1);
+ }
+ }
+ });
}
-fn librustc_parse(content: String, sess: &ParseSess) -> PResult<ast::Crate> {
- let name = FileName::Custom("test_round_trip".to_string());
+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 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);
+ }
+ }
+
+ NormalizeVisitor.visit_crate(krate);
+}