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

feat: Add rule no-constant-binary-expression #15296

Merged
merged 15 commits into from Apr 22, 2022

Conversation

captbaritone
Copy link
Contributor

@captbaritone captbaritone commented Nov 11, 2021

Prerequisites checklist

What is the purpose of this pull request? (put an "X" next to an item)

[X] New rule (template)

Please describe what the rule should do:

What category of rule is this? (place an "X" next to just one item)

[X] Warns about a potential error

Provide 2-3 code examples that this rule will warn about:

value === (0 || undefined || null)

!value !== null

someFunction() != null ?? false

x + b ?? 0

Why should this rule be included in ESLint (instead of a plugin)?

Recommended by TSC in #13752.

What changes did you make? (Give an overview)

Adds a rule which is similar in nature to no-constant-condition in that it flags conditions have constant behavior at runtime, since that is highly correlated with developer error. Specifically it checks for:

  • && and || logical expressions where the left hand side is either always truthy or always falsy
  • ?? logical expressions where the left hand side is either always nullish or always non-nullish
  • ==, ===, !== and != binary expressions where one side is either null, undefined, true or false, and the outcome of the comparison can be determined statically.

Is there anything you'd like reviewers to focus on?

The code ended up begin quite verbose with comments, which I personally found helpful during development, but may not be idiomatic in this code base. I'm happy to tone them down if desired.

At its heart this rule boils down to a "best effort". It understands many semantics of the JavaScript language, but it must draw the line somewhere, and those lines are necessarily somewhat arbitrary. I feel pretty good about the lines I ended up setting given the TSC Summary, but I'm open to feedback.

Impact and Representative Examples

I ran this rule on our large monorepo and it uncovered ~450 existing errors. Of those, about one third looked like changes which were intended to be temporary behavior changes, or hard coded feature flags:

const enableFeature = false && shouldEnableFeature();

Personally I think these would merit an /* eslint-disable-next-line */ comment to clarify the motivation, but I could also see us wanting to add an option to permit literal true or false on the left hand side of || or &&.

Below are some representative errors it uncovered, anonymized and reduced to their pure form:

value === (0 || undefined || null)

!value !== null

someFunction() != null ?? false

x + b ?? 0

String(someNumber) ?? 'DEFAULT_VALUE'

typeof someMemberExpression !== undefined

switch (status) {
  case 'A' || 'B' || 'C' || 'D':
    // some code
    break;
  // [...]
}

+value ?? 0

!!someArr.find(predicate) ?? false

Rule Name

Edit @mdjermanovic pointed out that the TSC had proposed a better name, but I missed the comment.

Is no-constant-binary-expression the right name for this rule given that it also applies to the left hand side of logical expressions? I'd rather not re-litigate, but my instinct tells me that the logical expression checks should live in no-constant-condition and this new rule should flag any comparison binary expression whose result is provably static.

@eslint-github-bot eslint-github-bot bot added the triage An ESLint team member will look at this issue soon label Nov 11, 2021
@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Nov 11, 2021

CLA Signed

The committers are authorized under a signed CLA.

@eslint-github-bot
Copy link

Hi @captbaritone!, thanks for the Pull Request

The first commit message isn't properly formatted. We ask that you update the message to match this format, as we use it to generate changelogs and automate releases.

  • The commit message tag wasn't recognized. Did you mean "docs", "fix", or "feat"?
  • There should be a space following the initial tag and colon, for example 'feat: Message'.
  • The first letter of the tag should be in lowercase

Read more about contributing to ESLint here

@nzakas nzakas changed the title Add rule no-constant-binary-operand feat: Add rule no-constant-binary-operand Nov 12, 2021
@nzakas nzakas added feature This change adds a new feature to ESLint rule Relates to ESLint's core rules and removed triage An ESLint team member will look at this issue soon labels Nov 12, 2021
lib/rules/no-constant-binary-operand.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-operand.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-operand.js Outdated Show resolved Hide resolved
conf/eslint-recommended.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-operand.js Outdated Show resolved Hide resolved
@j-f1
Copy link
Contributor

j-f1 commented Nov 23, 2021

Is no-constant-binary-operand the right name for this rule given that it also applies to the left hand side of logical expressions?

When first reading through this PR, I thought this rule prohibited constant operands in binary expressions (such as the 1 in x + 1). I assume at least some other people will feel the same way and may skip over it in a list because of that interpretation.

@captbaritone captbaritone force-pushed the no-constant-binary-operand branch 4 times, most recently from a131be0 to b7961dd Compare November 24, 2021 07:18
@captbaritone
Copy link
Contributor Author

I believe all previous feedback has been addressed.

Copy link

@Swapnull Swapnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, I hope this is ok, but I just noticed you are spelling always and short-circuiting wrong in a few comments. It seems to be fairly consistent, so a find a replace should sort it :)

lib/rules/no-constant-binary-operand.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-operand.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-operand.js Outdated Show resolved Hide resolved
@mdjermanovic
Copy link
Member

Is no-constant-binary-operand the right name for this rule given that it also applies to the left hand side of logical expressions?

Per #13752 (comment), the name should be no-constant-binary-expression.

@captbaritone
Copy link
Contributor Author

captbaritone commented Nov 25, 2021

the name should be no-constant-binary-expression

Ah thanks. Will fix.

Edit I've updated the PR, commit and PR description to use the name no-constant-binary-expression. Sorry I missed that comment the first time 'round.

@captbaritone captbaritone changed the title feat: Add rule no-constant-binary-operand feat: Add rule no-constant-binary-expression Nov 25, 2021
docs/rules/no-constant-binary-expression.md Outdated Show resolved Hide resolved
docs/rules/no-constant-binary-expression.md Outdated Show resolved Hide resolved
docs/rules/no-constant-binary-expression.md Outdated Show resolved Hide resolved
docs/rules/no-constant-binary-expression.md Outdated Show resolved Hide resolved
docs/rules/no-constant-binary-expression.md Outdated Show resolved Hide resolved
docs/rules/no-constant-condition.md Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
tests/lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
@captbaritone
Copy link
Contributor Author

@mdjermanovic Thanks for the detailed review with examples of code that would behave give incorrect results. Made it super easy to address the feedback and add the missing tests cases.

Updated and ready for another round.

lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
@captbaritone
Copy link
Contributor Author

@mdjermanovic Again, great feedback thanks! Ready for another pass.

@captbaritone
Copy link
Contributor Author

@mdjermanovic Feedback addressed.

Copy link
Member

@mdjermanovic mdjermanovic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one small typo to fix, and then LGTM.

lib/rules/no-constant-binary-expression.js Outdated Show resolved Hide resolved
@captbaritone
Copy link
Contributor Author

@mdjermanovic Fixed

Copy link
Member

@mdjermanovic mdjermanovic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, thanks!

I'll leave this open for @nzakas to confirm his feedback has been addressed.

Copy link
Member

@nzakas nzakas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I just left a few suggestions for cleaning up the docs and JSDoc.

lib/rules/utils/ast-utils.js Outdated Show resolved Hide resolved
docs/src/rules/no-constant-binary-expression.md Outdated Show resolved Hide resolved
docs/src/rules/no-constant-binary-expression.md Outdated Show resolved Hide resolved
docs/src/rules/no-constant-binary-expression.md Outdated Show resolved Hide resolved
captbaritone and others added 4 commits April 21, 2022 19:03
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
@captbaritone
Copy link
Contributor Author

@nzakas Thanks! I've merged committed them all.

@mdjermanovic
Copy link
Member

I'll update the doc file for #15782 in a follow-up PR.

@mdjermanovic mdjermanovic merged commit ab6363d into eslint:main Apr 22, 2022
@mdjermanovic
Copy link
Member

@captbaritone thanks for contributing!

crapStone pushed a commit to Calciumdibromid/CaBr2 that referenced this pull request Apr 25, 2022
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [eslint](https://eslint.org) ([source](https://github.com/eslint/eslint)) | devDependencies | minor | [`8.13.0` -> `8.14.0`](https://renovatebot.com/diffs/npm/eslint/8.13.0/8.14.0) |

---

### Release Notes

<details>
<summary>eslint/eslint</summary>

### [`v8.14.0`](https://github.com/eslint/eslint/releases/v8.14.0)

[Compare Source](eslint/eslint@v8.13.0...v8.14.0)

#### Features

-   [`ab6363d`](eslint/eslint@ab6363d) feat: Add rule no-constant-binary-expression ([#&#8203;15296](eslint/eslint#15296)) (Jordan Eldredge)

#### Bug Fixes

-   [`35fa1dd`](eslint/eslint@35fa1dd) fix: allow project paths to have URL-encoded characters ([#&#8203;15795](eslint/eslint#15795)) (Milos Djermanovic)
-   [`413f1d5`](eslint/eslint@413f1d5) fix: update `astUtils.isDirectiveComment` with `globals` and `exported` ([#&#8203;15775](eslint/eslint#15775)) (Milos Djermanovic)

#### Build Related

-   [`c2407e8`](eslint/eslint@c2407e8) build: add node v18 ([#&#8203;15791](eslint/eslint#15791)) (唯然)

#### Chores

-   [`735458c`](eslint/eslint@735458c) chore: add static frontmatter to no-constant-binary-expression docs ([#&#8203;15798](eslint/eslint#15798)) (Milos Djermanovic)
-   [`db28f2c`](eslint/eslint@db28f2c) chore: Add static frontmatter to docs ([#&#8203;15782](eslint/eslint#15782)) (Nicholas C. Zakas)
-   [`3bca59e`](eslint/eslint@3bca59e) chore: markdownlint autofix on commit ([#&#8203;15783](eslint/eslint#15783)) (Nicholas C. Zakas)

</details>

---

### Configuration

📅 **Schedule**: At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox.

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).

Co-authored-by: cabr2-bot <cabr2.help@gmail.com>
Reviewed-on: https://codeberg.org/Calciumdibromid/CaBr2/pulls/1318
Reviewed-by: Epsilon_02 <epsilon_02@noreply.codeberg.org>
Co-authored-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
Co-committed-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
srijan-deepsource pushed a commit to DeepSourceCorp/eslint that referenced this pull request May 30, 2022
* feat: Add rule no-constant-binary-expression

I proposed the core idea of this rule in
eslint#13752 as an addition to
`no-constant-condition`, but on the advice of the TSC, it was
restructured as a standalone rule.

* Share `no-constant-condition`'s `isConstant`

Here we extract `isConstant` into ast-utils and share it between
`no-constant-condition` and `no-constant-binary-expression`.

* Update title of  docs page

* Avoid false positive on shadowed builtins

* Use isLogicalAssignmentOperator

* Ensure we don't treat user defined new expressions as always new

* Move docs to the new docs directory

* Address review feedback

* Address more review feedback

* Address review feedback

* Fix typo

* Update lib/rules/utils/ast-utils.js

Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>

* Update docs/src/rules/no-constant-binary-expression.md

Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>

* Update docs/src/rules/no-constant-binary-expression.md

Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>

* Update docs/src/rules/no-constant-binary-expression.md

Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>

Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
srijan-deepsource added a commit to DeepSourceCorp/eslint that referenced this pull request May 30, 2022
@eslint-github-bot eslint-github-bot bot locked and limited conversation to collaborators Oct 20, 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 Oct 20, 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 feature This change adds a new feature to ESLint rule Relates to ESLint's core rules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Should we extend no-constant-condition to catch comparisons that are constant due to type mismatch?
7 participants