Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var NoMisusedPromisesRule = rule.CreateRule(rule.Rule{ Name: "no-misused-promises", Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { opts, ok := options.(NoMisusedPromisesOptions) if !ok { opts = NoMisusedPromisesOptions{} } if opts.ChecksConditionals == nil { opts.ChecksConditionals = utils.Ref(true) } if opts.ChecksSpreads == nil { opts.ChecksSpreads = utils.Ref(true) } if opts.ChecksVoidReturn == nil { opts.ChecksVoidReturn = utils.Ref(true) } if opts.ChecksVoidReturnOpts == nil { opts.ChecksVoidReturnOpts = utils.Ref(NoMisusedPromisesChecksVoidReturnOptions{}) } if opts.ChecksVoidReturnOpts.Arguments == nil { opts.ChecksVoidReturnOpts.Arguments = utils.Ref(true) } if opts.ChecksVoidReturnOpts.Attributes == nil { opts.ChecksVoidReturnOpts.Attributes = utils.Ref(true) } if opts.ChecksVoidReturnOpts.InheritedMethods == nil { opts.ChecksVoidReturnOpts.InheritedMethods = utils.Ref(true) } if opts.ChecksVoidReturnOpts.Properties == nil { opts.ChecksVoidReturnOpts.Properties = utils.Ref(true) } if opts.ChecksVoidReturnOpts.Returns == nil { opts.ChecksVoidReturnOpts.Returns = utils.Ref(true) } if opts.ChecksVoidReturnOpts.Variables == nil { opts.ChecksVoidReturnOpts.Variables = utils.Ref(true) } anySignatureIsThenableType := func( node *ast.Node, t *checker.Type, ) bool { return utils.Some(utils.GetCallSignatures(ctx.TypeChecker, t), func(sig *checker.Signature) bool { return utils.IsThenableType(ctx.TypeChecker, node, checker.Checker_getReturnTypeOfSignature(ctx.TypeChecker, sig)) }) } returnsThenable := func(node *ast.Node) bool { t := checker.Checker_getApparentType(ctx.TypeChecker, ctx.TypeChecker.GetTypeAtLocation(node)) return utils.Some(utils.UnionTypeParts(t), func(t *checker.Type) bool { return anySignatureIsThenableType(node, t) }) } checkArrayPredicates := func(node *ast.Node) { parent := node.Parent if !ast.IsCallExpression(parent) { return } expr := parent.AsCallExpression() arguments := expr.Arguments.Nodes if len(arguments) == 0 { return } callback := arguments[0] if utils.IsArrayMethodCallWithPredicate(ctx.TypeChecker, expr) && returnsThenable(callback) { ctx.ReportNode(callback, buildPredicateMessage()) } } isFunctionParam := func( param *ast.Symbol, node *ast.Node, ) bool { t := checker.Checker_getApparentType(ctx.TypeChecker, ctx.TypeChecker.GetTypeOfSymbolAtLocation(param, node)) if t == nil { return false } return utils.Some(utils.UnionTypeParts(t), func(t *checker.Type) bool { return len(utils.GetCallSignatures(ctx.TypeChecker, t)) != 0 }) } isAlwaysThenable := func(node *ast.Node) bool { t := ctx.TypeChecker.GetTypeAtLocation(node) for _, subType := range utils.UnionTypeParts(checker.Checker_getApparentType(ctx.TypeChecker, t)) { thenProp := checker.Checker_getPropertyOfType(ctx.TypeChecker, subType, "then") if thenProp == nil { return false } thenType := ctx.TypeChecker.GetTypeOfSymbolAtLocation(thenProp, node) hasThenableSignature := false for _, subType := range utils.UnionTypeParts(thenType) { for _, signature := range utils.GetCallSignatures(ctx.TypeChecker, subType) { params := checker.Signature_parameters(signature) if len(params) != 0 && isFunctionParam(params[0], node) { hasThenableSignature = true } } if hasThenableSignature { break } } if !hasThenableSignature { return false } } return true } checkedNodes := map[*ast.Node](struct{}){} var checkConditional func( node *ast.Expression, isTestExpr bool, ) checkConditional = func( node *ast.Expression, isTestExpr bool, ) { if node == nil || ast.IsAssignmentExpression(node, false) { return } if _, ok := checkedNodes[node]; ok { return } checkedNodes[node] = struct{}{} node = ast.SkipParentheses(node) if ast.IsBinaryExpression(node) && ast.IsLogicalExpression(node) { expr := node.AsBinaryExpression() if expr.OperatorToken.Kind != ast.KindQuestionQuestionToken || isTestExpr { checkConditional(expr.Left, isTestExpr) } if isTestExpr { checkConditional(expr.Right, isTestExpr) } return } if isAlwaysThenable(node) { ctx.ReportNode(node, buildConditionalMessage()) } } getMemberIfExists := func( t *checker.Type, memberName string, ) *ast.Symbol { symbol := checker.Type_symbol(t) if symbol != nil { symbol = symbol.Members[memberName] } if symbol != nil { return symbol } return checker.Checker_getPropertyOfType(ctx.TypeChecker, t, memberName) } isVoidReturningFunctionType := func( node *ast.Node, t *checker.Type, ) bool { hadVoidReturn := false for _, t := range utils.UnionTypeParts(t) { for _, sig := range utils.GetCallSignatures(ctx.TypeChecker, t) { returnType := checker.Checker_getReturnTypeOfSignature(ctx.TypeChecker, sig) if utils.IsThenableType(ctx.TypeChecker, node, returnType) { return false } hadVoidReturn = hadVoidReturn || utils.IsTypeFlagSet(returnType, checker.TypeFlagsVoid) } } return hadVoidReturn } checkHeritageTypeForMemberReturningVoid := func( nodeMember *ast.Node, heritageType *checker.Type, memberName string, ) { heritageMember := getMemberIfExists(heritageType, memberName) if heritageMember == nil { return } memberType := ctx.TypeChecker.GetTypeOfSymbolAtLocation( heritageMember, nodeMember, ) if !isVoidReturningFunctionType(nodeMember, memberType) { return } ctx.ReportNode(nodeMember, buildVoidReturnInheritedMethodMessage(ctx.TypeChecker.TypeToString(heritageType))) } checkJSXAttribute := func(node *ast.JsxAttribute) { if node.Initializer == nil || node.Initializer.Kind != ast.KindJsxExpression { return } expressionContainer := node.Initializer.AsJsxExpression() expression := expressionContainer.Expression contextualType := checker.Checker_getContextualType(ctx.TypeChecker, node.Initializer, checker.ContextFlagsNone) if contextualType != nil && isVoidReturningFunctionType(node.Initializer, contextualType) && returnsThenable(expression) { ctx.ReportNode(node.Initializer, buildVoidReturnAttributeMessage()) } } checkSpread := func(node *ast.Node) { if utils.IsThenableType(ctx.TypeChecker, node.Expression(), nil) { ctx.ReportNode(node.Expression(), buildSpreadMessage()) } } isThenableReturningFunctionType := func( node *ast.Node, t *checker.Type, ) bool { return utils.Some(utils.UnionTypeParts(t), func(t *checker.Type) bool { return anySignatureIsThenableType(node, t) }) } var checkThenableOrVoidArgument func( node *ast.Expression, t *checker.Type, index int, thenableReturnIndices *[]int, voidReturnIndices *[]int, ) checkThenableOrVoidArgument = func( node *ast.Expression, t *checker.Type, index int, thenableReturnIndices *[]int, voidReturnIndices *[]int, ) { if isThenableReturningFunctionType(node.Expression(), t) { (*thenableReturnIndices) = append(*thenableReturnIndices, index) } else if isVoidReturningFunctionType(node.Expression(), t) && !slices.Contains(*thenableReturnIndices, index) { (*voidReturnIndices) = append(*voidReturnIndices, index) } contextualType := checker.Checker_getContextualTypeForArgumentAtIndex(ctx.TypeChecker, node, index) if contextualType != t { checkThenableOrVoidArgument( node, contextualType, index, thenableReturnIndices, voidReturnIndices, ) } } voidFunctionArguments := func( node *ast.Expression, ) []int { if node.Arguments() == nil { return []int{} } thenableReturnIndices := []int{} voidReturnIndices := []int{} t := ctx.TypeChecker.GetTypeAtLocation(node.Expression()) for _, subType := range utils.UnionTypeParts(t) { // Standard function calls and `new` have two different types of signatures var signatures []*checker.Signature if ast.IsCallExpression(node) { signatures = utils.GetCallSignatures(ctx.TypeChecker, subType) } else { signatures = utils.GetConstructSignatures(ctx.TypeChecker, subType) } for _, signature := range signatures { for index, parameter := range checker.Signature_parameters(signature) { decl := parameter.ValueDeclaration t := ctx.TypeChecker.GetTypeOfSymbolAtLocation(parameter, node.Expression()) if decl != nil && utils.IsRestParameterDeclaration(decl) { if checker.Checker_isArrayType(ctx.TypeChecker, t) { t = checker.Checker_getTypeArguments(ctx.TypeChecker, t)[0] for i := index; i < len(node.Arguments()); i++ { checkThenableOrVoidArgument( node, t, i, &thenableReturnIndices, &voidReturnIndices, ) } } else if checker.IsTupleType(t) { typeArgs := checker.Checker_getTypeArguments(ctx.TypeChecker, t) for i := index; i < len(node.Arguments()) && i-index < len(typeArgs); i++ { checkThenableOrVoidArgument( node, typeArgs[i-index], i, &thenableReturnIndices, &voidReturnIndices, ) } } } else { checkThenableOrVoidArgument( node, t, index, &thenableReturnIndices, &voidReturnIndices, ) } } } } for _, index := range thenableReturnIndices { at := slices.Index(voidReturnIndices, index) if at >= 0 { voidReturnIndices = slices.Delete(voidReturnIndices, at, at+1) } } return voidReturnIndices } checkArguments := func( node *ast.Expression, ) { voidArgs := voidFunctionArguments(node) if len(voidArgs) == 0 { return } for index, argument := range node.Arguments() { if !slices.Contains(voidArgs, index) { continue } if returnsThenable(argument) { ctx.ReportNode(argument, buildVoidReturnArgumentMessage()) } } } checkClassLikeOrInterfaceNode := func( node *ast.Node, ) { heritageClauses := utils.GetHeritageClauses(node) if heritageClauses == nil || len(heritageClauses.Nodes) == 0 { return } heritageTypes := utils.Flatten(utils.Map(heritageClauses.Nodes, func(h *ast.Node) []*checker.Type { return utils.Map(h.AsHeritageClause().Types.Nodes, func(n *ast.Node) *checker.Type { return ctx.TypeChecker.GetTypeAtLocation(n) }) })) for _, nodeMember := range node.Members() { if nodeMember.Name() == nil { continue } if !ast.IsIdentifier(nodeMember.Name()) && !ast.IsPrivateIdentifier(nodeMember.Name()) && !ast.IsStringLiteral(nodeMember.Name()) && !ast.IsNumericLiteral(nodeMember.Name()) && !ast.IsBigIntLiteral(nodeMember.Name()) { continue } memberName := nodeMember.Name().Text() if ast.IsStatic(nodeMember) { continue } if !returnsThenable(nodeMember) { continue } for _, heritageType := range heritageTypes { checkHeritageTypeForMemberReturningVoid( nodeMember, heritageType, memberName, ) } } } checkProperty := func(node *ast.Node) { if ast.IsPropertyAssignment(node) { property := node.AsPropertyAssignment() contextualType := checker.Checker_getContextualType(ctx.TypeChecker, property.Initializer, checker.ContextFlagsNone) if contextualType != nil && isVoidReturningFunctionType( property.Initializer, contextualType, ) && returnsThenable(property.Initializer) { if ast.IsFunctionLike(property.Initializer) { returnType := property.Initializer.Type() if returnType != nil { ctx.ReportNode(returnType, buildVoidReturnPropertyMessage()) } else { ctx.ReportNode( property.Initializer, buildVoidReturnPropertyMessage(), ) } } else { ctx.ReportNode(property.Initializer, buildVoidReturnPropertyMessage()) } } } else if ast.IsShorthandPropertyAssignment(node) { contextualType := checker.Checker_getContextualType(ctx.TypeChecker, node.Name(), checker.ContextFlagsNone) if contextualType != nil && isVoidReturningFunctionType(node.Name(), contextualType) && returnsThenable(node.Name()) { ctx.ReportNode(node.Name(), buildVoidReturnPropertyMessage()) } } else if ast.IsMethodDeclaration(node) { if ast.IsComputedPropertyName(node.Name()) { return } obj := node.Parent if !ast.IsObjectLiteralExpression(obj) { return } if !returnsThenable(node) { return } objType := checker.Checker_getContextualType(ctx.TypeChecker, obj, checker.ContextFlagsNone) if objType == nil { return } propertySymbol := checker.Checker_getPropertyOfType(ctx.TypeChecker, objType, node.Name().Text()) if propertySymbol == nil { return } contextualType := ctx.TypeChecker.GetTypeOfSymbolAtLocation( propertySymbol, node.Name(), ) if isVoidReturningFunctionType(node.Name(), contextualType) { if ast.IsMethodDeclaration(node) { } if node.Type() != nil { ctx.ReportNode(node.Type(), buildVoidReturnPropertyMessage()) } else { ctx.ReportNode( node, buildVoidReturnPropertyMessage(), ) } } } } isPossiblyFunctionType := func(node *ast.Node) bool { switch node.Kind { case ast.KindConditionalType, ast.KindConstructorType, ast.KindFunctionType, ast.KindImportType, ast.KindIndexedAccessType, ast.KindInferType, ast.KindIntersectionType, ast.KindQualifiedName, ast.KindThisType, ast.KindTypeOperator, ast.KindTypeQuery, ast.KindTypeReference, ast.KindUnionType: return true case ast.KindTypeLiteral: return utils.Some(node.AsTypeLiteralNode().Members.Nodes, func(member *ast.Node) bool { return member.Kind == ast.KindCallSignature || member.Kind == ast.KindConstructSignature }) case ast.KindAbstractKeyword, ast.KindAnyKeyword, ast.KindArrayType, ast.KindAsyncKeyword, ast.KindBigIntKeyword, ast.KindBooleanKeyword, ast.KindDeclareKeyword, ast.KindExportKeyword, ast.KindIntrinsicKeyword, ast.KindLiteralType, ast.KindMappedType, ast.KindNamedTupleMember, ast.KindNeverKeyword, ast.KindNullKeyword, ast.KindNumberKeyword, ast.KindObjectKeyword, ast.KindOptionalType, ast.KindPrivateKeyword, ast.KindProtectedKeyword, ast.KindPublicKeyword, ast.KindReadonlyKeyword, ast.KindRestType, ast.KindStaticKeyword, ast.KindStringKeyword, ast.KindSymbolKeyword, ast.KindTemplateLiteralType, ast.KindTupleType, ast.KindTypePredicate, ast.KindUndefinedKeyword, ast.KindUnknownKeyword, ast.KindVoidKeyword: return false } return false } checkReturnStatement := func(node *ast.ReturnStatement) { if node.Expression == nil { return } functionNode := (func() *ast.Node { current := node.Parent for current != nil && !ast.IsFunctionLike(current) { current = current.Parent } if current == nil { panic("missing parent function") } return current })() if functionNode.Type() != nil && !isPossiblyFunctionType(functionNode.Type()) { return } contextualType := checker.Checker_getContextualType(ctx.TypeChecker, node.Expression, checker.ContextFlagsNone) if contextualType != nil && isVoidReturningFunctionType( node.Expression, contextualType, ) && returnsThenable(node.Expression) { ctx.ReportNode(node.Expression, buildVoidReturnReturnValueMessage()) } } checkAssignment := func(node *ast.BinaryExpression) { varType := ctx.TypeChecker.GetTypeAtLocation(node.Left) if !isVoidReturningFunctionType(node.Left, varType) { return } if returnsThenable(node.Right) { ctx.ReportNode(node.Right, buildVoidReturnVariableMessage()) } } checkVariableDeclaration := func(node *ast.VariableDeclaration) { if node.Initializer == nil || node.Type == nil { return } if !isPossiblyFunctionType(node.Type) { return } varType := ctx.TypeChecker.GetTypeAtLocation(node.Name()) if !isVoidReturningFunctionType(node.Initializer, varType) { return } if returnsThenable(node.Initializer) { ctx.ReportNode(node.Initializer, buildVoidReturnVariableMessage()) } } listeners := rule.RuleListeners{ ast.KindBinaryExpression: func(node *ast.Node) { if *opts.ChecksConditionals { checkConditional(node, false) } if *opts.ChecksVoidReturn && *opts.ChecksVoidReturnOpts.Variables && ast.IsAssignmentExpression(node, false) { checkAssignment(node.AsBinaryExpression()) } }, } if *opts.ChecksConditionals { listeners[ast.KindPropertyAccessExpression] = checkArrayPredicates listeners[ast.KindElementAccessExpression] = checkArrayPredicates listeners[ast.KindPrefixUnaryExpression] = func(node *ast.Node) { expr := node.AsPrefixUnaryExpression() if expr.Operator == ast.KindExclamationToken { checkConditional(expr.Operand, true) } } listeners[ast.KindConditionalExpression] = func(node *ast.Node) { checkConditional(node.AsConditionalExpression().Condition, true) } listeners[ast.KindForStatement] = func(node *ast.Node) { checkConditional(node.AsForStatement().Condition, true) } listeners[ast.KindDoStatement] = func(node *ast.Node) { checkConditional(node.Expression(), true) } listeners[ast.KindWhileStatement] = func(node *ast.Node) { checkConditional(node.Expression(), true) } listeners[ast.KindIfStatement] = func(node *ast.Node) { checkConditional(node.Expression(), true) } } if *opts.ChecksVoidReturn { if *opts.ChecksVoidReturnOpts.Arguments { listeners[ast.KindCallExpression] = checkArguments listeners[ast.KindNewExpression] = checkArguments } if *opts.ChecksVoidReturnOpts.Attributes { listeners[ast.KindJsxAttribute] = func(node *ast.Node) { checkJSXAttribute(node.AsJsxAttribute()) } } if *opts.ChecksVoidReturnOpts.InheritedMethods { listeners[ast.KindClassDeclaration] = checkClassLikeOrInterfaceNode listeners[ast.KindClassExpression] = checkClassLikeOrInterfaceNode listeners[ast.KindInterfaceDeclaration] = checkClassLikeOrInterfaceNode } if *opts.ChecksVoidReturnOpts.Properties { listeners[ast.KindPropertyAssignment] = checkProperty listeners[ast.KindMethodDeclaration] = checkProperty listeners[ast.KindShorthandPropertyAssignment] = checkProperty } if *opts.ChecksVoidReturnOpts.Returns { listeners[ast.KindReturnStatement] = func(node *ast.Node) { checkReturnStatement(node.AsReturnStatement()) } } if *opts.ChecksVoidReturnOpts.Variables { listeners[ast.KindVariableDeclaration] = func(node *ast.Node) { checkVariableDeclaration(node.AsVariableDeclaration()) } } } if *opts.ChecksSpreads { listeners[ast.KindSpreadElement] = checkSpread listeners[ast.KindSpreadAssignment] = checkSpread } return listeners }, })
Functions ¶
This section is empty.
Types ¶
type NoMisusedPromisesOptions ¶
type NoMisusedPromisesOptions struct {
ChecksConditionals *bool
ChecksSpreads *bool
ChecksVoidReturn *bool
ChecksVoidReturnOpts *NoMisusedPromisesChecksVoidReturnOptions
}
Click to show internal directories.
Click to hide internal directories.