From 538d2588142e2730380d2fd8893c21c0000402b0 Mon Sep 17 00:00:00 2001 From: Eli White Date: Thu, 8 Sep 2016 07:31:04 -0700 Subject: [PATCH] Update: make no-implicit-coercion support autofixing. (fixes #7056) (#7061) --- lib/rules/no-implicit-coercion.js | 69 ++++++++------- tests/lib/rules/no-implicit-coercion.js | 108 ++++++++++++++++++++---- 2 files changed, 129 insertions(+), 48 deletions(-) diff --git a/lib/rules/no-implicit-coercion.js b/lib/rules/no-implicit-coercion.js index 27ec8721347..3e665a2258b 100644 --- a/lib/rules/no-implicit-coercion.js +++ b/lib/rules/no-implicit-coercion.js @@ -169,6 +169,7 @@ module.exports = { recommended: false }, + fixable: "code", schema: [{ type: "object", properties: { @@ -197,6 +198,25 @@ module.exports = { const options = parseOptions(context.options[0]); const sourceCode = context.getSourceCode(); + /** + * Reports an error and autofixes the node + * @param {ASTNode} node - An ast node to report the error on. + * @param {string} recommendation - The recommended code for the issue + * @returns {void} + */ + function report(node, recommendation) { + context.report({ + node, + message: "use `{{recommendation}}` instead.", + data: { + recommendation + }, + fix(fixer) { + return fixer.replaceText(node, recommendation); + } + }); + } + return { UnaryExpression(node) { let operatorAllowed; @@ -204,31 +224,25 @@ module.exports = { // !!foo operatorAllowed = options.allow.indexOf("!!") >= 0; if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) { - context.report( - node, - "use `Boolean({{code}})` instead.", { - code: sourceCode.getText(node.argument.argument) - }); + const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`; + + report(node, recommendation); } // ~foo.indexOf(bar) operatorAllowed = options.allow.indexOf("~") >= 0; if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) { - context.report( - node, - "use `{{code}} !== -1` instead.", { - code: sourceCode.getText(node.argument) - }); + const recommendation = `${sourceCode.getText(node.argument)} !== -1`; + + report(node, recommendation); } // +foo operatorAllowed = options.allow.indexOf("+") >= 0; if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) { - context.report( - node, - "use `Number({{code}})` instead.", { - code: sourceCode.getText(node.argument) - }); + const recommendation = `Number(${sourceCode.getText(node.argument)})`; + + report(node, recommendation); } }, @@ -241,21 +255,17 @@ module.exports = { const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && getNonNumericOperand(node); if (nonNumericOperand) { - context.report( - node, - "use `Number({{code}})` instead.", { - code: sourceCode.getText(nonNumericOperand) - }); + const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`; + + report(node, recommendation); } // "" + foo operatorAllowed = options.allow.indexOf("+") >= 0; if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) { - context.report( - node, - "use `String({{code}})` instead.", { - code: sourceCode.getText(getNonEmptyOperand(node)) - }); + const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`; + + report(node, recommendation); } }, @@ -265,11 +275,10 @@ module.exports = { const operatorAllowed = options.allow.indexOf("+") >= 0; if (!operatorAllowed && options.string && isAppendEmptyString(node)) { - context.report( - node, - "use `{{code}} = String({{code}})` instead.", { - code: sourceCode.getText(getNonEmptyOperand(node)) - }); + const code = sourceCode.getText(getNonEmptyOperand(node)); + const recommendation = `${code} = String(${code})`; + + report(node, recommendation); } } }; diff --git a/tests/lib/rules/no-implicit-coercion.js b/tests/lib/rules/no-implicit-coercion.js index 3b89251f5a0..72518046ba0 100644 --- a/tests/lib/rules/no-implicit-coercion.js +++ b/tests/lib/rules/no-implicit-coercion.js @@ -84,23 +84,95 @@ ruleTester.run("no-implicit-coercion", rule, { {code: "+42"} ], invalid: [ - {code: "!!foo", errors: [{message: "use `Boolean(foo)` instead.", type: "UnaryExpression"}]}, - {code: "!!(foo + bar)", errors: [{message: "use `Boolean(foo + bar)` instead.", type: "UnaryExpression"}]}, - {code: "~foo.indexOf(1)", errors: [{message: "use `foo.indexOf(1) !== -1` instead.", type: "UnaryExpression"}]}, - {code: "~foo.bar.indexOf(2)", errors: [{message: "use `foo.bar.indexOf(2) !== -1` instead.", type: "UnaryExpression"}]}, - {code: "+foo", errors: [{message: "use `Number(foo)` instead.", type: "UnaryExpression"}]}, - {code: "+foo.bar", errors: [{message: "use `Number(foo.bar)` instead.", type: "UnaryExpression"}]}, - {code: "1*foo", errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}]}, - {code: "foo*1", errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}]}, - {code: "1*foo.bar", errors: [{message: "use `Number(foo.bar)` instead.", type: "BinaryExpression"}]}, - {code: "\"\"+foo", errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}]}, - {code: "foo+\"\"", errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}]}, - {code: "\"\"+foo.bar", errors: [{message: "use `String(foo.bar)` instead.", type: "BinaryExpression"}]}, - {code: "foo += \"\"", errors: [{message: "use `foo = String(foo)` instead.", type: "AssignmentExpression"}]}, - {code: "var a = !!foo", options: [{boolean: true, allow: ["~"]}], errors: [{message: "use `Boolean(foo)` instead.", type: "UnaryExpression"}]}, - {code: "var a = ~foo.indexOf(1)", options: [{boolean: true, allow: ["!!"]}], errors: [{message: "use `foo.indexOf(1) !== -1` instead.", type: "UnaryExpression"}]}, - {code: "var a = 1 * foo", options: [{boolean: true, allow: ["+"]}], errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}]}, - {code: "var a = +foo", options: [{boolean: true, allow: ["*"]}], errors: [{message: "use `Number(foo)` instead.", type: "UnaryExpression"}]}, - {code: "var a = \"\" + foo", options: [{boolean: true, allow: ["*"]}], errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}]} + { + code: "!!foo", + errors: [{message: "use `Boolean(foo)` instead.", type: "UnaryExpression"}], + output: "Boolean(foo)" + }, + { + code: "!!(foo + bar)", + errors: [{message: "use `Boolean(foo + bar)` instead.", type: "UnaryExpression"}], + output: "Boolean(foo + bar)" + }, + { + code: "~foo.indexOf(1)", + errors: [{message: "use `foo.indexOf(1) !== -1` instead.", type: "UnaryExpression"}], + output: "foo.indexOf(1) !== -1" + }, + { + code: "~foo.bar.indexOf(2)", + errors: [{message: "use `foo.bar.indexOf(2) !== -1` instead.", type: "UnaryExpression"}], + output: "foo.bar.indexOf(2) !== -1" + }, + { + code: "+foo", + errors: [{message: "use `Number(foo)` instead.", type: "UnaryExpression"}], + output: "Number(foo)" + }, + { + code: "+foo.bar", + errors: [{message: "use `Number(foo.bar)` instead.", type: "UnaryExpression"}], + output: "Number(foo.bar)" + }, + { + code: "1*foo", + errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}], + output: "Number(foo)" + }, + { + code: "foo*1", + errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}], + output: "Number(foo)" + }, + { + code: "1*foo.bar", + errors: [{message: "use `Number(foo.bar)` instead.", type: "BinaryExpression"}], + output: "Number(foo.bar)" + }, + { + code: "\"\"+foo", + errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}], + output: "String(foo)" + }, + { + code: "foo+\"\"", + errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}], + output: "String(foo)" + }, + { + code: "\"\"+foo.bar", + errors: [{message: "use `String(foo.bar)` instead.", type: "BinaryExpression"}], + output: "String(foo.bar)" + }, + { + code: "foo += \"\"", + errors: [{message: "use `foo = String(foo)` instead.", type: "AssignmentExpression"}], + output: "foo = String(foo)" + }, + { + code: "var a = !!foo", options: [{boolean: true, allow: ["~"]}], + errors: [{message: "use `Boolean(foo)` instead.", type: "UnaryExpression"}], + output: "var a = Boolean(foo)" + }, + { + code: "var a = ~foo.indexOf(1)", options: [{boolean: true, allow: ["!!"]}], + errors: [{message: "use `foo.indexOf(1) !== -1` instead.", type: "UnaryExpression"}], + output: "var a = foo.indexOf(1) !== -1" + }, + { + code: "var a = 1 * foo", options: [{boolean: true, allow: ["+"]}], + errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}], + output: "var a = Number(foo)" + }, + { + code: "var a = +foo", options: [{boolean: true, allow: ["*"]}], + errors: [{message: "use `Number(foo)` instead.", type: "UnaryExpression"}], + output: "var a = Number(foo)" + }, + { + code: "var a = \"\" + foo", options: [{boolean: true, allow: ["*"]}], + errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}], + output: "var a = String(foo)" + } ] });