1 // $ cargo bench --features full --bench rust
2 //
3 // Syn only, useful for profiling:
4 // $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full --bench rust
5 
6 #![cfg_attr(not(syn_only), feature(rustc_private))]
7 #![recursion_limit = "1024"]
8 
9 #[macro_use]
10 #[path = "../tests/macros/mod.rs"]
11 mod macros;
12 
13 #[path = "../tests/common/mod.rs"]
14 mod common;
15 #[path = "../tests/repo/mod.rs"]
16 mod repo;
17 
18 use std::fs;
19 use std::time::{Duration, Instant};
20 
21 #[cfg(not(syn_only))]
22 mod tokenstream_parse {
23     use proc_macro2::TokenStream;
24     use std::str::FromStr;
25 
bench(content: &str) -> Result<(), ()>26     pub fn bench(content: &str) -> Result<(), ()> {
27         TokenStream::from_str(content).map(drop).map_err(drop)
28     }
29 }
30 
31 mod syn_parse {
bench(content: &str) -> Result<(), ()>32     pub fn bench(content: &str) -> Result<(), ()> {
33         syn::parse_file(content).map(drop).map_err(drop)
34     }
35 }
36 
37 #[cfg(not(syn_only))]
38 mod libsyntax_parse {
39     extern crate rustc_ast;
40     extern crate rustc_data_structures;
41     extern crate rustc_errors;
42     extern crate rustc_parse;
43     extern crate rustc_session;
44     extern crate rustc_span;
45 
46     use rustc_data_structures::sync::Lrc;
47     use rustc_errors::{emitter::Emitter, Diagnostic, Handler};
48     use rustc_session::parse::ParseSess;
49     use rustc_span::source_map::{FilePathMapping, SourceMap};
50     use rustc_span::{edition::Edition, FileName};
51 
bench(content: &str) -> Result<(), ()>52     pub fn bench(content: &str) -> Result<(), ()> {
53         struct SilentEmitter;
54 
55         impl Emitter for SilentEmitter {
56             fn emit_diagnostic(&mut self, _diag: &Diagnostic) {}
57             fn source_map(&self) -> Option<&Lrc<SourceMap>> {
58                 None
59             }
60         }
61 
62         rustc_ast::with_globals(Edition::Edition2018, || {
63             let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
64             let emitter = Box::new(SilentEmitter);
65             let handler = Handler::with_emitter(false, None, emitter);
66             let sess = ParseSess::with_span_handler(handler, cm);
67             if let Err(mut diagnostic) = rustc_parse::parse_crate_from_source_str(
68                 FileName::Custom("bench".to_owned()),
69                 content.to_owned(),
70                 &sess,
71             ) {
72                 diagnostic.cancel();
73                 return Err(());
74             };
75             Ok(())
76         })
77     }
78 }
79 
80 #[cfg(not(syn_only))]
81 mod read_from_disk {
bench(content: &str) -> Result<(), ()>82     pub fn bench(content: &str) -> Result<(), ()> {
83         let _ = content;
84         Ok(())
85     }
86 }
87 
exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration88 fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
89     let begin = Instant::now();
90     let mut success = 0;
91     let mut total = 0;
92 
93     walkdir::WalkDir::new("tests/rust/src")
94         .into_iter()
95         .filter_entry(repo::base_dir_filter)
96         .for_each(|entry| {
97             let entry = entry.unwrap();
98             let path = entry.path();
99             if path.is_dir() {
100                 return;
101             }
102             let content = fs::read_to_string(path).unwrap();
103             let ok = codepath(&content).is_ok();
104             success += ok as usize;
105             total += 1;
106             if !ok {
107                 eprintln!("FAIL {}", path.display());
108             }
109         });
110 
111     assert_eq!(success, total);
112     begin.elapsed()
113 }
114 
main()115 fn main() {
116     repo::clone_rust();
117 
118     macro_rules! testcases {
119         ($($(#[$cfg:meta])* $name:path,)*) => {
120             vec![
121                 $(
122                     $(#[$cfg])*
123                     (stringify!($name), $name as fn(&str) -> Result<(), ()>),
124                 )*
125             ]
126         };
127     }
128 
129     #[cfg(not(syn_only))]
130     {
131         let mut lines = 0;
132         let mut files = 0;
133         exec(|content| {
134             lines += content.lines().count();
135             files += 1;
136             Ok(())
137         });
138         eprintln!("\n{} lines in {} files", lines, files);
139     }
140 
141     for (name, f) in testcases!(
142         #[cfg(not(syn_only))]
143         read_from_disk::bench,
144         #[cfg(not(syn_only))]
145         tokenstream_parse::bench,
146         syn_parse::bench,
147         #[cfg(not(syn_only))]
148         libsyntax_parse::bench,
149     ) {
150         eprint!("{:20}", format!("{}:", name));
151         let elapsed = exec(f);
152         eprintln!(
153             "elapsed={}.{:03}s",
154             elapsed.as_secs(),
155             elapsed.subsec_millis(),
156         );
157     }
158     eprintln!();
159 }
160