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
Should we extend no-constant-condition to catch comparisons that are constant due to type mismatch? #13752
Comments
I’m not clear on what your exact proposal is here as not all of the examples in the code will result in null. Can you describe more concisely what pattern(s) you are looking to add to this rule? Is it just == null? How does the ?? operator fit into this discussion? |
Thanks for taking a look, and my apologies that the explanation was not clear. The proposal is to add additional logic to the As a starting place, I propose that we could detect when the outcome of a comparison to null (either explicitly with I'll break down a few of the examples and hopefully that can help clarify as well:
|
To clarify the motivations - Jordan has already done some initial explorations of this on the Facebook codebase and found that there were several hundred violations. All of them were due to user error. Some were due to a simple misunderstanding of how operator precedence worked: const a = 1;
const b = nullOrNumber();
const fallback = 3;
const result = a + b ?? fallback;
// + has a higher precedence than ??, so it is executed first
// meaning this expression becomes (alwaysNumber ?? fallback) - i.e. the nullish coalesce is never evaluated
// The developer actually intended the code to be:
const result = a + (b ?? fallback); function foo(arg) {
if (!arg == null) {
// ! has a higher precedence than ==, so it is evalutated first
// meaning that this becomes boolean == null, which is a constantly false condition
}
} Some of them were due to deeper misunderstanding of how language features work: const result = <MyComponent /> ?? <Fallback />;
// The developer made the assumption that if MyComponent's render returns null,
// then the JSX also returns null. This is not the case as the result of a JSX expression
// is not at all related to the return value of the render function In most cases you can apply the same reasoning to We figured if we've found several hundred errors in the Facebook codebase, then likely people outside of Facebook have made the same errors. Some of these examples Jordan mentioned are covered by the Some might be covered by the
|
How can you know that |
you don't need to.
You know that if there is a Thus you know that these operators will never result in a nullish value. |
That's a really good point, thanks for clarifying. |
I think we could say that the left operand of It could be behind an option, and that option can apply to all
I support this, just not sure would it be better to make a separate rule that could check all
Per our policy around JSX, ESLint core doesn't assume any specific semantics for JSX. It's a React-specific fact that |
@mdjermanovic Thanks for breaking it down like this. Thinking about it in this way, I realize that there are a actually five things that our rule does more aggressively than 5 Proposals for making
|
Thanks for the thoughtful discussion and extra explanations — this has helped a lot. I agree that the proposals would provide a lot of value and I think it’s worth exploring the right way to consider them in ESLint. That said, I think we are trying to stretch the meaning of “condition” a bit too far. The point of the no-constant-condition rule is to watch for cases when a Boolean value in a condition position will never change. I’m having a hard time reconciling that definition with Boolean or equality operators. Maybe this functionality is better for no-unused-expressions (https://eslint.org/docs/rules/no-unused-expressions)? |
From my perspective proposal no. 1 fits within If you agree that proposal no. 1 fits inside Proposal no. 4 catches the exact same coding errors that That leaves proposal no. 2 (considering constant comparisons). I can see the argument for that belonging outside of I think the core maintainers of ESLint are likely best qualified to make the call of where these proposals belong, so I'll gladly defer to you, but these are my thoughts on the matter. |
Let me take a concrete example here:
These are both already flagged by Just to reiterate: I'm not in favor of adding the proposals to |
Looking at your examples, they are being flagged by For example, this would not be flagged by
Update: With proposal no. 1 the |
I'd be willing to champion this, it looks like a good fit for As @captbaritone noted, the rule would then cover all places where value of
If that value is constant, then there is some dead or useless code, which indicates an error. The same code that already checks the first 5 cases would check the left of
There is a similarity, but I think this doesn't fit the "check the control flow condition" purpose of
I'll have to think some more about
This can be a separate proposal for an enhancement in |
Unless anyone objects in the next day or so, I'll split no. 4 out into its own issue. I think that will simplify this discussion. Update: I've filed the new issue here: #13776 @mdjermanovic thanks for offering to take the lead on no. 1, just let me know if there's anything I can/should do to continue to advocate for its inclusion. |
I still think it's a stretch to say that short-circuiting logical operators should be considered in the same way that control flow statements are, but I'd like to hear from the rest of the team on that point. Otherwise, I agree with @mdjermanovic's comments about the other proposals. I do think we probably need to figure out if there are any existing rules where it makes sense to add |
Sounds like the next step is to build consensus among the team as to whether short-circuiting logical operators should be considered control flow statements. Maybe we need to tag a few people? |
I’d like to make a different proposal. What if we added a new rule called @captbaritone Sorry, everyone is pretty strapped lately. I’ll make sure this gets discussed one way or another next week. |
👍 to a new |
@mdjermanovic what do you think about my suggestion for a |
TSC Summary: This proposal seeks to add three checks into
There is consensus that proposals 2 and 3 do not belong in TSC Question: Shall we accept this as a new rule proposal for |
We discussed this in today's TSC meeting and want to add the rule to ESLint! We decided to name it |
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.
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.
PR is up #15296 |
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
* feat: Add rule no-constant-binary-expression I proposed the core idea of this rule in #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>
* 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>
What rule do you want to change?
no-constant-condition
Does this change cause the rule to produce more or fewer warnings?
More
How will the change be implemented? (New option, new default behavior, etc.)?
New default behavior
Please provide some example code that this change will affect:
What does the rule currently do for this code?
Nothing
What will the rule do after it's changed?
Warn/Error
Are you willing to submit a pull request to implement this change?
Possibly
No Useless Null Checks
There are some types of comparisons which we can tell statically will always create the same result. One such example is performing a null check on an ObjectExpression. In many cases, these type of coding errors are harmless since they merely guard against edge cases which can't actually exist. However, in other cases they represent a misunderstanding of how the code will behave. For example:
a + b ?? c
Misunderstanding of operator precedence.<SomeComponent /> ?? <Fallback />
Incorrect assumption about JSX returning the result of render functionno-constant-condition
guards against many of these types of errors but it does not currently try to understand comparisons that are constant due to the statically knowable type of the values being compared.My question is: should it?
On one hand this seems like these are constant conditions that ESLint can determine, so it should! On the other hand, if ESLint wanted to report all possible constant conditions of this sort, it would end up implementing a type system, which is clearly out of scope for this project.
Speaking of type systems, it's worth noting that many of these errors, type systems (Flow and TypeScript) do not currently report as errors.
Faced with the question of "how far should a rule like this go?" I decided to pick an arbitrary subset of "constant comparisons to null" and write a rule specifically to check for that. You can find it here.
Surprisingly it found hundreds of errors in our (very large) code base, so I believe the rule has some non-trivial value.
I would be curious to hear any maintainer's thoughts on if this would be a good addition to
no-constant-condition
, of if it seems out of scope in some way.If the rule that checks for constant null comparisons seems like a good addition, then it might be worth considering other similar checks that could be performed, such as constant boolean comparisons:
{} || fallback
.Thanks to @bradzacher for some initial insight which lead to this rule.
The text was updated successfully, but these errors were encountered: