| // $ cargo bench --features full,test --bench rust |
| // |
| // Syn only, useful for profiling: |
| // $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full,test --bench rust |
| |
| #![cfg_attr(not(syn_only), feature(rustc_private))] |
| #![recursion_limit = "1024"] |
| #![allow( |
| clippy::arc_with_non_send_sync, |
| clippy::cast_lossless, |
| clippy::let_underscore_untyped, |
| clippy::manual_let_else, |
| clippy::match_like_matches_macro, |
| clippy::uninlined_format_args, |
| clippy::unnecessary_wraps |
| )] |
| |
| #[macro_use] |
| #[path = "../tests/macros/mod.rs"] |
| mod macros; |
| |
| #[allow(dead_code)] |
| #[path = "../tests/repo/mod.rs"] |
| mod repo; |
| |
| use std::fs; |
| use std::path::Path; |
| use std::time::{Duration, Instant}; |
| |
| #[cfg(not(syn_only))] |
| mod tokenstream_parse { |
| use proc_macro2::TokenStream; |
| use std::path::Path; |
| use std::str::FromStr; |
| |
| pub fn bench(_path: &Path, content: &str) -> Result<(), ()> { |
| TokenStream::from_str(content).map(drop).map_err(drop) |
| } |
| } |
| |
| mod syn_parse { |
| use std::path::Path; |
| |
| pub fn bench(_path: &Path, content: &str) -> Result<(), ()> { |
| syn::parse_file(content).map(drop).map_err(drop) |
| } |
| } |
| |
| #[cfg(not(syn_only))] |
| mod librustc_parse { |
| extern crate rustc_data_structures; |
| extern crate rustc_driver; |
| extern crate rustc_error_messages; |
| extern crate rustc_errors; |
| extern crate rustc_parse; |
| extern crate rustc_session; |
| extern crate rustc_span; |
| |
| use crate::repo; |
| use rustc_data_structures::sync::Lrc; |
| use rustc_error_messages::FluentBundle; |
| use rustc_errors::{emitter::Emitter, translation::Translate, DiagCtxt, DiagInner}; |
| use rustc_session::parse::ParseSess; |
| use rustc_span::source_map::{FilePathMapping, SourceMap}; |
| use rustc_span::FileName; |
| use std::path::Path; |
| |
| pub fn bench(path: &Path, content: &str) -> Result<(), ()> { |
| struct SilentEmitter; |
| |
| impl Emitter for SilentEmitter { |
| fn emit_diagnostic(&mut self, _diag: DiagInner) {} |
| fn source_map(&self) -> Option<&Lrc<SourceMap>> { |
| None |
| } |
| } |
| |
| impl Translate for SilentEmitter { |
| fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { |
| None |
| } |
| fn fallback_fluent_bundle(&self) -> &FluentBundle { |
| panic!("silent emitter attempted to translate a diagnostic"); |
| } |
| } |
| |
| let edition = repo::edition(path).parse().unwrap(); |
| rustc_span::create_session_if_not_set_then(edition, |_| { |
| let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); |
| let emitter = Box::new(SilentEmitter); |
| let handler = DiagCtxt::new(emitter); |
| let sess = ParseSess::with_dcx(handler, source_map); |
| let name = FileName::Custom("bench".to_owned()); |
| let mut parser = |
| rustc_parse::new_parser_from_source_str(&sess, name, content.to_owned()).unwrap(); |
| if let Err(diagnostic) = parser.parse_crate_mod() { |
| diagnostic.cancel(); |
| return Err(()); |
| }; |
| Ok(()) |
| }) |
| } |
| } |
| |
| #[cfg(not(syn_only))] |
| mod read_from_disk { |
| use std::path::Path; |
| |
| pub fn bench(_path: &Path, content: &str) -> Result<(), ()> { |
| let _ = content; |
| Ok(()) |
| } |
| } |
| |
| fn exec(mut codepath: impl FnMut(&Path, &str) -> Result<(), ()>) -> Duration { |
| let begin = Instant::now(); |
| let mut success = 0; |
| let mut total = 0; |
| |
| ["tests/rust/compiler", "tests/rust/library"] |
| .iter() |
| .flat_map(|dir| { |
| walkdir::WalkDir::new(dir) |
| .into_iter() |
| .filter_entry(repo::base_dir_filter) |
| }) |
| .for_each(|entry| { |
| let entry = entry.unwrap(); |
| let path = entry.path(); |
| if path.is_dir() { |
| return; |
| } |
| let content = fs::read_to_string(path).unwrap(); |
| let ok = codepath(path, &content).is_ok(); |
| success += ok as usize; |
| total += 1; |
| if !ok { |
| eprintln!("FAIL {}", path.display()); |
| } |
| }); |
| |
| assert_eq!(success, total); |
| begin.elapsed() |
| } |
| |
| fn main() { |
| repo::clone_rust(); |
| |
| macro_rules! testcases { |
| ($($(#[$cfg:meta])* $name:ident,)*) => { |
| [ |
| $( |
| $(#[$cfg])* |
| (stringify!($name), $name::bench as fn(&Path, &str) -> Result<(), ()>), |
| )* |
| ] |
| }; |
| } |
| |
| #[cfg(not(syn_only))] |
| { |
| let mut lines = 0; |
| let mut files = 0; |
| exec(|_path, content| { |
| lines += content.lines().count(); |
| files += 1; |
| Ok(()) |
| }); |
| eprintln!("\n{} lines in {} files", lines, files); |
| } |
| |
| for (name, f) in testcases!( |
| #[cfg(not(syn_only))] |
| read_from_disk, |
| #[cfg(not(syn_only))] |
| tokenstream_parse, |
| syn_parse, |
| #[cfg(not(syn_only))] |
| librustc_parse, |
| ) { |
| eprint!("{:20}", format!("{}:", name)); |
| let elapsed = exec(f); |
| eprintln!( |
| "elapsed={}.{:03}s", |
| elapsed.as_secs(), |
| elapsed.subsec_millis(), |
| ); |
| } |
| eprintln!(); |
| } |