use_unknown_in_catch_callback_variable

package
v0.1.4 Latest Latest
Warning

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

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

Documentation

Index

Constants

This section is empty.

Variables

View Source
var UseUnknownInCatchCallbackVariableRule = rule.CreateRule(rule.Rule{
	Name: "use-unknown-in-catch-callback-variable",
	Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
		var collectFlaggedNodes func(node *ast.Node) []*ast.Node

		isFlaggableHandlerType := func(t *checker.Type) bool {
			for _, part := range utils.UnionTypeParts(t) {
				for _, callSignature := range utils.GetCallSignatures(ctx.TypeChecker, part) {
					params := checker.Signature_parameters(callSignature)
					if len(params) == 0 {
						continue
					}

					firstParam := params[0]

					firstParamType := checker.Checker_getTypeOfSymbol(ctx.TypeChecker, firstParam)
					decl := firstParam.ValueDeclaration

					if decl != nil && decl.AsParameterDeclaration().DotDotDotToken != nil {

						if !checker.Checker_isArrayOrTupleType(ctx.TypeChecker, firstParamType) {
							return true
						}
						firstParamType = checker.Checker_getTypeArguments(ctx.TypeChecker, firstParamType)[0]
					}

					if !utils.IsTypeFlagSet(firstParamType, checker.TypeFlagsUnknown) {
						return true
					}
				}
			}
			return false
		}

		collectFlaggedNodes = func(node *ast.Node) []*ast.Node {
			node = ast.SkipParentheses(node)
			switch node.Kind {
			case ast.KindBinaryExpression:
				n := node.AsBinaryExpression()
				if ast.IsLogicalExpression(node) {
					return append(collectFlaggedNodes(n.Left), collectFlaggedNodes(n.Right)...)
				}
				if n.OperatorToken.Kind == ast.KindCommaToken {
					return collectFlaggedNodes(n.Right)
				}
			case ast.KindConditionalExpression:
				n := node.AsConditionalExpression()
				return append(collectFlaggedNodes(n.WhenTrue), collectFlaggedNodes(n.WhenFalse)...)
			case ast.KindArrowFunction, ast.KindFunctionExpression:
				t := ctx.TypeChecker.GetTypeAtLocation(node)
				if isFlaggableHandlerType(t) {
					return []*ast.Node{node}
				}
			}
			return []*ast.Node{}
		}
		return rule.RuleListeners{
			ast.KindCallExpression: func(node *ast.Node) {
				expr := node.AsCallExpression()
				callee := expr.Expression

				if !ast.IsAccessExpression(callee) {
					return
				}

				propertyName, found := checker.Checker_getAccessedPropertyName(ctx.TypeChecker, callee)
				if !found {
					return
				}

				var method string
				var argIndexToCheck int

				switch propertyName {
				case "catch":
					method = "`catch`"
					argIndexToCheck = 0
				case "then":
					method = "`then` rejection"
					argIndexToCheck = 1
				default:
					return
				}

				if len(expr.Arguments.Nodes) < argIndexToCheck+1 {
					return
				}

				for i, arg := range expr.Arguments.Nodes {
					if ast.IsSpreadElement(arg) {
						return
					}
					if i == argIndexToCheck {
						break
					}
				}

				if !utils.IsThenableType(ctx.TypeChecker, callee, ctx.TypeChecker.GetTypeAtLocation(callee.Expression())) {
					return
				}

				for _, flagged := range collectFlaggedNodes(expr.Arguments.Nodes[argIndexToCheck]) {
					catchParamNode := flagged.Parameters()[0]
					catchParam := catchParamNode.AsParameterDeclaration()
					catchVariable := catchParam.Name()
					catchTypeAnnotation := catchParam.Type

					if catchParam.DotDotDotToken != nil {
						if catchTypeAnnotation == nil {
							ctx.ReportNodeWithSuggestions(catchParamNode, buildUseUnknownMessage(method), rule.RuleSuggestion{
								Message:  buildAddUnknownRestTypeAnnotationSuggestionMessage(),
								FixesArr: []rule.RuleFix{rule.RuleFixInsertAfter(catchVariable, ": [unknown]")},
							})
							continue
						}

						ctx.ReportNodeWithSuggestions(catchParamNode, buildUseUnknownMessage(method), rule.RuleSuggestion{
							Message: buildWrongRestTypeAnnotationSuggestionMessage(),
							FixesArr: []rule.RuleFix{
								rule.RuleFixReplace(ctx.SourceFile, catchTypeAnnotation, "[unknown]"),
							},
						})
						continue
					}

					switch catchVariable.Kind {
					case ast.KindIdentifier:
						if catchTypeAnnotation == nil {
							var fixes []rule.RuleFix
							if utils.IsParenlessArrowFunction(flagged) {
								fixes = []rule.RuleFix{
									rule.RuleFixInsertBefore(ctx.SourceFile, catchVariable, "("),
									rule.RuleFixInsertAfter(catchVariable, ": unknown)"),
								}
							} else {
								insertAfter := catchVariable
								if catchParam.QuestionToken != nil {
									insertAfter = catchParam.QuestionToken
								}
								fixes = []rule.RuleFix{
									rule.RuleFixInsertAfter(insertAfter, ": unknown"),
								}
							}

							ctx.ReportNodeWithSuggestions(catchParamNode, buildUseUnknownMessage(method), rule.RuleSuggestion{
								Message:  buildAddUnknownTypeAnnotationSuggestionMessage(),
								FixesArr: fixes,
							})
							break
						}
						ctx.ReportNodeWithSuggestions(catchParamNode, buildUseUnknownMessage(method), rule.RuleSuggestion{
							Message:  buildWrongTypeAnnotationSuggestionMessage(),
							FixesArr: []rule.RuleFix{rule.RuleFixReplace(ctx.SourceFile, catchTypeAnnotation, "unknown")},
						})
					case ast.KindArrayBindingPattern:
						ctx.ReportNode(catchParamNode, buildUseUnknownArrayDestructuringPatternMessage(method))
					case ast.KindObjectBindingPattern:
						ctx.ReportNode(catchParamNode, buildUseUnknownObjectDestructuringPatternMessage(method))
					}
				}
			},
		}
	},
})

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

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