Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support logical assignments #13569

Closed
12 tasks done
mdjermanovic opened this issue Aug 16, 2020 · 10 comments
Closed
12 tasks done

Support logical assignments #13569

mdjermanovic opened this issue Aug 16, 2020 · 10 comments
Assignees
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion core Relates to ESLint's core APIs and features new syntax This issue is related to new syntax that has reached stage 4 rule Relates to ESLint's core rules

Comments

@mdjermanovic
Copy link
Member

mdjermanovic commented Aug 16, 2020

Logical assignment proposal is now Stage 4.

https://github.com/tc39/proposal-logical-assignment

ESLint's default parser will support this syntax under ecmaVersion: 2021

@mdjermanovic mdjermanovic added rule Relates to ESLint's core rules core Relates to ESLint's core APIs and features accepted There is consensus among the team that this change meets the criteria for inclusion new syntax This issue is related to new syntax that has reached stage 4 labels Aug 16, 2020
@mdjermanovic
Copy link
Member Author

These rules/helpers should be probably updated:

  • operator-assignment: Currently, "never" reports a ||= b (and auto-fixes some cases that shouldn't be auto-fixed), but "always" doesn't report a = a || b, which is inconsistent behavior. The same applies to &&= and ??=. Possible solutions:
    • Fix the fixer for "never", and also check logical expressions on the right side (like a = a || b) if the option is "always" and ecmaVersion >= 2021. Though, we're usually trying to avoid ecmaVersion in rules. Also, update the documentation because it explicitly lists operators.
    • Or, fix the rule to just ignore a ||= b and other logical assignments.
    • Or, add a new option for logical assignments.
  • constructor-super: Since the rule already allows super() in constructor of class A extends (B || null), then it should probably allow super() in constructor of class A extends (B ||= null).
  • astUtils.couldBeError: returns true for a || 5, so it should probably return true for a ||= 5, too.

It looks like these rules should be also updated, but it might be a breaking change for babel-eslint users:

  • complexity: I guess ||= increases complexity just like ||. Same for others.
  • no-constant-condition: This rule already reports a = true and a || true, so it should probably report a ||= true as well. Same for a &&= false.
  • no-self-assign: Should it report a ||= a, a &&= a, and a ??= a?

This rule probably needs a documentation update:

  • func-names, option "as-needed": Per the specification it seems that all three of ||=, &&=, and ??= do assign function names (NamedEvaluation). The rule works in line with that fact, because it doesn't check the assignment operator (and also mistakenly allows += and other operators). However, we should probably rephrase the description in the docs since the condition "if the name cannot be assigned automatically in an ES6 environment" sounds ambiguous now.

These rules probably shouldn't be updated:

  • Rules that account for logical operands, but don't check assignments, like array-callback-return, prefer-arrow-callback, no-return-await, etc.

Some other rules and helpers not listed here don't check operator in assignment expressions, it might be good to doublecheck them.

@mdjermanovic mdjermanovic changed the title Support logical assignment Support logical assignments Aug 25, 2020
kaicataldo pushed a commit that referenced this issue Aug 31, 2020
* update operator-assignment rule

* update astUtils.couldBeError

* update constructor-super rule

* add tests for operator-linebreak rule

* add tests for space-infix-ops rule

* add tests for no-invalid-this rule

* add tests for no-param-reassign rule

* add tests for no-bitwise rule

* add tests for func-name-matching rule

* add tests for prefer-destructuring rule

* add tests for no-extend-native rule

* Update docs/rules/operator-assignment.md

Co-authored-by: Brandon Mills <btmills@users.noreply.github.com>

* Fix &&= in astUtils.couldBeError

* Fix &&= in constructor-super

* Fix comment

Co-authored-by: Brandon Mills <btmills@users.noreply.github.com>
kaicataldo pushed a commit that referenced this issue Aug 31, 2020
… (#13612)

* Update: support logical assignment in code path analysis (refs #13569)

* Fix minor formatting issues
@mdjermanovic mdjermanovic self-assigned this Oct 2, 2020
@mdjermanovic mdjermanovic added the breaking This change is backwards-incompatible label Oct 2, 2020
@mdjermanovic
Copy link
Member Author

Marked as "breaking" to update the complexity rule in the next major version.

@Sembiance
Copy link

Node.js v15 now supports logitcal assignment operators: https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V15.md

Chrome 85, Firefox 79 and Safari 14 all support it too.

With Node now supporting it, more and more developers are going to be adopting it I'm sure.

@mdjermanovic
Copy link
Member Author

ESLint also supports logical assignment operators, as of v7.8.0.

If you're using the default parser, just update the ecmaVersion to the latest 2021:

"parserOptions": {
   "ecmaVersion": 2021
}

The core and most of the rules have been updated in v7.8.0.

We left this issue open for changes that were considered to be breaking for babel-eslint users, and those changes will be implemented in ESLint v8. There are also some minor changes that were blocked at the moment, we'll implement them in some of the future v7 minor releases.

@mdjermanovic mdjermanovic removed the breaking This change is backwards-incompatible label Mar 7, 2021
@aduh95

This comment has been minimized.

@mdjermanovic
Copy link
Member Author

var foo ||= bar;

@aduh95 this isn't valid VariableStatement, ||= bar cannot be Initializer. This code is syntax error in engines, too.

@TimMoser92
Copy link

I just stumbled upon a complaint from ESLint that might be related to these changes. The rule is no-return-assign with the option always set (though that is not relevant). The code looks something like this follows:

const resultMap = {};
const accessGroup = (group) => resultMap[group] ??= [];

ESlint complains here, because the return of the arrow function conatins an assignment. That is technically correct, but I do not think that it is a problem. Let's leave aside, whether or not this is the most beautiful solution. The no-return-assign rule is justified as follows:

It is difficult to tell the intent of the return statement here. It's possible that the function is meant to return the result of bar + 2, but then why is it assigning to foo? It's also possible that the intent was to use a comparison operator such as == and that this code is an error.

This justification does not work as well for logical assignment operators. Since:
a) A logical assignment is not easily mistakenly written when the comparison operator == was intended.
b) It is a valid use-case to conditionally assign and return something, especially in an arrow function, as seen above.

The complaint even persists when the intent is shifted and thus explicated further by adding additional code (as long as the always option is specified):

const resultMap = {};
const addElementToGroup = ({ element, group }) => (resultMap[group] ??= []).push(element);

My current intuition would be to exempt logical assignments from this rule. Otherwise the rule's justification might have to be extended to cover logical assignments as different cases. What do you think?

@ljharb
Copy link
Sponsor Contributor

ljharb commented Oct 5, 2021

@TimMoser92 the rule helps prevent conflating assignments with expressions, and it absolutely should apply to the logical assignment operators. If the docs imply different, I think they should be updated.

@TimMoser92
Copy link

@TimMoser92 the rule helps prevent conflating assignments with expressions, and it absolutely should apply to the logical assignment operators. If the docs imply different, I think they should be updated.

If that is the correct justification, the docs do not seem to imply it. They primarily mention other specific problems that can arise from mixing non-logical assignments into return statements. The justification you give is much more general and would of course also cover logical assignment operators. But if the goal is to never mix assignments into expressions, why is there no general rule about it? Statements like (map[key] ??= []).push(sth) could be forbidden in general, if that were the goal.

So I am not sure, what the solution is here and where this should be decided, nor do I have a strong opinion in any direction, but it seems documented justification and behaviour do not currently match.

@mdjermanovic
Copy link
Member Author

All planned tasks are now completed, so I'm closing this issue.

@TimMoser92 the behavior for ??= is consistent with the behavior for other operators, for example:

/* eslint no-return-assign: ["error", "always"] */

const accessGroup1 = (group) => resultMap[group] ??= []; // reports error

const accessGroup2 = (group) => resultMap[group] += []; // also reports error

Online Demo

If there seems to be something in the rule and/or the documentation for this rule that should be corrected in this regard, feel free to open a separate issue.

@eslint-github-bot eslint-github-bot bot locked and limited conversation to collaborators Jul 6, 2022
@eslint-github-bot eslint-github-bot bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label Jul 6, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion core Relates to ESLint's core APIs and features new syntax This issue is related to new syntax that has reached stage 4 rule Relates to ESLint's core rules
Projects
None yet
Development

No branches or pull requests

5 participants