diff --git a/crates/oxc_linter/src/ast_util.rs b/crates/oxc_linter/src/ast_util.rs index a501edc92..726ff9465 100644 --- a/crates/oxc_linter/src/ast_util.rs +++ b/crates/oxc_linter/src/ast_util.rs @@ -430,11 +430,7 @@ pub fn get_function_like_declaration<'b>( ctx: &LintContext<'b>, ) -> Option<&'b BindingIdentifier<'b>> { let parent = outermost_paren_parent(node, ctx)?; + let decl = parent.kind().as_variable_declarator()?; - if let AstKind::VariableDeclarator(decl) = parent.kind() { - let ident = decl.id.get_binding_identifier()?; - Some(ident) - } else { - None - } + decl.id.get_binding_identifier() } diff --git a/crates/oxc_linter/src/rules/eslint/default_case.rs b/crates/oxc_linter/src/rules/eslint/default_case.rs index 7cd103449..e3e329dcf 100644 --- a/crates/oxc_linter/src/rules/eslint/default_case.rs +++ b/crates/oxc_linter/src/rules/eslint/default_case.rs @@ -65,25 +65,30 @@ impl Rule for DefaultCase { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::SwitchStatement(switch) = node.kind() { let cases = &switch.cases; - if !cases.is_empty() && !cases.iter().any(|case| case.test.is_none()) { - if let Some(last_case) = cases.last() { - let has_default_comment = ctx - .semantic() - .trivias() - .comments_range(last_case.span.start..switch.span.end) - .last() - .is_some_and(|comment| { - let raw = comment.span.source_text(ctx.semantic().source_text()).trim(); - match &self.comment_pattern { - Some(comment_pattern) => comment_pattern.is_match(raw), - None => raw.eq_ignore_ascii_case("no default"), - } - }); - if !has_default_comment { - ctx.diagnostic(default_case_diagnostic(switch.span)); + if cases.is_empty() || cases.iter().any(|case| case.test.is_none()) { + return; + } + + let Some(last_case) = cases.last() else { + return; + }; + + let has_default_comment = ctx + .semantic() + .trivias() + .comments_range(last_case.span.start..switch.span.end) + .last() + .is_some_and(|comment| { + let raw = comment.span.source_text(ctx.semantic().source_text()).trim(); + match &self.comment_pattern { + Some(comment_pattern) => comment_pattern.is_match(raw), + None => raw.eq_ignore_ascii_case("no default"), } - } + }); + + if !has_default_comment { + ctx.diagnostic(default_case_diagnostic(switch.span)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/for_direction.rs b/crates/oxc_linter/src/rules/eslint/for_direction.rs index 648535d90..e1ff0b9ee 100644 --- a/crates/oxc_linter/src/rules/eslint/for_direction.rs +++ b/crates/oxc_linter/src/rules/eslint/for_direction.rs @@ -83,68 +83,74 @@ declare_oxc_lint!( impl Rule for ForDirection { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::ForStatement(for_loop) = node.kind() { - if let Some(Expression::BinaryExpression(test)) = &for_loop.test { - let (counter, counter_position) = match (&test.left, &test.right) { - (Expression::Identifier(counter), _) => (counter, LEFT), - (_, Expression::Identifier(counter)) => (counter, RIGHT), - _ => return, - }; - let test_operator = &test.operator; - let wrong_direction = match (test_operator, counter_position) { - (BinaryOperator::LessEqualThan | BinaryOperator::LessThan, RIGHT) - | (BinaryOperator::GreaterEqualThan | BinaryOperator::GreaterThan, LEFT) => { - FORWARD + let AstKind::ForStatement(for_loop) = node.kind() else { + return; + }; + + let Some(Expression::BinaryExpression(test)) = &for_loop.test else { + return; + }; + + let (counter, counter_position) = match (&test.left, &test.right) { + (Expression::Identifier(counter), _) => (counter, LEFT), + (_, Expression::Identifier(counter)) => (counter, RIGHT), + _ => return, + }; + + let test_operator = &test.operator; + let wrong_direction = match (test_operator, counter_position) { + (BinaryOperator::LessEqualThan | BinaryOperator::LessThan, RIGHT) + | (BinaryOperator::GreaterEqualThan | BinaryOperator::GreaterThan, LEFT) => FORWARD, + (BinaryOperator::LessEqualThan | BinaryOperator::LessThan, LEFT) + | (BinaryOperator::GreaterEqualThan | BinaryOperator::GreaterThan, RIGHT) => BACKWARD, + _ => return, + }; + + let Some(update) = &for_loop.update else { + return; + }; + + let update_direction = get_update_direction(update, counter); + if update_direction == wrong_direction { + ctx.diagnostic_with_dangerous_fix( + for_direction_diagnostic(test.span, get_update_span(update)), + |fixer| { + let mut span = Span::new(0, 0); + + let mut new_operator_str = ""; + + match update { + Expression::UpdateExpression(update) => { + if update.span().start == update.argument.span().start { + span.start = update.argument.span().end; + span.end = update.span().end; + } else { + span.start = update.span().start; + span.end = update.argument.span().start; + } + + if let UpdateOperator::Increment = update.operator { + new_operator_str = "--"; + } else if let UpdateOperator::Decrement = update.operator { + new_operator_str = "++"; + } + } + Expression::AssignmentExpression(update) => { + span.start = update.left.span().end; + span.end = update.right.span().start; + + if let AssignmentOperator::Addition = update.operator { + new_operator_str = "-="; + } else if let AssignmentOperator::Subtraction = update.operator { + new_operator_str = "+="; + } + } + _ => {} } - (BinaryOperator::LessEqualThan | BinaryOperator::LessThan, LEFT) - | (BinaryOperator::GreaterEqualThan | BinaryOperator::GreaterThan, RIGHT) => { - BACKWARD - } - _ => return, - }; - if let Some(update) = &for_loop.update { - let update_direction = get_update_direction(update, counter); - if update_direction == wrong_direction { - let update_span = get_update_span(update); - ctx.diagnostic_with_dangerous_fix( - for_direction_diagnostic(test.span, update_span), - |fixer| { - let mut start = 0; - let mut end = 0; - if let Expression::UpdateExpression(update) = update { - if update.span().start == update.argument.span().start { - start = update.argument.span().end; - end = update.span().end; - } else { - start = update.span().start; - end = update.argument.span().start; - } - } else if let Expression::AssignmentExpression(update) = update { - start = update.left.span().end; - end = update.right.span().start; - } - let span = Span::new(start, end); - let mut new_operator_str = ""; - if let Expression::UpdateExpression(update) = update { - if let UpdateOperator::Increment = update.operator { - new_operator_str = "--"; - } else if let UpdateOperator::Decrement = update.operator { - new_operator_str = "++"; - } - } else if let Expression::AssignmentExpression(update) = update { - if let AssignmentOperator::Addition = update.operator { - new_operator_str = "-="; - } else if let AssignmentOperator::Subtraction = update.operator - { - new_operator_str = "+="; - } - } - fixer.replace(span, new_operator_str) - }, - ); - } - } - } + + fixer.replace(span, new_operator_str) + }, + ); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_async_promise_executor.rs b/crates/oxc_linter/src/rules/eslint/no_async_promise_executor.rs index 63d1b8523..9e8c1af1b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_async_promise_executor.rs +++ b/crates/oxc_linter/src/rules/eslint/no_async_promise_executor.rs @@ -60,7 +60,6 @@ impl Rule for NoAsyncPromiseExecutor { let mut span = match expression.get_inner_expression() { Expression::ArrowFunctionExpression(arrow) if arrow.r#async => arrow.span, Expression::FunctionExpression(func) if func.r#async => func.span, - _ => return, }; diff --git a/crates/oxc_linter/src/rules/eslint/no_console.rs b/crates/oxc_linter/src/rules/eslint/no_console.rs index c8a2ea595..5bf03d311 100644 --- a/crates/oxc_linter/src/rules/eslint/no_console.rs +++ b/crates/oxc_linter/src/rules/eslint/no_console.rs @@ -68,21 +68,22 @@ impl Rule for NoConsole { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::CallExpression(call_expr) = node.kind() { - if let Some(mem) = call_expr.callee.as_member_expression() { - if let Expression::Identifier(ident) = mem.object() { - if ctx.semantic().is_reference_to_global_variable(ident) - && ident.name == "console" - && !self - .allow - .iter() - .any(|s| mem.static_property_name().is_some_and(|f| f == s)) - { - if let Some(mem) = mem.static_property_info() { - ctx.diagnostic(no_console_diagnostic(mem.0)); - } - } - } + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; + let Some(mem) = call_expr.callee.as_member_expression() else { + return; + }; + let Expression::Identifier(ident) = mem.object() else { + return; + }; + + if ctx.semantic().is_reference_to_global_variable(ident) + && ident.name == "console" + && !self.allow.iter().any(|s| mem.static_property_name().is_some_and(|f| f == s)) + { + if let Some(mem) = mem.static_property_info() { + ctx.diagnostic(no_console_diagnostic(mem.0)); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_template_curly_in_string.rs b/crates/oxc_linter/src/rules/eslint/no_template_curly_in_string.rs index 06b85678f..fcc230ef1 100644 --- a/crates/oxc_linter/src/rules/eslint/no_template_curly_in_string.rs +++ b/crates/oxc_linter/src/rules/eslint/no_template_curly_in_string.rs @@ -34,36 +34,38 @@ declare_oxc_lint!( impl Rule for NoTemplateCurlyInString { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::StringLiteral(literal) = node.kind() { - let text = literal.value.as_str(); - if let Some(start_index) = text.find("${") { - let mut open_braces_count = 0; - let mut end_index = None; + let AstKind::StringLiteral(literal) = node.kind() else { + return; + }; - for (i, c) in text[start_index..].char_indices() { - let real_index = start_index + i; - if c == '{' { - open_braces_count += 1; - } else if c == '}' && open_braces_count > 0 { - open_braces_count -= 1; - if open_braces_count == 0 { - end_index = Some(real_index); - break; - } + let text = literal.value.as_str(); + if let Some(start_index) = text.find("${") { + let mut open_braces_count = 0; + let mut end_index = None; + + for (i, c) in text[start_index..].char_indices() { + let real_index = start_index + i; + if c == '{' { + open_braces_count += 1; + } else if c == '}' && open_braces_count > 0 { + open_braces_count -= 1; + if open_braces_count == 0 { + end_index = Some(real_index); + break; } } + } - if let Some(end_index) = end_index { - let literal_span_start = literal.span.start + 1; - let match_start = u32::try_from(start_index) - .expect("Conversion from usize to u32 failed for match_start"); - let match_end = u32::try_from(end_index + 1) - .expect("Conversion from usize to u32 failed for match_end"); - ctx.diagnostic(no_template_curly_in_string_diagnostic(Span::new( - literal_span_start + match_start, - literal_span_start + match_end, - ))); - } + if let Some(end_index) = end_index { + let literal_span_start = literal.span.start + 1; + let match_start = u32::try_from(start_index) + .expect("Conversion from usize to u32 failed for match_start"); + let match_end = u32::try_from(end_index + 1) + .expect("Conversion from usize to u32 failed for match_end"); + ctx.diagnostic(no_template_curly_in_string_diagnostic(Span::new( + literal_span_start + match_start, + literal_span_start + match_end, + ))); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs b/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs index b77a80e55..019328a86 100644 --- a/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs +++ b/crates/oxc_linter/src/rules/eslint/no_this_before_super.rs @@ -135,15 +135,15 @@ impl Rule for NoThisBeforeSuper { impl NoThisBeforeSuper { fn is_wanted_node(node: &AstNode, ctx: &LintContext<'_>) -> Option { let parent = ctx.nodes().parent_node(node.id())?; - if let AstKind::MethodDefinition(mdef) = parent.kind() { - if matches!(mdef.kind, MethodDefinitionKind::Constructor) { - let parent_2 = ctx.nodes().parent_node(parent.id())?; - let parent_3 = ctx.nodes().parent_node(parent_2.id())?; - if let AstKind::Class(c) = parent_3.kind() { - let super_class = c.super_class.as_ref()?; - return Some(!matches!(super_class, Expression::NullLiteral(_))); - } - } + let method_def = parent.kind().as_method_definition()?; + + if matches!(method_def.kind, MethodDefinitionKind::Constructor) { + let parent_2 = ctx.nodes().parent_node(parent.id())?; + let parent_3 = ctx.nodes().parent_node(parent_2.id())?; + + let class = parent_3.kind().as_class()?; + let super_class = class.super_class.as_ref()?; + return Some(!matches!(super_class, Expression::NullLiteral(_))); } Some(false) diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs index 6330ac5bb..1199491f0 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs @@ -176,11 +176,8 @@ impl NoUnusedVars { // find FormalParameters. Should be the next parent of param, but this // is safer. let Some((params, params_id)) = symbol.iter_parents().find_map(|p| { - if let AstKind::FormalParameters(params) = p.kind() { - Some((params, p.id())) - } else { - None - } + let params = p.kind().as_formal_parameters()?; + Some((params, p.id())) }) else { debug_assert!(false, "FormalParameter should always have a parent FormalParameters"); return false; diff --git a/crates/oxc_linter/src/rules/eslint/prefer_numeric_literals.rs b/crates/oxc_linter/src/rules/eslint/prefer_numeric_literals.rs index 63072d2eb..e7e2c8d69 100644 --- a/crates/oxc_linter/src/rules/eslint/prefer_numeric_literals.rs +++ b/crates/oxc_linter/src/rules/eslint/prefer_numeric_literals.rs @@ -58,41 +58,42 @@ declare_oxc_lint!( impl Rule for PreferNumericLiterals { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::CallExpression(call_expr) = node.kind() { - match &call_expr.callee.without_parentheses() { - Expression::Identifier(ident) if ident.name == "parseInt" => { - if is_parse_int_call(ctx, ident, None) { + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; + + match &call_expr.callee.without_parentheses() { + Expression::Identifier(ident) if ident.name == "parseInt" => { + if is_parse_int_call(ctx, ident, None) { + check_arguments(call_expr, ctx); + } + } + Expression::StaticMemberExpression(member_expr) => { + if let Expression::Identifier(ident) = &member_expr.object { + if is_parse_int_call(ctx, ident, Some(member_expr)) { check_arguments(call_expr, ctx); } + } else if let Expression::ParenthesizedExpression(paren_expr) = &member_expr.object + { + if let Expression::Identifier(ident) = &paren_expr.expression { + if is_parse_int_call(ctx, ident, Some(member_expr)) { + check_arguments(call_expr, ctx); + } + } } - Expression::StaticMemberExpression(member_expr) => { + } + Expression::ChainExpression(chain_expr) => { + if let Some(MemberExpression::StaticMemberExpression(member_expr)) = + chain_expr.expression.as_member_expression() + { if let Expression::Identifier(ident) = &member_expr.object { if is_parse_int_call(ctx, ident, Some(member_expr)) { check_arguments(call_expr, ctx); } - } else if let Expression::ParenthesizedExpression(paren_expr) = - &member_expr.object - { - if let Expression::Identifier(ident) = &paren_expr.expression { - if is_parse_int_call(ctx, ident, Some(member_expr)) { - check_arguments(call_expr, ctx); - } - } } } - Expression::ChainExpression(chain_expr) => { - if let Some(MemberExpression::StaticMemberExpression(member_expr)) = - chain_expr.expression.as_member_expression() - { - if let Expression::Identifier(ident) = &member_expr.object { - if is_parse_int_call(ctx, ident, Some(member_expr)) { - check_arguments(call_expr, ctx); - } - } - } - } - _ => {} } + _ => {} } } } diff --git a/crates/oxc_linter/src/rules/eslint/radix.rs b/crates/oxc_linter/src/rules/eslint/radix.rs index 0acf696ca..07fb0f6c1 100644 --- a/crates/oxc_linter/src/rules/eslint/radix.rs +++ b/crates/oxc_linter/src/rules/eslint/radix.rs @@ -66,40 +66,41 @@ impl Rule for Radix { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::CallExpression(call_expr) = node.kind() { - match call_expr.callee.without_parentheses() { - Expression::Identifier(ident) => { - if ident.name == "parseInt" + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; + + match call_expr.callee.without_parentheses() { + Expression::Identifier(ident) => { + if ident.name == "parseInt" + && ctx.symbols().is_global_reference(ident.reference_id().unwrap()) + { + Self::check_arguments(self, call_expr, ctx); + } + } + Expression::StaticMemberExpression(member_expr) => { + if let Expression::Identifier(ident) = member_expr.object.without_parentheses() { + if ident.name == "Number" + && member_expr.property.name == "parseInt" && ctx.symbols().is_global_reference(ident.reference_id().unwrap()) { Self::check_arguments(self, call_expr, ctx); } } - Expression::StaticMemberExpression(member_expr) => { - if let Expression::Identifier(ident) = member_expr.object.without_parentheses() - { + } + Expression::ChainExpression(chain_expr) => { + if let Some(member_expr) = chain_expr.expression.as_member_expression() { + if let Expression::Identifier(ident) = member_expr.object() { if ident.name == "Number" - && member_expr.property.name == "parseInt" + && member_expr.static_property_name() == Some("parseInt") && ctx.symbols().is_global_reference(ident.reference_id().unwrap()) { Self::check_arguments(self, call_expr, ctx); } } } - Expression::ChainExpression(chain_expr) => { - if let Some(member_expr) = chain_expr.expression.as_member_expression() { - if let Expression::Identifier(ident) = member_expr.object() { - if ident.name == "Number" - && member_expr.static_property_name() == Some("parseInt") - && ctx.symbols().is_global_reference(ident.reference_id().unwrap()) - { - Self::check_arguments(self, call_expr, ctx); - } - } - } - } - _ => {} } + _ => {} } } } diff --git a/crates/oxc_linter/src/rules/eslint/require_await.rs b/crates/oxc_linter/src/rules/eslint/require_await.rs index e35736f56..4e8482415 100644 --- a/crates/oxc_linter/src/rules/eslint/require_await.rs +++ b/crates/oxc_linter/src/rules/eslint/require_await.rs @@ -73,48 +73,48 @@ declare_oxc_lint!( impl Rule for RequireAwait { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::FunctionBody(body) = node.kind() { - if body.is_empty() { - return; - } + let AstKind::FunctionBody(body) = node.kind() else { + return; + }; + if body.is_empty() { + return; + } + let Some(parent) = ctx.nodes().parent_node(node.id()) else { + return; + }; - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; - - match parent.kind() { - AstKind::Function(func) => { - if func.r#async && !func.generator { - let mut finder = AwaitFinder { found: false }; - finder.visit_function_body(body); - if !finder.found { - if let Some(AstKind::ObjectProperty(p)) = - ctx.nodes().parent_kind(parent.id()) - { - if let PropertyKey::StaticIdentifier(iden) = &p.key { - ctx.diagnostic(require_await_diagnostic(iden.span)); - } else { - ctx.diagnostic(require_await_diagnostic(func.span)); - } + match parent.kind() { + AstKind::Function(func) => { + if func.r#async && !func.generator { + let mut finder = AwaitFinder { found: false }; + finder.visit_function_body(body); + if !finder.found { + if let Some(AstKind::ObjectProperty(p)) = + ctx.nodes().parent_kind(parent.id()) + { + if let PropertyKey::StaticIdentifier(iden) = &p.key { + ctx.diagnostic(require_await_diagnostic(iden.span)); } else { - ctx.diagnostic(require_await_diagnostic( - func.id.as_ref().map_or(func.span, |ident| ident.span), - )); + ctx.diagnostic(require_await_diagnostic(func.span)); } + } else { + ctx.diagnostic(require_await_diagnostic( + func.id.as_ref().map_or(func.span, |ident| ident.span), + )); } } } - AstKind::ArrowFunctionExpression(func) => { - if func.r#async { - let mut finder = AwaitFinder { found: false }; - finder.visit_function_body(body); - if !finder.found { - ctx.diagnostic(require_await_diagnostic(func.span)); - } - } - } - _ => {} } + AstKind::ArrowFunctionExpression(func) => { + if func.r#async { + let mut finder = AwaitFinder { found: false }; + finder.visit_function_body(body); + if !finder.found { + ctx.diagnostic(require_await_diagnostic(func.span)); + } + } + } + _ => {} } } } diff --git a/crates/oxc_linter/src/rules/import/namespace.rs b/crates/oxc_linter/src/rules/import/namespace.rs index 13d993e14..e89abf298 100644 --- a/crates/oxc_linter/src/rules/import/namespace.rs +++ b/crates/oxc_linter/src/rules/import/namespace.rs @@ -214,42 +214,32 @@ fn check_deep_namespace_for_node( namespaces: &[String], module: &Arc, ctx: &LintContext<'_>, -) { - if let AstKind::MemberExpression(expr) = node.kind() { - let Some((span, name)) = expr.static_property_info() else { - return; - }; +) -> Option<()> { + let expr = node.kind().as_member_expression()?; + let (span, name) = expr.static_property_info()?; - if let Some(module_source) = get_module_request_name(name, module) { - let Some(parent_node) = ctx.nodes().parent_node(node.id()) else { - return; - }; - if let Some(module_record) = module.loaded_modules.get(module_source.as_str()) { - let mut namespaces = namespaces.to_owned(); - namespaces.push(name.into()); - check_deep_namespace_for_node( - parent_node, - source, - &namespaces, - module_record.value(), - ctx, - ); - } - } else { - check_binding_exported( - name, - || { - if namespaces.len() > 1 { - no_export_in_deeply_imported_namespace(span, name, &namespaces.join(".")) - } else { - no_export(span, name, source) - } - }, - module, - ctx, - ); - } + if let Some(module_source) = get_module_request_name(name, module) { + let parent_node = ctx.nodes().parent_node(node.id())?; + let module_record = module.loaded_modules.get(module_source.as_str())?; + let mut namespaces = namespaces.to_owned(); + namespaces.push(name.into()); + check_deep_namespace_for_node(parent_node, source, &namespaces, module_record.value(), ctx); + } else { + check_binding_exported( + name, + || { + if namespaces.len() > 1 { + no_export_in_deeply_imported_namespace(span, name, &namespaces.join(".")) + } else { + no_export(span, name, source) + } + }, + module, + ctx, + ); } + + None } fn check_deep_namespace_for_object_pattern( diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs index 8c21c707c..183e13d6b 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs @@ -94,28 +94,31 @@ impl Rule for NoAutofocus { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::JSXElement(jsx_el) = node.kind() { - if let Option::Some(autofocus) = has_jsx_prop(&jsx_el.opening_element, "autoFocus") { - let Some(element_type) = get_element_type(ctx, &jsx_el.opening_element) else { - return; - }; - if self.ignore_non_dom { - if HTML_TAG.contains(&element_type) { - if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus { - ctx.diagnostic_with_fix(no_autofocus_diagnostic(attr.span), |fixer| { - fixer.delete(&attr.span) - }); - } - } - return; - } + let AstKind::JSXElement(jsx_el) = node.kind() else { + return; + }; + let Some(autofocus) = has_jsx_prop(&jsx_el.opening_element, "autoFocus") else { + return; + }; + let Some(element_type) = get_element_type(ctx, &jsx_el.opening_element) else { + return; + }; + if self.ignore_non_dom { + if HTML_TAG.contains(&element_type) { if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus { ctx.diagnostic_with_fix(no_autofocus_diagnostic(attr.span), |fixer| { fixer.delete(&attr.span) }); } } + return; + } + + if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus { + ctx.diagnostic_with_fix(no_autofocus_diagnostic(attr.span), |fixer| { + fixer.delete(&attr.span) + }); } } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs index 808ca9d67..bf1183646 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs @@ -58,26 +58,27 @@ static DEFAULT_ROLE_EXCEPTIONS: phf::Map<&'static str, &'static str> = phf_map! impl Rule for NoRedundantRoles { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::JSXOpeningElement(jsx_el) = node.kind() { - if let Some(component) = get_element_type(ctx, jsx_el) { - if let Some(JSXAttributeItem::Attribute(attr)) = - has_jsx_prop_ignore_case(jsx_el, "role") - { - if let Some(JSXAttributeValue::StringLiteral(role_values)) = &attr.value { - let roles: Vec = role_values - .value - .split_whitespace() - .map(std::string::ToString::to_string) - .collect(); - for role in &roles { - let exceptions = DEFAULT_ROLE_EXCEPTIONS.get(&component); - if exceptions.map_or(false, |set| set.contains(role)) { - ctx.diagnostic_with_fix( - no_redundant_roles_diagnostic(attr.span, &component, role), - |fixer| fixer.delete_range(attr.span), - ); - } - } + let AstKind::JSXOpeningElement(jsx_el) = node.kind() else { + return; + }; + let Some(component) = get_element_type(ctx, jsx_el) else { + return; + }; + + if let Some(JSXAttributeItem::Attribute(attr)) = has_jsx_prop_ignore_case(jsx_el, "role") { + if let Some(JSXAttributeValue::StringLiteral(role_values)) = &attr.value { + let roles: Vec = role_values + .value + .split_whitespace() + .map(std::string::ToString::to_string) + .collect(); + for role in &roles { + let exceptions = DEFAULT_ROLE_EXCEPTIONS.get(&component); + if exceptions.map_or(false, |set| set.contains(role)) { + ctx.diagnostic_with_fix( + no_redundant_roles_diagnostic(attr.span, &component, role), + |fixer| fixer.delete_range(attr.span), + ); } } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs b/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs index d658fe693..af5476e00 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs @@ -64,30 +64,33 @@ fn is_implicit_diagnostic(span: Span, x1: &str, x2: &str, x3: &str) -> OxcDiagno impl Rule for RoleSupportsAriaProps { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::JSXOpeningElement(jsx_el) = node.kind() { - if let Some(el_type) = get_element_type(ctx, jsx_el) { - let role = has_jsx_prop_ignore_case(jsx_el, "role"); - let role_value = role.map_or_else( - || get_implicit_role(jsx_el, &el_type), - |i| get_string_literal_prop_value(i), - ); - let is_implicit = role_value.is_some() && role.is_none(); - if let Some(role_value) = role_value { - if !VALID_ARIA_ROLES.contains(role_value) { - return; - } - let invalid_props = get_invalid_aria_props_for_role(role_value); - for attr in &jsx_el.attributes { - if let JSXAttributeItem::Attribute(attr) = attr { - let name = get_jsx_attribute_name(&attr.name).to_lowercase(); - if invalid_props.contains(&&name.as_str()) { - ctx.diagnostic(if is_implicit { - is_implicit_diagnostic(attr.span, &name, role_value, &el_type) - } else { - default(attr.span, &name, role_value) - }); - } - } + let AstKind::JSXOpeningElement(jsx_el) = node.kind() else { + return; + }; + let Some(el_type) = get_element_type(ctx, jsx_el) else { + return; + }; + + let role = has_jsx_prop_ignore_case(jsx_el, "role"); + let role_value = role.map_or_else( + || get_implicit_role(jsx_el, &el_type), + |i| get_string_literal_prop_value(i), + ); + let is_implicit = role_value.is_some() && role.is_none(); + if let Some(role_value) = role_value { + if !VALID_ARIA_ROLES.contains(role_value) { + return; + } + let invalid_props = get_invalid_aria_props_for_role(role_value); + for attr in &jsx_el.attributes { + if let JSXAttributeItem::Attribute(attr) = attr { + let name = get_jsx_attribute_name(&attr.name).to_lowercase(); + if invalid_props.contains(&&name.as_str()) { + ctx.diagnostic(if is_implicit { + is_implicit_diagnostic(attr.span, &name, role_value, &el_type) + } else { + default(attr.span, &name, role_value) + }); } } } diff --git a/crates/oxc_linter/src/rules/nextjs/no_unwanted_polyfillio.rs b/crates/oxc_linter/src/rules/nextjs/no_unwanted_polyfillio.rs index f9aff5d42..6f05555f6 100644 --- a/crates/oxc_linter/src/rules/nextjs/no_unwanted_polyfillio.rs +++ b/crates/oxc_linter/src/rules/nextjs/no_unwanted_polyfillio.rs @@ -107,62 +107,67 @@ const NEXT_POLYFILLED_FEATURES: Set<&'static str> = phf_set! { impl Rule for NoUnwantedPolyfillio { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::JSXOpeningElement(jsx_el) = node.kind() { - let Some(tag_name) = jsx_el.name.get_identifier_name() else { return }; + let AstKind::JSXOpeningElement(jsx_el) = node.kind() else { + return; + }; - if tag_name.as_str() != "script" { - let next_script_import_local_name = get_next_script_import_local_name(ctx); - if !matches!(next_script_import_local_name, Some(import) if tag_name.as_str() == import.as_str()) - { - return; - } - } + let Some(tag_name) = jsx_el.name.get_identifier_name() else { + return; + }; - if jsx_el.attributes.len() == 0 { + if tag_name.as_str() != "script" { + let next_script_import_local_name = get_next_script_import_local_name(ctx); + if !matches!(next_script_import_local_name, Some(import) if tag_name.as_str() == import.as_str()) + { return; } + } - let Some(JSXAttributeItem::Attribute(src)) = jsx_el.attributes.iter().find(|attr| { - matches!( - attr, - JSXAttributeItem::Attribute(jsx_attr) - if matches!( - &jsx_attr.name, - JSXAttributeName::Identifier(id) if id.name.as_str() == "src" - ) - ) - }) else { + if jsx_el.attributes.len() == 0 { + return; + } + + let Some(JSXAttributeItem::Attribute(src)) = jsx_el.attributes.iter().find(|attr| { + matches!( + attr, + JSXAttributeItem::Attribute(jsx_attr) + if matches!( + &jsx_attr.name, + JSXAttributeName::Identifier(id) if id.name.as_str() == "src" + ) + ) + }) else { + return; + }; + + let Some(JSXAttributeValue::StringLiteral(src_value)) = &src.value else { + return; + }; + + if src_value.value.as_str().starts_with("https://cdn.polyfill.io/v2/") + || src_value.value.as_str().starts_with("https://polyfill.io/v3/") + { + let Ok(url) = url::Url::parse(src_value.value.as_str()) else { + return; + }; + let Some((_, features_value)) = url.query_pairs().find(|(key, _)| key == "features") + else { return; }; - if let Some(JSXAttributeValue::StringLiteral(src_value)) = &src.value { - if src_value.value.as_str().starts_with("https://cdn.polyfill.io/v2/") - || src_value.value.as_str().starts_with("https://polyfill.io/v3/") - { - let Ok(url) = url::Url::parse(src_value.value.as_str()) else { - return; - }; - - let Some((_, features_value)) = - url.query_pairs().find(|(key, _)| key == "features") - else { - return; - }; - let unwanted_features: Vec<&str> = features_value - .split(',') - .filter(|feature| NEXT_POLYFILLED_FEATURES.contains(feature)) - .collect(); - if !unwanted_features.is_empty() { - ctx.diagnostic(no_unwanted_polyfillio_diagnostic( - &format!( - "{} {}", - unwanted_features.join(", "), - if unwanted_features.len() > 1 { "are" } else { "is" } - ), - src.span, - )); - } - } + let unwanted_features: Vec<&str> = features_value + .split(',') + .filter(|feature| NEXT_POLYFILLED_FEATURES.contains(feature)) + .collect(); + if !unwanted_features.is_empty() { + ctx.diagnostic(no_unwanted_polyfillio_diagnostic( + &format!( + "{} {}", + unwanted_features.join(", "), + if unwanted_features.len() > 1 { "are" } else { "is" } + ), + src.span, + )); } } } diff --git a/crates/oxc_linter/src/rules/oxc/bad_min_max_func.rs b/crates/oxc_linter/src/rules/oxc/bad_min_max_func.rs index 1943bed76..18983f5aa 100644 --- a/crates/oxc_linter/src/rules/oxc/bad_min_max_func.rs +++ b/crates/oxc_linter/src/rules/oxc/bad_min_max_func.rs @@ -39,31 +39,32 @@ impl Rule for BadMinMaxFunc { let AstKind::CallExpression(call_expr) = node.kind() else { return; }; + let Some((out_min_max, inner_exprs)) = Self::min_max(call_expr) else { + return; + }; - if let Some((out_min_max, inner_exprs)) = Self::min_max(call_expr) { - for expr in inner_exprs { - if let Some((inner_min_max, ..)) = Self::min_max(expr) { - let constant_result = match (&out_min_max, &inner_min_max) { - (MinMax::Max(max), MinMax::Min(min)) => { - if max > min { - Some(max) - } else { - None - } + for expr in inner_exprs { + if let Some((inner_min_max, ..)) = Self::min_max(expr) { + let constant_result = match (&out_min_max, &inner_min_max) { + (MinMax::Max(max), MinMax::Min(min)) => { + if max > min { + Some(max) + } else { + None } - (MinMax::Min(min), MinMax::Max(max)) => { - if min < max { - Some(min) - } else { - None - } - } - _ => None, - }; - - if let Some(constant) = constant_result { - ctx.diagnostic(bad_min_max_func_diagnostic(*constant, call_expr.span)); } + (MinMax::Min(min), MinMax::Max(max)) => { + if min < max { + Some(min) + } else { + None + } + } + _ => None, + }; + + if let Some(constant) = constant_result { + ctx.diagnostic(bad_min_max_func_diagnostic(*constant, call_expr.span)); } } } diff --git a/crates/oxc_linter/src/rules/oxc/bad_replace_all_arg.rs b/crates/oxc_linter/src/rules/oxc/bad_replace_all_arg.rs index 44e1da35c..99ab0edc7 100644 --- a/crates/oxc_linter/src/rules/oxc/bad_replace_all_arg.rs +++ b/crates/oxc_linter/src/rules/oxc/bad_replace_all_arg.rs @@ -96,12 +96,10 @@ fn resolve_flags<'a>( } } Expression::Identifier(ident) => { - if let Some(decl) = get_declaration_of_variable(ident, ctx) { - if let AstKind::VariableDeclarator(var_decl) = decl.kind() { - if let Some(init) = &var_decl.init { - return resolve_flags(init, ctx); - } - } + let decl = get_declaration_of_variable(ident, ctx)?; + let var_decl = decl.kind().as_variable_declarator()?; + if let Some(init) = &var_decl.init { + return resolve_flags(init, ctx); } None } diff --git a/crates/oxc_linter/src/rules/oxc/number_arg_out_of_range.rs b/crates/oxc_linter/src/rules/oxc/number_arg_out_of_range.rs index 2ba128e74..05ca3b90a 100644 --- a/crates/oxc_linter/src/rules/oxc/number_arg_out_of_range.rs +++ b/crates/oxc_linter/src/rules/oxc/number_arg_out_of_range.rs @@ -43,34 +43,29 @@ impl Rule for NumberArgOutOfRange { let AstKind::CallExpression(expr) = node.kind() else { return; }; + let Some(member) = expr.callee.get_member_expr() else { + return; + }; - if let Some(member) = expr.callee.get_member_expr() { - if let Some(Argument::NumericLiteral(literal)) = expr.arguments.first() { - let value = literal.value; - match member.static_property_name() { - Some(name @ "toString") => { - if !(2.0_f64..=36.0_f64).contains(&value) { - ctx.diagnostic(number_arg_out_of_range_diagnostic( - name, 2, 36, expr.span, - )); - } + if let Some(Argument::NumericLiteral(literal)) = expr.arguments.first() { + let value = literal.value; + match member.static_property_name() { + Some(name @ "toString") => { + if !(2.0_f64..=36.0_f64).contains(&value) { + ctx.diagnostic(number_arg_out_of_range_diagnostic(name, 2, 36, expr.span)); } - Some(name @ ("toFixed" | "toExponential")) => { - if !(0.0_f64..=20.0_f64).contains(&value) { - ctx.diagnostic(number_arg_out_of_range_diagnostic( - name, 0, 20, expr.span, - )); - } - } - Some(name @ "toPrecision") => { - if !(1.0_f64..=21.0_f64).contains(&value) { - ctx.diagnostic(number_arg_out_of_range_diagnostic( - name, 1, 21, expr.span, - )); - } - } - _ => {} } + Some(name @ ("toFixed" | "toExponential")) => { + if !(0.0_f64..=20.0_f64).contains(&value) { + ctx.diagnostic(number_arg_out_of_range_diagnostic(name, 0, 20, expr.span)); + } + } + Some(name @ "toPrecision") => { + if !(1.0_f64..=21.0_f64).contains(&value) { + ctx.diagnostic(number_arg_out_of_range_diagnostic(name, 1, 21, expr.span)); + } + } + _ => {} } } } diff --git a/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs index 5f68e2bb2..b17eb02b0 100644 --- a/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs +++ b/crates/oxc_linter/src/rules/react_perf/jsx_no_jsx_as_prop.rs @@ -50,9 +50,7 @@ impl ReactPerfRule for JsxNoJsxAsProp { kind: &AstKind<'_>, _symbol_id: SymbolId, ) -> Option<(/* decl */ Span, /* init */ Option)> { - let AstKind::VariableDeclarator(decl) = kind else { - return None; - }; + let decl = kind.as_variable_declarator()?; let init_span = decl.init.as_ref().and_then(check_expression)?; Some((decl.id.span(), Some(init_span))) } diff --git a/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs b/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs index 5613a11ab..4fce9cb24 100644 --- a/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs +++ b/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs @@ -547,9 +547,7 @@ fn get_type_only_named_import<'a>( source: &str, ) -> Option<&'a ImportDeclaration<'a>> { let root = ctx.nodes().root_node()?; - let AstKind::Program(program) = root.kind() else { - return None; - }; + let program = root.kind().as_program()?; for stmt in &program.body { let Statement::ImportDeclaration(import_decl) = stmt else { diff --git a/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs b/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs index 48a8ddfd5..0d81a720e 100644 --- a/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs +++ b/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs @@ -39,53 +39,55 @@ declare_oxc_lint!( impl Rule for NoNonNullAssertedOptionalChain { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::TSNonNullExpression(non_null_expr) = node.kind() { - let chain_span = match non_null_expr.expression.get_inner_expression() { - Expression::ChainExpression(chain) => match &chain.expression { - ChainElement::ComputedMemberExpression(member) if member.optional => { - Some(member.object.span()) - } - ChainElement::StaticMemberExpression(member) if member.optional => { - Some(member.object.span()) - } - ChainElement::PrivateFieldExpression(member) if member.optional => { - Some(member.object.span()) - } - ChainElement::CallExpression(call) if call.optional => Some(call.callee.span()), - _ => None, - }, - Expression::CallExpression(call) => { - if call.optional && !is_parent_member_or_call(node, ctx) { - Some(call.callee.span()) - } else if let Some(member) = call.callee.as_member_expression() { - if member.optional() && !is_parent_member_or_call(node, ctx) { - Some(member.object().span()) - } else { - None - } - } else { - None - } - } - expr @ match_member_expression!(Expression) => { - let member_expr = expr.to_member_expression(); - if member_expr.optional() && !is_parent_member_or_call(node, ctx) { - Some(member_expr.object().span()) - } else { - None - } - } - _ => None, - }; + let AstKind::TSNonNullExpression(non_null_expr) = node.kind() else { + return; + }; - if let Some(chain_span) = chain_span { - let chain_span_end = chain_span.end; - let non_null_end = non_null_expr.span.end - 1; - ctx.diagnostic(no_non_null_asserted_optional_chain_diagnostic( - Span::new(chain_span_end, chain_span_end), - Span::new(non_null_end, non_null_end), - )); + let chain_span = match non_null_expr.expression.get_inner_expression() { + Expression::ChainExpression(chain) => match &chain.expression { + ChainElement::ComputedMemberExpression(member) if member.optional => { + Some(member.object.span()) + } + ChainElement::StaticMemberExpression(member) if member.optional => { + Some(member.object.span()) + } + ChainElement::PrivateFieldExpression(member) if member.optional => { + Some(member.object.span()) + } + ChainElement::CallExpression(call) if call.optional => Some(call.callee.span()), + _ => None, + }, + Expression::CallExpression(call) => { + if call.optional && !is_parent_member_or_call(node, ctx) { + Some(call.callee.span()) + } else if let Some(member) = call.callee.as_member_expression() { + if member.optional() && !is_parent_member_or_call(node, ctx) { + Some(member.object().span()) + } else { + None + } + } else { + None + } } + expr @ match_member_expression!(Expression) => { + let member_expr = expr.to_member_expression(); + if member_expr.optional() && !is_parent_member_or_call(node, ctx) { + Some(member_expr.object().span()) + } else { + None + } + } + _ => None, + }; + + if let Some(chain_span) = chain_span { + let chain_span_end = chain_span.end; + let non_null_end = non_null_expr.span.end - 1; + ctx.diagnostic(no_non_null_asserted_optional_chain_diagnostic( + Span::new(chain_span_end, chain_span_end), + Span::new(non_null_end, non_null_end), + )); } } diff --git a/crates/oxc_linter/src/rules/unicorn/no_thenable.rs b/crates/oxc_linter/src/rules/unicorn/no_thenable.rs index 5b4205ae5..3da15aaa2 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_thenable.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_thenable.rs @@ -2,7 +2,7 @@ use oxc_ast::{ ast::{ match_expression, Argument, ArrayExpressionElement, AssignmentExpression, AssignmentTarget, BindingPatternKind, CallExpression, Declaration, Expression, ModuleDeclaration, - ObjectPropertyKind, PropertyKey, VariableDeclarator, + ObjectPropertyKind, PropertyKey, }, AstKind, }; @@ -239,18 +239,17 @@ fn check_expression(expr: &Expression, ctx: &LintContext<'_>) -> Option { + if lit.value == "then" { + Some(lit.span) + } else { + None + } } - } else { - None + _ => None, } }) }) diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_string_slice.rs b/crates/oxc_linter/src/rules/unicorn/prefer_string_slice.rs index 3bb91587b..1e7748578 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_string_slice.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_string_slice.rs @@ -47,19 +47,15 @@ impl Rule for PreferStringSlice { return; }; - let (span, name) = match member_expr { - MemberExpression::StaticMemberExpression(v) => { - if !matches!(v.property.name.as_str(), "substr" | "substring") { - return; - } - (v.property.span, &v.property.name) + if let MemberExpression::StaticMemberExpression(v) = member_expr { + if !matches!(v.property.name.as_str(), "substr" | "substring") { + return; } - _ => return, - }; - - ctx.diagnostic_with_fix(prefer_string_slice_diagnostic(span, name.as_str()), |fixer| { - fixer.replace(span, "slice") - }); + ctx.diagnostic_with_fix( + prefer_string_slice_diagnostic(v.property.span, v.property.name.as_str()), + |fixer| fixer.replace(v.property.span, "slice"), + ); + } } } diff --git a/crates/oxc_linter/src/rules/vitest/no_conditional_tests.rs b/crates/oxc_linter/src/rules/vitest/no_conditional_tests.rs index 17be91c88..2f3eacda2 100644 --- a/crates/oxc_linter/src/rules/vitest/no_conditional_tests.rs +++ b/crates/oxc_linter/src/rules/vitest/no_conditional_tests.rs @@ -66,30 +66,29 @@ impl Rule for NoConditionalTests { } } -fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) { +fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) -> Option<()> { let node = possible_jest_node.node; - if let AstKind::CallExpression(call_expr) = node.kind() { - if is_type_of_jest_fn_call( - call_expr, - possible_jest_node, - ctx, - &[ - JestFnKind::General(JestGeneralFnKind::Describe), - JestFnKind::General(JestGeneralFnKind::Test), - ], - ) { - let if_statement_node = ctx - .nodes() - .iter_parents(node.id()) - .find(|node| matches!(node.kind(), AstKind::IfStatement(_))); + let call_expr = node.kind().as_call_expression()?; - let Some(node) = if_statement_node else { return }; + if is_type_of_jest_fn_call( + call_expr, + possible_jest_node, + ctx, + &[ + JestFnKind::General(JestGeneralFnKind::Describe), + JestFnKind::General(JestGeneralFnKind::Test), + ], + ) { + let if_statement_node = ctx + .nodes() + .iter_parents(node.id()) + .find(|node| matches!(node.kind(), AstKind::IfStatement(_)))?; - if let AstKind::IfStatement(if_statement) = node.kind() { - ctx.diagnostic(no_conditional_tests(if_statement.span)); - } - } + let if_statement = if_statement_node.kind().as_if_statement()?; + ctx.diagnostic(no_conditional_tests(if_statement.span)); } + + None } #[test] diff --git a/crates/oxc_linter/src/utils/express.rs b/crates/oxc_linter/src/utils/express.rs index 41d19baf2..56e9e9e4b 100644 --- a/crates/oxc_linter/src/utils/express.rs +++ b/crates/oxc_linter/src/utils/express.rs @@ -21,9 +21,7 @@ use phf::{phf_set, set::Set}; pub fn as_endpoint_registration<'a, 'n>( node: &'n AstKind<'a>, ) -> Option<(Option>, &'n [Argument<'a>])> { - let AstKind::CallExpression(call) = node else { - return None; - }; + let call = node.as_call_expression()?; let callee = call.callee.as_member_expression()?; let method_name = callee.static_property_name()?; if !ROUTER_HANDLER_METHOD_NAMES.contains(method_name) { diff --git a/crates/oxc_semantic/tests/integration/scopes.rs b/crates/oxc_semantic/tests/integration/scopes.rs index cd12d4fe9..3be5e8dfb 100644 --- a/crates/oxc_semantic/tests/integration/scopes.rs +++ b/crates/oxc_semantic/tests/integration/scopes.rs @@ -188,11 +188,8 @@ fn test_enums() { .nodes() .iter() .find_map(|node| { - if let AstKind::TSEnumDeclaration(e) = node.kind() { - Some((node, e)) - } else { - None - } + let e = node.kind().as_ts_enum_declaration()?; + Some((node, e)) }) .expect("Expected TS test case to have an enum declaration for A."); diff --git a/crates/oxc_semantic/tests/integration/util/class_tester.rs b/crates/oxc_semantic/tests/integration/util/class_tester.rs index fca215162..757bd51b5 100644 --- a/crates/oxc_semantic/tests/integration/util/class_tester.rs +++ b/crates/oxc_semantic/tests/integration/util/class_tester.rs @@ -1,6 +1,5 @@ use std::rc::Rc; -use oxc_ast::AstKind; use oxc_semantic::Semantic; use oxc_syntax::class::ClassId; @@ -14,11 +13,12 @@ impl<'a> ClassTester<'a> { pub(super) fn has_class(semantic: Semantic<'a>, name: &str) -> Self { let class_id = semantic.classes().iter_enumerated().find_map(|(class_id, ast_node_id)| { let kind = semantic.nodes().kind(*ast_node_id); - if let AstKind::Class(class) = kind { - if class.id.clone().is_some_and(|id| id.name == name) { - return Some(class_id); - }; - } + let class = kind.as_class()?; + + if class.id.clone().is_some_and(|id| id.name == name) { + return Some(class_id); + }; + None }); ClassTester {