From 30f002204048d9e6e0605af2329e74d360f173a4 Mon Sep 17 00:00:00 2001
From: "Hur Hyeon Bin (Max)" <160996936+hyeonbinHur@users.noreply.github.com>
Date: Fri, 23 May 2025 02:33:20 +0900
Subject: [PATCH 1/7] [Fix] `display-name`: avoid false positive when React is
 shadowed
Fixes #3924
---
 lib/rules/display-name.js       | 193 ++++++++++++++++++++++++--------
 tests/lib/rules/display-name.js | 138 ++++++++++++++++++++---
 2 files changed, 271 insertions(+), 60 deletions(-)
diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js
index b85ec34f1c..cfa8e89339 100644
--- a/lib/rules/display-name.js
+++ b/lib/rules/display-name.js
@@ -31,7 +31,8 @@ const messages = {
 module.exports = {
   meta: {
     docs: {
-      description: 'Disallow missing displayName in a React component definition',
+      description:
+        'Disallow missing displayName in a React component definition',
       category: 'Best Practices',
       recommended: true,
       url: docsUrl('display-name'),
@@ -39,24 +40,27 @@ module.exports = {
 
     messages,
 
-    schema: [{
-      type: 'object',
-      properties: {
-        ignoreTranspilerName: {
-          type: 'boolean',
-        },
-        checkContextObjects: {
-          type: 'boolean',
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          ignoreTranspilerName: {
+            type: 'boolean',
+          },
+          checkContextObjects: {
+            type: 'boolean',
+          },
         },
+        additionalProperties: false,
       },
-      additionalProperties: false,
-    }],
+    ],
   },
 
   create: Components.detect((context, components, utils) => {
     const config = context.options[0] || {};
     const ignoreTranspilerName = config.ignoreTranspilerName || false;
-    const checkContextObjects = (config.checkContextObjects || false) && testReactVersion(context, '>= 16.3.0');
+    const checkContextObjects = (config.checkContextObjects || false)
+      && testReactVersion(context, '>= 16.3.0');
 
     const contextObjects = new Map();
 
@@ -76,10 +80,12 @@ module.exports = {
      * @returns {boolean} True if React.forwardRef is nested inside React.memo, false if not.
      */
     function isNestedMemo(node) {
-      return astUtil.isCallExpression(node)
+      return (
+        astUtil.isCallExpression(node)
         && node.arguments
         && astUtil.isCallExpression(node.arguments[0])
-        && utils.isPragmaComponentWrapper(node);
+        && utils.isPragmaComponentWrapper(node)
+      );
     }
 
     /**
@@ -115,52 +121,131 @@ module.exports = {
      * @returns {boolean} True if component has a name, false if not.
      */
     function hasTranspilerName(node) {
-      const namedObjectAssignment = (
-        node.type === 'ObjectExpression'
+      const namedObjectAssignment = node.type === 'ObjectExpression'
         && node.parent
         && node.parent.parent
         && node.parent.parent.type === 'AssignmentExpression'
-        && (
-          !node.parent.parent.left.object
+        && (!node.parent.parent.left.object
           || node.parent.parent.left.object.name !== 'module'
-          || node.parent.parent.left.property.name !== 'exports'
-        )
-      );
-      const namedObjectDeclaration = (
-        node.type === 'ObjectExpression'
+          || node.parent.parent.left.property.name !== 'exports');
+      const namedObjectDeclaration = node.type === 'ObjectExpression'
         && node.parent
         && node.parent.parent
-        && node.parent.parent.type === 'VariableDeclarator'
-      );
-      const namedClass = (
-        (node.type === 'ClassDeclaration' || node.type === 'ClassExpression')
+        && node.parent.parent.type === 'VariableDeclarator';
+      const namedClass = (node.type === 'ClassDeclaration' || node.type === 'ClassExpression')
         && node.id
-        && !!node.id.name
-      );
+        && !!node.id.name;
 
-      const namedFunctionDeclaration = (
-        (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression')
+      const namedFunctionDeclaration = (node.type === 'FunctionDeclaration'
+          || node.type === 'FunctionExpression')
         && node.id
-        && !!node.id.name
-      );
+        && !!node.id.name;
 
-      const namedFunctionExpression = (
-        astUtil.isFunctionLikeExpression(node)
+      const namedFunctionExpression = astUtil.isFunctionLikeExpression(node)
         && node.parent
-        && (node.parent.type === 'VariableDeclarator' || node.parent.type === 'Property' || node.parent.method === true)
-        && (!node.parent.parent || !componentUtil.isES5Component(node.parent.parent, context))
-      );
+        && (node.parent.type === 'VariableDeclarator'
+          || node.parent.type === 'Property'
+          || node.parent.method === true)
+        && (!node.parent.parent
+          || !componentUtil.isES5Component(node.parent.parent, context));
 
       if (
-        namedObjectAssignment || namedObjectDeclaration
+        namedObjectAssignment
+        || namedObjectDeclaration
         || namedClass
-        || namedFunctionDeclaration || namedFunctionExpression
+        || namedFunctionDeclaration
+        || namedFunctionExpression
       ) {
         return true;
       }
       return false;
     }
 
+    function hasVariableDeclaration(node, name) {
+      if (!node) return false;
+
+      if (node.type === 'VariableDeclaration') {
+        return node.declarations.some((decl) => {
+          if (!decl.id) return false;
+
+          // const name = ...
+          if (decl.id.type === 'Identifier' && decl.id.name === name) {
+            return true;
+          }
+
+          // const [name] = ...
+          if (decl.id.type === 'ArrayPattern') {
+            return decl.id.elements.some(
+              (el) => el && el.type === 'Identifier' && el.name === name
+            );
+          }
+
+          // const { name } = ...
+          if (decl.id.type === 'ObjectPattern') {
+            return decl.id.properties.some(
+              (prop) => prop.type === 'Property' && prop.key && prop.key.name === name
+            );
+          }
+
+          return false;
+        });
+      }
+
+      if (node.type === 'BlockStatement' && node.body) {
+        return node.body.some((stmt) => hasVariableDeclaration(stmt, name));
+      }
+
+      return false;
+    }
+
+    function isIdentifierShadowed(node, identifierName) {
+      while (node && node.parent) {
+        node = node.parent;
+        if (
+          node.type === 'FunctionDeclaration'
+          || node.type === 'FunctionExpression'
+          || node.type === 'ArrowFunctionExpression'
+        ) {
+          break;
+        }
+      }
+
+      if (!node || !node.body) {
+        return false;
+      }
+
+      return hasVariableDeclaration(node.body, identifierName);
+    }
+
+    /**
+     *
+     * Check is current component shadowed
+     * @param {ASTNode} node The AST node being checked.
+     * @returns {boolean} True if component has a name, false if not.
+     */
+
+    function isShadowedComponent(node) {
+      if (!node || node.type !== 'CallExpression') {
+        return false;
+      }
+
+      if (
+        node.callee.type === 'MemberExpression'
+        && node.callee.object.name === 'React'
+      ) {
+        return isIdentifierShadowed(node, 'React');
+      }
+
+      if (node.callee.type === 'Identifier') {
+        const name = node.callee.name;
+        if (name === 'memo' || name === 'forwardRef') {
+          return isIdentifierShadowed(node, name);
+        }
+      }
+
+      return false;
+    }
+
     // --------------------------------------------------------------------------
     // Public
     // --------------------------------------------------------------------------
@@ -168,7 +253,10 @@ module.exports = {
     return {
       ExpressionStatement(node) {
         if (checkContextObjects && isCreateContext(node)) {
-          contextObjects.set(node.expression.left.name, { node, hasDisplayName: false });
+          contextObjects.set(node.expression.left.name, {
+            node,
+            hasDisplayName: false,
+          });
         }
       },
       VariableDeclarator(node) {
@@ -232,7 +320,10 @@ module.exports = {
         if (ignoreTranspilerName || !hasTranspilerName(node)) {
           // Search for the displayName declaration
           node.properties.forEach((property) => {
-            if (!property.key || !propsUtil.isDisplayNameDeclaration(property.key)) {
+            if (
+              !property.key
+              || !propsUtil.isDisplayNameDeclaration(property.key)
+            ) {
               return;
             }
             markDisplayNameAsDeclared(node);
@@ -247,7 +338,10 @@ module.exports = {
           return;
         }
 
-        if (node.arguments.length > 0 && astUtil.isFunctionLikeExpression(node.arguments[0])) {
+        if (
+          node.arguments.length > 0
+          && astUtil.isFunctionLikeExpression(node.arguments[0])
+        ) {
           // Skip over React.forwardRef declarations that are embedded within
           // a React.memo i.e. React.memo(React.forwardRef(/* ... */))
           // This means that we raise a single error for the call to React.memo
@@ -269,9 +363,16 @@ module.exports = {
       'Program:exit'() {
         const list = components.list();
         // Report missing display name for all components
-        values(list).filter((component) => !component.hasDisplayName).forEach((component) => {
-          reportMissingDisplayName(component);
-        });
+        values(list)
+          .filter((component) => {
+            if (isShadowedComponent(component.node)) {
+              return false;
+            }
+            return !component.hasDisplayName;
+          })
+          .forEach((component) => {
+            reportMissingDisplayName(component);
+          });
         if (checkContextObjects) {
           // Report missing display name for all context objects
           forEach(
diff --git a/tests/lib/rules/display-name.js b/tests/lib/rules/display-name.js
index 18fd1ca83c..419c6b869d 100644
--- a/tests/lib/rules/display-name.js
+++ b/tests/lib/rules/display-name.js
@@ -29,6 +29,56 @@ const parserOptions = {
 const ruleTester = new RuleTester({ parserOptions });
 ruleTester.run('display-name', rule, {
   valid: parsers.all([
+    {
+      code: `
+        import React, { forwardRef } from 'react'
+        
+        const TestComponent = function () {
+          const { forwardRef } = { forwardRef: () => null }
+          
+          const OtherComp = forwardRef((props, ref) => \`\${props} \${ref}\`)
+          
+          return OtherComp
+        }
+      `,
+    },
+    {
+      code: `
+        import React, { memo } from 'react'
+        
+        const TestComponent = function () {
+          const memo = (cb) => cb()
+          
+          const Comp = memo(() => {
+            return 
shadowed
+          })
+          
+          return Comp
+        }
+      `,
+    },
+    {
+      code: `
+        import React, { memo, forwardRef } from 'react'
+
+        const MixedShadowed = function () {
+          const memo = (cb) => cb()
+          const { forwardRef } = { forwardRef: () => null }
+          const [React] = [{ memo, forwardRef }]
+
+          const Comp = memo(() => {
+            return shadowed
+          })
+          const ReactMemo = React.memo(() => null)
+          const ReactForward = React.forwardRef((props, ref) => {
+            return \`\${props} \${ref}\`
+          })
+          const OtherComp = forwardRef((props, ref) => \`\${props} \${ref}\`)
+
+          return [Comp, ReactMemo, ReactForward, OtherComp]
+        }
+      `,
+    },
     {
       code: `
         var Hello = createReactClass({
@@ -848,6 +898,66 @@ ruleTester.run('display-name', rule, {
   ]),
 
   invalid: parsers.all([
+    {
+      code: `
+        import React from 'react'
+
+        const Comp = React.forwardRef((props, ref) => {
+        return test
+        })
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 4,
+        },
+      ],
+    },
+    {
+      code: `
+        import {forwardRef} from 'react'
+
+        const Comp = forwardRef((props, ref) => {
+        return test
+        })
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 4,
+        },
+      ],
+    },
+    {
+      code: `
+        import React from 'react'
+        
+        const Comp = React.memo(() => {
+          return test
+        })
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 4,
+        },
+      ],
+    },
+    {
+      code: `
+        import { memo } from 'react'
+        
+        const Comp = memo(() => {
+          return test
+        })
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 4,
+        },
+      ],
+    },
     {
       code: `
         var Hello = createReactClass({
@@ -860,7 +970,8 @@ ruleTester.run('display-name', rule, {
       errors: [
         {
           messageId: 'noDisplayName',
-        }],
+        },
+      ],
     },
     {
       code: `
@@ -1067,9 +1178,9 @@ ruleTester.run('display-name', rule, {
       errors: [{ messageId: 'noDisplayName' }],
     },
     {
-    // Only trigger an error for the outer React.memo,
-    // if the React version is not in the following range:
-    // ^0.14.10 || ^15.7.0 || >= 16.12.0
+      // Only trigger an error for the outer React.memo,
+      // if the React version is not in the following range:
+      // ^0.14.10 || ^15.7.0 || >= 16.12.0
       code: `
         import React from 'react'
 
@@ -1082,7 +1193,8 @@ ruleTester.run('display-name', rule, {
       errors: [
         {
           messageId: 'noDisplayName',
-        }],
+        },
+      ],
       settings: {
         react: {
           version: '15.6.0',
@@ -1090,9 +1202,9 @@ ruleTester.run('display-name', rule, {
       },
     },
     {
-    // Only trigger an error for the outer React.memo,
-    // if the React version is not in the following range:
-    // ^0.14.10 || ^15.7.0 || >= ^16.12.0
+      // Only trigger an error for the outer React.memo,
+      // if the React version is not in the following range:
+      // ^0.14.10 || ^15.7.0 || >= ^16.12.0
       code: `
         import React from 'react'
 
@@ -1110,9 +1222,9 @@ ruleTester.run('display-name', rule, {
       },
     },
     {
-    // React does not handle the result of forwardRef being passed into memo
-    // ComponentWithMemoAndForwardRef gets shown as Memo(Anonymous)
-    // See https://github.com/facebook/react/issues/16722
+      // React does not handle the result of forwardRef being passed into memo
+      // ComponentWithMemoAndForwardRef gets shown as Memo(Anonymous)
+      // See https://github.com/facebook/react/issues/16722
       code: `
         import React from 'react'
 
@@ -1239,9 +1351,7 @@ ruleTester.run('display-name', rule, {
           {a} {listItem}
         );
       `,
-      errors: [
-        { message: 'Component definition is missing display name' },
-      ],
+      errors: [{ message: 'Component definition is missing display name' }],
     },
     {
       code: `
From 6e055364257faf3f1aacd2bcb9bfe301f42895c3 Mon Sep 17 00:00:00 2001
From: "Hur Hyeon Bin (Max)" <160996936+hyeonbinHur@users.noreply.github.com>
Date: Fri, 23 May 2025 04:37:11 +0900
Subject: [PATCH 2/7] style: fix lint,prettier  errors
---
 lib/rules/display-name.js       | 119 +++++++++++++++-----------------
 tests/lib/rules/display-name.js |  28 ++++----
 2 files changed, 68 insertions(+), 79 deletions(-)
diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js
index cfa8e89339..19f5283865 100644
--- a/lib/rules/display-name.js
+++ b/lib/rules/display-name.js
@@ -31,8 +31,7 @@ const messages = {
 module.exports = {
   meta: {
     docs: {
-      description:
-        'Disallow missing displayName in a React component definition',
+      description: 'Disallow missing displayName in a React component definition',
       category: 'Best Practices',
       recommended: true,
       url: docsUrl('display-name'),
@@ -40,27 +39,24 @@ module.exports = {
 
     messages,
 
-    schema: [
-      {
-        type: 'object',
-        properties: {
-          ignoreTranspilerName: {
-            type: 'boolean',
-          },
-          checkContextObjects: {
-            type: 'boolean',
-          },
+    schema: [{
+      type: 'object',
+      properties: {
+        ignoreTranspilerName: {
+          type: 'boolean',
+        },
+        checkContextObjects: {
+          type: 'boolean',
         },
-        additionalProperties: false,
       },
-    ],
+      additionalProperties: false,
+    }],
   },
 
   create: Components.detect((context, components, utils) => {
     const config = context.options[0] || {};
     const ignoreTranspilerName = config.ignoreTranspilerName || false;
-    const checkContextObjects = (config.checkContextObjects || false)
-      && testReactVersion(context, '>= 16.3.0');
+    const checkContextObjects = (config.checkContextObjects || false) && testReactVersion(context, '>= 16.3.0');
 
     const contextObjects = new Map();
 
@@ -80,12 +76,10 @@ module.exports = {
      * @returns {boolean} True if React.forwardRef is nested inside React.memo, false if not.
      */
     function isNestedMemo(node) {
-      return (
-        astUtil.isCallExpression(node)
+      return astUtil.isCallExpression(node)
         && node.arguments
         && astUtil.isCallExpression(node.arguments[0])
-        && utils.isPragmaComponentWrapper(node)
-      );
+        && utils.isPragmaComponentWrapper(node);
     }
 
     /**
@@ -121,40 +115,46 @@ module.exports = {
      * @returns {boolean} True if component has a name, false if not.
      */
     function hasTranspilerName(node) {
-      const namedObjectAssignment = node.type === 'ObjectExpression'
+      const namedObjectAssignment = (
+        node.type === 'ObjectExpression'
         && node.parent
         && node.parent.parent
         && node.parent.parent.type === 'AssignmentExpression'
-        && (!node.parent.parent.left.object
+        && (
+          !node.parent.parent.left.object
           || node.parent.parent.left.object.name !== 'module'
-          || node.parent.parent.left.property.name !== 'exports');
-      const namedObjectDeclaration = node.type === 'ObjectExpression'
+          || node.parent.parent.left.property.name !== 'exports'
+        )
+      );
+      const namedObjectDeclaration = (
+        node.type === 'ObjectExpression'
         && node.parent
         && node.parent.parent
-        && node.parent.parent.type === 'VariableDeclarator';
-      const namedClass = (node.type === 'ClassDeclaration' || node.type === 'ClassExpression')
+        && node.parent.parent.type === 'VariableDeclarator'
+      );
+      const namedClass = (
+        (node.type === 'ClassDeclaration' || node.type === 'ClassExpression')
         && node.id
-        && !!node.id.name;
+        && !!node.id.name
+      );
 
-      const namedFunctionDeclaration = (node.type === 'FunctionDeclaration'
-          || node.type === 'FunctionExpression')
+      const namedFunctionDeclaration = (
+        (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression')
         && node.id
-        && !!node.id.name;
+        && !!node.id.name
+      );
 
-      const namedFunctionExpression = astUtil.isFunctionLikeExpression(node)
+      const namedFunctionExpression = (
+        astUtil.isFunctionLikeExpression(node)
         && node.parent
-        && (node.parent.type === 'VariableDeclarator'
-          || node.parent.type === 'Property'
-          || node.parent.method === true)
-        && (!node.parent.parent
-          || !componentUtil.isES5Component(node.parent.parent, context));
+        && (node.parent.type === 'VariableDeclarator' || node.parent.type === 'Property' || node.parent.method === true)
+        && (!node.parent.parent || !componentUtil.isES5Component(node.parent.parent, context))
+      );
 
       if (
-        namedObjectAssignment
-        || namedObjectDeclaration
+        namedObjectAssignment || namedObjectDeclaration
         || namedClass
-        || namedFunctionDeclaration
-        || namedFunctionExpression
+        || namedFunctionDeclaration || namedFunctionExpression
       ) {
         return true;
       }
@@ -199,31 +199,29 @@ module.exports = {
     }
 
     function isIdentifierShadowed(node, identifierName) {
-      while (node && node.parent) {
-        node = node.parent;
+      let currentNode = node;
+      while (currentNode && currentNode.parent) {
+        currentNode = currentNode.parent;
         if (
-          node.type === 'FunctionDeclaration'
-          || node.type === 'FunctionExpression'
-          || node.type === 'ArrowFunctionExpression'
+          currentNode.type === 'FunctionDeclaration'
+          || currentNode.type === 'FunctionExpression'
+          || currentNode.type === 'ArrowFunctionExpression'
         ) {
           break;
         }
       }
 
-      if (!node || !node.body) {
+      if (!currentNode || !currentNode.body) {
         return false;
       }
 
-      return hasVariableDeclaration(node.body, identifierName);
+      return hasVariableDeclaration(currentNode.body, identifierName);
     }
-
     /**
-     *
-     * Check is current component shadowed
-     * @param {ASTNode} node The AST node being checked.
-     * @returns {boolean} True if component has a name, false if not.
+     * Checks whether the component wrapper (e.g. React.memo or forwardRef) is shadowed in the current scope.
+     * @param {ASTNode} node - The CallExpression AST node representing a potential component wrapper.
+     * @returns {boolean} True if the wrapper identifier (e.g. 'React', 'memo', 'forwardRef') is shadowed, false otherwise.
      */
-
     function isShadowedComponent(node) {
       if (!node || node.type !== 'CallExpression') {
         return false;
@@ -253,10 +251,7 @@ module.exports = {
     return {
       ExpressionStatement(node) {
         if (checkContextObjects && isCreateContext(node)) {
-          contextObjects.set(node.expression.left.name, {
-            node,
-            hasDisplayName: false,
-          });
+          contextObjects.set(node.expression.left.name, { node, hasDisplayName: false });
         }
       },
       VariableDeclarator(node) {
@@ -320,10 +315,7 @@ module.exports = {
         if (ignoreTranspilerName || !hasTranspilerName(node)) {
           // Search for the displayName declaration
           node.properties.forEach((property) => {
-            if (
-              !property.key
-              || !propsUtil.isDisplayNameDeclaration(property.key)
-            ) {
+            if (!property.key || !propsUtil.isDisplayNameDeclaration(property.key)) {
               return;
             }
             markDisplayNameAsDeclared(node);
@@ -338,10 +330,7 @@ module.exports = {
           return;
         }
 
-        if (
-          node.arguments.length > 0
-          && astUtil.isFunctionLikeExpression(node.arguments[0])
-        ) {
+        if (node.arguments.length > 0 && astUtil.isFunctionLikeExpression(node.arguments[0])) {
           // Skip over React.forwardRef declarations that are embedded within
           // a React.memo i.e. React.memo(React.forwardRef(/* ... */))
           // This means that we raise a single error for the call to React.memo
diff --git a/tests/lib/rules/display-name.js b/tests/lib/rules/display-name.js
index 419c6b869d..82fea3526e 100644
--- a/tests/lib/rules/display-name.js
+++ b/tests/lib/rules/display-name.js
@@ -970,8 +970,7 @@ ruleTester.run('display-name', rule, {
       errors: [
         {
           messageId: 'noDisplayName',
-        },
-      ],
+        }],
     },
     {
       code: `
@@ -1178,9 +1177,9 @@ ruleTester.run('display-name', rule, {
       errors: [{ messageId: 'noDisplayName' }],
     },
     {
-      // Only trigger an error for the outer React.memo,
-      // if the React version is not in the following range:
-      // ^0.14.10 || ^15.7.0 || >= 16.12.0
+    // Only trigger an error for the outer React.memo,
+    // if the React version is not in the following range:
+    // ^0.14.10 || ^15.7.0 || >= 16.12.0
       code: `
         import React from 'react'
 
@@ -1193,8 +1192,7 @@ ruleTester.run('display-name', rule, {
       errors: [
         {
           messageId: 'noDisplayName',
-        },
-      ],
+        }],
       settings: {
         react: {
           version: '15.6.0',
@@ -1202,9 +1200,9 @@ ruleTester.run('display-name', rule, {
       },
     },
     {
-      // Only trigger an error for the outer React.memo,
-      // if the React version is not in the following range:
-      // ^0.14.10 || ^15.7.0 || >= ^16.12.0
+    // Only trigger an error for the outer React.memo,
+    // if the React version is not in the following range:
+    // ^0.14.10 || ^15.7.0 || >= ^16.12.0
       code: `
         import React from 'react'
 
@@ -1222,9 +1220,9 @@ ruleTester.run('display-name', rule, {
       },
     },
     {
-      // React does not handle the result of forwardRef being passed into memo
-      // ComponentWithMemoAndForwardRef gets shown as Memo(Anonymous)
-      // See https://github.com/facebook/react/issues/16722
+    // React does not handle the result of forwardRef being passed into memo
+    // ComponentWithMemoAndForwardRef gets shown as Memo(Anonymous)
+    // See https://github.com/facebook/react/issues/16722
       code: `
         import React from 'react'
 
@@ -1351,7 +1349,9 @@ ruleTester.run('display-name', rule, {
           {a} {listItem}
         );
       `,
-      errors: [{ message: 'Component definition is missing display name' }],
+      errors: [
+        { message: 'Component definition is missing display name' },
+      ],
     },
     {
       code: `
From 16e8aefb48dae55d14b13831f145a447368eea72 Mon Sep 17 00:00:00 2001
From: "Hur Hyeon Bin (Max)" <160996936+hyeonbinHur@users.noreply.github.com>
Date: Fri, 23 May 2025 15:04:20 +0900
Subject: [PATCH 3/7] Update lib/rules/display-name.js
Co-authored-by: Jordan Harband 
---
 lib/rules/display-name.js | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js
index 19f5283865..64ad7dfe60 100644
--- a/lib/rules/display-name.js
+++ b/lib/rules/display-name.js
@@ -353,15 +353,8 @@ module.exports = {
         const list = components.list();
         // Report missing display name for all components
         values(list)
-          .filter((component) => {
-            if (isShadowedComponent(component.node)) {
-              return false;
-            }
-            return !component.hasDisplayName;
-          })
-          .forEach((component) => {
-            reportMissingDisplayName(component);
-          });
+          .filter((component) => !isShadowedComponent(component.node) && !component.hasDisplayName)
+          .forEach((component) => { reportMissingDisplayName(component); });
         if (checkContextObjects) {
           // Report missing display name for all context objects
           forEach(
From 88092cd676c16037e4083e4189c245f8d2c7123d Mon Sep 17 00:00:00 2001
From: "Hur Hyeon Bin (Max)" <160996936+hyeonbinHur@users.noreply.github.com>
Date: Fri, 23 May 2025 15:04:35 +0900
Subject: [PATCH 4/7] Update tests/lib/rules/display-name.js
Co-authored-by: Jordan Harband 
---
 tests/lib/rules/display-name.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/lib/rules/display-name.js b/tests/lib/rules/display-name.js
index 82fea3526e..b4e7e97828 100644
--- a/tests/lib/rules/display-name.js
+++ b/tests/lib/rules/display-name.js
@@ -903,7 +903,7 @@ ruleTester.run('display-name', rule, {
         import React from 'react'
 
         const Comp = React.forwardRef((props, ref) => {
-        return test
+          return test
         })
       `,
       errors: [
From 48491b9d24a0cd146f53382db45e242e26a45c10 Mon Sep 17 00:00:00 2001
From: "Hur Hyeon Bin (Max)" <160996936+hyeonbinHur@users.noreply.github.com>
Date: Mon, 26 May 2025 19:16:58 +0900
Subject: [PATCH 5/7] fix(display-name): expand shadowing test coverage and
 functionalities
---
 lib/rules/display-name.js       |  48 ++++++-
 tests/lib/rules/display-name.js | 242 +++++++++++++++++++++++++++++++-
 2 files changed, 279 insertions(+), 11 deletions(-)
diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js
index 64ad7dfe60..4816304c3f 100644
--- a/lib/rules/display-name.js
+++ b/lib/rules/display-name.js
@@ -200,22 +200,56 @@ module.exports = {
 
     function isIdentifierShadowed(node, identifierName) {
       let currentNode = node;
+
       while (currentNode && currentNode.parent) {
         currentNode = currentNode.parent;
+
         if (
           currentNode.type === 'FunctionDeclaration'
-          || currentNode.type === 'FunctionExpression'
-          || currentNode.type === 'ArrowFunctionExpression'
+      || currentNode.type === 'FunctionExpression'
+      || currentNode.type === 'ArrowFunctionExpression'
         ) {
-          break;
+          if (currentNode.body && hasVariableDeclaration(currentNode.body, identifierName)) {
+            return true;
+          }
         }
-      }
 
-      if (!currentNode || !currentNode.body) {
-        return false;
+        if (currentNode.type === 'BlockStatement') {
+          if (hasVariableDeclaration(currentNode, identifierName)) {
+            return true;
+          }
+        }
+
+        if (
+          (currentNode.type === 'FunctionDeclaration'
+       || currentNode.type === 'FunctionExpression'
+       || currentNode.type === 'ArrowFunctionExpression')
+      && currentNode.params
+        ) {
+          const isParamShadowed = currentNode.params.some((param) => {
+            if (param.type === 'Identifier' && param.name === identifierName) {
+              return true;
+            }
+            if (param.type === 'ObjectPattern') {
+              return param.properties.some(
+                (prop) => prop.type === 'Property' && prop.key && prop.key.name === identifierName
+              );
+            }
+            if (param.type === 'ArrayPattern') {
+              return param.elements.some(
+                (el) => el && el.type === 'Identifier' && el.name === identifierName
+              );
+            }
+            return false;
+          });
+
+          if (isParamShadowed) {
+            return true;
+          }
+        }
       }
 
-      return hasVariableDeclaration(currentNode.body, identifierName);
+      return false;
     }
     /**
      * Checks whether the component wrapper (e.g. React.memo or forwardRef) is shadowed in the current scope.
diff --git a/tests/lib/rules/display-name.js b/tests/lib/rules/display-name.js
index b4e7e97828..2b98500c85 100644
--- a/tests/lib/rules/display-name.js
+++ b/tests/lib/rules/display-name.js
@@ -29,15 +29,76 @@ const parserOptions = {
 const ruleTester = new RuleTester({ parserOptions });
 ruleTester.run('display-name', rule, {
   valid: parsers.all([
+    {
+      code: `
+        import React, { memo, forwardRef } from 'react'
+    
+        const TestComponent = function () {
+          {
+            const memo = (cb) => cb()
+            const forwardRef = (cb) => cb()
+            const React = { memo, forwardRef }
+            const BlockReactShadowedMemo = React.memo(() => {
+              return shadowed
+            })
+            const BlockShadowedMemo = memo(() => {
+              return shadowed
+            })
+            const BlockShadowedForwardRef = forwardRef((props, ref) => {
+              return \`\${props} \${ref}\`
+            })
+          }
+          return null
+        }
+      `,
+    },
+    {
+      code: `
+        import React, { memo } from 'react'
+    
+        const TestComponent = function (memo) {
+          const Comp = memo(() => {
+            return param shadowed
+          })
+          return Comp
+        }
+      `,
+    },
+    {
+      code: `
+        import React, { memo } from 'react'
+    
+        const TestComponent = function ({ memo }) {
+          const Comp = memo(() => {
+            return destructured param shadowed
+          })
+          return Comp
+        }
+      `,
+    },
+    {
+      code: `
+        import React, { memo, forwardRef } from 'react'
+    
+        const TestComponent = function () {
+          function innerFunction() {
+            const memo = (cb) => cb()
+            const React = { forwardRef }
+            const Comp = memo(() => nested shadowed
)
+            const ForwardComp = React.forwardRef(() => nested
)
+            return [Comp, ForwardComp]
+          }
+          return innerFunction()
+        }
+      `,
+    },
     {
       code: `
         import React, { forwardRef } from 'react'
         
         const TestComponent = function () {
           const { forwardRef } = { forwardRef: () => null }
-          
           const OtherComp = forwardRef((props, ref) => \`\${props} \${ref}\`)
-          
           return OtherComp
         }
       `,
@@ -48,11 +109,9 @@ ruleTester.run('display-name', rule, {
         
         const TestComponent = function () {
           const memo = (cb) => cb()
-          
           const Comp = memo(() => {
             return shadowed
           })
-          
           return Comp
         }
       `,
@@ -898,6 +957,181 @@ ruleTester.run('display-name', rule, {
   ]),
 
   invalid: parsers.all([
+    {
+      code: `
+        import React, { memo, forwardRef } from 'react'
+    
+        const TestComponent = function () {
+          {
+            const BlockReactMemo = React.memo(() => {
+              return not shadowed
+            })
+        
+            const BlockMemo = memo(() => {
+              return not shadowed
+            })
+        
+            const BlockForwardRef = forwardRef((props, ref) => {
+              return \`\${props} \${ref}\`
+            })
+          }
+      
+          return null
+        }
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 6,
+        },
+        {
+          messageId: 'noDisplayName',
+          line: 10,
+        },
+        {
+          messageId: 'noDisplayName',
+          line: 14,
+        },
+      ],
+    },
+
+    {
+      code: `
+        import React, { memo } from 'react'
+    
+          const TestComponent = function () {
+          const Comp = memo(() => {
+            return not param shadowed
+          })
+      
+          return Comp
+        }
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 5,
+        },
+      ],
+    },
+
+    {
+      code: `
+        import React, { memo } from 'react'
+    
+        const TestComponent = function () {
+          const Comp = memo(() => {
+            return not destructured param shadowed
+          })
+      
+          return Comp
+        }
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 5,
+        },
+      ],
+    },
+
+    {
+      code: `
+        import React, { memo, forwardRef } from 'react'
+    
+        const TestComponent = function () {
+          function innerFunction() {
+            const Comp = memo(() => nested not shadowed
)
+            const ForwardComp = React.forwardRef(() => nested
)
+        
+            return [Comp, ForwardComp]
+          }
+      
+          return innerFunction()
+        }
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 6,
+        },
+        {
+          messageId: 'noDisplayName',
+          line: 7,
+        },
+      ],
+    },
+    {
+      code: `
+        import React, { forwardRef } from 'react'
+    
+        const TestComponent = function () {
+        const OtherComp = forwardRef((props, ref) => \`\${props} \${ref}\`)
+      
+        return OtherComp
+        }
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 5,
+        },
+      ],
+    },
+    {
+      code: `
+        import React, { memo } from 'react'
+
+        const TestComponent = function () {
+          const Comp = memo(() => {
+            return not shadowed
+          })
+        return Comp
+        }
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 5,
+        },
+      ],
+    },
+    {
+      code: `
+        import React, { memo, forwardRef } from 'react'
+
+        const MixedNotShadowed = function () {
+          const Comp = memo(() => {
+            return not shadowed
+          })
+          const ReactMemo = React.memo(() => null)
+          const ReactForward = React.forwardRef((props, ref) => {
+            return \`\${props} \${ref}\`
+          })
+          const OtherComp = forwardRef((props, ref) => \`\${props} \${ref}\`)
+
+          return [Comp, ReactMemo, ReactForward, OtherComp]
+        }
+      `,
+      errors: [
+        {
+          messageId: 'noDisplayName',
+          line: 5,
+        },
+        {
+          messageId: 'noDisplayName',
+          line: 8,
+        },
+        {
+          messageId: 'noDisplayName',
+          line: 9,
+        },
+        {
+          messageId: 'noDisplayName',
+          line: 12,
+        },
+      ],
+    },
     {
       code: `
         import React from 'react'
From 513cdf9de694d88d5f0e46bec338c9ad33bf2d8d Mon Sep 17 00:00:00 2001
From: "Hur Hyeon Bin (Max)" <160996936+hyeonbinHur@users.noreply.github.com>
Date: Mon, 26 May 2025 19:44:34 +0900
Subject: [PATCH 6/7] fix(display-name): optimize shadowing test cases
---
 tests/lib/rules/display-name.js | 266 ++++----------------------------
 1 file changed, 32 insertions(+), 234 deletions(-)
diff --git a/tests/lib/rules/display-name.js b/tests/lib/rules/display-name.js
index 2b98500c85..44b271de55 100644
--- a/tests/lib/rules/display-name.js
+++ b/tests/lib/rules/display-name.js
@@ -32,21 +32,16 @@ ruleTester.run('display-name', rule, {
     {
       code: `
         import React, { memo, forwardRef } from 'react'
-    
+        
         const TestComponent = function () {
           {
             const memo = (cb) => cb()
             const forwardRef = (cb) => cb()
             const React = { memo, forwardRef }
-            const BlockReactShadowedMemo = React.memo(() => {
-              return shadowed
-            })
-            const BlockShadowedMemo = memo(() => {
-              return shadowed
-            })
-            const BlockShadowedForwardRef = forwardRef((props, ref) => {
-              return \`\${props} \${ref}\`
-            })
+            
+            const BlockMemo = memo(() => shadowed
)
+            const BlockForwardRef = forwardRef(() => shadowed
)
+            const BlockReactMemo = React.memo(() => shadowed
)
           }
           return null
         }
@@ -54,37 +49,27 @@ ruleTester.run('display-name', rule, {
     },
     {
       code: `
-        import React, { memo } from 'react'
-    
-        const TestComponent = function (memo) {
-          const Comp = memo(() => {
-            return param shadowed
-          })
-          return Comp
+        import React, { memo, forwardRef } from 'react'
+        
+        const Test1 = function (memo) {
+          return memo(() => param shadowed
)
         }
-      `,
-    },
-    {
-      code: `
-        import React, { memo } from 'react'
-    
-        const TestComponent = function ({ memo }) {
-          const Comp = memo(() => {
-            return destructured param shadowed
-          })
-          return Comp
+        
+        const Test2 = function ({ forwardRef }) {
+          return forwardRef(() => destructured param
)
         }
       `,
     },
     {
       code: `
         import React, { memo, forwardRef } from 'react'
-    
+        
         const TestComponent = function () {
           function innerFunction() {
             const memo = (cb) => cb()
             const React = { forwardRef }
-            const Comp = memo(() => nested shadowed
)
+            
+            const Comp = memo(() => nested
)
             const ForwardComp = React.forwardRef(() => nested
)
             return [Comp, ForwardComp]
           }
@@ -92,30 +77,6 @@ ruleTester.run('display-name', rule, {
         }
       `,
     },
-    {
-      code: `
-        import React, { forwardRef } from 'react'
-        
-        const TestComponent = function () {
-          const { forwardRef } = { forwardRef: () => null }
-          const OtherComp = forwardRef((props, ref) => \`\${props} \${ref}\`)
-          return OtherComp
-        }
-      `,
-    },
-    {
-      code: `
-        import React, { memo } from 'react'
-        
-        const TestComponent = function () {
-          const memo = (cb) => cb()
-          const Comp = memo(() => {
-            return shadowed
-          })
-          return Comp
-        }
-      `,
-    },
     {
       code: `
         import React, { memo, forwardRef } from 'react'
@@ -125,13 +86,9 @@ ruleTester.run('display-name', rule, {
           const { forwardRef } = { forwardRef: () => null }
           const [React] = [{ memo, forwardRef }]
 
-          const Comp = memo(() => {
-            return shadowed
-          })
+          const Comp = memo(() => shadowed
)
           const ReactMemo = React.memo(() => null)
-          const ReactForward = React.forwardRef((props, ref) => {
-            return \`\${props} \${ref}\`
-          })
+          const ReactForward = React.forwardRef((props, ref) => \`\${props} \${ref}\`)
           const OtherComp = forwardRef((props, ref) => \`\${props} \${ref}\`)
 
           return [Comp, ReactMemo, ReactForward, OtherComp]
@@ -960,7 +917,7 @@ ruleTester.run('display-name', rule, {
     {
       code: `
         import React, { memo, forwardRef } from 'react'
-    
+
         const TestComponent = function () {
           {
             const BlockReactMemo = React.memo(() => {
@@ -980,120 +937,33 @@ ruleTester.run('display-name', rule, {
         }
       `,
       errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 6,
-        },
-        {
-          messageId: 'noDisplayName',
-          line: 10,
-        },
-        {
-          messageId: 'noDisplayName',
-          line: 14,
-        },
+        { messageId: 'noDisplayName' },
+        { messageId: 'noDisplayName' },
+        { messageId: 'noDisplayName' }
       ],
     },
-
     {
       code: `
-        import React, { memo } from 'react'
-    
-          const TestComponent = function () {
-          const Comp = memo(() => {
-            return not param shadowed
-          })
-      
-          return Comp
-        }
-      `,
-      errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 5,
-        },
-      ],
-    },
+        import React, { memo, forwardRef } from 'react'
 
-    {
-      code: `
-        import React, { memo } from 'react'
-    
-        const TestComponent = function () {
-          const Comp = memo(() => {
-            return not destructured param shadowed
-          })
-      
+        const Test1 = function () {
+          const Comp = memo(() => not param shadowed
)
           return Comp
         }
-      `,
-      errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 5,
-        },
-      ],
-    },
 
-    {
-      code: `
-        import React, { memo, forwardRef } from 'react'
-    
-        const TestComponent = function () {
+        const Test2 = function () {
           function innerFunction() {
             const Comp = memo(() => nested not shadowed
)
             const ForwardComp = React.forwardRef(() => nested
)
-        
             return [Comp, ForwardComp]
           }
-      
           return innerFunction()
         }
       `,
       errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 6,
-        },
-        {
-          messageId: 'noDisplayName',
-          line: 7,
-        },
-      ],
-    },
-    {
-      code: `
-        import React, { forwardRef } from 'react'
-    
-        const TestComponent = function () {
-        const OtherComp = forwardRef((props, ref) => \`\${props} \${ref}\`)
-      
-        return OtherComp
-        }
-      `,
-      errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 5,
-        },
-      ],
-    },
-    {
-      code: `
-        import React, { memo } from 'react'
-
-        const TestComponent = function () {
-          const Comp = memo(() => {
-            return not shadowed
-          })
-        return Comp
-        }
-      `,
-      errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 5,
-        },
+        { messageId: 'noDisplayName' },
+        { messageId: 'noDisplayName' },
+        { messageId: 'noDisplayName' }
       ],
     },
     {
@@ -1114,82 +984,10 @@ ruleTester.run('display-name', rule, {
         }
       `,
       errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 5,
-        },
-        {
-          messageId: 'noDisplayName',
-          line: 8,
-        },
-        {
-          messageId: 'noDisplayName',
-          line: 9,
-        },
-        {
-          messageId: 'noDisplayName',
-          line: 12,
-        },
-      ],
-    },
-    {
-      code: `
-        import React from 'react'
-
-        const Comp = React.forwardRef((props, ref) => {
-          return test
-        })
-      `,
-      errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 4,
-        },
-      ],
-    },
-    {
-      code: `
-        import {forwardRef} from 'react'
-
-        const Comp = forwardRef((props, ref) => {
-        return test
-        })
-      `,
-      errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 4,
-        },
-      ],
-    },
-    {
-      code: `
-        import React from 'react'
-        
-        const Comp = React.memo(() => {
-          return test
-        })
-      `,
-      errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 4,
-        },
-      ],
-    },
-    {
-      code: `
-        import { memo } from 'react'
-        
-        const Comp = memo(() => {
-          return test
-        })
-      `,
-      errors: [
-        {
-          messageId: 'noDisplayName',
-          line: 4,
-        },
+        { messageId: 'noDisplayName' },
+        { messageId: 'noDisplayName' },
+        { messageId: 'noDisplayName' },
+        { messageId: 'noDisplayName' }
       ],
     },
     {
From f6800d3f7c427a1225375e14e9c7daff083c2927 Mon Sep 17 00:00:00 2001
From: "Hur Hyeon Bin (Max)" <160996936+hyeonbinHur@users.noreply.github.com>
Date: Mon, 26 May 2025 20:02:25 +0900
Subject: [PATCH 7/7] Update display-name.js
---
 lib/rules/display-name.js | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js
index 4816304c3f..9c812767be 100644
--- a/lib/rules/display-name.js
+++ b/lib/rules/display-name.js
@@ -206,8 +206,8 @@ module.exports = {
 
         if (
           currentNode.type === 'FunctionDeclaration'
-      || currentNode.type === 'FunctionExpression'
-      || currentNode.type === 'ArrowFunctionExpression'
+          || currentNode.type === 'FunctionExpression'
+          || currentNode.type === 'ArrowFunctionExpression'
         ) {
           if (currentNode.body && hasVariableDeclaration(currentNode.body, identifierName)) {
             return true;
@@ -222,9 +222,9 @@ module.exports = {
 
         if (
           (currentNode.type === 'FunctionDeclaration'
-       || currentNode.type === 'FunctionExpression'
-       || currentNode.type === 'ArrowFunctionExpression')
-      && currentNode.params
+           || currentNode.type === 'FunctionExpression'
+           || currentNode.type === 'ArrowFunctionExpression')
+          && currentNode.params
         ) {
           const isParamShadowed = currentNode.params.some((param) => {
             if (param.type === 'Identifier' && param.name === identifierName) {