Skip to content

Commit 83303b9

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

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)