perf(linter/react-exhaustive-deps): use stack of AstTypes instead of AstKinds (#8522)

This lint rule keeps a stack tracing the "visitation path" during `Visit`. Only the type of the nodes is used, not their values, so store only `AstType` (1 byte) rather than `AstKind` (16 bytes).

This should be more performant, but the main motivation is #8461. This is one of very few places in the codebase which uses `enter_node` and `leave_node`. Not storing `AstKind`s here clears the way to remove the unsound lifetime extension in `Visit::alloc`.
This commit is contained in:
overlookmotel 2025-01-16 00:32:38 +00:00
parent a6d71f8f27
commit 250bbd193b

View file

@ -12,7 +12,7 @@ use oxc_ast::{
}, },
match_expression, match_expression,
visit::walk::walk_function_body, visit::walk::walk_function_body,
AstKind, Visit, AstKind, AstType, Visit,
}; };
use oxc_diagnostics::OxcDiagnostic; use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint; use oxc_macros::declare_oxc_lint;
@ -973,7 +973,7 @@ fn func_call_without_react_namespace<'a>(
struct ExhaustiveDepsVisitor<'a, 'b> { struct ExhaustiveDepsVisitor<'a, 'b> {
semantic: &'b Semantic<'a>, semantic: &'b Semantic<'a>,
stack: Vec<AstKind<'a>>, stack: Vec<AstType>,
skip_reporting_dependency: bool, skip_reporting_dependency: bool,
set_state_call: bool, set_state_call: bool,
found_dependencies: FxHashSet<Dependency<'a>>, found_dependencies: FxHashSet<Dependency<'a>>,
@ -999,7 +999,7 @@ impl<'a, 'b> ExhaustiveDepsVisitor<'a, 'b> {
impl<'a> Visit<'a> for ExhaustiveDepsVisitor<'a, '_> { impl<'a> Visit<'a> for ExhaustiveDepsVisitor<'a, '_> {
fn enter_node(&mut self, kind: AstKind<'a>) { fn enter_node(&mut self, kind: AstKind<'a>) {
self.stack.push(kind); self.stack.push(kind.ty());
} }
fn leave_node(&mut self, _kind: AstKind<'a>) { fn leave_node(&mut self, _kind: AstKind<'a>) {
@ -1041,10 +1041,8 @@ impl<'a> Visit<'a> for ExhaustiveDepsVisitor<'a, '_> {
return; return;
} }
let is_parent_call_expr = self let is_parent_call_expr =
.stack self.stack.get(self.stack.len() - 2).is_some_and(|&ty| ty == AstType::CallExpression);
.get(self.stack.len() - 2)
.is_some_and(|kind| matches!(kind, AstKind::CallExpression(_)));
match analyze_property_chain(&it.object, self.semantic) { match analyze_property_chain(&it.object, self.semantic) {
Ok(source) => { Ok(source) => {
@ -1110,9 +1108,10 @@ impl<'a> Visit<'a> for ExhaustiveDepsVisitor<'a, '_> {
}; };
if is_set_state_call if is_set_state_call
&& self.stack.iter().all(|kind| { && self
!matches!(kind, AstKind::Function(_) | AstKind::ArrowFunctionExpression(_)) .stack
}) .iter()
.all(|&ty| !matches!(ty, AstType::Function | AstType::ArrowFunctionExpression))
{ {
self.set_state_call = true; self.set_state_call = true;
} }
@ -1120,21 +1119,18 @@ impl<'a> Visit<'a> for ExhaustiveDepsVisitor<'a, '_> {
} }
} }
fn is_inside_effect_cleanup(stack: &[AstKind<'_>]) -> bool { fn is_inside_effect_cleanup(stack: &[AstType]) -> bool {
let mut iter = stack.iter().rev(); let mut iter = stack.iter().rev();
let mut is_in_returned_function = false; let mut is_in_returned_function = false;
while let Some(cur) = iter.next() { while let Some(&cur) = iter.next() {
match cur { if matches!(cur, AstType::Function | AstType::ArrowFunctionExpression) {
AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) => { if let Some(&parent) = iter.next() {
if let Some(parent) = iter.next() { if parent == AstType::ReturnStatement {
if matches!(parent, AstKind::ReturnStatement(_)) {
is_in_returned_function = true; is_in_returned_function = true;
} }
} }
} }
_ => {}
}
} }
is_in_returned_function is_in_returned_function