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

Fix: prefer-numeric-literals invalid autofix with adjacent tokens #12387

Merged
merged 1 commit into from Oct 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
36 changes: 32 additions & 4 deletions lib/rules/prefer-numeric-literals.js
Expand Up @@ -5,6 +5,12 @@

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -94,21 +100,43 @@ module.exports = {
functionName: sourceCode.getText(node.callee)
},
fix(fixer) {
const newPrefix = prefixMap[node.arguments[1].value];

if (sourceCode.getCommentsInside(node).length) {
return null;
}

if (+(newPrefix + node.arguments[0].value) !== parseInt(node.arguments[0].value, node.arguments[1].value)) {
const replacement = `${prefixMap[node.arguments[1].value]}${node.arguments[0].value}`;

if (+replacement !== parseInt(node.arguments[0].value, node.arguments[1].value)) {

/*
* If the newly-produced literal would be invalid, (e.g. 0b1234),
* or it would yield an incorrect parseInt result for some other reason, don't make a fix.
*/
return null;
}
return fixer.replaceText(node, prefixMap[node.arguments[1].value] + node.arguments[0].value);

const tokenBefore = sourceCode.getTokenBefore(node),
tokenAfter = sourceCode.getTokenAfter(node);
let prefix = "",
suffix = "";

if (
tokenBefore &&
tokenBefore.range[1] === node.range[0] &&
!astUtils.canTokensBeAdjacent(tokenBefore, replacement)
) {
prefix = " ";
}

if (
tokenAfter &&
node.range[1] === tokenAfter.range[0] &&
!astUtils.canTokensBeAdjacent(replacement, tokenAfter)
) {
suffix = " ";
}

return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);
}
});
}
Expand Down
98 changes: 98 additions & 0 deletions tests/lib/rules/prefer-numeric-literals.js
Expand Up @@ -92,6 +92,104 @@ ruleTester.run("prefer-numeric-literals", rule, {
errors: [{ message: "Use hexadecimal literals instead of Number.parseInt()." }]
},

// Adjacent tokens tests
{
code: "parseInt('11', 2)",
output: "0b11",
errors: [{ message: "Use binary literals instead of parseInt()." }]
},
{
code: "Number.parseInt('67', 8)",
output: "0o67",
errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
},
{
code: "5+parseInt('A', 16)",
output: "5+0xA",
errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
},
{
code: "function *f(){ yield(Number).parseInt('11', 2) }",
output: "function *f(){ yield 0b11 }",
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "Use binary literals instead of (Number).parseInt()." }]
},
{
code: "function *f(){ yield(Number.parseInt)('67', 8) }",
output: "function *f(){ yield 0o67 }",
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
},
{
code: "function *f(){ yield(parseInt)('A', 16) }",
output: "function *f(){ yield 0xA }",
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
},
{
code: "function *f(){ yield Number.parseInt('11', 2) }",
output: "function *f(){ yield 0b11 }",
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "Use binary literals instead of Number.parseInt()." }]
},
{
code: "function *f(){ yield/**/Number.parseInt('67', 8) }",
output: "function *f(){ yield/**/0o67 }",
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
},
{
code: "function *f(){ yield(parseInt('A', 16)) }",
output: "function *f(){ yield(0xA) }",
parserOptions: { ecmaVersion: 6 },
errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
},
{
code: "parseInt('11', 2)+5",
output: "0b11+5",
errors: [{ message: "Use binary literals instead of parseInt()." }]
},
{
code: "Number.parseInt('17', 8)+5",
output: "0o17+5",
errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
},
{
code: "parseInt('A', 16)+5",
output: "0xA+5",
errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
},
{
code: "parseInt('11', 2)in foo",
output: "0b11 in foo",
errors: [{ message: "Use binary literals instead of parseInt()." }]
},
{
code: "Number.parseInt('17', 8)in foo",
output: "0o17 in foo",
errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
},
{
code: "parseInt('A', 16)in foo",
output: "0xA in foo",
errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
},
{
code: "parseInt('11', 2) in foo",
output: "0b11 in foo",
errors: [{ message: "Use binary literals instead of parseInt()." }]
},
{
code: "Number.parseInt('17', 8)/**/in foo",
output: "0o17/**/in foo",
errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
},
{
code: "(parseInt('A', 16))in foo",
output: "(0xA)in foo",
errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
},

// Should not autofix if it would remove comments
{
code: "/* comment */Number.parseInt('11', 2);",
Expand Down