mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(linter): catch more cases in const-comparisons (#8215)
This commit is contained in:
parent
cd274eeb02
commit
0e168b8c19
2 changed files with 83 additions and 2 deletions
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue