perf(linter): reduce no-unmodified-loop-condition allocations (#28478)

Use smallvec to avoid frequent heap allocs of small `Vec`s.

On local fixtures:


Metric | Before → After
-- | --
Allocations | 5,338 → 2,707 (−49.3%)
Allocated bytes | 406,280 → 268,840 (−33.8%)
Maximum peak bytes | 1,868 → 1,772 (−5.1%)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Cam McHenryandCopilot authored and GitHub committed 2026-10-30 12:26:33 -04:00
1 parent f01d52da4c
commit 92deebfded
1 file changed
+12 -10
@@ -2,6 +2,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use schemars::JsonSchema;
use serde::Deserialize;
use smallvec::SmallVec;
use oxc_allocator::GetAddress;
use oxc_ast::{
@@ -145,6 +146,8 @@ impl LoopInfo {
}
}
type ConditionSymbols = SmallVec<[(SymbolId, NodeId, bool); 2]>;
impl NoUnmodifiedLoopCondition {
fn check_loop_condition<'a>(
&self,
@@ -155,18 +158,17 @@ impl NoUnmodifiedLoopCondition {
let mut collector = ConditionSymbolsCollector::new(ctx, self.check_conditional_expressions);
collector.visit_expression(condition);
let mut standalone_symbols: Vec<(SymbolId, NodeId, bool)> = vec![];
let mut standalone_symbols = ConditionSymbols::new();
let mut standalone_seen: FxHashSet<SymbolId> = FxHashSet::default();
let mut grouped_symbols: FxHashMap<Span, Vec<(SymbolId, NodeId, bool)>> =
FxHashMap::default();
let mut grouped_symbols: FxHashMap<Span, ConditionSymbols> = FxHashMap::default();
let mut grouped_seen: FxHashMap<Span, FxHashSet<SymbolId>> = FxHashMap::default();
let mut group_order: Vec<Span> = vec![];
let mut group_order: SmallVec<[Span; 2]> = SmallVec::new();
for symbol in collector.symbols {
if let Some(group_span) = symbol.group_span {
if let std::collections::hash_map::Entry::Vacant(entry) =
grouped_symbols.entry(group_span)
{
entry.insert(vec![]);
entry.insert(SmallVec::new());
group_order.push(group_span);
}
let seen = grouped_seen.entry(group_span).or_default();
@@ -187,7 +189,7 @@ impl NoUnmodifiedLoopCondition {
}
let mut modified_cache: FxHashMap<SymbolId, bool> = FxHashMap::default();
let mut diagnostics: Vec<NodeId> = vec![];
let mut diagnostics: SmallVec<[NodeId; 2]> = SmallVec::new();
let mut reported_symbols: FxHashSet<SymbolId> = FxHashSet::default();
for (symbol_id, reference_node_id, report_independently) in standalone_symbols {
@@ -330,8 +332,8 @@ struct ConditionSymbolsCollector<'a, 'ctx> {
ctx: &'ctx LintContext<'a>,
check_conditional_expressions: bool,
conditional_expression_depth: u32,
symbols: Vec<ConditionSymbolInfo>,
group_stack: Vec<Span>,
symbols: SmallVec<[ConditionSymbolInfo; 2]>,
group_stack: SmallVec<[Span; 4]>,
dynamic_groups: FxHashSet<Span>,
}
@@ -341,8 +343,8 @@ impl<'a, 'ctx> ConditionSymbolsCollector<'a, 'ctx> {
ctx,
check_conditional_expressions,
conditional_expression_depth: 0,
symbols: vec![],
group_stack: vec![],
symbols: SmallVec::new(),
group_stack: SmallVec::new(),
dynamic_groups: FxHashSet::default(),
}
}