oxc-project/oxc · error · OxcDiagnostic

Bad comparison sequence

Error message

Bad comparison sequence

What it means

Diagnostic from oxlint rule oxc/bad-comparison-sequence (correctness category). JavaScript comparisons are binary and left-associative: `a == b == c` parses as `(a == b) == c`, i.e. the boolean from the first comparison is compared against c — not a three-way check as in Python. The rule fires when the left operand of an equality or relational expression is itself an equality or relational expression of the same class, and labels both spans: which comparison produces the boolean, and which operand that boolean is then compared against.

Source

Thrown at crates/oxc_linter/src/rules/oxc/bad_comparison_sequence.rs:15

use oxc_ast::{
    AstKind,
    ast::{BinaryExpression, Expression},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};

use crate::{AstNode, context::LintContext, rule::Rule};

fn bad_comparison_sequence_diagnostic(
    comparison_result: Span,
    compared_against: Span,
) -> OxcDiagnostic {
    OxcDiagnostic::warn("Bad comparison sequence")
        .with_help("Comparison result should not be used directly as an operand of another comparison. If you need to compare three or more operands, you should connect each comparison operation with logical AND operator (`&&`)")
        .with_labels([
            comparison_result.label("This comparison expression produces a boolean"),
            compared_against.label("That boolean is then compared with this operand"),
        ])
}

#[derive(Debug, Default, Clone)]
pub struct BadComparisonSequence;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// This rule applies when the comparison operator is applied two or more times in a row.
    ///
    /// ### Why is this bad?
    ///
    /// Because comparison operator is a binary operator, it is impossible to compare three or more operands at once.

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Split into pairwise comparisons joined by &&: a === b && b === c
  2. For range checks: lo < x && x < hi
  3. Extract each comparison into a well-named boolean (const inRange = lo < x && x < hi) for readability

Example fix

// before
if (a == b == c) {
  console.log('a, b, and c are the same'); // actually (a == b) == c
}

// after
if (a == b && b == c) {
  console.log('a, b, and c are the same');
}
Defensive patterns

Strategy: validation

Validate before calling

// .oxlintrc.json
"rules": { "oxc/bad-comparison-sequence": "error" }

npx oxlint -c .oxlintrc.json --deny-warning .

Prevention

When it happens

Trigger: A BinaryExpression whose operator is equality (==/!=/===/!==) or relational (</<=/>/>=) AND whose left operand is a BinaryExpression of the same class (both equality, or both relational). Triggers: if (a == b == c), if (x < y <= z), and chained forms like a == b == c == d. ParenthesizedExpression boundaries and statement/declaration boundaries cap the walk so a chain reports exactly once (the ancestor check has_no_bad_comparison_in_parents suppresses inner duplicates).

Common situations: Developers coming from Python, where a < b < c chains natively; math-range checks (if (lo < x < hi)) typed without unpacking; the expression compiles and 'works' — the boolean just coerces (true == 1) so tests built on the same assumption pass while the logic is wrong.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/dfb816e664392ba5. Report an issue: GitHub.