Skip to content

Commit 31c1afa

Browse files
committed
perf(linter): refactor rules to take advantage of node type skipping
1 parent 198f2e9 commit 31c1afa

File tree

7 files changed

+163
-104
lines changed

7 files changed

+163
-104
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: 13 additions & 10 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
}
@@ -179,7 +182,7 @@ fn test() {
179182
"A: switch (a) { case 0: break A; }",
180183
"X: while (x) { A: switch (a) { case 0: break A; } }",
181184
"X: switch (a) { case 0: A: while (b) break A; }",
182-
"
185+
"
183186
A: while (true) {
184187
break A;
185188
while (true) {

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: 90 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -54,79 +54,103 @@ 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+
match node.kind() {
59+
AstKind::ImportDeclaration(import_decl) => {
60+
let symbol_table = ctx.scoping();
61+
if let Some(specifiers) = &import_decl.specifiers {
62+
for specifier in specifiers {
63+
let symbol_id = specifier.local().symbol_id();
64+
let is_namespace_specifier = matches!(
65+
specifier,
66+
oxc_ast::ast::ImportDeclarationSpecifier::ImportNamespaceSpecifier(_)
67+
);
68+
for reference in symbol_table.get_resolved_references(symbol_id) {
69+
if is_namespace_specifier {
70+
let parent_node = ctx.nodes().parent_node(reference.node_id());
71+
if parent_node.kind().is_member_expression_kind() {
72+
let expr = parent_node.kind();
73+
let parent_parent_node =
74+
ctx.nodes().parent_node(parent_node.id());
75+
let is_unary_expression_with_delete_operator = |kind| {
76+
matches!(
77+
kind,
78+
AstKind::UnaryExpression(expr)
79+
if expr.operator == UnaryOperator::Delete
80+
)
81+
};
82+
let parent_parent_kind = parent_parent_node.kind();
83+
if (matches!(
84+
parent_parent_kind,
85+
AstKind::IdentifierReference(_)
86+
) || is_unary_expression_with_delete_operator(
87+
parent_parent_kind,
88+
) || matches!(parent_parent_kind, AstKind::ChainExpression(_) if is_unary_expression_with_delete_operator(ctx.nodes().parent_kind(parent_parent_node.id()))))
89+
&& let Some((span, _)) = match expr {
90+
AstKind::StaticMemberExpression(expr) => {
91+
Some(expr.static_property_info())
92+
}
93+
AstKind::ComputedMemberExpression(expr) => {
94+
expr.static_property_info()
95+
}
96+
_ => return,
97+
}
98+
&& span != ctx.semantic().reference_span(reference)
99+
{
100+
return ctx
101+
.diagnostic(no_import_assign_diagnostic(expr.span()));
102+
}
103+
// Check for assignment to namespace property
104+
match expr {
105+
AstKind::StaticMemberExpression(member_expr) => {
106+
let condition_met = is_assignment_condition_met(
107+
&parent_parent_kind,
108+
parent_node.span(),
109+
true, // is_static
110+
);
111+
check_namespace_member_assignment(
112+
&member_expr.object,
113+
parent_node,
114+
reference,
115+
ctx,
116+
condition_met,
117+
);
118+
}
119+
AstKind::ComputedMemberExpression(member_expr) => {
120+
let condition_met = is_assignment_condition_met(
121+
&parent_parent_kind,
122+
parent_node.span(),
123+
false, // is_static
124+
);
125+
check_namespace_member_assignment(
126+
&member_expr.object,
127+
parent_node,
128+
reference,
129+
ctx,
130+
condition_met,
131+
);
132+
}
133+
_ => {}
134+
}
76135
}
77-
AstKind::ComputedMemberExpression(expr) => {
78-
expr.static_property_info()
79-
}
80-
_ => return,
81-
}
82-
&& span != ctx.semantic().reference_span(reference)
83-
{
84-
return ctx.diagnostic(no_import_assign_diagnostic(expr.span()));
85-
}
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-
);
101136
}
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,
112-
ctx,
113-
condition_met,
114-
);
137+
138+
if reference.is_write()
139+
|| (is_namespace_specifier
140+
&& is_argument_of_well_known_mutation_function(
141+
reference.node_id(),
142+
ctx,
143+
))
144+
{
145+
ctx.diagnostic(no_import_assign_diagnostic(
146+
ctx.semantic().reference_span(reference),
147+
));
115148
}
116-
_ => {}
117149
}
118150
}
119151
}
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-
}
129152
}
153+
_ => {}
130154
}
131155
}
132156
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ impl Rule for NoInnerDeclarations {
100100
if self.config == NoInnerDeclarationsConfig::Functions || !decl.kind.is_var() {
101101
return;
102102
}
103+
104+
self.check_rule(node, ctx);
103105
}
104106
AstKind::Function(func) => {
105107
if !func.is_function_declaration() {
@@ -118,10 +120,16 @@ impl Rule for NoInnerDeclarations {
118120
return;
119121
}
120122
}
123+
124+
self.check_rule(node, ctx);
121125
}
122-
_ => return,
126+
_ => {}
123127
}
128+
}
129+
}
124130

131+
impl NoInnerDeclarations {
132+
fn check_rule<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
125133
let parent_node = ctx.nodes().parent_node(node.id());
126134
if matches!(
127135
parent_node.kind(),

0 commit comments

Comments
 (0)