diff --git a/lib/rules/no-implicit-coercion.js b/lib/rules/no-implicit-coercion.js index 0810781cf583..00d26fc24203 100644 --- a/lib/rules/no-implicit-coercion.js +++ b/lib/rules/no-implicit-coercion.js @@ -5,6 +5,8 @@ "use strict"; +const astUtils = require("../ast-utils"); + //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ @@ -105,6 +107,16 @@ function getNonNumericOperand(node) { return null; } +/** + * Checks whether a node is an empty string literal or not. + * @param {ASTNode} node The node to check. + * @returns {boolean} Whether or not the passed in node is an + * empty string literal or not. + */ +function isEmptyString(node) { + return astUtils.isStringLiteral(node) && (node.value === "" || (node.type === "TemplateLiteral" && node.quasis.length === 1 && node.quasis[0].value.cooked === "")); +} + /** * Checks whether or not a node is a concatenating with an empty string. * @param {ASTNode} node - A BinaryExpression node to check. @@ -112,8 +124,8 @@ function getNonNumericOperand(node) { */ function isConcatWithEmptyString(node) { return node.operator === "+" && ( - (node.left.type === "Literal" && node.left.value === "") || - (node.right.type === "Literal" && node.right.value === "") + (isEmptyString(node.left) && !astUtils.isStringLiteral(node.right)) || + (isEmptyString(node.right) && !astUtils.isStringLiteral(node.left)) ); } @@ -123,17 +135,16 @@ function isConcatWithEmptyString(node) { * @returns {boolean} Whether or not the node is appended with an empty string. */ function isAppendEmptyString(node) { - return node.operator === "+=" && node.right.type === "Literal" && node.right.value === ""; + return node.operator === "+=" && isEmptyString(node.right); } /** * Gets a node that is the left or right operand of a node, is not the specified literal. * @param {ASTNode} node - A BinaryExpression node to get. - * @param {any} value - A literal value to check. * @returns {ASTNode} A node that is the left or right operand of the node, is not the specified literal. */ -function getOtherOperand(node, value) { - if (node.left.type === "Literal" && node.left.value === value) { +function getEmptyStringOperand(node) { + if (isEmptyString(node.left)) { return node.right; } return node.left; @@ -236,7 +247,7 @@ module.exports = { context.report( node, "use `String({{code}})` instead.", { - code: sourceCode.getText(getOtherOperand(node, "")) + code: sourceCode.getText(getEmptyStringOperand(node)) }); } }, @@ -250,7 +261,7 @@ module.exports = { context.report( node, "use `{{code}} = String({{code}})` instead.", { - code: sourceCode.getText(getOtherOperand(node, "")) + code: sourceCode.getText(getEmptyStringOperand(node)) }); } } diff --git a/tests/lib/rules/no-implicit-coercion.js b/tests/lib/rules/no-implicit-coercion.js index 639ef046cb75..8fccbe931957 100644 --- a/tests/lib/rules/no-implicit-coercion.js +++ b/tests/lib/rules/no-implicit-coercion.js @@ -64,6 +64,16 @@ ruleTester.run("no-implicit-coercion", rule, { {code: "0 + foo"}, {code: "~foo.bar()"}, + // https://github.com/eslint/eslint/issues/7057 + {code: "'' + 'foo'"}, + {code: "'' + `${foo}`", parserOptions: { ecmaVersion: 6 } }, + {code: "`` + 'foo'", parserOptions: { ecmaVersion: 6 } }, + {code: "'foo' + ''"}, + {code: "`${foo}` + ''", parserOptions: { ecmaVersion: 6 } }, + {code: "'foo' + ``", parserOptions: { ecmaVersion: 6 } }, + {code: "foo += 'bar'"}, + {code: "+42"}, + {code: "!!foo", options: [{boolean: false}]}, {code: "~foo.indexOf(1)", options: [{boolean: false}]}, {code: "+foo", options: [{number: false}]}, @@ -95,6 +105,11 @@ ruleTester.run("no-implicit-coercion", rule, { {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: "var a = \"\" + foo", options: [{boolean: true, allow: ["*"]}], errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}]}, + + // https://github.com/eslint/eslint/issues/7057 + {code: "'' + foo", errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}]}, + {code: "`` + foo", parserOptions: { ecmaVersion: 6 }, errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}]}, + {code: "foo += ``", parserOptions: { ecmaVersion: 6 }, errors: [{message: "use `foo = String(foo)` instead.", type: "AssignmentExpression"}]}, ] });