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
- Split into pairwise comparisons joined by &&: a === b && b === c
- For range checks: lo < x && x < hi
- 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
- Unlearn Python chaining: in JS every comparison takes exactly two operands, so split chains with &&
- Write range checks as lo < x && x < hi and consider extracting them into a named helper (inRange)
- Add boundary tests around comparisons — chained-comparison bugs produce wrong-but-plausible booleans that unit tests catch immediately
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
- Invalid character comparison
- This comparison will always evaluate to {evaluates_to}
- Math.min and Math.max combination leads to constant result
- Unexpected object literal comparison.
- Unexpected array literal comparison.
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/dfb816e664392ba5.
Report an issue: GitHub.