feat(linter): catch more cases in const-comparisons (#8215)

This commit is contained in:
Cameron 2025-01-02 10:32:47 +00:00 committed by GitHub
parent cd274eeb02
commit 0e168b8c19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 83 additions and 2 deletions

View file

@ -2,7 +2,7 @@
use std::cmp::Ordering;
use oxc_ast::{
ast::{Expression, LogicalExpression, NumericLiteral},
ast::{Expression, LogicalExpression, NumericLiteral, UnaryOperator},
AstKind,
};
use oxc_diagnostics::OxcDiagnostic;
@ -53,6 +53,19 @@ fn identical_expressions_logical_operator(left_span: Span, right_span: Span) ->
])
}
fn identical_expressions_logical_operator_negated(
always_truthy: bool,
left_span: Span,
right_span: Span,
) -> OxcDiagnostic {
OxcDiagnostic::warn("Unexpected constant comparison")
.with_help(format!("This logical expression will always evaluate to {always_truthy}"))
.with_labels([
left_span.label("If this expression evaluates to true"),
right_span.label("This expression will never evaluate to true"),
])
}
/// <https://rust-lang.github.io/rust-clippy/master/index.html#/impossible>
/// <https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_comparisons>
#[derive(Debug, Default, Clone)]
@ -197,7 +210,13 @@ impl ConstComparisons {
}
}
// checks for `a === b && a === b` and `a === b && a !== b`
/// checks for:
/// ```ts
/// a === b && b === a
/// a === b && a !== b
/// !a && a
/// a && !a
/// ```
fn check_redundant_logical_expression<'a>(
logical_expr: &LogicalExpression<'a>,
ctx: &LintContext<'a>,
@ -216,6 +235,24 @@ impl ConstComparisons {
logical_expr.right.span(),
));
}
// if either are `!foo`, check whether it looks like `foo && !foo` or `foo || !foo`
match (logical_expr.left.get_inner_expression(), logical_expr.right.get_inner_expression())
{
(Expression::UnaryExpression(negated_expr), other_expr)
| (other_expr, Expression::UnaryExpression(negated_expr)) => {
if negated_expr.operator == UnaryOperator::LogicalNot
&& is_same_expression(&negated_expr.argument, other_expr, ctx)
{
ctx.diagnostic(identical_expressions_logical_operator_negated(
matches!(logical_expr.operator, LogicalOperator::Or),
logical_expr.left.span(),
logical_expr.right.span(),
));
}
}
_ => {}
}
}
fn check_binary_expression<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) {
@ -420,6 +457,10 @@ fn test() {
"a > b",
"a >= b",
"class Foo { #a; #b; constructor() { this.#a = 1; }; test() { return this.#a > this.#b } }",
"!foo && bar",
"!foo && !bar",
"foo || bar",
"!foo || bar",
];
let fail = vec![
@ -508,6 +549,10 @@ fn test() {
"!foo && !foo",
"!foo || !foo",
"class Foo { #a; #b; constructor() { this.#a = 1; }; test() { return this.#a > this.#a } }",
"!foo && foo",
"foo && !foo",
"!foo || foo",
"foo || !foo",
];
Tester::new(ConstComparisons::NAME, ConstComparisons::CATEGORY, pass, fail).test_and_snapshot();

View file

@ -312,3 +312,39 @@ source: crates/oxc_linter/src/tester.rs
· ─────────────────
╰────
help: Because `this.#a` will never be greater than itself
⚠ oxc(const-comparisons): Unexpected constant comparison
╭─[const_comparisons.tsx:1:1]
1 │ !foo && foo
· ──┬─ ─┬─
· │ ╰── This expression will never evaluate to true
· ╰── If this expression evaluates to true
╰────
help: This logical expression will always evaluate to false
⚠ oxc(const-comparisons): Unexpected constant comparison
╭─[const_comparisons.tsx:1:1]
1 │ foo && !foo
· ─┬─ ──┬─
· │ ╰── This expression will never evaluate to true
· ╰── If this expression evaluates to true
╰────
help: This logical expression will always evaluate to false
⚠ oxc(const-comparisons): Unexpected constant comparison
╭─[const_comparisons.tsx:1:1]
1 │ !foo || foo
· ──┬─ ─┬─
· │ ╰── This expression will never evaluate to true
· ╰── If this expression evaluates to true
╰────
help: This logical expression will always evaluate to true
⚠ oxc(const-comparisons): Unexpected constant comparison
╭─[const_comparisons.tsx:1:1]
1 │ foo || !foo
· ─┬─ ──┬─
· │ ╰── This expression will never evaluate to true
· ╰── If this expression evaluates to true
╰────
help: This logical expression will always evaluate to true