Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 43 additions & 18 deletions crates/oxc_linter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ impl Linter {
let should_run_on_jest_node =
ctx_host.plugins().has_test() && ctx_host.frameworks().is_test();

let execute_rules = |with_ast_kind_filtering: bool| {
let execute_rules = |with_runtime_optimization: bool| {
// IMPORTANT: We have two branches here for performance reasons:
//
// 1) Branch where we iterate over each node, then each rule
Expand Down Expand Up @@ -217,22 +217,32 @@ impl Linter {

for (rule, ctx) in &rules {
let rule = *rule;
let run_info = rule.run_info();
// Collect node type information for rules. In large files, benchmarking showed it was worth
// collecting rules into buckets by AST node type to avoid iterating over all rules for each node.
if with_ast_kind_filtering && let Some(ast_types) = rule.types_info() {
if with_runtime_optimization
&& let Some(ast_types) = rule.types_info()
&& run_info.is_run_implemented()
{
for ty in ast_types {
rules_by_ast_type[ty as usize].push((rule, ctx));
}
} else {
rules_any_ast_type.push((rule, ctx));
}

rule.run_once(ctx);
if !with_runtime_optimization || run_info.is_run_once_implemented() {
rule.run_once(ctx);
}
}

for symbol in semantic.scoping().symbol_ids() {
for (rule, ctx) in &rules {
rule.run_on_symbol(symbol, ctx);
if !with_runtime_optimization
|| rule.run_info().is_run_on_symbol_implemented()
{
rule.run_on_symbol(symbol, ctx);
}
}
}

Expand All @@ -249,33 +259,48 @@ impl Linter {
if should_run_on_jest_node {
for jest_node in iter_possible_jest_call_node(semantic) {
for (rule, ctx) in &rules {
rule.run_on_jest_node(&jest_node, ctx);
if !with_runtime_optimization
|| rule.run_info().is_run_on_jest_node_implemented()
{
rule.run_on_jest_node(&jest_node, ctx);
}
}
}
}
} else {
for (rule, ctx) in &rules {
rule.run_once(ctx);
let run_info = rule.run_info();
if !with_runtime_optimization || run_info.is_run_once_implemented() {
rule.run_once(ctx);
}

for symbol in semantic.scoping().symbol_ids() {
rule.run_on_symbol(symbol, ctx);
if !with_runtime_optimization || run_info.is_run_on_symbol_implemented() {
for symbol in semantic.scoping().symbol_ids() {
rule.run_on_symbol(symbol, ctx);
}
}

// For smaller files, benchmarking showed it was faster to iterate over all rules and just check the
// node types as we go, rather than pre-bucketing rules by AST node type and doing extra allocations.
if with_ast_kind_filtering && let Some(ast_types) = rule.types_info() {
for node in semantic.nodes() {
if ast_types.has(node.kind().ty()) {
if !with_runtime_optimization || run_info.is_run_implemented() {
// For smaller files, benchmarking showed it was faster to iterate over all rules and just check the
// node types as we go, rather than pre-bucketing rules by AST node type and doing extra allocations.
if with_runtime_optimization && let Some(ast_types) = rule.types_info()
{
for node in semantic.nodes() {
if ast_types.has(node.kind().ty()) {
rule.run(node, ctx);
}
}
} else {
for node in semantic.nodes() {
rule.run(node, ctx);
}
}
} else {
for node in semantic.nodes() {
rule.run(node, ctx);
}
}

if should_run_on_jest_node {
if should_run_on_jest_node
&& (!with_runtime_optimization
|| run_info.is_run_on_jest_node_implemented())
{
for jest_node in iter_possible_jest_call_node(semantic) {
rule.run_on_jest_node(&jest_node, ctx);
}
Expand Down
18 changes: 18 additions & 0 deletions crates/oxc_linter/src/rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,24 @@ pub enum RuleRunFunctionsImplemented {
RunOnJestNode,
}

impl RuleRunFunctionsImplemented {
pub fn is_run_implemented(self) -> bool {
matches!(self, Self::Run | Self::Unknown)
}

pub fn is_run_once_implemented(self) -> bool {
matches!(self, Self::RunOnce | Self::Unknown)
}

pub fn is_run_on_symbol_implemented(self) -> bool {
matches!(self, Self::RunOnSymbol | Self::Unknown)
}

pub fn is_run_on_jest_node_implemented(self) -> bool {
matches!(self, Self::RunOnJestNode | Self::Unknown)
}
}

pub trait RuleMeta {
const NAME: &'static str;

Expand Down
Loading