return_await

package
v0.1.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2025 License: MIT Imports: 4 Imported by: 0

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
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL