Skip to content

Commit

Permalink
Update: prefer-numeric-literals warns Number.parseInt (fixes #8913) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
platinumazure committed Jul 16, 2017
1 parent 846f8b1 commit 128591f
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 12 deletions.
15 changes: 11 additions & 4 deletions docs/rules/prefer-numeric-literals.md
@@ -1,6 +1,6 @@
# disallow `parseInt()` in favor of binary, octal, and hexadecimal literals (prefer-numeric-literals)
# disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals (prefer-numeric-literals)

The `parseInt()` function can be used to turn binary, octal, and hexadecimal strings into integers. As binary, octal, and hexadecimal literals are supported in ES6, this rule encourages use of those numeric literals instead of `parseInt()`.
The `parseInt()` and `Number.parseInt()` functions can be used to turn binary, octal, and hexadecimal strings into integers. As binary, octal, and hexadecimal literals are supported in ES6, this rule encourages use of those numeric literals instead of `parseInt()` or `Number.parseInt()`.

```js
0b111110111 === 503;
Expand All @@ -9,7 +9,7 @@ The `parseInt()` function can be used to turn binary, octal, and hexadecimal str

## Rule Details

This rule disallows `parseInt()` if it is called with two arguments: a string and a radix option of 2 (binary), 8 (octal), or 16 (hexadecimal).
This rule disallows calls to `parseInt()` or `Number.parseInt()` if called with two arguments: a string; and a radix option of 2 (binary), 8 (octal), or 16 (hexadecimal).

Examples of **incorrect** code for this rule:

Expand All @@ -19,6 +19,9 @@ Examples of **incorrect** code for this rule:
parseInt("111110111", 2) === 503;
parseInt("767", 8) === 503;
parseInt("1F7", 16) === 255;
Number.parseInt("111110111", 2) === 503;
Number.parseInt("767", 8) === 503;
Number.parseInt("1F7", 16) === 255;
```

Examples of **correct** code for this rule:
Expand All @@ -29,6 +32,8 @@ Examples of **correct** code for this rule:

parseInt(1);
parseInt(1, 3);
Number.parseInt(1);
Number.parseInt(1, 3);

0b111110111 === 503;
0o767 === 503;
Expand All @@ -38,11 +43,13 @@ a[parseInt](1,2);

parseInt(foo);
parseInt(foo, 2);
Number.parseInt(foo);
Number.parseInt(foo, 2);
```

## When Not To Use It

If you want to allow use of `parseInt()` for binary, octal, or hexadecimal integers. If you are not using ES6 (because binary and octal literals are not supported in ES5 and below).
If you want to allow use of `parseInt()` or `Number.parseInt()` for binary, octal, or hexadecimal integers, or if you are not using ES6 (because binary and octal literals are not supported in ES5 and below), you may wish to disable this rule.

## Compatibility

Expand Down
43 changes: 36 additions & 7 deletions lib/rules/prefer-numeric-literals.js
Expand Up @@ -5,14 +5,41 @@

"use strict";

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Checks to see if a CallExpression's callee node is `parseInt` or
* `Number.parseInt`.
* @param {ASTNode} calleeNode The callee node to evaluate.
* @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`,
* false otherwise.
*/
function isParseInt(calleeNode) {
switch (calleeNode.type) {
case "Identifier":
return calleeNode.name === "parseInt";
case "MemberExpression":
return calleeNode.object.type === "Identifier" &&
calleeNode.object.name === "Number" &&
calleeNode.property.type === "Identifier" &&
calleeNode.property.name === "parseInt";

// no default
}

return false;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: "disallow `parseInt()` in favor of binary, octal, and hexadecimal literals",
description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
category: "ECMAScript 6",
recommended: false
},
Expand All @@ -23,6 +50,8 @@ module.exports = {
},

create(context) {
const sourceCode = context.getSourceCode();

const radixMap = {
2: "binary",
8: "octal",
Expand All @@ -35,9 +64,9 @@ module.exports = {
16: "0x"
};

//--------------------------------------------------------------------------
//----------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
//----------------------------------------------------------------------

return {

Expand All @@ -51,16 +80,16 @@ module.exports = {
// only error if the radix is 2, 8, or 16
const radixName = radixMap[node.arguments[1].value];

if (node.callee.type === "Identifier" &&
node.callee.name === "parseInt" &&
if (isParseInt(node.callee) &&
radixName &&
node.arguments[0].type === "Literal"
) {
context.report({
node,
message: "Use {{radixName}} literals instead of parseInt().",
message: "Use {{radixName}} literals instead of {{functionName}}().",
data: {
radixName
radixName,
functionName: sourceCode.getText(node.callee)
},
fix(fixer) {
const newPrefix = prefixMap[node.arguments[1].value];
Expand Down
34 changes: 33 additions & 1 deletion tests/lib/rules/prefer-numeric-literals.js
Expand Up @@ -22,12 +22,16 @@ ruleTester.run("prefer-numeric-literals", rule, {
valid: [
"parseInt(1);",
"parseInt(1, 3);",
"Number.parseInt(1);",
"Number.parseInt(1, 3);",
"0b111110111 === 503;",
"0o767 === 503;",
"0x1F7 === 503;",
"a[parseInt](1,2);",
"parseInt(foo);",
"parseInt(foo, 2);"
"parseInt(foo, 2);",
"Number.parseInt(foo);",
"Number.parseInt(foo, 2);"
],
invalid: [
{
Expand All @@ -42,6 +46,18 @@ ruleTester.run("prefer-numeric-literals", rule, {
code: "parseInt(\"1F7\", 16) === 255;",
output: "0x1F7 === 255;",
errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
}, {
code: "Number.parseInt(\"111110111\", 2) === 503;",
output: "0b111110111 === 503;",
errors: [{ message: "Use binary literals instead of Number.parseInt()." }]
}, {
code: "Number.parseInt(\"767\", 8) === 503;",
output: "0o767 === 503;",
errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
}, {
code: "Number.parseInt(\"1F7\", 16) === 255;",
output: "0x1F7 === 255;",
errors: [{ message: "Use hexadecimal literals instead of Number.parseInt()." }]
}, {
code: "parseInt('7999', 8);",
output: null, // not fixed, unexpected 9 in parseInt string
Expand All @@ -58,6 +74,22 @@ ruleTester.run("prefer-numeric-literals", rule, {
code: "parseInt('1️⃣3️⃣3️⃣7️⃣', 16);",
output: null, // not fixed, javascript doesn't support emoji literals
errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
}, {
code: "Number.parseInt('7999', 8);",
output: null, // not fixed, unexpected 9 in parseInt string
errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
}, {
code: "Number.parseInt('1234', 2);",
output: null, // not fixed, invalid binary string
errors: [{ message: "Use binary literals instead of Number.parseInt()." }]
}, {
code: "Number.parseInt('1234.5', 8);",
output: null, // not fixed, this isn't an integer
errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
}, {
code: "Number.parseInt('1️⃣3️⃣3️⃣7️⃣', 16);",
output: null, // not fixed, javascript doesn't support emoji literals
errors: [{ message: "Use hexadecimal literals instead of Number.parseInt()." }]
}
]
});

0 comments on commit 128591f

Please sign in to comment.