use criterion::{criterion_group, criterion_main, Criterion};
use debtmap::analyzers::rust_constructor_detector::{
analyze_function_body, extract_return_type, ConstructorReturnType,
};
use std::hint::black_box;
use syn::{parse_quote, ItemFn};
fn generate_constructor_code(num_funcs: usize) -> Vec<ItemFn> {
let mut functions = Vec::new();
for i in 0..num_funcs {
functions.push(parse_quote! {
pub fn new() -> Self {
Self { field: 0 }
}
});
functions.push(parse_quote! {
pub fn create_default() -> Self {
Self { field: #i }
}
});
functions.push(parse_quote! {
pub fn try_new() -> Result<Self, Error> {
Ok(Self { field: #i })
}
});
functions.push(parse_quote! {
pub fn set_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
});
functions.push(parse_quote! {
pub fn get_value(&self) -> i32 {
self.field
}
});
}
functions
}
fn bench_extract_return_type(c: &mut Criterion) {
let functions = generate_constructor_code(20);
c.bench_function("ast_extract_return_type", |b| {
b.iter(|| {
for func in &functions {
black_box(extract_return_type(func));
}
})
});
}
fn bench_analyze_function_body(c: &mut Criterion) {
let functions = generate_constructor_code(20);
c.bench_function("ast_analyze_function_body", |b| {
b.iter(|| {
for func in &functions {
black_box(analyze_function_body(func));
}
})
});
}
fn bench_full_ast_detection(c: &mut Criterion) {
let functions = generate_constructor_code(20);
c.bench_function("ast_full_constructor_detection", |b| {
b.iter(|| {
for func in &functions {
let return_type = extract_return_type(func);
let is_self_return = matches!(
return_type,
Some(
ConstructorReturnType::OwnedSelf
| ConstructorReturnType::ResultSelf
| ConstructorReturnType::OptionSelf
)
);
if is_self_return {
let pattern = analyze_function_body(func);
black_box(pattern.is_constructor_like());
}
}
})
});
}
fn bench_name_only_detection(c: &mut Criterion) {
let functions = generate_constructor_code(20);
c.bench_function("name_only_constructor_detection", |b| {
b.iter(|| {
for func in &functions {
let name = func.sig.ident.to_string();
let is_constructor = name == "new"
|| name.starts_with("new_")
|| name.starts_with("try_new")
|| name.starts_with("from_");
black_box(is_constructor);
}
})
});
}
fn bench_overhead_comparison(c: &mut Criterion) {
let functions = generate_constructor_code(50);
c.bench_function("comparison_name_only", |b| {
b.iter(|| {
let mut count = 0;
for func in &functions {
let name = func.sig.ident.to_string();
if name == "new" || name.starts_with("new_") {
count += 1;
}
}
black_box(count)
})
});
c.bench_function("comparison_ast_based", |b| {
b.iter(|| {
let mut count = 0;
for func in &functions {
if let Some(return_type) = extract_return_type(func) {
if matches!(
return_type,
ConstructorReturnType::OwnedSelf
| ConstructorReturnType::ResultSelf
| ConstructorReturnType::OptionSelf
) {
let pattern = analyze_function_body(func);
if pattern.is_constructor_like() {
count += 1;
}
}
}
}
black_box(count)
})
});
}
fn bench_scalability(c: &mut Criterion) {
let sizes = vec![("small", 10), ("medium", 50), ("large", 200)];
for (name, num_funcs) in sizes {
let functions = generate_constructor_code(num_funcs);
c.bench_function(&format!("ast_detection_{name}"), |b| {
b.iter(|| {
for func in &functions {
if let Some(return_type) = extract_return_type(func) {
if matches!(
return_type,
ConstructorReturnType::OwnedSelf
| ConstructorReturnType::ResultSelf
| ConstructorReturnType::OptionSelf
) {
let pattern = analyze_function_body(func);
black_box(pattern.is_constructor_like());
}
}
}
})
});
c.bench_function(&format!("name_only_{name}"), |b| {
b.iter(|| {
for func in &functions {
let name = func.sig.ident.to_string();
black_box(
name == "new" || name.starts_with("new_") || name.starts_with("try_new"),
);
}
})
});
}
}
fn bench_realistic_mixed_code(c: &mut Criterion) {
let functions: Vec<ItemFn> = vec![
parse_quote! { pub fn new() -> Self { Self { timeout: 30 } } },
parse_quote! { pub fn try_new(val: i32) -> Result<Self, Error> { Ok(Self { val }) } },
parse_quote! { pub fn create_default() -> Self { Self::new() } },
parse_quote! { pub fn get_value(&self) -> i32 { self.val } },
parse_quote! { pub fn process(&mut self) { self.val += 1; } },
parse_quote! {
pub fn complex_logic(x: i32) -> i32 {
if x > 0 {
for i in 0..x {
println!("{}", i);
}
}
x * 2
}
},
parse_quote! {
pub fn set_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
},
parse_quote! {
pub fn with_config(mut self, config: Config) -> Self {
self.config = config;
self
}
},
];
c.bench_function("realistic_mixed_ast", |b| {
b.iter(|| {
for func in &functions {
if let Some(return_type) = extract_return_type(func) {
if matches!(
return_type,
ConstructorReturnType::OwnedSelf
| ConstructorReturnType::ResultSelf
| ConstructorReturnType::OptionSelf
) {
let pattern = analyze_function_body(func);
black_box(pattern.is_constructor_like());
}
}
}
})
});
c.bench_function("realistic_mixed_name_only", |b| {
b.iter(|| {
for func in &functions {
let name = func.sig.ident.to_string();
black_box(
name == "new"
|| name.starts_with("new_")
|| name.starts_with("try_new")
|| name.starts_with("from_"),
);
}
})
});
}
criterion_group!(
constructor_benches,
bench_extract_return_type,
bench_analyze_function_body,
bench_full_ast_detection,
bench_name_only_detection,
bench_overhead_comparison,
bench_scalability,
bench_realistic_mixed_code
);
criterion_main!(constructor_benches);