Skip to content

Commit a9f68b2

Browse files
committed
perf(linter): move up rule retainment into initial collection to reduce allocation size (#14822)
In the current implementation, we collect all rules, and then remove rules that only run on specific node types and that have no relevant node types in the current file. This leads to a larger allocation size because we only remove the rules after we've already collected all of the rules. This PR switches the order so that we filter out the rules before we actually collect them into a `Vec`. The downside of this is I can't see an easy way currently to make this work with the debug assertions we have for ensuring the runtime optimizations don't affect the output. The benefit of this is that the constant overhead for small files is much less, as shown by the benchmarks.
1 parent e41dee5 commit a9f68b2

File tree

1 file changed

+19
-19
lines changed

1 file changed

+19
-19
lines changed

crates/oxc_linter/src/lib.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -173,32 +173,32 @@ impl Linter {
173173
.is_some_and(|ext| LINT_PARTIAL_LOADER_EXTENSIONS.iter().any(|e| e == &ext));
174174

175175
loop {
176-
let mut rules = rules
176+
let semantic = ctx_host.semantic();
177+
let rules = rules
177178
.iter()
178-
.filter(|(rule, _)| rule.should_run(&ctx_host) && !rule.is_tsgolint_rule())
179+
.filter(|(rule, _)| {
180+
if rule.is_tsgolint_rule() {
181+
return false;
182+
}
183+
184+
// If only the `run` function is implemented, we can skip running the file entirely if the current
185+
// file does not contain any of the relevant AST node types.
186+
if rule.run_info() == RuleRunFunctionsImplemented::Run
187+
&& let Some(ast_types) = rule.types_info()
188+
&& !semantic.nodes().contains_any(ast_types)
189+
{
190+
return false;
191+
}
192+
193+
rule.should_run(&ctx_host)
194+
})
179195
.map(|(rule, severity)| (rule, Rc::clone(&ctx_host).spawn(rule, *severity)))
180196
.collect::<Vec<_>>();
181197

182-
let semantic = ctx_host.semantic();
183-
184198
let should_run_on_jest_node =
185199
ctx_host.plugins().has_test() && ctx_host.frameworks().is_test();
186200

187-
let mut execute_rules = |with_runtime_optimization: bool| {
188-
// If only the `run` function is implemented, we can skip running the file entirely if the current
189-
// file does not contain any of the relevant AST node types.
190-
if with_runtime_optimization {
191-
rules.retain(|(rule, _)| {
192-
let run_info = rule.run_info();
193-
if run_info == RuleRunFunctionsImplemented::Run
194-
&& let Some(ast_types) = rule.types_info()
195-
{
196-
semantic.nodes().contains_any(ast_types)
197-
} else {
198-
true
199-
}
200-
});
201-
}
201+
let execute_rules = |with_runtime_optimization: bool| {
202202
// IMPORTANT: We have two branches here for performance reasons:
203203
//
204204
// 1) Branch where we iterate over each node, then each rule

0 commit comments

Comments
 (0)