{"record":{"id":"cb3c4fc7452fb990","repo":"oxc-project/oxc","slug":"expected-a-conditional-expression-and-instead-saw","errorCode":null,"errorMessage":"Expected a conditional expression and instead saw an assignment","messagePattern":"Expected a conditional expression and instead saw an assignment","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/oxc_linter/src/rules/eslint/no_cond_assign.rs","lineNumber":18,"sourceCode":"use oxc_ast::{\n    AstKind,\n    ast::{AssignmentExpression, Expression},\n};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_span::{GetSpan, Span};\nuse schemars::JsonSchema;\nuse serde::Deserialize;\n\nuse crate::{\n    AstNode,\n    context::LintContext,\n    rule::{DefaultRuleConfig, Rule},\n};\n\nfn no_cond_assign_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Expected a conditional expression and instead saw an assignment\")\n        .with_help(\"Consider wrapping the assignment in additional parentheses\")\n        .with_label(span)\n}\n\n#[derive(Debug, Default, Clone, Deserialize)]\npub struct NoCondAssign(NoCondAssignConfig);\n\n#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Deserialize, JsonSchema)]\n#[serde(rename_all = \"kebab-case\")]\nenum NoCondAssignConfig {\n    /// Allow assignments in conditional expressions only if they are\n    /// enclosed in parentheses.\n    #[default]\n    ExceptParens,\n    /// Disallow all assignments in conditional expressions.\n    Always,\n}\n","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/eslint/no_cond_assign.rs#L1-L36","documentation":"Diagnostic from the oxlint rule `no-cond-assign` (crates/oxc_linter/src/rules/eslint/no_cond_assign.rs). It fires when an assignment expression (`=`, `+=`, ...) is used as the test of an `if`, `while`, `do-while`, or `for(...;;...)` condition or of a ternary. The default option `except-parens` permits assignments only when wrapped in additional parentheses; the `always` option flags every assignment inside a condition, even parenthesized ones. The diagnostic's span is narrowed to just the assignment operator token.","triggerScenarios":"Under default config: `if (x = 0) {}`, `while (x += 1) {}`, `for (; x = y; ) {}`, `cond = a ? b : c` where the ternary test is an assignment. Under `always`, additionally forms like `if ((x = 0)) {}` and `if (a || (b = c)) {}` inside the condition's span (assignments in the body still pass).","commonSituations":"Typo `=` for `===` in conditions (the classic `if (user.jobTitle = \"manager\")` bug); intentional readline/walker idioms like `while ((line = reader.next()) !== null)` that need the extra parens under default config; switching the config from `except-parens` to `always` and tripping on previously-allowed parenthesized assignments.","solutions":["If it is a typo, change `=` to `===`/`==` in the condition.","If the assignment-in-condition is intentional, wrap it in extra parentheses: `if ((x = compute())) {...}` (allowed by the default `except-parens` mode).","Prefer separating the assignment from the test: `x = compute(); if (x) {...}` — clearest under both modes.","If your team relies on parenthesized condition assignments, ensure the rule is configured as `\"no-cond-assign\": \"error\"` or `[\"error\", \"except-parens\"]`, not `\"always\"`."],"exampleFix":"// before\nif (user.jobTitle = \"manager\") { /* always truthy; also corrupts jobTitle */ }\n\n// after\nif (user.jobTitle === \"manager\") { }\n\n// intentional assignment case\nwhile ((line = readLine()) !== null) { process(line); }","handlingStrategy":"validation","validationCode":"// Heuristic pre-check for single-= conditions (oxlint's AST check is authoritative)\nconst suspect = /\\b(if|while)\\s*\\(\\s*[\\w.$]+\\s*(=[^=]|\\+=|-=)/.test(src);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write conditions with explicit comparison operators; never assign in a test.","Keep the intentional walker idiom double-parenthesized: while ((x = next()) !== null).","Decide team-wide between except-parens (default) and always, and encode it in .oxlintrc.json."],"tags":["lint","assignment","condition","typo","oxlint"],"backgroundTag":"assignment-in-condition","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}