Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var ReturnAwaitRule = rule.CreateRule(rule.Rule{ Name: "return-await", Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { opts, ok := options.(ReturnAwaitOptions) if !ok { opts = ReturnAwaitOptions{} } if opts.Option == nil { opts.Option = utils.Ref(ReturnAwaitOptionInTryCatch) } var scope *scopeInfo enterFunction := func(node *ast.Node) { scope = &scopeInfo{ hasAsync: ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync), owningFunc: node, parent: scope, } } exitFunction := func(node *ast.Node) { scope = scope.parent } affectsExplicitResourceManagement := func(node *ast.Node) bool { if !ast.IsBlock(scope.owningFunc.Body()) { return false } for declarationScope := ast.GetEnclosingBlockScopeContainer(node); declarationScope != nil; declarationScope = ast.GetEnclosingBlockScopeContainer(declarationScope) { locals := declarationScope.Locals() for _, local := range locals { decl := local.ValueDeclaration if decl != nil && ast.IsVariableDeclaration(decl) && decl.Parent.Flags&ast.NodeFlagsUsing != 0 && decl.Pos() < node.Pos() { return true } } if scope.owningFunc == declarationScope { break } } return false } findContainingTryStatement := func(node *ast.Node) (containingTryStatementBlock, *ast.TryStatement, bool) { child := node ancestor := node.Parent for ancestor != nil && !ast.IsFunctionLike(ancestor) { if !ast.IsTryStatement(ancestor) { child = ancestor ancestor = ancestor.Parent continue } statement := ancestor.AsTryStatement() var block containingTryStatementBlock switch child { case statement.TryBlock: block = containingTryStatementBlockTry case statement.CatchClause: block = containingTryStatementBlockCatch case statement.FinallyBlock: block = containingTryStatementBlockFinally } return block, statement, true } return 0, nil, false } affectsExplicitErrorHandling := func(node *ast.Node) bool { for { containingTryBlock, tryStatement, found := findContainingTryStatement(node) if !found { return false } switch containingTryBlock { case containingTryStatementBlockCatch: if tryStatement.FinallyBlock != nil { return true } node = tryStatement.AsNode() case containingTryStatementBlockFinally: node = tryStatement.AsNode() case containingTryStatementBlockTry: return true } } } removeAwaitFix := func(node *ast.Node) rule.RuleFix { return rule.RuleFixRemoveRange(scanner.GetRangeOfTokenAtPosition(ctx.SourceFile, node.Pos())) } insertAwaitFix := func(node *ast.Node, isHighPrecedence bool) []rule.RuleFix { if isHighPrecedence { return []rule.RuleFix{ rule.RuleFixInsertBefore(ctx.SourceFile, node, "await "), } } return []rule.RuleFix{ rule.RuleFixInsertBefore(ctx.SourceFile, node, "await ("), rule.RuleFixInsertAfter(node, ")"), } } test := func(node *ast.Node) { var child *ast.Node isAwait := ast.IsAwaitExpression(node) if isAwait { child = node.Expression() } else { child = node } t := ctx.TypeChecker.GetTypeAtLocation(child) certainty := utils.NeedsToBeAwaited(ctx.TypeChecker, node, t) if certainty != utils.TypeAwaitableAlways { if isAwait { if certainty == utils.TypeAwaitableMay { return } ctx.ReportNodeWithFixes(node, buildNonPromiseAwaitMessage(), removeAwaitFix(node)) } return } affectsErrorHandling := affectsExplicitErrorHandling(node) || affectsExplicitResourceManagement(node) useAutoFix := !affectsErrorHandling shouldAwaitInCurrentContext := getWhetherToAwait(affectsErrorHandling, *opts.Option) switch shouldAwaitInCurrentContext { case whetherToAwaitAwait: if isAwait { break } rule.ReportNodeWithFixesOrSuggestions(ctx, node, useAutoFix, buildRequiredPromiseAwaitMessage(), buildRequiredPromiseAwaitSuggestionMessage(), insertAwaitFix(node, utils.IsHigherPrecedenceThanAwait(node))...) case whetherToAwaitDontCare: break case whetherToAwaitNoAwait: if !isAwait { break } rule.ReportNodeWithFixesOrSuggestions(ctx, node, useAutoFix, buildDisallowedPromiseAwaitMessage(), buildDisallowedPromiseAwaitSuggestionMessage(), removeAwaitFix(node)) } } var testEachPossiblyReturnedNode func(node *ast.Node) testEachPossiblyReturnedNode = func(node *ast.Node) { node = ast.SkipParentheses(node) if node.Kind == ast.KindConditionalExpression { expr := node.AsConditionalExpression() testEachPossiblyReturnedNode(expr.WhenFalse) testEachPossiblyReturnedNode(expr.WhenTrue) } else { test(node) } } return rule.RuleListeners{ ast.KindArrowFunction: enterFunction, ast.KindFunctionDeclaration: enterFunction, ast.KindFunctionExpression: enterFunction, ast.KindMethodDeclaration: enterFunction, ast.KindConstructor: enterFunction, ast.KindGetAccessor: enterFunction, ast.KindSetAccessor: enterFunction, rule.ListenerOnExit(ast.KindArrowFunction): func(node *ast.Node) { body := node.Body() if !ast.IsBlock(body) { testEachPossiblyReturnedNode(body) } exitFunction(node) }, rule.ListenerOnExit(ast.KindFunctionDeclaration): exitFunction, rule.ListenerOnExit(ast.KindFunctionExpression): exitFunction, rule.ListenerOnExit(ast.KindMethodDeclaration): exitFunction, rule.ListenerOnExit(ast.KindConstructor): exitFunction, rule.ListenerOnExit(ast.KindGetAccessor): exitFunction, rule.ListenerOnExit(ast.KindSetAccessor): exitFunction, ast.KindReturnStatement: func(node *ast.Node) { expr := node.AsReturnStatement().Expression if scope == nil || !scope.hasAsync || expr == nil { return } testEachPossiblyReturnedNode(expr) }, } }, })
Functions ¶
This section is empty.
Types ¶
type ReturnAwaitOption ¶
type ReturnAwaitOption uint8
const ( ReturnAwaitOptionAlways ReturnAwaitOption = iota ReturnAwaitOptionErrorHandlingCorrectnessOnly ReturnAwaitOptionInTryCatch ReturnAwaitOptionNever )
type ReturnAwaitOptions ¶
type ReturnAwaitOptions struct {
Option *ReturnAwaitOption
}
Click to show internal directories.
Click to hide internal directories.