Skip to content

Commit 1f995c3

Browse files
kaicataldoilyavolodin
authored andcommitted
Fix: no-implicit-coercion string concat false positive (fixes #7057) (#7060)
1 parent 6718749 commit 1f995c3

File tree

2 files changed

+39
-15
lines changed

2 files changed

+39
-15
lines changed

lib/rules/no-implicit-coercion.js

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
"use strict";
77

8+
const astUtils = require("../ast-utils");
9+
810
//------------------------------------------------------------------------------
911
// Helpers
1012
//------------------------------------------------------------------------------
@@ -105,15 +107,35 @@ function getNonNumericOperand(node) {
105107
return null;
106108
}
107109

110+
/**
111+
* Checks whether a node is a string literal or not.
112+
* @param {ASTNode} node The node to check.
113+
* @returns {boolean} Whether or not the passed in node is a
114+
* string literal or not.
115+
*/
116+
function isStringLiteral(node) {
117+
return astUtils.isStringLiteral(node) && node.type !== "TemplateLiteral";
118+
}
119+
120+
/**
121+
* Checks whether a node is an empty string literal or not.
122+
* @param {ASTNode} node The node to check.
123+
* @returns {boolean} Whether or not the passed in node is an
124+
* empty string literal or not.
125+
*/
126+
function isEmptyString(node) {
127+
return isStringLiteral(node) && node.value === "";
128+
}
129+
108130
/**
109131
* Checks whether or not a node is a concatenating with an empty string.
110132
* @param {ASTNode} node - A BinaryExpression node to check.
111133
* @returns {boolean} Whether or not the node is a concatenating with an empty string.
112134
*/
113135
function isConcatWithEmptyString(node) {
114136
return node.operator === "+" && (
115-
(node.left.type === "Literal" && node.left.value === "") ||
116-
(node.right.type === "Literal" && node.right.value === "")
137+
(isEmptyString(node.left) && !isStringLiteral(node.right)) ||
138+
(isEmptyString(node.right) && !isStringLiteral(node.left))
117139
);
118140
}
119141

@@ -123,20 +145,16 @@ function isConcatWithEmptyString(node) {
123145
* @returns {boolean} Whether or not the node is appended with an empty string.
124146
*/
125147
function isAppendEmptyString(node) {
126-
return node.operator === "+=" && node.right.type === "Literal" && node.right.value === "";
148+
return node.operator === "+=" && isEmptyString(node.right);
127149
}
128150

129151
/**
130-
* Gets a node that is the left or right operand of a node, is not the specified literal.
131-
* @param {ASTNode} node - A BinaryExpression node to get.
132-
* @param {any} value - A literal value to check.
133-
* @returns {ASTNode} A node that is the left or right operand of the node, is not the specified literal.
152+
* Returns the operand that is not an empty string from a flagged BinaryExpression.
153+
* @param {ASTNode} node - The flagged BinaryExpression node to check.
154+
* @returns {ASTNode} The operand that is not an empty string from a flagged BinaryExpression.
134155
*/
135-
function getOtherOperand(node, value) {
136-
if (node.left.type === "Literal" && node.left.value === value) {
137-
return node.right;
138-
}
139-
return node.left;
156+
function getNonEmptyOperand(node) {
157+
return isEmptyString(node.left) ? node.right : node.left;
140158
}
141159

142160
//------------------------------------------------------------------------------
@@ -236,7 +254,7 @@ module.exports = {
236254
context.report(
237255
node,
238256
"use `String({{code}})` instead.", {
239-
code: sourceCode.getText(getOtherOperand(node, ""))
257+
code: sourceCode.getText(getNonEmptyOperand(node))
240258
});
241259
}
242260
},
@@ -250,7 +268,7 @@ module.exports = {
250268
context.report(
251269
node,
252270
"use `{{code}} = String({{code}})` instead.", {
253-
code: sourceCode.getText(getOtherOperand(node, ""))
271+
code: sourceCode.getText(getNonEmptyOperand(node))
254272
});
255273
}
256274
}

tests/lib/rules/no-implicit-coercion.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,13 @@ ruleTester.run("no-implicit-coercion", rule, {
7575
{code: "var a = ~foo", options: [{boolean: true}]},
7676
{code: "var a = 1 * foo", options: [{boolean: true, allow: ["*"]}]},
7777
{code: "var a = +foo", options: [{boolean: true, allow: ["+"]}]},
78-
{code: "var a = \"\" + foo", options: [{boolean: true, string: true, allow: ["+"]}]}
78+
{code: "var a = \"\" + foo", options: [{boolean: true, string: true, allow: ["+"]}]},
79+
80+
// https://github.com/eslint/eslint/issues/7057
81+
{code: "'' + 'foo'"},
82+
{code: "'foo' + ''"},
83+
{code: "foo += 'bar'"},
84+
{code: "+42"}
7985
],
8086
invalid: [
8187
{code: "!!foo", errors: [{message: "use `Boolean(foo)` instead.", type: "UnaryExpression"}]},

0 commit comments

Comments
 (0)