Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 53 additions & 19 deletions crates/oxc_linter/src/generated/rule_runner_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use oxc_semantic::AstTypesBitset;
use crate::rule::{RuleRunFunctionsImplemented, RuleRunner};

impl RuleRunner for crate::rules::eslint::array_callback_return::ArrayCallbackReturn {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::ArrowFunctionExpression, AstType::Function]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -164,7 +165,8 @@ impl RuleRunner for crate::rules::eslint::no_alert::NoAlert {
}

impl RuleRunner for crate::rules::eslint::no_array_constructor::NoArrayConstructor {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::CallExpression, AstType::NewExpression]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand All @@ -175,7 +177,8 @@ impl RuleRunner for crate::rules::eslint::no_async_promise_executor::NoAsyncProm
}

impl RuleRunner for crate::rules::eslint::no_await_in_loop::NoAwaitInLoop {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::AwaitExpression, AstType::ForOfStatement]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -225,7 +228,10 @@ impl RuleRunner for crate::rules::eslint::no_cond_assign::NoCondAssign {
}

impl RuleRunner for crate::rules::eslint::no_console::NoConsole {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
AstType::ComputedMemberExpression,
AstType::StaticMemberExpression,
]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -429,7 +435,8 @@ impl RuleRunner for crate::rules::eslint::no_inner_declarations::NoInnerDeclarat
}

impl RuleRunner for crate::rules::eslint::no_invalid_regexp::NoInvalidRegexp {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::CallExpression, AstType::NewExpression]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -547,7 +554,8 @@ impl RuleRunner for crate::rules::eslint::no_obj_calls::NoObjCalls {
}

impl RuleRunner for crate::rules::eslint::no_object_constructor::NoObjectConstructor {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::CallExpression, AstType::NewExpression]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -964,7 +972,8 @@ impl RuleRunner for crate::rules::eslint::use_isnan::UseIsnan {
}

impl RuleRunner for crate::rules::eslint::valid_typeof::ValidTypeof {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::UnaryExpression]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -1434,12 +1443,14 @@ impl RuleRunner for crate::rules::jsdoc::no_defaults::NoDefaults {
}

impl RuleRunner for crate::rules::jsdoc::require_param::RequireParam {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::ArrowFunctionExpression, AstType::Function]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

impl RuleRunner for crate::rules::jsdoc::require_param_description::RequireParamDescription {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::ArrowFunctionExpression, AstType::Function]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand All @@ -1449,7 +1460,8 @@ impl RuleRunner for crate::rules::jsdoc::require_param_name::RequireParamName {
}

impl RuleRunner for crate::rules::jsdoc::require_param_type::RequireParamType {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::ArrowFunctionExpression, AstType::Function]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -1953,7 +1965,8 @@ impl RuleRunner for crate::rules::oxc::number_arg_out_of_range::NumberArgOutOfRa
}

impl RuleRunner for crate::rules::oxc::only_used_in_recursion::OnlyUsedInRecursion {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::ArrowFunctionExpression, AstType::Function]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -2269,7 +2282,8 @@ impl RuleRunner for crate::rules::react::prefer_es6_class::PreferEs6Class {
}

impl RuleRunner for crate::rules::react::react_in_jsx_scope::ReactInJsxScope {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::JSXFragment, AstType::JSXOpeningElement]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -2707,7 +2721,11 @@ impl RuleRunner for crate::rules::typescript::no_var_requires::NoVarRequires {
}

impl RuleRunner for crate::rules::typescript::no_wrapper_object_types::NoWrapperObjectTypes {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
AstType::TSClassImplements,
AstType::TSInterfaceHeritage,
AstType::TSTypeReference,
]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -2885,12 +2903,19 @@ impl RuleRunner
}

impl RuleRunner for crate::rules::unicorn::consistent_function_scoping::ConsistentFunctionScoping {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::ArrowFunctionExpression, AstType::Function]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

impl RuleRunner for crate::rules::unicorn::empty_brace_spaces::EmptyBraceSpaces {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
AstType::BlockStatement,
AstType::Class,
AstType::FunctionBody,
AstType::ObjectExpression,
AstType::StaticBlock,
]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -2937,7 +2962,10 @@ impl RuleRunner for crate::rules::unicorn::no_accessor_recursion::NoAccessorRecu
}

impl RuleRunner for crate::rules::unicorn::no_anonymous_default_export::NoAnonymousDefaultExport {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
AstType::AssignmentExpression,
AstType::ExportDefaultDeclaration,
]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -3240,7 +3268,8 @@ impl RuleRunner for crate::rules::unicorn::no_zero_fractions::NoZeroFractions {
}

impl RuleRunner for crate::rules::unicorn::number_literal_case::NumberLiteralCase {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::BigIntLiteral, AstType::NumericLiteral]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -3384,7 +3413,11 @@ impl RuleRunner for crate::rules::unicorn::prefer_math_min_max::PreferMathMinMax
}

impl RuleRunner for crate::rules::unicorn::prefer_math_trunc::PreferMathTrunc {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[
AstType::AssignmentExpression,
AstType::BinaryExpression,
AstType::UnaryExpression,
]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down Expand Up @@ -3566,7 +3599,8 @@ impl RuleRunner for crate::rules::unicorn::switch_case_braces::SwitchCaseBraces
impl RuleRunner
for crate::rules::unicorn::text_encoding_identifier_case::TextEncodingIdentifierCase
{
const NODE_TYPES: Option<&AstTypesBitset> = None;
const NODE_TYPES: Option<&AstTypesBitset> =
Some(&AstTypesBitset::from_types(&[AstType::JSXText, AstType::StringLiteral]));
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}

Expand Down
78 changes: 78 additions & 0 deletions tasks/linter_codegen/src/early_diverge_detector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use syn::{Arm, Expr, Pat, Stmt};

use crate::{
CollectionResult, NodeTypeSet,
utils::{astkind_variant_from_path, is_node_kind_call},
};

/// Detects various kinds of diverging statements that narrow by more than one AST node type.
pub struct EarlyDivergeDetector {
node_types: NodeTypeSet,
}

impl EarlyDivergeDetector {
pub fn from_run_func(run_func: &syn::ImplItemFn) -> Option<NodeTypeSet> {
// Only look at cases where the body has more than one top-level statement.
let block = &run_func.block;
if block.stmts.len() <= 1 {
return None;
}

// Look at the first statement in the function body.
let stmt = block.stmts.first()?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a small note here:

IMO it'd likely be preferable to use block.stmts.first().unwrap().

.unwrap() will often cause the compiler to backtrack and try to prove that the unwrap can't fail. In this case I'd assume it can prove this fairly easily as the block.stmts.len() <= 1 check is right above. So it'll remove the branch. Whereas with ? it probably won't backtrack and won't figure this out.

Someone taught me this surprising fact last year - sometimes a well-placed assert! can result in less code, rather than more.


// Check if it's `let something = match node.kind() { ... }` that diverges
if let Stmt::Local(local) = stmt
&& let Some(init) = &local.init
&& let Expr::Match(match_expr) = &*init.expr
&& is_node_kind_call(&match_expr.expr)
{
let mut detector = Self { node_types: NodeTypeSet::new() };
let result = detector.extract_variants_from_diverging_match_expr(match_expr);
if result == CollectionResult::Incomplete || detector.node_types.is_empty() {
return None;
}
return Some(detector.node_types);
}

None
}

fn extract_variants_from_diverging_match_expr(
&mut self,
match_expr: &syn::ExprMatch,
) -> CollectionResult {
let mut overall_result = CollectionResult::Complete;
for arm in &match_expr.arms {
let result = self.extract_variants_from_diverging_match_arm(arm);
if result == CollectionResult::Incomplete {
overall_result = CollectionResult::Incomplete;
}
}
overall_result
}

fn extract_variants_from_diverging_match_arm(&mut self, arm: &Arm) -> CollectionResult {
let pat = &arm.pat;
match pat {
Pat::TupleStruct(ts) => {
if let Some(variant) = astkind_variant_from_path(&ts.path) {
// NOTE: If there is a guard, we assume that it may or may not be taken and collect all AST kinds
// regardless of the guard condition.
self.node_types.insert(variant);
CollectionResult::Complete
} else {
CollectionResult::Incomplete
}
}
Pat::Wild(_) => {
// Body must be diverging (i.e., returning from function)
if let Expr::Return(_) = *arm.body {
return CollectionResult::Complete;
}
CollectionResult::Incomplete
}
_ => CollectionResult::Incomplete,
}
}
}
9 changes: 9 additions & 0 deletions tasks/linter_codegen/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![allow(clippy::print_stdout)]

use crate::{
early_diverge_detector::EarlyDivergeDetector,
if_else_detector::IfElseKindDetector,
let_else_detector::LetElseDetector,
match_detector::MatchDetector,
Expand All @@ -17,6 +18,7 @@ use std::{
};
use syn::File;

mod early_diverge_detector;
mod if_else_detector;
mod let_else_detector;
mod match_detector;
Expand Down Expand Up @@ -131,6 +133,13 @@ fn detect_top_level_node_types(file: &File, rule: &RuleEntry) -> Option<NodeType
return Some(node_types);
}

let node_types = EarlyDivergeDetector::from_run_func(run_func);
if let Some(node_types) = node_types
&& !node_types.is_empty()
{
return Some(node_types);
}

None
}

Expand Down
Loading