|
1 |
| -import { TSESTree as es, TSESLint as eslint, ESLintUtils } from '@typescript-eslint/utils'; |
| 1 | +import { AST_NODE_TYPES, TSESTree as es, TSESLint as eslint, ESLintUtils } from '@typescript-eslint/utils'; |
2 | 2 | import * as tsutils from 'ts-api-utils';
|
3 | 3 | import ts from 'typescript';
|
4 |
| -import { getTypeServices, isJSXExpressionContainer, isMethodDefinition, isPropertyDefinition } from '../etc'; |
| 4 | +import { |
| 5 | + getTypeServices, |
| 6 | + isArrowFunctionExpression, |
| 7 | + isFunctionDeclaration, |
| 8 | + isFunctionExpression, |
| 9 | + isJSXExpressionContainer, |
| 10 | + isMethodDefinition, |
| 11 | + isPropertyDefinition, |
| 12 | +} from '../etc'; |
5 | 13 | import { ruleCreator } from '../utils';
|
6 | 14 |
|
7 | 15 | // The implementation of this rule is similar to typescript-eslint's no-misused-promises. MIT License.
|
@@ -55,7 +63,7 @@ export const noMisusedObservablesRule = ruleCreator({
|
55 | 63 | ClassExpression: checkClassLikeOrInterfaceNode,
|
56 | 64 | TSInterfaceDeclaration: checkClassLikeOrInterfaceNode,
|
57 | 65 | Property: checkProperty,
|
58 |
| - // ReturnStatement: checkReturnStatement, |
| 66 | + ReturnStatement: checkReturnStatement, |
59 | 67 | // AssignmentExpression: checkAssignment,
|
60 | 68 | // VariableDeclarator: checkVariableDeclarator,
|
61 | 69 | };
|
@@ -171,6 +179,50 @@ export const noMisusedObservablesRule = ruleCreator({
|
171 | 179 | });
|
172 | 180 | }
|
173 | 181 |
|
| 182 | + function checkReturnStatement(node: es.ReturnStatement): void { |
| 183 | + const tsNode = esTreeNodeToTSNodeMap.get(node); |
| 184 | + if (tsNode.expression === undefined || !node.argument) { |
| 185 | + return; |
| 186 | + } |
| 187 | + |
| 188 | + // Optimization to avoid touching type info. |
| 189 | + function getFunctionNode() { |
| 190 | + let current: es.Node | undefined = node.parent; |
| 191 | + while ( |
| 192 | + current |
| 193 | + && !isArrowFunctionExpression(current) |
| 194 | + && !isFunctionExpression(current) |
| 195 | + && !isFunctionDeclaration(current) |
| 196 | + ) { |
| 197 | + current = current.parent; |
| 198 | + } |
| 199 | + return current; |
| 200 | + } |
| 201 | + const functionNode = getFunctionNode(); |
| 202 | + if ( |
| 203 | + functionNode?.returnType |
| 204 | + && !isPossiblyFunctionType(functionNode.returnType) |
| 205 | + ) { |
| 206 | + return; |
| 207 | + } |
| 208 | + |
| 209 | + const contextualType = checker.getContextualType(tsNode.expression); |
| 210 | + if (contextualType === undefined) { |
| 211 | + return; |
| 212 | + } |
| 213 | + if (!isVoidReturningFunctionType(contextualType)) { |
| 214 | + return; |
| 215 | + } |
| 216 | + if (!couldReturnObservable(node.argument)) { |
| 217 | + return; |
| 218 | + } |
| 219 | + |
| 220 | + context.report({ |
| 221 | + node: node.argument, |
| 222 | + messageId: 'forbiddenVoidReturnReturnValue', |
| 223 | + }); |
| 224 | + } |
| 225 | + |
174 | 226 | return {
|
175 | 227 | ...(checksVoidReturn ? voidReturnChecks : {}),
|
176 | 228 | ...(checksSpreads ? spreadChecks : {}),
|
@@ -278,3 +330,65 @@ function getPropertyContextualType(
|
278 | 330 | return undefined;
|
279 | 331 | }
|
280 | 332 | }
|
| 333 | + |
| 334 | +/** |
| 335 | + * From no-misused-promises. |
| 336 | + */ |
| 337 | +function isPossiblyFunctionType(node: es.TSTypeAnnotation): boolean { |
| 338 | + switch (node.typeAnnotation.type) { |
| 339 | + case AST_NODE_TYPES.TSConditionalType: |
| 340 | + case AST_NODE_TYPES.TSConstructorType: |
| 341 | + case AST_NODE_TYPES.TSFunctionType: |
| 342 | + case AST_NODE_TYPES.TSImportType: |
| 343 | + case AST_NODE_TYPES.TSIndexedAccessType: |
| 344 | + case AST_NODE_TYPES.TSInferType: |
| 345 | + case AST_NODE_TYPES.TSIntersectionType: |
| 346 | + case AST_NODE_TYPES.TSQualifiedName: |
| 347 | + case AST_NODE_TYPES.TSThisType: |
| 348 | + case AST_NODE_TYPES.TSTypeOperator: |
| 349 | + case AST_NODE_TYPES.TSTypeQuery: |
| 350 | + case AST_NODE_TYPES.TSTypeReference: |
| 351 | + case AST_NODE_TYPES.TSUnionType: |
| 352 | + return true; |
| 353 | + |
| 354 | + case AST_NODE_TYPES.TSTypeLiteral: |
| 355 | + return node.typeAnnotation.members.some( |
| 356 | + member => |
| 357 | + member.type === AST_NODE_TYPES.TSCallSignatureDeclaration |
| 358 | + || member.type === AST_NODE_TYPES.TSConstructSignatureDeclaration, |
| 359 | + ); |
| 360 | + |
| 361 | + case AST_NODE_TYPES.TSAbstractKeyword: |
| 362 | + case AST_NODE_TYPES.TSAnyKeyword: |
| 363 | + case AST_NODE_TYPES.TSArrayType: |
| 364 | + case AST_NODE_TYPES.TSAsyncKeyword: |
| 365 | + case AST_NODE_TYPES.TSBigIntKeyword: |
| 366 | + case AST_NODE_TYPES.TSBooleanKeyword: |
| 367 | + case AST_NODE_TYPES.TSDeclareKeyword: |
| 368 | + case AST_NODE_TYPES.TSExportKeyword: |
| 369 | + case AST_NODE_TYPES.TSIntrinsicKeyword: |
| 370 | + case AST_NODE_TYPES.TSLiteralType: |
| 371 | + case AST_NODE_TYPES.TSMappedType: |
| 372 | + case AST_NODE_TYPES.TSNamedTupleMember: |
| 373 | + case AST_NODE_TYPES.TSNeverKeyword: |
| 374 | + case AST_NODE_TYPES.TSNullKeyword: |
| 375 | + case AST_NODE_TYPES.TSNumberKeyword: |
| 376 | + case AST_NODE_TYPES.TSObjectKeyword: |
| 377 | + case AST_NODE_TYPES.TSOptionalType: |
| 378 | + case AST_NODE_TYPES.TSPrivateKeyword: |
| 379 | + case AST_NODE_TYPES.TSProtectedKeyword: |
| 380 | + case AST_NODE_TYPES.TSPublicKeyword: |
| 381 | + case AST_NODE_TYPES.TSReadonlyKeyword: |
| 382 | + case AST_NODE_TYPES.TSRestType: |
| 383 | + case AST_NODE_TYPES.TSStaticKeyword: |
| 384 | + case AST_NODE_TYPES.TSStringKeyword: |
| 385 | + case AST_NODE_TYPES.TSSymbolKeyword: |
| 386 | + case AST_NODE_TYPES.TSTemplateLiteralType: |
| 387 | + case AST_NODE_TYPES.TSTupleType: |
| 388 | + case AST_NODE_TYPES.TSTypePredicate: |
| 389 | + case AST_NODE_TYPES.TSUndefinedKeyword: |
| 390 | + case AST_NODE_TYPES.TSUnknownKeyword: |
| 391 | + case AST_NODE_TYPES.TSVoidKeyword: |
| 392 | + return false; |
| 393 | + } |
| 394 | +} |
0 commit comments