From de51c69a831d992015e0729155e06a3f33b952b8 Mon Sep 17 00:00:00 2001 From: SyMind Date: Fri, 19 Feb 2021 04:23:32 +0000 Subject: [PATCH] [Fix] `destructuring-assignment`: fix a false prositive for local prop named `context` in SFC --- lib/rules/destructuring-assignment.js | 71 +++++++++++++++++++-- tests/lib/rules/destructuring-assignment.js | 11 ++++ 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/lib/rules/destructuring-assignment.js b/lib/rules/destructuring-assignment.js index ab600f3329..f16ea78482 100644 --- a/lib/rules/destructuring-assignment.js +++ b/lib/rules/destructuring-assignment.js @@ -10,6 +10,44 @@ const isAssignmentLHS = require('../util/ast').isAssignmentLHS; const DEFAULT_OPTION = 'always'; +function createSFCParams() { + const queue = []; + + return { + push(params) { + queue.unshift(params); + }, + pop() { + queue.shift(); + }, + get propsName() { + for (const params of queue) { + const props = params[0]; + if (props && !props.destructuring && props.name) { + return props.name; + } + } + return null; + }, + get contextName() { + for (const params of queue) { + const context = params[1]; + if (context && !context.destructuring && context.name) { + return context.name; + } + } + return null; + } + }; +} + +function evalParams(params) { + return params.map((param) => ({ + destructuring: param.type === 'ObjectPattern', + name: param.type === 'Identifier' && param.name + })); +} + module.exports = { meta: { docs: { @@ -46,21 +84,27 @@ module.exports = { create: Components.detect((context, components, utils) => { const configuration = context.options[0] || DEFAULT_OPTION; const ignoreClassFields = (context.options[1] && (context.options[1].ignoreClassFields === true)) || false; + const sfcParams = createSFCParams(); /** * @param {ASTNode} node We expect either an ArrowFunctionExpression, * FunctionDeclaration, or FunctionExpression */ function handleStatelessComponent(node) { - const destructuringProps = node.params && node.params[0] && node.params[0].type === 'ObjectPattern'; - const destructuringContext = node.params && node.params[1] && node.params[1].type === 'ObjectPattern'; + const params = evalParams(node.params); + + const SFCComponent = components.get(context.getScope(node).block); + if (!SFCComponent) { + return; + } + sfcParams.push(params); - if (destructuringProps && components.get(node) && configuration === 'never') { + if (params[0] && params[0].destructuring && components.get(node) && configuration === 'never') { context.report({ node, messageId: 'noDestructPropsInSFCArg' }); - } else if (destructuringContext && components.get(node) && configuration === 'never') { + } else if (params[1] && params[1].destructuring && components.get(node) && configuration === 'never') { context.report({ node, messageId: 'noDestructContextInSFCArg' @@ -68,9 +112,20 @@ module.exports = { } } + function handleStatelessComponentExit(node) { + const SFCComponent = components.get(context.getScope(node).block); + if (SFCComponent) { + sfcParams.pop(); + } + } + function handleSFCUsage(node) { // props.aProp || context.aProp - const isPropUsed = (node.object.name === 'props' || node.object.name === 'context') && !isAssignmentLHS(node); + const isPropUsed = ( + (sfcParams.propsName && node.object.name === sfcParams.propsName) + || (sfcParams.contextName && node.object.name === sfcParams.contextName) + ) + && !isAssignmentLHS(node); if (isPropUsed && configuration === 'always') { context.report({ node, @@ -123,6 +178,12 @@ module.exports = { FunctionExpression: handleStatelessComponent, + 'FunctionDeclaration:exit': handleStatelessComponentExit, + + 'ArrowFunctionExpression:exit': handleStatelessComponentExit, + + 'FunctionExpression:exit': handleStatelessComponentExit, + MemberExpression(node) { const SFCComponent = components.get(context.getScope(node).block); const classComponent = utils.getParentComponent(node); diff --git a/tests/lib/rules/destructuring-assignment.js b/tests/lib/rules/destructuring-assignment.js index a38d897979..a62e3a31cc 100644 --- a/tests/lib/rules/destructuring-assignment.js +++ b/tests/lib/rules/destructuring-assignment.js @@ -176,6 +176,17 @@ ruleTester.run('destructuring-assignment', rule, { ].join('\n'), options: ['always', {ignoreClassFields: true}], parser: parsers.BABEL_ESLINT + }, + // https://github.com/yannickcr/eslint-plugin-react/issues/2911 + { + code: ` + function Foo({context}) { + const d = context.describe() + return
{d}
+ } + `, + options: ['always'], + parser: parsers.BABEL_ESLINT }], invalid: [{