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

Update: Add enforceForSwitchCase option to use-isnan #12106

Merged
merged 2 commits into from Sep 29, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/rules/use-isnan.md
Expand Up @@ -40,3 +40,50 @@ if (!isNaN(foo)) {
// ...
}
```

## Options

This rule has an object option, with one option:

* `"enforceForSwitchCase"` when set to `true` disallows `case NaN` in `switch` statements. Default is `false`, meaning
that this rule by default does not warn about `case NaN`.

### enforceForSwitchCase

The `switch` statement internally uses the `===` operator to match the expression's value to a case clause.
Therefore, it can never match `case NaN`.

Set `"enforceForSwitchCase"` to `true` if you want this rule to report `case NaN` in `switch` statements.

Examples of **incorrect** code for this rule with `"enforceForSwitchCase"` option set to `true`:

```js
/*eslint use-isnan: ["error", {"enforceForSwitchCase": true}]*/

switch (foo) {
case NaN:
bar();
break;
case 1:
baz();
break;
// ...
}
```

Examples of **correct** code for this rule with `"enforceForSwitchCase"` option set to `true`:

```js
/*eslint use-isnan: ["error", {"enforceForSwitchCase": true}]*/

if (Number.isNaN(foo)) {
bar();
} else {
switch (foo) {
case 1:
baz();
break;
// ...
}
}
```
30 changes: 27 additions & 3 deletions lib/rules/use-isnan.js
Expand Up @@ -20,18 +20,42 @@ module.exports = {
url: "https://eslint.org/docs/rules/use-isnan"
},

schema: [],
schema: [
{
type: "object",
properties: {
enforceForSwitchCase: {
type: "boolean",
default: false
}
},
additionalProperties: false
}
],

messages: {
useIsNaN: "Use the isNaN function to compare with NaN."
comparisonWithNaN: "Use the isNaN function to compare with NaN.",
caseNaN: "'case NaN' can never match. Use Number.isNaN before the switch."
}
},

create(context) {

const enforceForSwitchCase = context.options[0] && context.options[0].enforceForSwitchCase;

return {
BinaryExpression(node) {
if (/^(?:[<>]|[!=]=)=?$/u.test(node.operator) && (node.left.name === "NaN" || node.right.name === "NaN")) {
context.report({ node, messageId: "useIsNaN" });
context.report({ node, messageId: "comparisonWithNaN" });
}
},
SwitchCase(node) {
if (enforceForSwitchCase) {
const test = node.test;

if (test && test.type === "Identifier" && test.name === "NaN") {
context.report({ node, messageId: "caseNaN" });
}
}
}
};
Expand Down
116 changes: 98 additions & 18 deletions tests/lib/rules/use-isnan.js
Expand Up @@ -18,7 +18,7 @@ const rule = require("../../../lib/rules/use-isnan"),

const ruleTester = new RuleTester();

const error = { messageId: "useIsNaN", type: "BinaryExpression" };
const comparisonError = { messageId: "comparisonWithNaN", type: "BinaryExpression" };

ruleTester.run("use-isnan", rule, {
valid: [
Expand All @@ -35,72 +35,152 @@ ruleTester.run("use-isnan", rule, {
"foo(2 * NaN)",
"foo(NaN / 2)",
"foo(2 / NaN)",
"var x; if (x = NaN) { }"
"var x; if (x = NaN) { }",

//------------------------------------------------------------------------------
// enforceForSwitchCase
//------------------------------------------------------------------------------

"switch(foo) { case NaN: break; }",
{
code: "switch(foo) { case NaN: break; }",
options: [{}]
},
{
code: "switch(foo) { case NaN: break; }",
options: [{ enforceForSwitchCase: false }]
},
{
code: "switch(foo) { case bar: break; case NaN: break; default: break; }",
options: [{ enforceForSwitchCase: false }]
},
{
code: "switch(foo) {}",
options: [{ enforceForSwitchCase: true }]
},
{
code: "switch(NaN) {}",
options: [{ enforceForSwitchCase: true }]
},
{
code: "switch(foo) { case Nan: break }",
options: [{ enforceForSwitchCase: true }]
},
{
code: "switch(foo) { case 'NaN': break }",
options: [{ enforceForSwitchCase: true }]
},
{
code: "switch(foo) { case foo(NaN): break }",
options: [{ enforceForSwitchCase: true }]
},
{
code: "switch(foo) { case bar: break; case 1: break; default: break; }",
options: [{ enforceForSwitchCase: true }]
}
],
invalid: [
{
code: "123 == NaN;",
errors: [error]
errors: [comparisonError]
},
{
code: "123 === NaN;",
errors: [error]
errors: [comparisonError]
},
{
code: "NaN === \"abc\";",
errors: [error]
errors: [comparisonError]
},
{
code: "NaN == \"abc\";",
errors: [error]
errors: [comparisonError]
},
{
code: "123 != NaN;",
errors: [error]
errors: [comparisonError]
},
{
code: "123 !== NaN;",
errors: [error]
errors: [comparisonError]
},
{
code: "NaN !== \"abc\";",
errors: [error]
errors: [comparisonError]
},
{
code: "NaN != \"abc\";",
errors: [error]
errors: [comparisonError]
},
{
code: "NaN < \"abc\";",
errors: [error]
errors: [comparisonError]
},
{
code: "\"abc\" < NaN;",
errors: [error]
errors: [comparisonError]
},
{
code: "NaN > \"abc\";",
errors: [error]
errors: [comparisonError]
},
{
code: "\"abc\" > NaN;",
errors: [error]
errors: [comparisonError]
},
{
code: "NaN <= \"abc\";",
errors: [error]
errors: [comparisonError]
},
{
code: "\"abc\" <= NaN;",
errors: [error]
errors: [comparisonError]
},
{
code: "NaN >= \"abc\";",
errors: [error]
errors: [comparisonError]
},
{
code: "\"abc\" >= NaN;",
errors: [error]
errors: [comparisonError]
},

//------------------------------------------------------------------------------
// enforceForSwitchCase
//------------------------------------------------------------------------------

{
code: "switch(foo) { case NaN: }",
options: [{ enforceForSwitchCase: true }],
errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 15 }]
},
{
code: "switch(foo) { case NaN: break; }",
options: [{ enforceForSwitchCase: true }],
errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 15 }]
},
{
code: "switch(foo) { case (NaN): break; }",
options: [{ enforceForSwitchCase: true }],
errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 15 }]
},
{
code: "switch(foo) { case bar: break; case NaN: break; default: break; }",
options: [{ enforceForSwitchCase: true }],
errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 32 }]
},
{
code: "switch(foo) { case bar: case NaN: default: break; }",
options: [{ enforceForSwitchCase: true }],
errors: [{ messageId: "caseNaN", type: "SwitchCase", column: 25 }]
},
{
code: "switch(foo) { case bar: break; case NaN: break; case baz: break; case NaN: break;}",
options: [{ enforceForSwitchCase: true }],
errors: [
{ messageId: "caseNaN", type: "SwitchCase", column: 32 },
{ messageId: "caseNaN", type: "SwitchCase", column: 66 }
]
}
]
});