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