no_confusing_void_expression

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: 6 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var NoConfusingVoidExpressionRule = rule.CreateRule(rule.Rule{
	Name: "no-confusing-void-expression",
	Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
		opts, ok := options.(NoConfusingVoidExpressionOptions)

		if !ok {

			opts = NoConfusingVoidExpressionOptions{}

			options_array, _ := options.([]interface{})

			if len(options_array) > 0 {
				optsJSON, err := json.Marshal(options_array[0])
				if err == nil {
					json.Unmarshal(optsJSON, &opts)
				}

			}
		}

		canFix := func(node *ast.Node) bool {
			t := utils.GetConstrainedTypeAtLocation(ctx.TypeChecker, node)
			return utils.IsTypeFlagSet(t, checker.TypeFlagsVoidLike)
		}

		findInvalidAncestor := func(node *ast.Node) *ast.Node {
			parent := node

			for {
				node = parent
				parent = parent.Parent

				switch parent.Kind {
				case ast.KindParenthesizedExpression:
					continue
				case ast.KindBinaryExpression:
					n := parent.AsBinaryExpression()
					if ast.IsLogicalOrCoalescingBinaryOperator(n.OperatorToken.Kind) && n.Right == node {

						continue
					}
					if n.OperatorToken.Kind == ast.KindCommaToken {
						if n.Left == node {
							return nil
						}

					}
				case ast.KindExpressionStatement:

					return nil
				case ast.KindConditionalExpression:
					n := parent.AsConditionalExpression()
					if n.WhenTrue == node || n.WhenFalse == node {

						continue
					}
				case ast.KindArrowFunction:
					if opts.IgnoreArrowShorthand {

						return nil
					}
				case ast.KindVoidExpression:
					if opts.IgnoreVoidOperator {

						return nil
					}

				}
				break
			}

			return parent
		}

		isVoidReturningFunction := func(functionNode *ast.Node) bool {

			returnTypeNode := functionNode.Type()
			if returnTypeNode != nil {
				returnType := checker.Checker_getTypeFromTypeNode(ctx.TypeChecker, returnTypeNode)

				return utils.Some(utils.UnionTypeParts(returnType), utils.IsIntrinsicVoidType)
			}

			if !ast.IsArrowFunction(functionNode) && !ast.IsFunctionExpression(functionNode) {
				return false
			}

			functionType := checker.Checker_getContextualType(ctx.TypeChecker, functionNode, checker.ContextFlagsNone)

			if functionType == nil {
				return false
			}
			return utils.Some(utils.UnionTypeParts(functionType), func(t *checker.Type) bool {
				callSignatures := utils.GetCallSignatures(ctx.TypeChecker, t)

				return utils.Some(callSignatures, func(s *checker.Signature) bool {
					returnType := checker.Checker_getReturnTypeOfSignature(ctx.TypeChecker, s)

					return utils.Some(utils.UnionTypeParts(returnType), utils.IsIntrinsicVoidType)
				})
			})
		}

		isFinalReturn := func(node *ast.Node) bool {
			block := node.Parent
			if !ast.IsBlock(block) {
				return false
			}

			if !ast.IsFunctionLikeDeclaration(block.Parent) {
				return false
			}

			statements := block.AsBlock().Statements.Nodes

			return len(statements) > 0 && statements[len(statements)-1] == node
		}

		checkExpression := func(node *ast.Node) {
			t := utils.GetConstrainedTypeAtLocation(ctx.TypeChecker, node)
			if !utils.IsTypeFlagSet(t, checker.TypeFlagsVoidLike) {
				return
			}

			invalidAncestor := findInvalidAncestor(node)
			if invalidAncestor == nil {

				return
			}

			insertVoidFix := func() rule.RuleFix {
				return rule.RuleFixInsertBefore(ctx.SourceFile, node, "void ")
			}

			if ast.IsArrowFunction(invalidAncestor) {
				if opts.IgnoreVoidReturningFunctions && isVoidReturningFunction(invalidAncestor) {
					return
				}

				if opts.IgnoreVoidOperator {
					ctx.ReportNodeWithFixes(node, buildInvalidVoidExprArrowWrapVoidMessage(), insertVoidFix())
					return
				}

				var fixes []rule.RuleFix
				body := invalidAncestor.Body()
				if !ast.IsBlock(body) && canFix(body) {
					withoutParens := ast.SkipParentheses(body)
					fixes = []rule.RuleFix{
						rule.RuleFixReplaceRange(body.Loc.WithEnd(withoutParens.Pos()), "{ "),
						rule.RuleFixReplaceRange(body.Loc.WithPos(withoutParens.End()), "; }"),
					}
				}

				ctx.ReportNodeWithFixes(node, buildInvalidVoidExprArrowMessage(), fixes...)
				return
			}

			if ast.IsReturnStatement(invalidAncestor) {
				if opts.IgnoreVoidReturningFunctions {
					functionNode := utils.GetParentFunctionNode(invalidAncestor)

					if functionNode != nil && isVoidReturningFunction(functionNode) {
						return
					}
				}

				if opts.IgnoreVoidOperator {
					ctx.ReportNodeWithFixes(node, buildInvalidVoidExprReturnWrapVoidMessage(), insertVoidFix())
					return
				}

				if isFinalReturn(invalidAncestor) {
					expr := invalidAncestor.AsReturnStatement().Expression
					var fixes []rule.RuleFix
					if canFix(expr) {
						replaceText := ""
						nextToken := scanner.ScanTokenAtPosition(ctx.SourceFile, expr.Pos())
						if nextToken == ast.KindOpenParenToken || nextToken == ast.KindOpenBracketToken || nextToken == ast.KindBacktickToken {
							replaceText = ";"
						}
						returnToken := scanner.GetRangeOfTokenAtPosition(ctx.SourceFile, invalidAncestor.Pos())
						fixes = append(fixes, rule.RuleFixReplaceRange(returnToken, replaceText))
					}

					ctx.ReportNodeWithFixes(node, buildInvalidVoidExprReturnLastMessage(), fixes...)
					return
				}

				var fixes []rule.RuleFix
				replaceText := ""
				nextToken := scanner.ScanTokenAtPosition(ctx.SourceFile, invalidAncestor.AsReturnStatement().Expression.Pos())
				if nextToken == ast.KindOpenParenToken || nextToken == ast.KindOpenBracketToken || nextToken == ast.KindBacktickToken {
					replaceText = ";"
				}
				returnToken := scanner.GetRangeOfTokenAtPosition(ctx.SourceFile, invalidAncestor.Pos())
				fixes = append(fixes, rule.RuleFixReplaceRange(returnToken, replaceText), rule.RuleFixInsertAfter(invalidAncestor, "; return;"))

				if !ast.IsBlock(invalidAncestor.Parent) {

					fixes = append(fixes,
						rule.RuleFixInsertBefore(ctx.SourceFile, invalidAncestor, "{ "),
						rule.RuleFixInsertAfter(invalidAncestor, " }"),
					)
				}
				ctx.ReportNodeWithFixes(node, buildInvalidVoidExprReturnMessage(), fixes...)
				return
			}

			if opts.IgnoreVoidOperator {
				ctx.ReportNodeWithSuggestions(node, buildInvalidVoidExprWrapVoidMessage(), rule.RuleSuggestion{
					Message:  buildVoidExprWrapVoidMessage(),
					FixesArr: []rule.RuleFix{insertVoidFix()},
				})
				return
			}

			ctx.ReportNode(node, buildInvalidVoidExprMessage())
		}

		return rule.RuleListeners{
			ast.KindAwaitExpression:          checkExpression,
			ast.KindCallExpression:           checkExpression,
			ast.KindTaggedTemplateExpression: checkExpression,
		}
	},
})

Functions

This section is empty.

Types

type NoConfusingVoidExpressionOptions

type NoConfusingVoidExpressionOptions struct {
	IgnoreArrowShorthand         bool `json:"ignoreArrowShorthand,omitempty"`
	IgnoreVoidOperator           bool `json:"ignoreVoidOperator,omitempty"`
	IgnoreVoidReturningFunctions bool `json:"ignoreVoidReturningFunctions,omitempty"`
}

Jump to

Keyboard shortcuts

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