Skip to content

Commit d1f1f6f

Browse files
committed
perf(linter): support as_* for node type codegen
1 parent 89cb41e commit d1f1f6f

File tree

2 files changed

+66
-6
lines changed

2 files changed

+66
-6
lines changed

crates/oxc_linter/src/generated/rule_runner_impls.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,8 @@ impl RuleRunner for crate::rules::eslint::no_dupe_keys::NoDupeKeys {
305305
}
306306

307307
impl RuleRunner for crate::rules::eslint::no_duplicate_case::NoDuplicateCase {
308-
const NODE_TYPES: Option<&AstTypesBitset> = None;
308+
const NODE_TYPES: Option<&AstTypesBitset> =
309+
Some(&AstTypesBitset::from_types(&[AstType::SwitchStatement]));
309310
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
310311
}
311312

@@ -1968,12 +1969,14 @@ impl RuleRunner for crate::rules::promise::catch_or_return::CatchOrReturn {
19681969
}
19691970

19701971
impl RuleRunner for crate::rules::promise::no_callback_in_promise::NoCallbackInPromise {
1971-
const NODE_TYPES: Option<&AstTypesBitset> = None;
1972+
const NODE_TYPES: Option<&AstTypesBitset> =
1973+
Some(&AstTypesBitset::from_types(&[AstType::CallExpression]));
19721974
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
19731975
}
19741976

19751977
impl RuleRunner for crate::rules::promise::no_multiple_resolved::NoMultipleResolved {
1976-
const NODE_TYPES: Option<&AstTypesBitset> = None;
1978+
const NODE_TYPES: Option<&AstTypesBitset> =
1979+
Some(&AstTypesBitset::from_types(&[AstType::NewExpression]));
19771980
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
19781981
}
19791982

tasks/linter_codegen/src/let_else_detector.rs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ impl LetElseDetector {
2121
let Stmt::Local(local) = first_stmt else { return None };
2222
// Must have an initializer that is a `node.kind()` call
2323
let Some(init) = &local.init else { return None };
24-
if !is_node_kind_call(&init.expr) {
24+
if !(is_node_kind_call(&init.expr) || is_node_kind_as_call(&init.expr)) {
2525
return None;
2626
}
2727
// Must have a diverging `else` block
@@ -38,8 +38,23 @@ impl LetElseDetector {
3838
}
3939

4040
let mut detector = Self { node_types: NodeTypeSet::new() };
41-
let result = detector.extract_variants_from_pat(&local.pat);
42-
if detector.node_types.is_empty() || result == CollectionResult::Incomplete {
41+
42+
if is_node_kind_as_call(&init.expr) {
43+
// If the initializer is `node.kind().as_<variant>()`, extract that variant.
44+
if let Expr::MethodCall(mc) = &*init.expr
45+
&& let Some(variant) = extract_variant_from_as_call(mc)
46+
{
47+
detector.node_types.insert(variant);
48+
}
49+
} else {
50+
// Otherwise, the initializer is `node.kind()`, so extract from the pattern.
51+
// Expecting `AstKind::Variant` pattern
52+
let result = detector.extract_variants_from_pat(&local.pat);
53+
if result == CollectionResult::Incomplete {
54+
return None;
55+
}
56+
}
57+
if detector.node_types.is_empty() {
4358
return None;
4459
}
4560

@@ -60,3 +75,45 @@ impl LetElseDetector {
6075
}
6176
}
6277
}
78+
79+
/// Checks if is `node.kind().as_some_ast_kind()`
80+
pub fn is_node_kind_as_call(expr: &Expr) -> bool {
81+
if let Expr::MethodCall(mc) = expr
82+
&& mc.method.to_string().starts_with("as_")
83+
&& mc.args.is_empty()
84+
&& is_node_kind_call(&mc.receiver)
85+
{
86+
return true;
87+
}
88+
false
89+
}
90+
91+
fn extract_variant_from_as_call(mc: &syn::ExprMethodCall) -> Option<String> {
92+
// Looking for `node.kind().as_<snake_case_variant>()`
93+
let method_ident = mc.method.to_string();
94+
if !method_ident.starts_with("as_") || !mc.args.is_empty() {
95+
return None;
96+
}
97+
// Receiver must be `node.kind()`
98+
if !is_node_kind_call(&mc.receiver) {
99+
return None;
100+
}
101+
let snake_variant = &method_ident[3..]; // strip `as_`
102+
if snake_variant == "member_expression_kind" {
103+
return None;
104+
}
105+
Some(snake_to_pascal_case(snake_variant))
106+
}
107+
108+
fn snake_to_pascal_case(s: &str) -> String {
109+
s.split('_')
110+
.filter(|seg| !seg.is_empty())
111+
.map(|seg| {
112+
let mut chars = seg.chars();
113+
match chars.next() {
114+
Some(first) => first.to_ascii_uppercase().to_string() + chars.as_str(),
115+
None => String::new(),
116+
}
117+
})
118+
.collect()
119+
}

0 commit comments

Comments
 (0)