Repository: yannickcr/eslint-plugin-react
Pull request #3065 (2021-09-01 00:14:34)

[Fix] `display-name`: fix misinterpreted function components
- when determining if an `ArrowFunctionExpression` returns JSX, do not check arguments to a returned `CallExpression`

Arrow function expressions that return the result of a function call taking JSX arguments are incorrectly deemed to be returning JSX. This leads to some misinterpretations of arrow functions as function components, which are then flagged as "missing display name".

## Examples:

### `ArrowFunctionExpression` as `Property` value
```
const obj = {
  prop: () => console.log(<a>something</a>)
};
const obj2 = {
  prop: () => { return console.log(<a>something</a>); }
};
```

### `ArrowFunctionExpression` as RHS of `AssignmentExpression`
```
let obj3;
obj3 = () => console.log(<a>something</a>);
```

I think any other examples of `ArrowFunctionExpressions` that pass `utils.isInAllowedPositionForComponent` at https://github.com/yannickcr/eslint-plugin-react/blob/810806e58d5a611e6743227c91d859c2c02563bb/lib/util/Components.js#L648-L650 will hit this too (excl. a `VariableDeclarator` as it's handled on [L635](https://github.com/yannickcr/eslint-plugin-react/blob/810806e58d5a611e6743227c91d859c2c02563bb/lib/util/Components.js#L635) ahead of time).

codecov-commenter: # [Codecov](https://codecov.io/gh/yannickcr/eslint-plugin-react/pull/3065?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#3065](https://codecov.io/gh/yannickcr/eslint-plugin-react/pull/3065?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (0ed3bdc) into [master](https://codecov.io/gh/yannickcr/eslint-plugin-react/commit/810806e58d5a611e6743227c91d859c2c02563bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (810806e) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/yannickcr/eslint-plugin-react/pull/3065/graphs/tree.svg?width=650&height=150&src=pr&token=Nm2xQsStiY&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)](https://codecov.io/gh/yannickcr/eslint-plugin-react/pull/3065?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)

```diff
@@           Coverage Diff           @@
##           master    #3065   +/-   ##
=======================================
  Coverage   97.43%   97.43%           
=======================================
  Files         111      111           
  Lines        7522     7523    +1     
  Branches     2767     2767           
=======================================
+ Hits         7329     7330    +1     
  Misses        193      193           
```


| [Impacted Files](https://codecov.io/gh/yannickcr/eslint-plugin-react/pull/3065?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [lib/util/jsx.js](https://codecov.io/gh/yannickcr/eslint-plugin-react/pull/3065/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bGliL3V0aWwvanN4Lmpz) | `92.75% <100.00%> (+0.10%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/yannickcr/eslint-plugin-react/pull/3065?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/yannickcr/eslint-plugin-react/pull/3065?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Last update [810806e...0ed3bdc](https://codecov.io/gh/yannickcr/eslint-plugin-react/pull/3065?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).

ljharb: @danielfinke unfortunately since you made this PR from a fork you don't own, i can't force push to it due to a github bug.

Please do *not* close this PR and open a new one; what would be ideal is if you can add me to https://github.com/Unity-Technologies/eslint-plugin-react for long enough that I can get this PR landed.
danielfinke: @ljharb I'll see what I can do - not sure what hoops I have to jump thru for that.

If there's anything simple you need changed to the commit message, etc., I can do that on your behalf (as that may be quicker). Let me know.
ljharb: You may also be able to check the "allow edits" box in the RHS of the PR, but github's bug means that probably won't work (but is worth a shot)

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5d90578..1de52aec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 ### Fixed
 * [`jsx-no-useless-fragments`]: Handle insignificant whitespace correctly when `allowExpressions` is `true` ([#3061][] @benj-dobs)
 * [`prop-types`], `propTypes`: handle implicit `children` prop in react's generic types ([#3064][] @vedadeepta)
+* [`display-name`]: fix arrow function returning result of function call with JSX arguments being interpreted as component ([#3065][], @danielfinke)
 
+[#3065]: https://github.com/yannickcr/eslint-plugin-react/pull/3065
 [#3064]: https://github.com/yannickcr/eslint-plugin-react/pull/3064
 [#3061]: https://github.com/yannickcr/eslint-plugin-react/pull/3061
 
diff --git a/lib/util/jsx.js b/lib/util/jsx.js
index 56bdf221..3afac915 100644
--- a/lib/util/jsx.js
+++ b/lib/util/jsx.js
@@ -130,6 +130,7 @@ function isReturningJSX(isCreateElement, ASTnode, context, strict, ignoreNull) {
             if (isCreateElement(childNode)) {
               setFound();
             }
+            this.skip();
             break;
           case 'Literal':
             if (!ignoreNull && childNode.value === null) {


Test patch:
diff --git a/tests/util/jsx.js b/tests/util/jsx.js
index 1062cebb..dce540f0 100644
--- a/tests/util/jsx.js
+++ b/tests/util/jsx.js
@@ -27,9 +27,7 @@ describe('jsxUtil', () => {
     const assertValid = (codeStr) => assert(
       isReturningJSX(() => false, parseCode(codeStr), mockContext)
     );
-    const assertInValid = (codeStr) => assert(
-      !!isReturningJSX(() => false, parseCode(codeStr), mockContext)
-    );
+
     it('Works when returning JSX', () => {
       assertValid(`
         function Test() {
@@ -71,11 +69,27 @@ describe('jsxUtil', () => {
     });
 
     it('Can ignore null', () => {
-      assertInValid(`
+      assertValid(`
         function Test() {
           return null;
         }
       `);
     });
+
+    it('Ignores JSX arguments to function calls used as return value of arrow functions', () => {
+      let astNode = parseCode(`const obj = {
+        prop: () => test(<a>something</a>)
+      }`);
+      let arrowFunctionExpression = astNode.declarations[0].init.properties[0].value;
+
+      assert(!isReturningJSX(() => false, arrowFunctionExpression, mockContext));
+
+      astNode = parseCode(`const obj = {
+        prop: () => { return test(<a>something</a>); }
+      }`);
+      arrowFunctionExpression = astNode.declarations[0].init.properties[0].value;
+
+      assert(!isReturningJSX(() => false, arrowFunctionExpression, mockContext));
+    });
   });
 });