1 // $ cargo bench --features full,test --bench rust
2 //
3 // Syn only, useful for profiling:
4 // $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full,test --bench rust
5
6 #![cfg_attr(not(syn_only), feature(rustc_private))]
7 #![recursion_limit = "1024"]
8 #![allow(
9 clippy::arc_with_non_send_sync,
10 clippy::cast_lossless,
11 clippy::elidable_lifetime_names,
12 clippy::let_underscore_untyped,
13 clippy::manual_let_else,
14 clippy::match_like_matches_macro,
15 clippy::needless_lifetimes,
16 clippy::uninlined_format_args,
17 clippy::unnecessary_wraps
18 )]
19
20 #[macro_use]
21 #[path = "../tests/macros/mod.rs"]
22 mod macros;
23
24 #[allow(dead_code)]
25 #[path = "../tests/repo/mod.rs"]
26 mod repo;
27
28 use std::fs;
29 use std::path::Path;
30 use std::time::{Duration, Instant};
31
32 #[cfg(not(syn_only))]
33 mod tokenstream_parse {
34 use proc_macro2::TokenStream;
35 use std::path::Path;
36 use std::str::FromStr;
37
bench(_path: &Path, content: &str) -> Result<(), ()>38 pub fn bench(_path: &Path, content: &str) -> Result<(), ()> {
39 TokenStream::from_str(content).map(drop).map_err(drop)
40 }
41 }
42
43 mod syn_parse {
44 use std::path::Path;
45
bench(_path: &Path, content: &str) -> Result<(), ()>46 pub fn bench(_path: &Path, content: &str) -> Result<(), ()> {
47 syn::parse_file(content).map(drop).map_err(drop)
48 }
49 }
50
51 #[cfg(not(syn_only))]
52 mod librustc_parse {
53 extern crate rustc_data_structures;
54 extern crate rustc_driver;
55 extern crate rustc_error_messages;
56 extern crate rustc_errors;
57 extern crate rustc_parse;
58 extern crate rustc_session;
59 extern crate rustc_span;
60
61 use crate::repo;
62 use rustc_error_messages::FluentBundle;
63 use rustc_errors::emitter::Emitter;
64 use rustc_errors::registry::Registry;
65 use rustc_errors::translation::Translate;
66 use rustc_errors::{DiagCtxt, DiagInner};
67 use rustc_session::parse::ParseSess;
68 use rustc_span::source_map::{FilePathMapping, SourceMap};
69 use rustc_span::FileName;
70 use std::path::Path;
71 use std::sync::Arc;
72
bench(path: &Path, content: &str) -> Result<(), ()>73 pub fn bench(path: &Path, content: &str) -> Result<(), ()> {
74 struct SilentEmitter;
75
76 impl Emitter for SilentEmitter {
77 fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {}
78 fn source_map(&self) -> Option<&SourceMap> {
79 None
80 }
81 }
82
83 impl Translate for SilentEmitter {
84 fn fluent_bundle(&self) -> Option<&FluentBundle> {
85 None
86 }
87 fn fallback_fluent_bundle(&self) -> &FluentBundle {
88 panic!("silent emitter attempted to translate a diagnostic");
89 }
90 }
91
92 let edition = repo::edition(path).parse().unwrap();
93 rustc_span::create_session_if_not_set_then(edition, |_| {
94 let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
95 let emitter = Box::new(SilentEmitter);
96 let handler = DiagCtxt::new(emitter);
97 let sess = ParseSess::with_dcx(handler, source_map);
98 let name = FileName::Custom("bench".to_owned());
99 let mut parser =
100 rustc_parse::new_parser_from_source_str(&sess, name, content.to_owned()).unwrap();
101 if let Err(diagnostic) = parser.parse_crate_mod() {
102 diagnostic.cancel();
103 return Err(());
104 }
105 Ok(())
106 })
107 }
108 }
109
110 #[cfg(not(syn_only))]
111 mod read_from_disk {
112 use std::path::Path;
113
bench(_path: &Path, content: &str) -> Result<(), ()>114 pub fn bench(_path: &Path, content: &str) -> Result<(), ()> {
115 let _ = content;
116 Ok(())
117 }
118 }
119
exec(mut codepath: impl FnMut(&Path, &str) -> Result<(), ()>) -> Duration120 fn exec(mut codepath: impl FnMut(&Path, &str) -> Result<(), ()>) -> Duration {
121 let begin = Instant::now();
122 let mut success = 0;
123 let mut total = 0;
124
125 ["tests/rust/compiler", "tests/rust/library"]
126 .iter()
127 .flat_map(|dir| {
128 walkdir::WalkDir::new(dir)
129 .into_iter()
130 .filter_entry(repo::base_dir_filter)
131 })
132 .for_each(|entry| {
133 let entry = entry.unwrap();
134 let path = entry.path();
135 if path.is_dir() {
136 return;
137 }
138 let content = fs::read_to_string(path).unwrap();
139 let ok = codepath(path, &content).is_ok();
140 success += ok as usize;
141 total += 1;
142 if !ok {
143 eprintln!("FAIL {}", path.display());
144 }
145 });
146
147 assert_eq!(success, total);
148 begin.elapsed()
149 }
150
main()151 fn main() {
152 repo::clone_rust();
153
154 macro_rules! testcases {
155 ($($(#[$cfg:meta])* $name:ident,)*) => {
156 [
157 $(
158 $(#[$cfg])*
159 (stringify!($name), $name::bench as fn(&Path, &str) -> Result<(), ()>),
160 )*
161 ]
162 };
163 }
164
165 #[cfg(not(syn_only))]
166 {
167 let mut lines = 0;
168 let mut files = 0;
169 exec(|_path, content| {
170 lines += content.lines().count();
171 files += 1;
172 Ok(())
173 });
174 eprintln!("\n{} lines in {} files", lines, files);
175 }
176
177 for (name, f) in testcases!(
178 #[cfg(not(syn_only))]
179 read_from_disk,
180 #[cfg(not(syn_only))]
181 tokenstream_parse,
182 syn_parse,
183 #[cfg(not(syn_only))]
184 librustc_parse,
185 ) {
186 eprint!("{:20}", format!("{}:", name));
187 let elapsed = exec(f);
188 eprintln!(
189 "elapsed={}.{:03}s",
190 elapsed.as_secs(),
191 elapsed.subsec_millis(),
192 );
193 }
194 eprintln!();
195 }
196