Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var NoFloatingPromisesRule = rule.CreateRule(rule.Rule{ Name: "no-floating-promises", Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { opts, ok := options.(NoFloatingPromisesOptions) if !ok { opts = NoFloatingPromisesOptions{ AllowForKnownSafeCalls: []utils.TypeOrValueSpecifier{}, AllowForKnownSafeCallsInline: []string{}, AllowForKnownSafePromises: []utils.TypeOrValueSpecifier{}, AllowForKnownSafePromisesInline: []string{}, } options_array, _ := options.([]interface{}) if len(options_array) > 0 { optsJSON, err := json.Marshal(options_array[0]) if err == nil { json.Unmarshal(optsJSON, &opts) } } } if opts.CheckThenables == nil { opts.CheckThenables = utils.Ref(false) } if opts.IgnoreIIFE == nil { opts.IgnoreIIFE = utils.Ref(false) } if opts.IgnoreVoid == nil { opts.IgnoreVoid = utils.Ref(true) } isHigherPrecedenceThanUnary := func(node *ast.Node) bool { operator := ast.KindUnknown if ast.IsBinaryExpression(node) { operator = node.AsBinaryExpression().OperatorToken.Kind } nodePrecedence := ast.GetOperatorPrecedence(node.Kind, operator, ast.OperatorPrecedenceFlagsNone) return nodePrecedence > ast.OperatorPrecedenceUnary } addAwait := func( expression *ast.Expression, node *ast.ExpressionStatement, ) []rule.RuleFix { if ast.IsVoidExpression(expression) { voidTokenRange := scanner.GetRangeOfTokenAtPosition(ctx.SourceFile, expression.Pos()) return []rule.RuleFix{rule.RuleFixReplaceRange(voidTokenRange, "await")} } if isHigherPrecedenceThanUnary(node.Expression) { return []rule.RuleFix{rule.RuleFixInsertBefore(ctx.SourceFile, &node.Node, "await ")} } return []rule.RuleFix{ rule.RuleFixInsertBefore(ctx.SourceFile, &node.Node, "await ("), rule.RuleFixInsertAfter(expression, ")"), } } hasMatchingSignature := func( t *checker.Type, matcher func(signature *checker.Signature) bool, ) bool { for _, part := range utils.UnionTypeParts(t) { if utils.Some(utils.GetCallSignatures(ctx.TypeChecker, part), matcher) { return true } } return false } isFunctionParam := func( param *ast.Symbol, node *ast.Node, ) bool { t := checker.Checker_getApparentType(ctx.TypeChecker, ctx.TypeChecker.GetTypeOfSymbolAtLocation(param, node)) for _, part := range utils.UnionTypeParts(t) { if len(utils.GetCallSignatures(ctx.TypeChecker, part)) != 0 { return true } } return false } isPromiseLike := func(node *ast.Node, t *checker.Type) bool { if t == nil { t = ctx.TypeChecker.GetTypeAtLocation(node) } if utils.TypeMatchesSomeSpecifier( t, opts.AllowForKnownSafePromises, opts.AllowForKnownSafePromisesInline, ctx.Program, ) { return false } typeParts := utils.UnionTypeParts(checker.Checker_getApparentType(ctx.TypeChecker, t)) if utils.Some(typeParts, func(typePart *checker.Type) bool { return utils.IsPromiseLike(ctx.Program, ctx.TypeChecker, typePart) }) { return true } if !*opts.CheckThenables { return false } for _, typePart := range typeParts { then := checker.Checker_getPropertyOfType(ctx.TypeChecker, typePart, "then") if then == nil { continue } thenType := ctx.TypeChecker.GetTypeOfSymbolAtLocation(then, node) if hasMatchingSignature( thenType, func(signature *checker.Signature) bool { params := checker.Signature_parameters(signature) return len(params) >= 2 && isFunctionParam(params[0], node) && isFunctionParam(params[1], node) }) { return true } } return false } isPromiseArray := func(node *ast.Node) bool { t := ctx.TypeChecker.GetTypeAtLocation(node) for _, typePart := range utils.UnionTypeParts(t) { apparent := checker.Checker_getApparentType(ctx.TypeChecker, typePart) if checker.Checker_isArrayType(ctx.TypeChecker, apparent) { arrayType := checker.Checker_getTypeArguments(ctx.TypeChecker, apparent)[0] if isPromiseLike(node, arrayType) { return true } } if checker.IsTupleType(apparent) { for _, tupleElementType := range checker.Checker_getTypeArguments(ctx.TypeChecker, apparent) { if isPromiseLike(node, tupleElementType) { return true } } } } return false } isKnownSafePromiseReturn := func(node *ast.Node) bool { if !ast.IsCallExpression(node) { return false } t := ctx.TypeChecker.GetTypeAtLocation(node.AsCallExpression().Expression) return utils.TypeMatchesSomeSpecifier( t, opts.AllowForKnownSafeCalls, opts.AllowForKnownSafeCallsInline, ctx.Program, ) } isAsyncIife := func(node *ast.ExpressionStatement) bool { if !ast.IsCallExpression(node.Expression) { return false } callee := ast.SkipParentheses(node.Expression.AsCallExpression().Expression) return ast.IsArrowFunction(callee) || ast.IsFunctionExpression(callee) } isValidRejectionHandler := func(rejectionHandler *ast.Node) bool { return len(utils.GetCallSignatures(ctx.TypeChecker, ctx.TypeChecker.GetTypeAtLocation(rejectionHandler))) > 0 } var isUnhandledPromise func( node *ast.Node, ) ( bool, bool, bool, ) isUnhandledPromise = func( node *ast.Node, ) ( bool, bool, bool, ) { if ast.IsAssignmentExpression(node, false) { return false, false, false } if ast.IsCommaExpression(node) { expr := node.AsBinaryExpression() isUnhandled, nonFunctionHandler, promiseArray := isUnhandledPromise(expr.Left) if isUnhandled { return isUnhandled, nonFunctionHandler, promiseArray } return isUnhandledPromise(expr.Right) } if !*opts.IgnoreVoid && ast.IsVoidExpression(node) { return isUnhandledPromise(node.Expression()) } if isPromiseArray(node) { return true, false, true } if ast.IsAwaitExpression(node) { return false, false, false } if !isPromiseLike(node, nil) { return false, false, false } if ast.IsCallExpression(node) { callExpr := node.AsCallExpression() callee := callExpr.Expression if ast.IsAccessExpression(callee) { methodName, _ := checker.Checker_getAccessedPropertyName(ctx.TypeChecker, callee) if methodName == "catch" && len(callExpr.Arguments.Nodes) >= 1 { if isValidRejectionHandler(callExpr.Arguments.Nodes[0]) { return false, false, false } return true, true, false } if methodName == "then" && len(callExpr.Arguments.Nodes) >= 2 { if isValidRejectionHandler(callExpr.Arguments.Nodes[1]) { return false, false, false } return true, true, false } if methodName == "finally" { return isUnhandledPromise(callee.Expression()) } } return true, false, false } if node.Kind == ast.KindConditionalExpression { expr := node.AsConditionalExpression() isUnhandled, nonFunctionHandler, promiseArray := isUnhandledPromise(expr.WhenFalse) if isUnhandled { return isUnhandled, nonFunctionHandler, promiseArray } return isUnhandledPromise(expr.WhenTrue) } if ast.IsLogicalOrCoalescingBinaryExpression(node) { expr := node.AsBinaryExpression() isUnhandled, nonFunctionHandler, promiseArray := isUnhandledPromise(expr.Left) if isUnhandled { return isUnhandled, nonFunctionHandler, promiseArray } return isUnhandledPromise(expr.Right) } return true, false, false } return rule.RuleListeners{ ast.KindExpressionStatement: func(node *ast.Node) { exprStatement := node.AsExpressionStatement() if *opts.IgnoreIIFE && isAsyncIife(exprStatement) { return } expression := ast.SkipParentheses(exprStatement.Expression) if isKnownSafePromiseReturn(expression) { return } isUnhandled, nonFunctionHandler, promiseArray := isUnhandledPromise(expression) if !isUnhandled { return } if promiseArray { var msg rule.RuleMessage if *opts.IgnoreVoid { msg = buildFloatingPromiseArrayVoidMessage() } else { msg = buildFloatingPromiseArrayMessage() } ctx.ReportNode(node, msg) } else if *opts.IgnoreVoid { var msg rule.RuleMessage if nonFunctionHandler { msg = buildFloatingUselessRejectionHandlerVoidMessage() } else { msg = buildFloatingVoidMessage() } ctx.ReportNodeWithSuggestions(node, msg, rule.RuleSuggestion{ Message: buildFloatingFixVoidMessage(), FixesArr: (func() []rule.RuleFix { if isHigherPrecedenceThanUnary(exprStatement.Expression) { return []rule.RuleFix{rule.RuleFixInsertBefore(ctx.SourceFile, node, "void ")} } return []rule.RuleFix{ rule.RuleFixInsertBefore(ctx.SourceFile, node, "void ("), rule.RuleFixInsertAfter(expression, ")"), } })(), }, rule.RuleSuggestion{ Message: buildFloatingFixAwaitMessage(), FixesArr: addAwait(expression, exprStatement), }) } else { var msg rule.RuleMessage if nonFunctionHandler { msg = buildFloatingUselessRejectionHandlerMessage() } else { msg = buildFloatingMessage() } ctx.ReportNodeWithSuggestions(node, msg, rule.RuleSuggestion{ Message: buildFloatingFixAwaitMessage(), FixesArr: addAwait(expression, exprStatement), }) } }, } }, })
Functions ¶
This section is empty.
Types ¶
type NoFloatingPromisesOptions ¶
type NoFloatingPromisesOptions struct {
AllowForKnownSafeCalls []utils.TypeOrValueSpecifier `json:"allowForKnownSafeCalls"`
AllowForKnownSafeCallsInline []string `json:"allowForKnownSafeCallsInline"`
AllowForKnownSafePromises []utils.TypeOrValueSpecifier `json:"allowForKnownSafePromises"`
AllowForKnownSafePromisesInline []string `json:"allowForKnownSafePromisesInline"`
CheckThenables *bool `json:"checkThenables"`
IgnoreIIFE *bool `json:"ignoreIIFE"`
IgnoreVoid *bool `json:"ignoreVoid"`
}
Click to show internal directories.
Click to hide internal directories.