mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(linter/no-dupe-else-if): use smallvec to reduce allocs (#28474)
Use smallvec for storing conditions inline instead of heap allocating lots of small `Vec`s. On local fixtures: Allocations: 13,744 → 845 (−93.9%). Allocated bytes: −72.4%. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
69f0204a0a
commit
2ea9f586d4
1 file changed
+31
-24
@@ -6,6 +6,7 @@ use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{ContentEq, GetSpan, Span};
|
||||
use oxc_syntax::operator::LogicalOperator;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
|
||||
use crate::{AstNode, context::LintContext, rule::Rule};
|
||||
|
||||
@@ -128,7 +129,7 @@ impl Rule for NoDupeElseIf {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut conditions_to_check = vec![&if_stmt.test];
|
||||
let mut conditions_to_check: SmallVec<[_; 2]> = smallvec![&if_stmt.test];
|
||||
|
||||
if let Expression::LogicalExpression(expr) = &if_stmt.test
|
||||
&& expr.operator == LogicalOperator::And
|
||||
@@ -136,7 +137,7 @@ impl Rule for NoDupeElseIf {
|
||||
conditions_to_check.extend(split_by_and(&if_stmt.test));
|
||||
}
|
||||
|
||||
let mut list_to_check: Vec<Vec<Vec<_>>> = conditions_to_check
|
||||
let mut list_to_check: SmallVec<[Alternatives<'_, '_>; 1]> = conditions_to_check
|
||||
.iter()
|
||||
.map(|expr| split_by_or(expr).into_iter().map(split_by_and).collect())
|
||||
.collect();
|
||||
@@ -155,24 +156,18 @@ impl Rule for NoDupeElseIf {
|
||||
|
||||
current_node = parent_node;
|
||||
|
||||
let current_or_operands: Vec<_> =
|
||||
let current_or_operands: Alternatives<'_, '_> =
|
||||
split_by_or(&stmt.test).into_iter().map(split_by_and).collect();
|
||||
|
||||
list_to_check = list_to_check
|
||||
.into_iter()
|
||||
.map(|or_operands| {
|
||||
or_operands
|
||||
.into_iter()
|
||||
.filter(|or_operand| {
|
||||
for or_operands in &mut list_to_check {
|
||||
or_operands.retain(|or_operand| {
|
||||
!current_or_operands
|
||||
.iter()
|
||||
.any(|current_or_operand| is_subset(current_or_operand, or_operand))
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
});
|
||||
}
|
||||
|
||||
if list_to_check.iter().any(Vec::is_empty) {
|
||||
if list_to_check.iter().any(SmallVec::is_empty) {
|
||||
ctx.diagnostic(no_dupe_else_if_diagnostic(stmt.test.span(), if_stmt.test.span()));
|
||||
break;
|
||||
}
|
||||
@@ -180,31 +175,43 @@ impl Rule for NoDupeElseIf {
|
||||
}
|
||||
}
|
||||
|
||||
fn split_by_or<'a, 'b>(expr: &'a Expression<'b>) -> Vec<&'a Expression<'b>> {
|
||||
type Operands<'a, 'b> = SmallVec<[&'a Expression<'b>; 2]>;
|
||||
type Alternatives<'a, 'b> = SmallVec<[Operands<'a, 'b>; 1]>;
|
||||
|
||||
fn split_by_or<'a, 'b>(expr: &'a Expression<'b>) -> Operands<'a, 'b> {
|
||||
split_by_logical_operator(expr, LogicalOperator::Or)
|
||||
}
|
||||
|
||||
fn split_by_and<'a, 'b>(expr: &'a Expression<'b>) -> Vec<&'a Expression<'b>> {
|
||||
fn split_by_and<'a, 'b>(expr: &'a Expression<'b>) -> Operands<'a, 'b> {
|
||||
split_by_logical_operator(expr, LogicalOperator::And)
|
||||
}
|
||||
|
||||
fn split_by_logical_operator<'a, 'b>(
|
||||
expr: &'a Expression<'b>,
|
||||
operator: LogicalOperator,
|
||||
) -> Vec<&'a Expression<'b>> {
|
||||
) -> Operands<'a, 'b> {
|
||||
fn collect<'a, 'b>(
|
||||
expr: &'a Expression<'b>,
|
||||
operator: LogicalOperator,
|
||||
result: &mut Operands<'a, 'b>,
|
||||
) {
|
||||
match expr {
|
||||
Expression::LogicalExpression(expr) if expr.operator == operator => [
|
||||
split_by_logical_operator(&expr.left, operator),
|
||||
split_by_logical_operator(&expr.right, operator),
|
||||
]
|
||||
.concat(),
|
||||
Expression::LogicalExpression(expr) if expr.operator == operator => {
|
||||
collect(&expr.left, operator, result);
|
||||
collect(&expr.right, operator, result);
|
||||
}
|
||||
Expression::ParenthesizedExpression(expr) => {
|
||||
split_by_logical_operator(&expr.expression, operator)
|
||||
collect(&expr.expression, operator, result);
|
||||
}
|
||||
_ => vec![expr],
|
||||
_ => result.push(expr),
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = SmallVec::new();
|
||||
collect(expr, operator, &mut result);
|
||||
result
|
||||
}
|
||||
|
||||
fn is_subset<'a, 'b>(a: &'a [&'a Expression<'b>], b: &'a [&'a Expression<'b>]) -> bool {
|
||||
a.iter().all(|expr_a| b.iter().any(|expr_b| is_equal(expr_a, expr_b)))
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user