From f3503037bf6b74dffeab2bfa07d085302443c266 Mon Sep 17 00:00:00 2001
From: Akul Srivastava <akulsr0@gmail.com>
Date: Sat, 24 Dec 2022 23:47:38 +0530
Subject: [PATCH] [Fix] `jsx-no-leaked-render`: invalid fixes in coerce mode

---
 CHANGELOG.md                            |  2 ++
 lib/rules/jsx-no-leaked-render.js       | 15 +++++++++
 tests/lib/rules/jsx-no-leaked-render.js | 44 +++++++++++++++++++++++--
 3 files changed, 58 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8aa0a8f864..184f87485f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
 * [`jsx-no-leaked-render`]: Don't report errors on empty strings if React >= v18 ([#3488][] @himanshu007-creator)
 * [`no-invalid-html-attribute`]: convert autofix to suggestion ([#3474][] @himanshu007-creator @ljharb)
 * [`jsx-no-leaked-render`]: fix removing parentheses for conditionals ([#3502][] @akulsr0)
+* [`jsx-no-leaked-render`]: invalid fixes in coerce mode ([#3511][] @akulsr0)
 
 ### Changed
 * [Docs] [`jsx-no-leaked-render`]: Remove mentions of empty strings for React 18 ([#3468][] @karlhorky)
@@ -28,6 +29,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
 * [Docs] [`prefer-exact-props`]: fix example flow syntax ([#3510][] @smackfu)
 * [Perf] use `anyOf` instead of `oneOf` (@ljharb @remcohaszing)
 
+[#3511]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3511
 [#3510]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3510
 [#3504]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3504
 [#3502]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3502
diff --git a/lib/rules/jsx-no-leaked-render.js b/lib/rules/jsx-no-leaked-render.js
index dccba5b695..c36042bc1d 100644
--- a/lib/rules/jsx-no-leaked-render.js
+++ b/lib/rules/jsx-no-leaked-render.js
@@ -61,12 +61,27 @@ function ruleFixer(context, fixStrategy, fixer, reportedNode, leftNode, rightNod
       if (isParenthesized(context, node)) {
         nodeText = `(${nodeText})`;
       }
+      if (node.parent && node.parent.type === 'ConditionalExpression' && node.parent.consequent.value === false) {
+        return `${getIsCoerceValidNestedLogicalExpression(node) ? '' : '!'}${nodeText}`;
+      }
       return `${getIsCoerceValidNestedLogicalExpression(node) ? '' : '!!'}${nodeText}`;
     }).join(' && ');
 
+    if (rightNode.parent && rightNode.parent.type === 'ConditionalExpression' && rightNode.parent.consequent.value === false) {
+      const consequentVal = rightNode.parent.consequent.raw || rightNode.parent.consequent.name;
+      const alternateVal = rightNode.parent.alternate.raw || rightNode.parent.alternate.name;
+      if (rightNode.parent.test && rightNode.parent.test.type === 'LogicalExpression') {
+        return fixer.replaceText(reportedNode, `${newText} ? ${consequentVal} : ${alternateVal}`);
+      }
+      return fixer.replaceText(reportedNode, `${newText} && ${alternateVal}`);
+    }
+
     if (rightNode.type === 'ConditionalExpression') {
       return fixer.replaceText(reportedNode, `${newText} && (${rightSideText})`);
     }
+    if (rightNode.type === 'Literal') {
+      return null;
+    }
     return fixer.replaceText(reportedNode, `${newText} && ${rightSideText}`);
   }
 
diff --git a/tests/lib/rules/jsx-no-leaked-render.js b/tests/lib/rules/jsx-no-leaked-render.js
index cd9a1f982c..fe9a577003 100644
--- a/tests/lib/rules/jsx-no-leaked-render.js
+++ b/tests/lib/rules/jsx-no-leaked-render.js
@@ -9,6 +9,8 @@
 // Requirements
 //------------------------------------------------------------------------------
 
+const semver = require('semver');
+const eslintPkg = require('eslint/package.json');
 const RuleTester = require('eslint').RuleTester;
 const rule = require('../../../lib/rules/jsx-no-leaked-render');
 
@@ -193,9 +195,9 @@ ruleTester.run('jsx-no-leaked-render', rule, {
       `,
       options: [{ validStrategies: ['coerce'] }],
     },
-  ]),
+  ]) || [],
 
-  invalid: parsers.all([
+  invalid: parsers.all([].concat(
     // Common invalid cases with default options
     {
       code: `
@@ -847,5 +849,41 @@ ruleTester.run('jsx-no-leaked-render', rule, {
         column: 24,
       }],
     },
-  ]),
+    {
+      code: `
+        const MyComponent = () => {
+          return <Something checked={isIndeterminate ? false : isChecked} />
+        }
+      `,
+      output: semver.satisfies(eslintPkg.version, '> 4') ? `
+        const MyComponent = () => {
+          return <Something checked={!isIndeterminate && isChecked} />
+        }
+      ` : null,
+      options: [{ validStrategies: ['coerce'] }],
+      errors: [{
+        message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
+        line: 3,
+        column: 38,
+      }],
+    },
+    {
+      code: `
+        const MyComponent = () => {
+          return <Something checked={cond && isIndeterminate ? false : isChecked} />
+        }
+      `,
+      output: semver.satisfies(eslintPkg.version, '> 4') ? `
+        const MyComponent = () => {
+          return <Something checked={!!cond && !!isIndeterminate ? false : isChecked} />
+        }
+      ` : null,
+      options: [{ validStrategies: ['coerce'] }],
+      errors: [{
+        message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
+        line: 3,
+        column: 38,
+      }],
+    }
+  )),
 });