From 85d424cdae208ab51cc45b9cbe027af8951954f6 Mon Sep 17 00:00:00 2001 From: Will Heslam Date: Sun, 27 Sep 2020 13:23:52 +0100 Subject: [PATCH] Track JSX presence per-function, fixing some false negatives (#830) Co-authored-by: fisker --- rules/consistent-function-scoping.js | 17 ++++--- test/consistent-function-scoping.js | 68 ++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/rules/consistent-function-scoping.js b/rules/consistent-function-scoping.js index 25cc381b86..fa3f0d732c 100644 --- a/rules/consistent-function-scoping.js +++ b/rules/consistent-function-scoping.js @@ -153,17 +153,21 @@ const create = context => { const {scopeManager} = sourceCode; const functions = []; - let hasJsx = false; return { - 'ArrowFunctionExpression, FunctionDeclaration': node => functions.push(node), + 'ArrowFunctionExpression, FunctionDeclaration': () => { + functions.push(false); + }, JSXElement: () => { // Turn off this rule if we see a JSX element because scope // references does not include JSXElement nodes. - hasJsx = true; + if (functions.length !== 0) { + functions[functions.length - 1] = true; + } }, ':matches(ArrowFunctionExpression, FunctionDeclaration):exit': node => { - if (!hasJsx && !checkNode(node, scopeManager)) { + const currentFunctionHasJsx = functions.pop(); + if (!currentFunctionHasJsx && !checkNode(node, scopeManager)) { context.report({ node, loc: getFunctionHeadLocation(node, sourceCode), @@ -173,11 +177,6 @@ const create = context => { } }); } - - functions.pop(); - if (functions.length === 0) { - hasJsx = false; - } } }; }; diff --git a/test/consistent-function-scoping.js b/test/consistent-function-scoping.js index 2e91394c8d..2b2c9a9a72 100644 --- a/test/consistent-function-scoping.js +++ b/test/consistent-function-scoping.js @@ -192,6 +192,32 @@ ruleTester.run('consistent-function-scoping', rule, { return Bar; }; `, + outdent` + const foo = ; + `, + // Functions that could be extracted are conservatively ignored due to JSX masking references + outdent` + function Foo() { + function Bar () { + return
+ } + return
{ Bar() }
+ } + `, + outdent` + function foo() { + function bar() { + return ; + } + } + `, + outdent` + function foo() { + function bar() { + return ; + } + } + `, // `this` outdent` function doFoo(Foo) { @@ -594,6 +620,48 @@ ruleTester.run('consistent-function-scoping', rule, { ) `, errors: [createError('function \'baz\'')] + }, + { + code: outdent` + function Foo() { + const Bar =
+ function doBaz() { + return 42 + } + return
{ doBaz() }
+ } + `, + errors: [createError('function \'doBaz\'')] + }, + { + code: outdent` + function Foo() { + function Bar () { + return
+ } + function doBaz() { + return 42 + } + return
{ doBaz() }
+ } + `, + errors: [createError('function \'doBaz\'')] + }, + // JSX + { + code: outdent` + function fn1() { + function a() { + return ; + } + function b() {} + function c() {} + } + function fn2() { + function foo() {} + } + `, + errors: ['b', 'c', 'foo'].map(functionName => createError(`function '${functionName}'`)) } ] });