Skip to content

Commit

Permalink
feat: add rule logical-assignment-operators (#16102)
Browse files Browse the repository at this point in the history
* feat: add rule logical-assignment-operators
Fixes #13689

* docs: update rule documentation
- State the concept of the rule clearer
- Increase the heading level for options

* chore: unify ast-utils import

* fix: only check for void 0 in undefined checks

* fix: always fix for the logical pattern

* feat: support yoda conditions in if conditions

* fix: remove parenthesis around assignment target if necessary

* fix: parenthesize logical pattern if needed

* fix: add semicolon for if patterns with a body if needed

* fix: remove file extension from import

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>

* fix: check strictness of global scope instead of current scope to avoid checking for with

* fix: check for mixed ?? and ||/&& operators in the fix for never

* fix: check previous token for continuation problems in the if fix

* feat: support else if (and fix suggest cases for if)

* fix: do not remove else keyword for else if

* fix: if cases also suggest based on potential getter

* fix: also fix if only a single property is accessed for the if pattern

* fix: do not fix logical patterns with a deeper access

* fix: check whether Boolean references a global

* fix: allow test conditions to access the same static property (a.b <=> a['b'])

* fix: use the whole assignment operator with equals in the message and add operator for 'if' messages

* docs: remove edit_link

* docs: remove description from docs as it is autogenerated from rule.meta.docs.description

* docs: move introductory text before rule details

* docs: add missing 'logical' to rule description

* docs: fix formatting for options

* docs: include all logical operators for option 'never'

* docs: add examples for option 'always'

* docs: add examples of for the 'enforceForIfStatements' option and swap correct with incorrect sections

* fix: disallow optional chaining for the logical pattern

* fix: fixer does not delete parenthesis around the right operand in the assignment pattern

* fix: remove multiple parenthesis around the right operand in the logical pattern

* test: add data property for suggestions

* docs: add missing comma in description

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>

* test: clean up unnecessary data, add missing data and pass missing options for test case

* fix: do not allow property accesses in a computed property when checking for a single property access

* test: add test cases for private identifiers

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
  • Loading branch information
DMartens and mdjermanovic authored Sep 16, 2022
1 parent f02bcd9 commit b0d72c9
Show file tree
Hide file tree
Showing 5 changed files with 2,067 additions and 0 deletions.
131 changes: 131 additions & 0 deletions docs/src/rules/logical-assignment-operators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
title: logical-assignment-operators
layout: doc
rule_type: suggestion
---

ES2021 introduces the assignment operator shorthand for the logical operators `||`, `&&` and `??`.
Before, this was only allowed for mathematical operations such as `+` or `*` (see the rule [operator-assignment](./operator-assignment)).
The shorthand can be used if the assignment target and the left expression of a logical expression are the same.
For example `a = a || b` can be shortened to `a ||= b`.

## Rule Details

This rule requires or disallows logical assignment operator shorthand.

### Options

This rule has a string and an object option.
String option:

* `"always"` (default)
* `"never"`

Object option (only available if string option is set to `"always"`):

* `"enforceForIfStatements": false`(default) Do *not* check for equivalent `if` statements
* `"enforceForIfStatements": true` Check for equivalent `if` statements

#### always

Examples of **incorrect** code for this rule with the default `"always"` option:

::: incorrect

```js
/*eslint logical-assignment-operators: ["error", "always"]*/

a = a || b
a = a && b
a = a ?? b
a || (a = b)
a && (a = b)
a ?? (a = b)
```
:::
Examples of **correct** code for this rule with the default `"always"` option:
::: correct
```js
/*eslint logical-assignment-operators: ["error", "always"]*/

a = b
a += b
a ||= b
a = b || c
a || (b = c)

if (a) a = b
```
:::
#### never
Examples of **incorrect** code for this rule with the `"never"` option:
::: incorrect
```js
/*eslint logical-assignment-operators: ["error", "never"]*/

a ||= b
a &&= b
a ??= b
```
:::
Examples of **correct** code for this rule with the `"never"` option:
::: correct
```js
/*eslint logical-assignment-operators: ["error", "never"]*/

a = a || b
a = a && b
a = a ?? b
```
:::
#### enforceForIfStatements
This option checks for additional patterns with if statements which could be expressed with the logical assignment operator.
::: incorrect
Examples of **incorrect** code for this rule with the `["always", { enforceIfStatements: true }]` option:
```js
/*eslint logical-assignment-operators: ["error", "always", { enforceForIfStatements: true }]*/

if (a) a = b // <=> a &&= b
if (!a) a = b // <=> a ||= b

if (a == null) a = b // <=> a ??= b
if (a === null || a === undefined) a = b // <=> a ??= b
```
:::
Examples of **correct** code for this rule with the `["always", { enforceIfStatements: true }]` option:
::: correct
```js
/*eslint logical-assignment-operators: ["error", "always", { enforceForIfStatements: true }]*/

if (a) b = c
if (a === 0) a = b
```
:::
## When Not To Use It
Use of logical operator assignment shorthand is a stylistic choice. Leaving this rule turned off would allow developers to choose which style is more readable on a case-by-case basis.
1 change: 1 addition & 0 deletions lib/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
"lines-around-comment": () => require("./lines-around-comment"),
"lines-around-directive": () => require("./lines-around-directive"),
"lines-between-class-members": () => require("./lines-between-class-members"),
"logical-assignment-operators": () => require("./logical-assignment-operators"),
"max-classes-per-file": () => require("./max-classes-per-file"),
"max-depth": () => require("./max-depth"),
"max-len": () => require("./max-len"),
Expand Down
Loading

0 comments on commit b0d72c9

Please sign in to comment.