@samitouri / QOS-React-2 / commits / af9afe9b3f

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.

lauren committed Oct 4, 2022 at 12:29 UTC af9afe9b3f622aa5eb4caf873025423ba8163c9f
2 files changed +57 -8
scripts/eslint-rules/__tests__/safe-string-coercion-test.internal.js
+27 -4
@@ -11,6 +11,15 @@
11
12 const rule = require('../safe-string-coercion');
13 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 +
23 const ruleTester = new RuleTester();
24
25 const missingDevCheckMessage =
@@ -57,7 +66,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
66 }
67 `,
68 `
60 - if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
69 + if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
70 '' + obj;
71 `,
72 `
@@ -87,6 +96,9 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
96 // doesn't violate this rule.
97 "if (typeof obj === 'string') { if (typeof obj === 'string' && obj.length) {} else {'' + obj} }",
98 "if (typeof obj === 'string') if (typeof obj === 'string' && obj.length) {} else {'' + obj}",
99 + "'' + ''",
100 + "'' + '' + ''",
101 + "`test${foo}` + ''",
102 ],
103 invalid: [
104 {
@@ -145,7 +157,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
157 },
158 {
159 code: `
148 - if (__D__) { checkFormFieldValueStringCoercion (obj) }
160 + if (__D__) { checkFormFieldValueStringCoercion (obj) }
161 '' + obj;
162 `,
163 errors: [
@@ -156,7 +168,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
168 },
169 {
170 code: `
159 - if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
171 + if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
172 '' + notobjj;
173 `,
174 errors: [
@@ -172,7 +184,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
184 code: `
185 if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
186 // must be right before the check call
175 - someOtherCode();
187 + someOtherCode();
188 '' + objj;
189 `,
190 errors: [
@@ -261,5 +273,16 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
273 },
274 ],
275 },
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 + },
287 ],
288 });
scripts/eslint-rules/safe-string-coercion.js
+30 -4
@@ -17,6 +17,15 @@ function isEmptyLiteral(node) {
17 );
18 }
19
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 +
29 // Symbols and Temporal.* objects will throw when using `'' + value`, but that
30 // pattern can be faster than `String(value)` because JS engines can optimize
31 // `+` better in some cases. Therefore, in perf-sensitive production codepaths
@@ -120,9 +129,9 @@ function isSafeTypeofExpression(originalValueNode, node) {
129 return false;
130 }
131
123 -/**
132 +/**
133 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:
135 * if (typeof value === 'string') { }
136 * if (typeof value === 'string' || typeof value === 'number') { }
137 * if (typeof value === 'string' || someOtherTest) { }
@@ -259,7 +268,24 @@ function hasCoercionCheck(node) {
268 }
269 }
270
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 +
289 if (
290 node.operator === '+' &&
291 (isEmptyLiteral(node.left) || isEmptyLiteral(node.right))
@@ -337,7 +363,7 @@ module.exports = {
363 },
364 create(context) {
365 return {
340 - BinaryExpression: node => plusEmptyString(context, node),
366 + BinaryExpression: node => checkBinaryExpression(context, node),
367 CallExpression: node => coerceWithStringConstructor(context, node),
368 };
369 },