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_data_structures;
40     extern crate rustc_parse;
41     extern crate rustc_span;
42     extern crate syntax;
43 
44     use rustc_data_structures::sync::Lrc;
45     use rustc_span::FileName;
46     use syntax::edition::Edition;
47     use syntax::errors::{emitter::Emitter, Diagnostic, Handler};
48     use syntax::sess::ParseSess;
49     use syntax::source_map::{FilePathMapping, SourceMap};
50 
bench(content: &str) -> Result<(), ()>51     pub fn bench(content: &str) -> Result<(), ()> {
52         struct SilentEmitter;
53 
54         impl Emitter for SilentEmitter {
55             fn emit_diagnostic(&mut self, _diag: &Diagnostic) {}
56             fn source_map(&self) -> Option<&Lrc<SourceMap>> {
57                 None
58             }
59         }
60 
61         syntax::with_globals(Edition::Edition2018, || {
62             let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
63             let emitter = Box::new(SilentEmitter);
64             let handler = Handler::with_emitter(false, None, emitter);
65             let sess = ParseSess::with_span_handler(handler, cm);
66             if let Err(mut diagnostic) = rustc_parse::parse_crate_from_source_str(
67                 FileName::Custom("bench".to_owned()),
68                 content.to_owned(),
69                 &sess,
70             ) {
71                 diagnostic.cancel();
72                 return Err(());
73             };
74             Ok(())
75         })
76     }
77 }
78 
79 #[cfg(not(syn_only))]
80 mod read_from_disk {
bench(content: &str) -> Result<(), ()>81     pub fn bench(content: &str) -> Result<(), ()> {
82         let _ = content;
83         Ok(())
84     }
85 }
86 
exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration87 fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
88     let begin = Instant::now();
89     let mut success = 0;
90     let mut total = 0;
91 
92     walkdir::WalkDir::new("tests/rust/src")
93         .into_iter()
94         .filter_entry(repo::base_dir_filter)
95         .for_each(|entry| {
96             let entry = entry.unwrap();
97             let path = entry.path();
98             if path.is_dir() {
99                 return;
100             }
101             let content = fs::read_to_string(path).unwrap();
102             let ok = codepath(&content).is_ok();
103             success += ok as usize;
104             total += 1;
105             if !ok {
106                 eprintln!("FAIL {}", path.display());
107             }
108         });
109 
110     assert_eq!(success, total);
111     begin.elapsed()
112 }
113 
main()114 fn main() {
115     repo::clone_rust();
116 
117     macro_rules! testcases {
118         ($($(#[$cfg:meta])* $name:path,)*) => {
119             vec![
120                 $(
121                     $(#[$cfg])*
122                     (stringify!($name), $name as fn(&str) -> Result<(), ()>),
123                 )*
124             ]
125         };
126     }
127 
128     #[cfg(not(syn_only))]
129     {
130         let mut lines = 0;
131         let mut files = 0;
132         exec(|content| {
133             lines += content.lines().count();
134             files += 1;
135             Ok(())
136         });
137         eprintln!("\n{} lines in {} files", lines, files);
138     }
139 
140     for (name, f) in testcases!(
141         #[cfg(not(syn_only))]
142         read_from_disk::bench,
143         #[cfg(not(syn_only))]
144         tokenstream_parse::bench,
145         syn_parse::bench,
146         #[cfg(not(syn_only))]
147         libsyntax_parse::bench,
148     ) {
149         eprint!("{:20}", format!("{}:", name));
150         let elapsed = exec(f);
151         eprintln!(
152             "elapsed={}.{:03}s",
153             elapsed.as_secs(),
154             elapsed.subsec_millis(),
155         );
156     }
157     eprintln!();
158 }
159