diff --git a/lib/rules/no-useless-return.js b/lib/rules/no-useless-return.js index be8d4dfd3a52..739aae7aa997 100644 --- a/lib/rules/no-useless-return.js +++ b/lib/rules/no-useless-return.js @@ -38,6 +38,15 @@ function isRemovable(node) { return astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type); } +/** + * Checks if given node is a try statement or not. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is a try statement. + */ +function isTryStatement(node) { + return node.type === "TryStatement"; +} + /** * Checks whether the given return statement is in a `finally` block or not. * @param {ASTNode} node The return statement node to check. @@ -49,7 +58,7 @@ function isInFinally(node) { currentNode && currentNode.parent && !astUtils.isFunction(currentNode); currentNode = currentNode.parent ) { - if (currentNode.parent.type === "TryStatement" && currentNode.parent.finalizer === currentNode) { + if (isTryStatement(currentNode.parent) && currentNode.parent.finalizer === currentNode) { return true; } } @@ -57,6 +66,19 @@ function isInFinally(node) { return false; } +/** + * Checks whether the given node is the last one in the parentNode. + * @param {ASTNode} node The node to check. + * @param {ASTNode} parentNode The node that possibly contains the given `node`. + * @param {any} sourceCode The source code + * @returns {boolean} `true` if the node is the last one in the `parentNode` children. + */ +function isLastNode(node, parentNode, sourceCode) { + const lastNodeInParent = parentNode.body[parentNode.body.length - 1]; + + return astUtils.equalTokens(node, lastNodeInParent, sourceCode); +} + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -274,6 +296,22 @@ module.exports = { } scopeInfo.uselessReturns.push(node); }, + "BlockStatement:exit"(node) { + if ( + !isTryStatement(node.parent) || + !scopeInfo.upper || + !scopeInfo.uselessReturns + ) { + return; + } + + const lastUseselessReturn = scopeInfo.uselessReturns[scopeInfo.uselessReturns.length - 1]; + + + if (lastUseselessReturn && isLastNode(lastUseselessReturn, node, sourceCode)) { + scopeInfo.upper.uselessReturns.push(lastUseselessReturn); + } + }, /* * Registers for all statement nodes except FunctionDeclaration, BlockStatement, BreakStatement. diff --git a/tests/lib/rules/no-useless-return.js b/tests/lib/rules/no-useless-return.js index 434700cc2d80..c0f695ca88f9 100644 --- a/tests/lib/rules/no-useless-return.js +++ b/tests/lib/rules/no-useless-return.js @@ -391,18 +391,25 @@ ruleTester.run("no-useless-return", rule, { * FIXME: Re-add this case (removed due to https://github.com/eslint/eslint/issues/7481): * https://github.com/eslint/eslint/blob/261d7287820253408ec87c344beccdba2fe829a4/tests/lib/rules/no-useless-return.js#L308-L329 */ - { code: ` function foo() { - try {} finally {} - return; + try { + foo(); + return; + } catch (err) { + return 5; + } } `, output: ` function foo() { - try {} finally {} - + try { + foo(); + + } catch (err) { + return 5; + } } ` }, @@ -410,22 +417,18 @@ ruleTester.run("no-useless-return", rule, { code: ` function foo() { try { - return 5; - } finally { - function bar() { - return; - } + return; + } catch (err) { + foo(); } } `, output: ` function foo() { try { - return 5; - } finally { - function bar() { - - } + + } catch (err) { + foo(); } } `