Skip to content

Commit 4440516

Browse files
committed
perf(linter): refactor rules to take advantage of node type skipping (#14519)
Updating rules to take advantage of the node type skipping so that they don't need to be run on every file. I forgot to split this out, so this is a composite PR of several rules, which I'll try to avoid in the future. Some symbol-based rules have been refactored to search for the node type first and then look for symbols based on that. For example, if there are no imports in the file, we can skip `no-import-assign` instead of looping over all symbols and looking for an import.
1 parent 198f2e9 commit 4440516

File tree

7 files changed

+190
-140
lines changed

7 files changed

+190
-140
lines changed

crates/oxc_linter/src/generated/rule_runner_impls.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ impl RuleRunner for crate::rules::eslint::no_empty_function::NoEmptyFunction {
270270
}
271271

272272
impl RuleRunner for crate::rules::eslint::no_empty_pattern::NoEmptyPattern {
273-
const NODE_TYPES: Option<&AstTypesBitset> = None;
273+
const NODE_TYPES: Option<&AstTypesBitset> =
274+
Some(&AstTypesBitset::from_types(&[AstType::ArrayPattern, AstType::ObjectPattern]));
274275
}
275276

276277
impl RuleRunner for crate::rules::eslint::no_empty_static_block::NoEmptyStaticBlock {
@@ -288,7 +289,8 @@ impl RuleRunner for crate::rules::eslint::no_eval::NoEval {
288289
}
289290

290291
impl RuleRunner for crate::rules::eslint::no_ex_assign::NoExAssign {
291-
const NODE_TYPES: Option<&AstTypesBitset> = None;
292+
const NODE_TYPES: Option<&AstTypesBitset> =
293+
Some(&AstTypesBitset::from_types(&[AstType::CatchParameter]));
292294
}
293295

294296
impl RuleRunner for crate::rules::eslint::no_extend_native::NoExtendNative {
@@ -305,7 +307,8 @@ impl RuleRunner for crate::rules::eslint::no_extra_boolean_cast::NoExtraBooleanC
305307
}
306308

307309
impl RuleRunner for crate::rules::eslint::no_extra_label::NoExtraLabel {
308-
const NODE_TYPES: Option<&AstTypesBitset> = None;
310+
const NODE_TYPES: Option<&AstTypesBitset> =
311+
Some(&AstTypesBitset::from_types(&[AstType::BreakStatement, AstType::ContinueStatement]));
309312
}
310313

311314
impl RuleRunner for crate::rules::eslint::no_fallthrough::NoFallthrough {
@@ -314,19 +317,22 @@ impl RuleRunner for crate::rules::eslint::no_fallthrough::NoFallthrough {
314317
}
315318

316319
impl RuleRunner for crate::rules::eslint::no_func_assign::NoFuncAssign {
317-
const NODE_TYPES: Option<&AstTypesBitset> = None;
320+
const NODE_TYPES: Option<&AstTypesBitset> =
321+
Some(&AstTypesBitset::from_types(&[AstType::Function]));
318322
}
319323

320324
impl RuleRunner for crate::rules::eslint::no_global_assign::NoGlobalAssign {
321325
const NODE_TYPES: Option<&AstTypesBitset> = None;
322326
}
323327

324328
impl RuleRunner for crate::rules::eslint::no_import_assign::NoImportAssign {
325-
const NODE_TYPES: Option<&AstTypesBitset> = None;
329+
const NODE_TYPES: Option<&AstTypesBitset> =
330+
Some(&AstTypesBitset::from_types(&[AstType::ImportDeclaration]));
326331
}
327332

328333
impl RuleRunner for crate::rules::eslint::no_inner_declarations::NoInnerDeclarations {
329-
const NODE_TYPES: Option<&AstTypesBitset> = None;
334+
const NODE_TYPES: Option<&AstTypesBitset> =
335+
Some(&AstTypesBitset::from_types(&[AstType::Function, AstType::VariableDeclaration]));
330336
}
331337

332338
impl RuleRunner for crate::rules::eslint::no_invalid_regexp::NoInvalidRegexp {

crates/oxc_linter/src/rules/eslint/no_empty_pattern.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,19 @@ declare_oxc_lint!(
7676

7777
impl Rule for NoEmptyPattern {
7878
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
79-
let (pattern_type, span) = match node.kind() {
80-
AstKind::ArrayPattern(array) if array.is_empty() => ("array", array.span),
81-
AstKind::ObjectPattern(object) if object.is_empty() => ("object", object.span),
82-
_ => return,
83-
};
84-
ctx.diagnostic(no_empty_pattern_diagnostic(pattern_type, span));
79+
match node.kind() {
80+
AstKind::ArrayPattern(array) => {
81+
if array.is_empty() {
82+
ctx.diagnostic(no_empty_pattern_diagnostic("array", array.span));
83+
}
84+
}
85+
AstKind::ObjectPattern(object) => {
86+
if object.is_empty() {
87+
ctx.diagnostic(no_empty_pattern_diagnostic("object", object.span));
88+
}
89+
}
90+
_ => {}
91+
}
8592
}
8693
}
8794

crates/oxc_linter/src/rules/eslint/no_ex_assign.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
use oxc_ast::AstKind;
12
use oxc_diagnostics::OxcDiagnostic;
23
use oxc_macros::declare_oxc_lint;
3-
use oxc_semantic::SymbolId;
4+
use oxc_semantic::AstNode;
45
use oxc_span::Span;
56

67
use crate::{context::LintContext, rule::Rule};
@@ -50,14 +51,21 @@ declare_oxc_lint!(
5051
);
5152

5253
impl Rule for NoExAssign {
53-
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
54-
let symbol_table = ctx.scoping();
55-
if symbol_table.symbol_flags(symbol_id).is_catch_variable() {
56-
for reference in symbol_table.get_resolved_references(symbol_id) {
57-
if reference.is_write() {
58-
ctx.diagnostic(no_ex_assign_diagnostic(
59-
ctx.semantic().reference_span(reference),
60-
));
54+
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
55+
if let AstKind::CatchParameter(catch_param) = node.kind() {
56+
let idents = catch_param.pattern.get_binding_identifiers();
57+
let symbol_table = ctx.scoping();
58+
for ident in idents {
59+
let symbol_id = ident.symbol_id();
60+
// This symbol _should_ always be considered a catch variable (since we got it from a catch param),
61+
// but we check in debug mode just to be sure.
62+
debug_assert!(symbol_table.symbol_flags(symbol_id).is_catch_variable());
63+
for reference in symbol_table.get_resolved_references(symbol_id) {
64+
if reference.is_write() {
65+
ctx.diagnostic(no_ex_assign_diagnostic(
66+
ctx.semantic().reference_span(reference),
67+
));
68+
}
6169
}
6270
}
6371
}

crates/oxc_linter/src/rules/eslint/no_extra_label.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,18 @@ declare_oxc_lint!(
9090

9191
impl Rule for NoExtraLabel {
9292
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
93-
if let AstKind::BreakStatement(break_stmt) = node.kind()
94-
&& let Some(label) = &break_stmt.label
95-
{
96-
report_label_if_extra(label, node, ctx);
97-
}
98-
if let AstKind::ContinueStatement(cont_stmt) = node.kind()
99-
&& let Some(label) = &cont_stmt.label
100-
{
101-
report_label_if_extra(label, node, ctx);
93+
match node.kind() {
94+
AstKind::BreakStatement(break_stmt) => {
95+
if let Some(label) = &break_stmt.label {
96+
report_label_if_extra(label, node, ctx);
97+
}
98+
}
99+
AstKind::ContinueStatement(cont_stmt) => {
100+
if let Some(label) = &cont_stmt.label {
101+
report_label_if_extra(label, node, ctx);
102+
}
103+
}
104+
_ => {}
102105
}
103106
}
104107
}

crates/oxc_linter/src/rules/eslint/no_func_assign.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use oxc_ast::AstKind;
22
use oxc_diagnostics::OxcDiagnostic;
33
use oxc_macros::declare_oxc_lint;
4-
use oxc_semantic::SymbolId;
4+
use oxc_semantic::AstNode;
55
use oxc_span::Span;
66

77
use crate::{context::LintContext, rule::Rule};
@@ -67,14 +67,17 @@ declare_oxc_lint!(
6767
);
6868

6969
impl Rule for NoFuncAssign {
70-
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
71-
let symbol_table = ctx.scoping();
72-
let decl = symbol_table.symbol_declaration(symbol_id);
73-
if let AstKind::Function(_) = ctx.nodes().kind(decl) {
70+
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
71+
if let AstKind::Function(func) = node.kind() {
72+
let (func_name, symbol_id) = match &func.id {
73+
Some(id) => (id.name.as_str(), id.symbol_id()),
74+
None => return,
75+
};
76+
let symbol_table = ctx.scoping();
7477
for reference in symbol_table.get_resolved_references(symbol_id) {
7578
if reference.is_write() {
7679
ctx.diagnostic(no_func_assign_diagnostic(
77-
symbol_table.symbol_name(symbol_id),
80+
func_name,
7881
ctx.semantic().reference_span(reference),
7982
));
8083
}

crates/oxc_linter/src/rules/eslint/no_import_assign.rs

Lines changed: 82 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use oxc_ast::{
44
};
55
use oxc_diagnostics::OxcDiagnostic;
66
use oxc_macros::declare_oxc_lint;
7-
use oxc_semantic::{AstNode, NodeId, Reference, SymbolId};
7+
use oxc_semantic::{AstNode, NodeId, Reference};
88
use oxc_span::{GetSpan, Span};
99
use oxc_syntax::operator::UnaryOperator;
1010

@@ -54,78 +54,95 @@ const REFLECT_MUTATION_METHODS: [&str; 4] =
5454
["defineProperty", "deleteProperty", "set", "setPrototypeOf"];
5555

5656
impl Rule for NoImportAssign {
57-
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
58-
let symbol_table = ctx.scoping();
59-
if symbol_table.symbol_flags(symbol_id).is_import() {
60-
let kind = ctx.nodes().kind(symbol_table.symbol_declaration(symbol_id));
61-
let is_namespace_specifier = matches!(kind, AstKind::ImportNamespaceSpecifier(_));
62-
for reference in symbol_table.get_resolved_references(symbol_id) {
63-
if is_namespace_specifier {
64-
let parent_node = ctx.nodes().parent_node(reference.node_id());
65-
if parent_node.kind().is_member_expression_kind() {
66-
let expr = parent_node.kind();
67-
let parent_parent_node = ctx.nodes().parent_node(parent_node.id());
68-
let is_unary_expression_with_delete_operator = |kind| matches!(kind, AstKind::UnaryExpression(expr) if expr.operator == UnaryOperator::Delete);
69-
let parent_parent_kind = parent_parent_node.kind();
70-
if (matches!(parent_parent_kind, AstKind::IdentifierReference(_))
71-
|| is_unary_expression_with_delete_operator(parent_parent_kind)
72-
|| matches!(parent_parent_kind, AstKind::ChainExpression(_) if is_unary_expression_with_delete_operator(ctx.nodes().parent_kind(parent_parent_node.id()))))
73-
&& let Some((span, _)) = match expr {
74-
AstKind::StaticMemberExpression(expr) => {
75-
Some(expr.static_property_info())
57+
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
58+
if let AstKind::ImportDeclaration(import_decl) = node.kind() {
59+
let symbol_table = ctx.scoping();
60+
if let Some(specifiers) = &import_decl.specifiers {
61+
for specifier in specifiers {
62+
let symbol_id = specifier.local().symbol_id();
63+
let is_namespace_specifier = matches!(
64+
specifier,
65+
oxc_ast::ast::ImportDeclarationSpecifier::ImportNamespaceSpecifier(_)
66+
);
67+
for reference in symbol_table.get_resolved_references(symbol_id) {
68+
if is_namespace_specifier {
69+
let parent_node = ctx.nodes().parent_node(reference.node_id());
70+
if parent_node.kind().is_member_expression_kind() {
71+
let expr = parent_node.kind();
72+
let parent_parent_node = ctx.nodes().parent_node(parent_node.id());
73+
let is_unary_expression_with_delete_operator = |kind| {
74+
matches!(
75+
kind,
76+
AstKind::UnaryExpression(expr)
77+
if expr.operator == UnaryOperator::Delete
78+
)
79+
};
80+
let parent_parent_kind = parent_parent_node.kind();
81+
if (matches!(parent_parent_kind, AstKind::IdentifierReference(_))
82+
|| is_unary_expression_with_delete_operator(parent_parent_kind)
83+
|| matches!(parent_parent_kind, AstKind::ChainExpression(_) if is_unary_expression_with_delete_operator(ctx.nodes().parent_kind(parent_parent_node.id()))))
84+
&& let Some((span, _)) = match expr {
85+
AstKind::StaticMemberExpression(expr) => {
86+
Some(expr.static_property_info())
87+
}
88+
AstKind::ComputedMemberExpression(expr) => {
89+
expr.static_property_info()
90+
}
91+
_ => return,
92+
}
93+
&& span != ctx.semantic().reference_span(reference)
94+
{
95+
return ctx
96+
.diagnostic(no_import_assign_diagnostic(expr.span()));
7697
}
77-
AstKind::ComputedMemberExpression(expr) => {
78-
expr.static_property_info()
98+
// Check for assignment to namespace property
99+
match expr {
100+
AstKind::StaticMemberExpression(member_expr) => {
101+
let condition_met = is_assignment_condition_met(
102+
&parent_parent_kind,
103+
parent_node.span(),
104+
true, // is_static
105+
);
106+
check_namespace_member_assignment(
107+
&member_expr.object,
108+
parent_node,
109+
reference,
110+
ctx,
111+
condition_met,
112+
);
113+
}
114+
AstKind::ComputedMemberExpression(member_expr) => {
115+
let condition_met = is_assignment_condition_met(
116+
&parent_parent_kind,
117+
parent_node.span(),
118+
false, // is_static
119+
);
120+
check_namespace_member_assignment(
121+
&member_expr.object,
122+
parent_node,
123+
reference,
124+
ctx,
125+
condition_met,
126+
);
127+
}
128+
_ => {}
79129
}
80-
_ => return,
81130
}
82-
&& span != ctx.semantic().reference_span(reference)
83-
{
84-
return ctx.diagnostic(no_import_assign_diagnostic(expr.span()));
85131
}
86-
// Check for assignment to namespace property
87-
match expr {
88-
AstKind::StaticMemberExpression(member_expr) => {
89-
let condition_met = is_assignment_condition_met(
90-
&parent_parent_kind,
91-
parent_node.span(),
92-
true, // is_static
93-
);
94-
check_namespace_member_assignment(
95-
&member_expr.object,
96-
parent_node,
97-
reference,
98-
ctx,
99-
condition_met,
100-
);
101-
}
102-
AstKind::ComputedMemberExpression(member_expr) => {
103-
let condition_met = is_assignment_condition_met(
104-
&parent_parent_kind,
105-
parent_node.span(),
106-
false, // is_static
107-
);
108-
check_namespace_member_assignment(
109-
&member_expr.object,
110-
parent_node,
111-
reference,
132+
133+
if reference.is_write()
134+
|| (is_namespace_specifier
135+
&& is_argument_of_well_known_mutation_function(
136+
reference.node_id(),
112137
ctx,
113-
condition_met,
114-
);
115-
}
116-
_ => {}
138+
))
139+
{
140+
ctx.diagnostic(no_import_assign_diagnostic(
141+
ctx.semantic().reference_span(reference),
142+
));
117143
}
118144
}
119145
}
120-
121-
if reference.is_write()
122-
|| (is_namespace_specifier
123-
&& is_argument_of_well_known_mutation_function(reference.node_id(), ctx))
124-
{
125-
ctx.diagnostic(no_import_assign_diagnostic(
126-
ctx.semantic().reference_span(reference),
127-
));
128-
}
129146
}
130147
}
131148
}

0 commit comments

Comments
 (0)