Skip to content

Commit af9afe9

Browse files
authored
Update safe-string-coercion to handle additions of string literals (#25286)
* Update safe-string-coercion to handle additions of string literals Adding strings shouldn't trigger a lint violation of this rule, since adding strings are always safe.
1 parent aed33a4 commit af9afe9

File tree

2 files changed

+57
-8
lines changed

2 files changed

+57
-8
lines changed

scripts/eslint-rules/__tests__/safe-string-coercion-test.internal.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@
1111

1212
const rule = require('../safe-string-coercion');
1313
const {RuleTester} = require('eslint');
14+
15+
RuleTester.setDefaultConfig({
16+
parser: require.resolve('babel-eslint'),
17+
parserOptions: {
18+
ecmaVersion: 6,
19+
sourceType: 'module',
20+
},
21+
});
22+
1423
const ruleTester = new RuleTester();
1524

1625
const missingDevCheckMessage =
@@ -57,7 +66,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
5766
}
5867
`,
5968
`
60-
if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
69+
if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
6170
'' + obj;
6271
`,
6372
`
@@ -87,6 +96,9 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
8796
// doesn't violate this rule.
8897
"if (typeof obj === 'string') { if (typeof obj === 'string' && obj.length) {} else {'' + obj} }",
8998
"if (typeof obj === 'string') if (typeof obj === 'string' && obj.length) {} else {'' + obj}",
99+
"'' + ''",
100+
"'' + '' + ''",
101+
"`test${foo}` + ''",
90102
],
91103
invalid: [
92104
{
@@ -145,7 +157,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
145157
},
146158
{
147159
code: `
148-
if (__D__) { checkFormFieldValueStringCoercion (obj) }
160+
if (__D__) { checkFormFieldValueStringCoercion (obj) }
149161
'' + obj;
150162
`,
151163
errors: [
@@ -156,7 +168,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
156168
},
157169
{
158170
code: `
159-
if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
171+
if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
160172
'' + notobjj;
161173
`,
162174
errors: [
@@ -172,7 +184,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
172184
code: `
173185
if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
174186
// must be right before the check call
175-
someOtherCode();
187+
someOtherCode();
176188
'' + objj;
177189
`,
178190
errors: [
@@ -261,5 +273,16 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
261273
},
262274
],
263275
},
276+
{
277+
code: `'' + obj + ''`,
278+
errors: [
279+
{message: missingDevCheckMessage + '\n' + message},
280+
{message: missingDevCheckMessage + '\n' + message},
281+
],
282+
},
283+
{
284+
code: `foo\`text\` + ""`,
285+
errors: [{message: missingDevCheckMessage + '\n' + message}],
286+
},
264287
],
265288
});

scripts/eslint-rules/safe-string-coercion.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ function isEmptyLiteral(node) {
1717
);
1818
}
1919

20+
function isStringLiteral(node) {
21+
return (
22+
// TaggedTemplateExpressions can return non-strings
23+
(node.type === 'TemplateLiteral' &&
24+
node.parent.type !== 'TaggedTemplateExpression') ||
25+
(node.type === 'Literal' && typeof node.value === 'string')
26+
);
27+
}
28+
2029
// Symbols and Temporal.* objects will throw when using `'' + value`, but that
2130
// pattern can be faster than `String(value)` because JS engines can optimize
2231
// `+` better in some cases. Therefore, in perf-sensitive production codepaths
@@ -120,9 +129,9 @@ function isSafeTypeofExpression(originalValueNode, node) {
120129
return false;
121130
}
122131

123-
/**
132+
/**
124133
Returns true if the code is inside an `if` block that validates the value
125-
excludes symbols and objects. Examples:
134+
excludes symbols and objects. Examples:
126135
* if (typeof value === 'string') { }
127136
* if (typeof value === 'string' || typeof value === 'number') { }
128137
* if (typeof value === 'string' || someOtherTest) { }
@@ -259,7 +268,24 @@ function hasCoercionCheck(node) {
259268
}
260269
}
261270

262-
function plusEmptyString(context, node) {
271+
function isOnlyAddingStrings(node) {
272+
if (node.operator !== '+') {
273+
return;
274+
}
275+
if (isStringLiteral(node.left) && isStringLiteral(node.right)) {
276+
// It's always safe to add string literals
277+
return true;
278+
}
279+
if (node.left.type === 'BinaryExpression' && isStringLiteral(node.right)) {
280+
return isOnlyAddingStrings(node.left);
281+
}
282+
}
283+
284+
function checkBinaryExpression(context, node) {
285+
if (isOnlyAddingStrings(node)) {
286+
return;
287+
}
288+
263289
if (
264290
node.operator === '+' &&
265291
(isEmptyLiteral(node.left) || isEmptyLiteral(node.right))
@@ -337,7 +363,7 @@ module.exports = {
337363
},
338364
create(context) {
339365
return {
340-
BinaryExpression: node => plusEmptyString(context, node),
366+
BinaryExpression: node => checkBinaryExpression(context, node),
341367
CallExpression: node => coerceWithStringConstructor(context, node),
342368
};
343369
},

0 commit comments

Comments
 (0)