Skip to content
This repository has been archived by the owner on Oct 3, 2024. It is now read-only.

Fix FP 3699 (no-use-of-empty-return-values) to handle ambient functions #451

Merged
merged 6 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/test-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the pipeline is failing on cirrus, since Februrary 24th - https://cirrus-ci.com/github/SonarSource/eslint-plugin-sonarjs

This allows it to actually finish and not exceed the allowed 8gb

else
echo 'Running tests'
npm run test
Expand Down
42 changes: 29 additions & 13 deletions src/rules/no-use-of-empty-return-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>([
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
);
}

Expand Down Expand Up @@ -102,9 +108,9 @@ const rule: TSESLint.RuleModule<string, string[]> = {
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);
Expand Down Expand Up @@ -132,6 +138,16 @@ const rule: TSESLint.RuleModule<string, string[]> = {
}
},

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)) {
Expand Down
4 changes: 4 additions & 0 deletions tests/rules/no-use-of-empty-return-value.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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());'),
Expand All @@ -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();'),
],
});

Expand Down