use std::cell::Cell;
use oxc_ast::{
AstKind,
ast::{BindingIdentifier, IdentifierReference, Program, TSEnumMemberName},
};
use oxc_ast_visit::{Visit, walk::walk_ts_enum_member_name};
use oxc_syntax::scope::{ScopeFlags, ScopeId};
macro_rules! assert_ge {
($left:expr, $right:expr, $($msg_args:tt)+) => {
match (&$left, &$right) {
(left, right) => if !(left >= right) {
panic!(
"assertion failed: `(left >= right)`\n left: `{:?}`,\n right: `{:?}`\n {}",
left, right,
::std::format_args!($($msg_args)+),
);
}
}
};
($left:expr, $right:expr) => {
match (&$left, &$right) {
(left, right) => if !(left >= right) {
panic!(
"assertion failed: `(left >= right)`\n left: `{:?}`,\n right: `{:?}`",
left, right,
);
}
}
};
($lhs:expr, $rhs:expr,) => {
assert_le!($lhs, $rhs);
};
}
#[derive(Clone, Copy, Default, Debug)]
pub struct Stats {
pub nodes: u32,
pub scopes: u32,
pub symbols: u32,
pub references: u32,
}
impl Stats {
pub fn new(nodes: u32, scopes: u32, symbols: u32, references: u32) -> Self {
Stats { nodes, scopes, symbols, references }
}
pub fn count(program: &Program) -> Self {
let mut counter = Counter::default();
counter.visit_program(program);
counter.stats
}
#[must_use]
pub fn increase_by(mut self, excess: f64) -> Self {
let factor = excess + 1.0;
#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_lossless)]
let increase = |n: u32| (n as f64 * factor) as u32;
self.scopes = increase(self.scopes);
self.symbols = increase(self.symbols);
self.references = increase(self.references);
self
}
pub fn assert_accurate(self, actual: Self) {
assert_eq!(self.nodes, actual.nodes, "nodes count mismatch");
assert_eq!(self.scopes, actual.scopes, "scopes count mismatch");
assert_eq!(self.references, actual.references, "references count mismatch");
assert_ge!(self.symbols, actual.symbols, "symbols count mismatch");
}
}
#[derive(Default)]
struct Counter {
stats: Stats,
}
impl<'a> Visit<'a> for Counter {
#[inline]
fn enter_node(&mut self, _: AstKind<'a>) {
self.stats.nodes += 1;
}
#[inline]
fn enter_scope(&mut self, _: ScopeFlags, _: &Cell<Option<ScopeId>>) {
self.stats.scopes += 1;
}
#[inline]
fn visit_binding_identifier(&mut self, _: &BindingIdentifier<'a>) {
self.stats.nodes += 1;
self.stats.symbols += 1;
}
#[inline]
fn visit_identifier_reference(&mut self, _: &IdentifierReference<'a>) {
self.stats.nodes += 1;
self.stats.references += 1;
}
#[inline]
fn visit_ts_enum_member_name(&mut self, it: &TSEnumMemberName<'a>) {
self.stats.symbols += 1;
walk_ts_enum_member_name(self, it);
}
}