From a102eaa9ac19e1c6d92f79a4033e9048cfb64c0d Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Sun, 20 Oct 2019 21:26:32 +0200 Subject: [PATCH] Fix: prefer-numeric-literals invalid autofix with adjacent tokens (#12387) --- lib/rules/prefer-numeric-literals.js | 36 +++++++- tests/lib/rules/prefer-numeric-literals.js | 98 ++++++++++++++++++++++ 2 files changed, 130 insertions(+), 4 deletions(-) diff --git a/lib/rules/prefer-numeric-literals.js b/lib/rules/prefer-numeric-literals.js index b4113a3b327..672d9bcaa0c 100644 --- a/lib/rules/prefer-numeric-literals.js +++ b/lib/rules/prefer-numeric-literals.js @@ -5,6 +5,12 @@ "use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ @@ -94,13 +100,13 @@ 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), @@ -108,7 +114,29 @@ module.exports = { */ 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}`); } }); } diff --git a/tests/lib/rules/prefer-numeric-literals.js b/tests/lib/rules/prefer-numeric-literals.js index 800047051f3..ddd6e36c8ac 100644 --- a/tests/lib/rules/prefer-numeric-literals.js +++ b/tests/lib/rules/prefer-numeric-literals.js @@ -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);",