diff --git a/scripts/test-ci.sh b/scripts/test-ci.sh index dc126708..2b76feb1 100755 --- a/scripts/test-ci.sh +++ b/scripts/test-ci.sh @@ -7,7 +7,7 @@ set -euo pipefail # variable is set in dockerfile if [ "${SONARCLOUD_ANALYSIS:-}" == "true" ]; then echo 'Running tests with coverage and reporter' - npm run test -- --coverage --testResultsProcessor jest-sonar-reporter + npm run test -- --maxWorkers=50% --coverage --testResultsProcessor jest-sonar-reporter else echo 'Running tests' npm run test diff --git a/src/rules/no-use-of-empty-return-value.ts b/src/rules/no-use-of-empty-return-value.ts index 94117b12..36991353 100644 --- a/src/rules/no-use-of-empty-return-value.ts +++ b/src/rules/no-use-of-empty-return-value.ts @@ -19,35 +19,41 @@ */ // https://sonarsource.github.io/rspec/#/rspec/S3699 -import type { TSESTree, TSESLint } from '@typescript-eslint/experimental-utils'; +import { TSESTree, TSESLint } from '@typescript-eslint/experimental-utils'; import { isFunctionExpression, isArrowFunctionExpression, isBlockStatement } from '../utils/nodes'; import docsUrl from '../utils/docs-url'; +const EMPTY_RETURN_VALUE_KEYWORDS = new Set([ + TSESTree.AST_NODE_TYPES.TSVoidKeyword, + TSESTree.AST_NODE_TYPES.TSNeverKeyword, + TSESTree.AST_NODE_TYPES.TSUndefinedKeyword, +]); + function isReturnValueUsed(callExpr: TSESTree.Node) { const { parent } = callExpr; if (!parent) { return false; } - if (parent.type === 'LogicalExpression') { + if (parent.type === TSESTree.AST_NODE_TYPES.LogicalExpression) { return parent.left === callExpr; } - if (parent.type === 'SequenceExpression') { + if (parent.type === TSESTree.AST_NODE_TYPES.SequenceExpression) { return parent.expressions[parent.expressions.length - 1] === callExpr; } - if (parent.type === 'ConditionalExpression') { + if (parent.type === TSESTree.AST_NODE_TYPES.ConditionalExpression) { return parent.test === callExpr; } return ( - parent.type !== 'ExpressionStatement' && - parent.type !== 'ArrowFunctionExpression' && - parent.type !== 'UnaryExpression' && - parent.type !== 'AwaitExpression' && - parent.type !== 'ReturnStatement' && - parent.type !== 'ThrowStatement' + parent.type !== TSESTree.AST_NODE_TYPES.ExpressionStatement && + parent.type !== TSESTree.AST_NODE_TYPES.ArrowFunctionExpression && + parent.type !== TSESTree.AST_NODE_TYPES.UnaryExpression && + parent.type !== TSESTree.AST_NODE_TYPES.AwaitExpression && + parent.type !== TSESTree.AST_NODE_TYPES.ReturnStatement && + parent.type !== TSESTree.AST_NODE_TYPES.ThrowStatement ); } @@ -102,9 +108,9 @@ const rule: TSESLint.RuleModule = { const ancestors = [...context.getAncestors()].reverse(); const functionNode = ancestors.find( node => - node.type === 'FunctionExpression' || - node.type === 'FunctionDeclaration' || - node.type === 'ArrowFunctionExpression', + node.type === TSESTree.AST_NODE_TYPES.FunctionExpression || + node.type === TSESTree.AST_NODE_TYPES.FunctionDeclaration || + node.type === TSESTree.AST_NODE_TYPES.ArrowFunctionExpression, ); functionsWithReturnValue.add(functionNode as TSESTree.FunctionLike); @@ -132,6 +138,16 @@ const rule: TSESLint.RuleModule = { } }, + TSDeclareFunction(node: TSESTree.Node) { + const declareFunction = node as TSESTree.TSDeclareFunction; + if ( + declareFunction.returnType?.typeAnnotation.type && + !EMPTY_RETURN_VALUE_KEYWORDS.has(declareFunction.returnType?.typeAnnotation.type) + ) { + functionsWithReturnValue.add(declareFunction); + } + }, + 'Program:exit'() { callExpressionsToCheck.forEach((functionDeclaration, callee) => { if (!functionsWithReturnValue.has(functionDeclaration)) { diff --git a/tests/rules/no-use-of-empty-return-value.test.ts b/tests/rules/no-use-of-empty-return-value.test.ts index 8546ba25..ea296848 100644 --- a/tests/rules/no-use-of-empty-return-value.test.ts +++ b/tests/rules/no-use-of-empty-return-value.test.ts @@ -51,6 +51,7 @@ ruleTester.run('no-use-of-empty-return-value', rule, { }, { code: 'function* noReturn() { yield 1; } noReturn().next();' }, { code: 'function* noReturn() { yield 1; } noReturn();' }, + { code: 'declare function withReturn(): number; let x = withReturn();' }, ], invalid: [ invalidPrefixWithFunction('console.log(noReturn());'), @@ -69,6 +70,9 @@ ruleTester.run('no-use-of-empty-return-value', rule, { ), invalid('var funcExpr = function noReturn () { 1; console.log(noReturn()); };'), invalid('var noReturn = () => { var x = () => {return 1} }; x = noReturn();'), + invalid('declare function noReturn(): never; let x = noReturn();'), + invalid('declare function noReturn(): void; let x = noReturn();'), + invalid('declare function noReturn(): undefined; let x = noReturn();'), ], });