use std::time::Instant;
use frontend::frontend_facts::syntax::{Fragment, parses, parses_as};
use frontend::rustc_span::{create_session_globals_then, edition::Edition};
fn catcher(f: &mut dyn FnMut()) -> Result<(), frontend::unwind_janky::Payload> {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(f))
}
const FRAGMENTS: &[(&str, Fragment)] = &[
("s.chars().filter(|c| \"aeiou\".contains(*c)).count()", Fragment::Expr),
("s.chars().rev().collect()", Fragment::Expr),
("n >= 2 && (2..n).all(|d| n % d != 0)", Fragment::Expr),
("{ let mut v = Vec::new(); v.push(1); v }", Fragment::Block),
("let x: u64 = a.wrapping_add(b);", Fragment::Stmt),
("fn f(s: &str) -> usize { s.len() }", Fragment::Item),
("Result<i64, std::num::ParseIntError>", Fragment::Type),
("Some((a, b)) | None", Fragment::Pat),
("1 +", Fragment::Expr),
("fn f() {}", Fragment::Expr),
];
const REPEAT: usize = 100;
fn fragments_pass() -> usize {
let mut ok = 0;
for _ in 0..REPEAT {
for &(source, kind) in FRAGMENTS {
ok += usize::from(parses_as(source, kind).is_ok());
}
}
ok
}
fn files_pass(files: &[String]) -> usize {
files.iter().filter(|text| parses(text).is_ok()).count()
}
fn shared<R>(f: impl FnOnce() -> R) -> R {
create_session_globals_then(Edition::Edition2024, &[], None, f)
}
fn time<R>(label: &str, calls: usize, f: impl FnOnce() -> R) -> R {
let start = Instant::now();
let out = f();
let elapsed = start.elapsed();
println!(
"{label:<14} {calls:>6} calls {:>9.3} ms {:>8.2} us/call",
elapsed.as_secs_f64() * 1e3,
elapsed.as_secs_f64() * 1e6 / calls as f64
);
out
}
fn main() {
frontend::unwind_janky::install_catcher(catcher);
let passes: usize = std::env::args().nth(1).and_then(|n| n.parse().ok()).unwrap_or(5);
let root = concat!(env!("CARGO_MANIFEST_DIR"), "/src/frontend_facts");
let mut files: Vec<String> = Vec::new();
let mut stack = vec![std::path::PathBuf::from(root)];
while let Some(dir) = stack.pop() {
let mut entries: Vec<_> = std::fs::read_dir(&dir).expect("read_dir").flatten().collect();
entries.sort_by_key(|e| e.path());
for entry in entries {
let path = entry.path();
if path.is_dir() {
stack.push(path);
} else if path.extension().is_some_and(|e| e == "rs") {
files.push(std::fs::read_to_string(&path).expect("read"));
}
}
}
let bytes: usize = files.iter().map(String::len).sum();
println!(
"{} fragments x {REPEAT}, {} files ({bytes} bytes), {passes} passes",
FRAGMENTS.len(),
files.len()
);
let calls = FRAGMENTS.len() * REPEAT;
for pass in 0..passes {
println!("pass {pass}");
let a = time("fresh", calls, fragments_pass);
let b = time("fresh-again", calls, fragments_pass);
let c = time("shared", calls, || shared(fragments_pass));
assert!(a == b && b == c, "the arms must agree on every answer: {a} {b} {c}");
let d = time("files-fresh", files.len(), || files_pass(&files));
let e = time("files-shared", files.len(), || shared(|| files_pass(&files)));
assert_eq!(d, e, "the file arms must agree");
}
}