From 7ce9106e481de153ab373e6867c1aa598686c522 Mon Sep 17 00:00:00 2001 From: Ari Perkkio Date: Tue, 30 Mar 2021 20:28:12 +0300 Subject: [PATCH] [Fix] `jsx-max-depth`: Prevent getting stuck in circular references Fixes #2880. --- CHANGELOG.md | 4 ++++ lib/rules/jsx-max-depth.js | 22 +++++++++++++++++----- tests/lib/rules/jsx-max-depth.js | 27 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31049bd03e..73276dc81f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,13 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## Unreleased +### Fixed +* [`jsx-max-depth`]: Prevent getting stuck in circular references ([#2957][] @AriPerkkio) + ### Changed * Fix CHANGELOG.md ([#2950][] @JounQin) +[#2957]: https://github.com/yannickcr/eslint-plugin-react/pull/2957 [#2950]: https://github.com/yannickcr/eslint-plugin-react/pull/2950 ## [7.23.1] - 2021.03.23 diff --git a/lib/rules/jsx-max-depth.js b/lib/rules/jsx-max-depth.js index 87aeccf97c..b69e284595 100644 --- a/lib/rules/jsx-max-depth.js +++ b/lib/rules/jsx-max-depth.js @@ -6,6 +6,7 @@ 'use strict'; const has = require('has'); +const includes = require('array-includes'); const variableUtil = require('../util/variable'); const jsxUtil = require('../util/jsx'); const docsUrl = require('../util/docsUrl'); @@ -83,8 +84,8 @@ module.exports = { }); } - function findJSXElementOrFragment(variables, name) { - function find(refs) { + function findJSXElementOrFragment(variables, name, previousReferences) { + function find(refs, prevRefs) { let i = refs.length; while (--i >= 0) { @@ -94,7 +95,7 @@ module.exports = { return (jsxUtil.isJSX(writeExpr) && writeExpr) || ((writeExpr && writeExpr.type === 'Identifier') - && findJSXElementOrFragment(variables, writeExpr.name)); + && findJSXElementOrFragment(variables, writeExpr.name, prevRefs)); } } @@ -102,7 +103,18 @@ module.exports = { } const variable = variableUtil.getVariable(variables, name); - return variable && variable.references && find(variable.references); + if (variable && variable.references) { + const containDuplicates = previousReferences.some((ref) => includes(variable.references, ref)); + + // Prevent getting stuck in circular references + if (containDuplicates) { + return false; + } + + return find(variable.references, previousReferences.concat(variable.references)); + } + + return false; } function checkDescendant(baseDepth, children) { @@ -141,7 +153,7 @@ module.exports = { } const variables = variableUtil.variablesInScope(context); - const element = findJSXElementOrFragment(variables, node.expression.name); + const element = findJSXElementOrFragment(variables, node.expression.name, []); if (element) { const baseDepth = getDepth(node); diff --git a/tests/lib/rules/jsx-max-depth.js b/tests/lib/rules/jsx-max-depth.js index 14bc69b99a..0ea9dfa21f 100644 --- a/tests/lib/rules/jsx-max-depth.js +++ b/tests/lib/rules/jsx-max-depth.js @@ -114,6 +114,33 @@ ruleTester.run('jsx-max-depth', rule, { ' return
{A}
;', '}' ].join('\n') + }, { + // Validates circular references don't get rule stuck + code: ` + function Component() { + let first = ""; + const second = first; + first = second; + return
; + }; + ` + }, + { + // Validates circular references are checked at multiple levels + code: ` + function Component() { + let first = ""; + let second = ""; + let third = ""; + let fourth = ""; + const fifth = first; + first = second; + second = third; + third = fourth; + fourth = fifth; + return
; + }; + ` }], invalid: [{