no_unnecessary_boolean_literal_compare

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 NoUnnecessaryBooleanLiteralCompareRule = rule.CreateRule(rule.Rule{
	Name: "no-unnecessary-boolean-literal-compare",
	Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
		opts, ok := options.(NoUnnecessaryBooleanLiteralCompareOptions)
		if !ok {
			opts = NoUnnecessaryBooleanLiteralCompareOptions{}
		}
		if opts.AllowComparingNullableBooleansToFalse == nil {
			opts.AllowComparingNullableBooleansToFalse = utils.Ref(true)
		}
		if opts.AllowComparingNullableBooleansToTrue == nil {
			opts.AllowComparingNullableBooleansToTrue = utils.Ref(true)
		}
		if opts.AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing == nil {
			opts.AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing = utils.Ref(false)
		}

		compilerOptions := ctx.Program.Options()
		isStrictNullChecks := utils.IsStrictCompilerOptionEnabled(
			compilerOptions,
			compilerOptions.StrictNullChecks,
		)

		if !isStrictNullChecks && !*opts.AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing {
			ctx.ReportRange(core.NewTextRange(0, 0), buildNoStrictNullCheckMessage())
		}

		isNullableBoolean := func(t *checker.Type) bool {
			if !utils.IsUnionType(t) {
				return false
			}

			var flags checker.TypeFlags
			for _, t := range t.Types() {
				flags |= checker.Type_flags(t)
			}
			return flags&checker.TypeFlagsNullable != 0 && flags&checker.TypeFlagsBooleanLike != 0
		}

		getBooleanComparison := func(node *ast.BinaryExpression) (booleanComparison, bool) {
			res := booleanComparison{}
			if node.OperatorToken.Kind == ast.KindExclamationEqualsToken || node.OperatorToken.Kind == ast.KindExclamationEqualsEqualsToken {
				res.negated = true
			} else if node.OperatorToken.Kind != ast.KindEqualsEqualsToken && node.OperatorToken.Kind != ast.KindEqualsEqualsEqualsToken {
				return res, false
			}

			deconstruct := func(against, expression *ast.Node) bool {
				if against.Kind == ast.KindTrueKeyword {
					res.literalBooleanInComparison = true
				} else if against.Kind != ast.KindFalseKeyword {
					return false
				}
				res.expression = expression
				return true
			}

			if !deconstruct(ast.SkipParentheses(node.Right), node.Left) && !deconstruct(ast.SkipParentheses(node.Left), node.Right) {
				return res, false
			}

			constraintType, isTypeParameter := utils.GetConstraintInfo(ctx.TypeChecker, ctx.TypeChecker.GetTypeAtLocation(res.expression))

			if isTypeParameter && constraintType == nil {
				return res, false
			}

			if isBooleanType(constraintType) {
				return res, true
			}

			if isNullableBoolean(constraintType) {
				res.expressionIsNullableBoolean = true
				return res, true
			}

			return res, false
		}

		return rule.RuleListeners{
			ast.KindBinaryExpression: func(node *ast.Node) {
				comparison, found := getBooleanComparison(node.AsBinaryExpression())
				if !found {
					return
				}

				if comparison.expressionIsNullableBoolean && ((comparison.literalBooleanInComparison && *opts.AllowComparingNullableBooleansToTrue) || (!comparison.literalBooleanInComparison && *opts.AllowComparingNullableBooleansToFalse)) {
					return
				}

				var msg rule.RuleMessage
				if comparison.expressionIsNullableBoolean {
					if comparison.literalBooleanInComparison {
						if comparison.negated {
							msg = buildComparingNullableToTrueNegatedMessage()
						} else {
							msg = buildComparingNullableToTrueDirectMessage()
						}
					} else {
						msg = buildComparingNullableToFalseMessage()
					}
				} else if comparison.negated {
					msg = buildNegatedMessage()
				} else {
					msg = buildDirectMessage()
				}

				parent := node.Parent
				for ast.IsParenthesizedExpression(parent) {
					parent = parent.Parent
				}
				isUnaryNegation := ast.IsPrefixUnaryExpression(parent) && parent.AsPrefixUnaryExpression().Operator == ast.KindExclamationToken

				shouldNegate := comparison.negated != comparison.literalBooleanInComparison

				mutatedNode := node
				if isUnaryNegation {
					mutatedNode = parent
				}

				fixes := make([]rule.RuleFix, 0, 6)

				fixes = append(fixes, rule.RuleFixReplace(ctx.SourceFile, mutatedNode, ctx.SourceFile.Text()[comparison.expression.Pos():comparison.expression.End()]))

				if shouldNegate == isUnaryNegation {
					fixes = append(fixes, rule.RuleFixInsertBefore(ctx.SourceFile, mutatedNode, "!"))

					if !utils.IsStrongPrecedenceNode(comparison.expression) {
						fixes = append(fixes, rule.RuleFixInsertBefore(ctx.SourceFile, mutatedNode, "("), rule.RuleFixInsertAfter(mutatedNode, ")"))
					}
				}

				if comparison.expressionIsNullableBoolean && !comparison.literalBooleanInComparison {
					fixes = append(fixes, rule.RuleFixInsertBefore(ctx.SourceFile, mutatedNode, "("), rule.RuleFixInsertAfter(mutatedNode, " ?? true)"))
				}

				ctx.ReportNodeWithFixes(node, msg, fixes...)
			},
		}
	},
})

Functions

This section is empty.

Types

type NoUnnecessaryBooleanLiteralCompareOptions

type NoUnnecessaryBooleanLiteralCompareOptions struct {
	AllowComparingNullableBooleansToFalse                  *bool
	AllowComparingNullableBooleansToTrue                   *bool
	AllowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing *bool
}

Jump to

Keyboard shortcuts

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