diff --git a/.changeset/fix-assign-arrow-body.md b/.changeset/fix-assign-arrow-body.md new file mode 100644 index 000000000000..a7aac7bee086 --- /dev/null +++ b/.changeset/fix-assign-arrow-body.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fixed [#8790](https://github.com/biomejs/biome/issues/8790): The [`noAssignInExpressions`](https://biomejs.dev/linter/rules/no-assign-in-expressions/) rule no longer reports a false positive when an assignment is used as the expression body of an arrow function (e.g., `const f = b => a += b`). diff --git a/crates/biome_js_analyze/src/lint/suspicious/no_assign_in_expressions.rs b/crates/biome_js_analyze/src/lint/suspicious/no_assign_in_expressions.rs index e6068299a325..618552f2e7a5 100644 --- a/crates/biome_js_analyze/src/lint/suspicious/no_assign_in_expressions.rs +++ b/crates/biome_js_analyze/src/lint/suspicious/no_assign_in_expressions.rs @@ -3,8 +3,8 @@ use biome_analyze::{Ast, Rule, RuleDiagnostic, RuleSource, declare_lint_rule}; use biome_console::markup; use biome_diagnostics::Severity; use biome_js_syntax::{ - JsAssignmentExpression, JsExpressionStatement, JsForStatement, JsParenthesizedExpression, - JsSequenceExpression, + AnyJsFunctionBody, JsArrowFunctionExpression, JsAssignmentExpression, JsExpressionStatement, + JsForStatement, JsParenthesizedExpression, JsSequenceExpression, }; use biome_rowan::AstNode; use biome_rule_options::no_assign_in_expressions::NoAssignInExpressionsOptions; @@ -43,6 +43,11 @@ declare_lint_rule! { /// let a; /// a = 1; /// ``` + /// + /// ```ts + /// let a = 0; + /// const f = b => a += b; + /// ``` pub NoAssignInExpressions { version: "1.0.0", name: "noAssignInExpressions", @@ -79,7 +84,7 @@ impl Rule for NoAssignInExpressions { } if JsExpressionStatement::can_cast(ancestor.kind()) { None - } else if let Some(for_stmt) = JsForStatement::cast(ancestor) { + } else if let Some(for_stmt) = JsForStatement::cast_ref(&ancestor) { if let Some(for_test) = for_stmt.test() { // Disallow assignment in test part of a `for` (for_test.syntax() == &prev_ancestor).then_some(()) @@ -87,6 +92,15 @@ impl Rule for NoAssignInExpressions { // Allow assignment in initializer and update parts of a `for` None } + } else if let Some(arrow) = JsArrowFunctionExpression::cast(ancestor) { + // Allow assignment as the expression body of an arrow function + // e.g., `const f = b => a += b` + if matches!(arrow.body(), Ok(AnyJsFunctionBody::AnyJsExpression(expr)) if expr.syntax() == &prev_ancestor) + { + None + } else { + Some(()) + } } else { Some(()) } diff --git a/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/invalid.js.snap b/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/invalid.js.snap index 8f17beab7d92..4a3c082c9feb 100644 --- a/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/invalid.js.snap @@ -559,23 +559,6 @@ invalid.js:81:2 lint/suspicious/noAssignInExpressions ━━━━━━━━ Expressions are often considered as side-effect free. -``` - -``` -invalid.js:83:32 lint/suspicious/noAssignInExpressions ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × The assignment should not be in an expression. - - 81 │ ((3496.29/*1*/)/*2*/.bkufyydt/*3*/ = /*4*/2e308/*5*/)/*6*/ ? foo : bar; - 82 │ - > 83 │ res.onAborted(() => /*0*/(/*1*/(/*2*/a/*3*/./*4*/b/*5*/)/*6*/ /*7*/ = /*8*/ /*9*/true/*10*/)); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 84 │ - - i The use of assignments in expressions is confusing. - Expressions are often considered as side-effect free. - - ``` ``` diff --git a/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/valid.js b/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/valid.js index f279e13fc483..641adef252c9 100644 --- a/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/valid.js +++ b/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/valid.js @@ -43,3 +43,9 @@ if (x == 0) { while (x < 5) { x = x + 1; } + +// Arrow function expression body - assignment is the return value +let a0 = 0; +const f0 = b => a0 += b; +const f1 = (x, y) => x += y; +const f2 = x => a0 = b = x; diff --git a/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/valid.js.snap b/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/valid.js.snap index d078e1e5bfd7..49793e8e4ada 100644 --- a/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/suspicious/noAssignInExpressions/valid.js.snap @@ -50,4 +50,10 @@ while (x < 5) { x = x + 1; } +// Arrow function expression body - assignment is the return value +let a0 = 0; +const f0 = b => a0 += b; +const f1 = (x, y) => x += y; +const f2 = x => a0 = b = x; + ```