Skip to content

Commit 35c0726

Browse files
committed
perf(linter): support getting as_member_expression_kind() variants
1 parent 44bc4df commit 35c0726

File tree

2 files changed

+88
-11
lines changed

2 files changed

+88
-11
lines changed

crates/oxc_linter/src/generated/rule_runner_impls.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,11 @@ impl RuleRunner for crate::rules::eslint::no_irregular_whitespace::NoIrregularWh
446446
}
447447

448448
impl RuleRunner for crate::rules::eslint::no_iterator::NoIterator {
449-
const NODE_TYPES: Option<&AstTypesBitset> = None;
449+
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
450+
AstType::ComputedMemberExpression,
451+
AstType::PrivateFieldExpression,
452+
AstType::StaticMemberExpression,
453+
]));
450454
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
451455
}
452456

@@ -572,7 +576,11 @@ impl RuleRunner for crate::rules::eslint::no_plusplus::NoPlusplus {
572576
}
573577

574578
impl RuleRunner for crate::rules::eslint::no_proto::NoProto {
575-
const NODE_TYPES: Option<&AstTypesBitset> = None;
579+
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
580+
AstType::ComputedMemberExpression,
581+
AstType::PrivateFieldExpression,
582+
AstType::StaticMemberExpression,
583+
]));
576584
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
577585
}
578586

@@ -1201,7 +1209,11 @@ impl RuleRunner for crate::rules::jest::no_confusing_set_timeout::NoConfusingSet
12011209
}
12021210

12031211
impl RuleRunner for crate::rules::jest::no_deprecated_functions::NoDeprecatedFunctions {
1204-
const NODE_TYPES: Option<&AstTypesBitset> = None;
1212+
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
1213+
AstType::ComputedMemberExpression,
1214+
AstType::PrivateFieldExpression,
1215+
AstType::StaticMemberExpression,
1216+
]));
12051217
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
12061218
}
12071219

@@ -2063,7 +2075,11 @@ impl RuleRunner for crate::rules::promise::prefer_catch::PreferCatch {
20632075
}
20642076

20652077
impl RuleRunner for crate::rules::promise::spec_only::SpecOnly {
2066-
const NODE_TYPES: Option<&AstTypesBitset> = None;
2078+
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
2079+
AstType::ComputedMemberExpression,
2080+
AstType::PrivateFieldExpression,
2081+
AstType::StaticMemberExpression,
2082+
]));
20672083
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
20682084
}
20692085

@@ -3008,7 +3024,11 @@ impl RuleRunner for crate::rules::unicorn::no_array_sort::NoArraySort {
30083024
}
30093025

30103026
impl RuleRunner for crate::rules::unicorn::no_await_expression_member::NoAwaitExpressionMember {
3011-
const NODE_TYPES: Option<&AstTypesBitset> = None;
3027+
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
3028+
AstType::ComputedMemberExpression,
3029+
AstType::PrivateFieldExpression,
3030+
AstType::StaticMemberExpression,
3031+
]));
30123032
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
30133033
}
30143034

tasks/linter_codegen/src/let_else_detector.rs

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use syn::{Expr, Pat, Stmt};
33
use crate::{
44
CollectionResult,
55
node_type_set::NodeTypeSet,
6-
utils::{astkind_variant_from_path, is_node_kind_call},
6+
utils::{astkind_variant_from_path, find_impl_function, is_node_kind_call},
77
};
88

99
/// Detects top-level `let AstKind::... = node.kind() else { return; }` patterns in the `run` method.
@@ -42,9 +42,9 @@ impl LetElseDetector {
4242
if is_node_kind_as_call(&init.expr) {
4343
// If the initializer is `node.kind().as_<variant>()`, extract that variant.
4444
if let Expr::MethodCall(mc) = &*init.expr
45-
&& let Some(variant) = extract_variant_from_as_call(mc)
45+
&& let Some(variants) = extract_variants_from_as_call(mc)
4646
{
47-
detector.node_types.insert(variant);
47+
detector.node_types.extend(variants);
4848
}
4949
} else {
5050
// Otherwise, the initializer is `node.kind()`, so extract from the pattern.
@@ -88,7 +88,7 @@ pub fn is_node_kind_as_call(expr: &Expr) -> bool {
8888
false
8989
}
9090

91-
fn extract_variant_from_as_call(mc: &syn::ExprMethodCall) -> Option<String> {
91+
fn extract_variants_from_as_call(mc: &syn::ExprMethodCall) -> Option<NodeTypeSet> {
9292
// Looking for `node.kind().as_<snake_case_variant>()`
9393
let method_ident = mc.method.to_string();
9494
if !method_ident.starts_with("as_") || !mc.args.is_empty() {
@@ -100,9 +100,55 @@ fn extract_variant_from_as_call(mc: &syn::ExprMethodCall) -> Option<String> {
100100
}
101101
let snake_variant = &method_ident[3..]; // strip `as_`
102102
if snake_variant == "member_expression_kind" {
103-
return None;
103+
return get_member_expression_kinds();
104+
}
105+
let mut node_type_set = NodeTypeSet::new();
106+
node_type_set.insert(snake_to_pascal_case(snake_variant));
107+
Some(node_type_set)
108+
}
109+
110+
/// Fetches the current list of variants that can be returned by `AstKind::as_member_expression_kind()`.
111+
/// We read the source file to avoid hardcoding the list here and ensure this will stay updated.
112+
fn get_member_expression_kinds() -> Option<NodeTypeSet> {
113+
// Read crates/oxc_ast/src/ast_kind_impl.rs and extract all variants in `as_member_expression_kind` function
114+
let ast_kind_impl_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
115+
.parent()?
116+
.parent()?
117+
.join("crates")
118+
.join("oxc_ast")
119+
.join("src")
120+
.join("ast_kind_impl.rs");
121+
let content = std::fs::read_to_string(ast_kind_impl_path).ok()?;
122+
let syntax = syn::parse_file(&content).ok()?;
123+
let mut node_type_set = NodeTypeSet::new();
124+
for item in syntax.items {
125+
if let syn::Item::Impl(impl_block) = item
126+
&& let syn::Type::Path(type_path) = impl_block.self_ty.as_ref()
127+
&& type_path.path.segments.last()?.ident == "AstKind"
128+
{
129+
let impl_fn = find_impl_function(&impl_block, "as_member_expression_kind");
130+
if let Some(impl_fn) = impl_fn {
131+
// Look for `match self { ... }` inside the function body
132+
if impl_fn.block.stmts.len() != 1 {
133+
return None;
134+
}
135+
let stmt = &impl_fn.block.stmts[0];
136+
if let Stmt::Expr(Expr::Match(match_expr), _) = stmt {
137+
for arm in &match_expr.arms {
138+
if let Pat::TupleStruct(ts) = &arm.pat
139+
&& let Some(variant) = self_astkind_variant_from_path(&ts.path)
140+
{
141+
node_type_set.insert(variant);
142+
}
143+
}
144+
if !node_type_set.is_empty() {
145+
return Some(node_type_set);
146+
}
147+
}
148+
}
149+
}
104150
}
105-
Some(snake_to_pascal_case(snake_variant))
151+
None
106152
}
107153

108154
fn snake_to_pascal_case(s: &str) -> String {
@@ -117,3 +163,14 @@ fn snake_to_pascal_case(s: &str) -> String {
117163
})
118164
.collect()
119165
}
166+
167+
pub fn self_astkind_variant_from_path(path: &syn::Path) -> Option<String> {
168+
// Expect `Self::Variant`
169+
if path.segments.len() != 2 {
170+
return None;
171+
}
172+
if path.segments[0].ident != "Self" {
173+
return None;
174+
}
175+
Some(path.segments[1].ident.to_string())
176+
}

0 commit comments

Comments
 (0)