mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 18:43:44 +00:00
perf(linter/eslint/no-unsafe-negation): narrow AST dispatch (#28351)
Visit logical negations and inspect their direct binary parent, preserving operand position and explicit-parentheses behavior. Reduce fixture callbacks from 20,604 to 9,106. --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
84d974b9f7
commit
56c99c6fe7
3 files changed
+31
-22
No files matched your search
+1
-1
@@ -1178,7 +1178,7 @@ impl RuleRunner for crate::rules::eslint::no_unsafe_finally::NoUnsafeFinally {
|
||||
|
||||
impl RuleRunner for crate::rules::eslint::no_unsafe_negation::NoUnsafeNegation {
|
||||
const NODE_TYPES: Option<&AstTypesBitset> =
|
||||
Some(&AstTypesBitset::from_types(&[AstType::BinaryExpression]));
|
||||
Some(&AstTypesBitset::from_types(&[AstType::UnaryExpression]));
|
||||
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use oxc_ast::{AstKind, ast::Expression};
|
||||
use oxc_ast::AstKind;
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
@@ -82,35 +82,39 @@ impl Rule for NoUnsafeNegation {
|
||||
}
|
||||
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
let AstKind::BinaryExpression(expr) = node.kind() else {
|
||||
let AstKind::UnaryExpression(left) = node.kind() else {
|
||||
return;
|
||||
};
|
||||
if left.operator != UnaryOperator::LogicalNot {
|
||||
return;
|
||||
}
|
||||
let AstKind::BinaryExpression(expr) = ctx.nodes().parent_kind(node.id()) else {
|
||||
return;
|
||||
};
|
||||
if expr.left.span() != left.span {
|
||||
return;
|
||||
}
|
||||
|
||||
let should_check = expr.operator.is_relational()
|
||||
|| (self.enforce_for_ordering_relations && expr.operator.is_compare());
|
||||
if should_check {
|
||||
let Expression::UnaryExpression(left) = &expr.left else {
|
||||
return;
|
||||
};
|
||||
if left.operator == UnaryOperator::LogicalNot {
|
||||
// Diagnostic points at the unexpected negation
|
||||
let diagnostic =
|
||||
no_unsafe_negation_diagnostic(expr.operator.as_str(), expr.left.span());
|
||||
// Diagnostic points at the unexpected negation
|
||||
let diagnostic =
|
||||
no_unsafe_negation_diagnostic(expr.operator.as_str(), expr.left.span());
|
||||
|
||||
let fix_producer = |fixer: RuleFixer<'_, 'a>| {
|
||||
// modify `!a instance of B` to `!(a instanceof B)`
|
||||
let modified_code = {
|
||||
let left = ctx.source_range(left.argument.span());
|
||||
let operator = expr.operator.as_str();
|
||||
let right = ctx.source_range(expr.right.span());
|
||||
let fix_producer = |fixer: RuleFixer<'_, 'a>| {
|
||||
// modify `!a instance of B` to `!(a instanceof B)`
|
||||
let modified_code = {
|
||||
let left = ctx.source_range(left.argument.span());
|
||||
let operator = expr.operator.as_str();
|
||||
let right = ctx.source_range(expr.right.span());
|
||||
|
||||
format!("!({left} {operator} {right})")
|
||||
};
|
||||
fixer.replace(expr.span, modified_code)
|
||||
format!("!({left} {operator} {right})")
|
||||
};
|
||||
fixer.replace(expr.span, modified_code)
|
||||
};
|
||||
|
||||
ctx.diagnostic_with_fix(diagnostic, fix_producer);
|
||||
}
|
||||
ctx.diagnostic_with_fix(diagnostic, fix_producer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +128,9 @@ fn test() {
|
||||
("a in b === false", None),
|
||||
("!(a in b)", None),
|
||||
("(!a) in b", None),
|
||||
("a in !b", None),
|
||||
("(!a as boolean) in b", None),
|
||||
("a < !b", Some(serde_json::json!([{ "enforceForOrderingRelations": true }]))),
|
||||
("a instanceof b", None),
|
||||
("a instanceof b === false", None),
|
||||
("!(a instanceof b)", None),
|
||||
@@ -157,6 +164,8 @@ fn test() {
|
||||
|
||||
let fix = vec![
|
||||
("!a in b", "!(a in b)", None),
|
||||
("!a in !b", "!(a in !b)", None),
|
||||
("!!a in b", "!(!a in b)", None),
|
||||
("(!a in b)", "(!(a in b))", None),
|
||||
("!(a) in b", "!((a) in b)", None),
|
||||
("!a instanceof b", "!(a instanceof b)", None),
|
||||
|
||||
@@ -131,7 +131,7 @@ eslint/no-unneeded-ternary | 4722
|
||||
eslint/no-unreachable | 7
|
||||
eslint/no-unreachable-loop | 1607
|
||||
eslint/no-unsafe-finally | 15259
|
||||
eslint/no-unsafe-negation | 20604
|
||||
eslint/no-unsafe-negation | 9106
|
||||
eslint/no-unsafe-optional-chaining | 792
|
||||
eslint/no-unused-expressions | 24802
|
||||
eslint/no-unused-labels | 7
|
||||
|
||||
Reference in new issue
Block a user