no_unsafe_assignment

package
v0.1.6 Latest Latest
Warning

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

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

Documentation

Index

Constants

This section is empty.

Variables

View Source
var NoUnsafeAssignmentRule = rule.CreateRule(rule.Rule{
	Name: "no-unsafe-assignment",
	Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
		compilerOptions := ctx.Program.Options()
		isNoImplicitThis := utils.IsStrictCompilerOptionEnabled(
			compilerOptions,
			compilerOptions.NoImplicitThis,
		)

		var checkArrayDestructure func(
			receiverNode *ast.Node,
			senderType *checker.Type,
			senderNode *ast.Node,
		) bool
		var checkObjectDestructure func(
			receiverNode *ast.Node,
			senderType *checker.Type,
			senderNode *ast.Node,
		) bool

		checkObjectDestructure = func(
			receiverNode *ast.Node,
			senderType *checker.Type,
			senderNode *ast.Node,
		) bool {
			propertySymbols := checker.Checker_getPropertiesOfType(ctx.TypeChecker, senderType)
			if propertySymbols == nil {
				return false
			}
			properties := make(map[string]*checker.Type, len(propertySymbols))
			for _, property := range propertySymbols {
				properties[property.Name] = ctx.TypeChecker.GetTypeOfSymbolAtLocation(property, senderNode)
			}

			checkObjectProperty := func(propertyKey *ast.Node, propertyValue *ast.Node) bool {
				var key string
				if !ast.IsComputedPropertyName(propertyKey) {
					key = propertyKey.Text()
				} else if ast.IsLiteralExpression(propertyKey.Expression()) {
					key = propertyKey.Expression().Text()
				} else {

					return false
				}

				senderType, ok := properties[key]
				if !ok {
					return false
				}

				if utils.IsTypeAnyType(senderType) {

					ctx.ReportNode(propertyValue, buildUnsafeArrayPatternFromTupleMessage(senderType))
					return true
				} else if ast.IsArrayBindingPattern(propertyValue) || ast.IsArrayLiteralExpression(propertyValue) {
					return checkArrayDestructure(
						propertyValue,
						senderType,
						senderNode,
					)
				} else if ast.IsObjectBindingPattern(propertyValue) || ast.IsObjectLiteralExpression(propertyValue) {
					return checkObjectDestructure(
						propertyValue,
						senderType,
						senderNode,
					)
				}
				return false
			}

			didReport := false
			if ast.IsObjectLiteralExpression(receiverNode) {
				for _, receiverProperty := range receiverNode.AsObjectLiteralExpression().Properties.Nodes {
					if ast.IsSpreadAssignment(receiverProperty) {

						continue
					}

					if (ast.IsPropertyAssignment(receiverProperty) && checkObjectProperty(receiverProperty.Name(), receiverProperty.Initializer())) || (ast.IsShorthandPropertyAssignment(receiverProperty) && checkObjectProperty(receiverProperty.Name(), receiverProperty.Name())) {
						didReport = true
					}
				}
			} else if ast.IsObjectBindingPattern(receiverNode) {
				for _, receiverProperty := range receiverNode.AsBindingPattern().Elements.Nodes {
					property := receiverProperty.AsBindingElement()
					if property.DotDotDotToken != nil {

						continue
					}

					propertyKey := property.PropertyName
					if propertyKey == nil {
						propertyKey = property.Name()
					}

					if checkObjectProperty(propertyKey, property.Name()) {
						didReport = true
					}
				}
			}

			return didReport
		}

		checkObjectDestructureHelper := func(
			receiverNode *ast.Node,
			senderNode *ast.Node,
		) bool {
			if !ast.IsObjectBindingPattern(receiverNode) && !ast.IsObjectLiteralExpression(receiverNode) {
				return false
			}

			senderType := ctx.TypeChecker.GetTypeAtLocation(senderNode)

			return checkObjectDestructure(receiverNode, senderType, senderNode)
		}

		checkArrayDestructure = func(
			receiverNode *ast.Node,
			senderType *checker.Type,
			senderNode *ast.Node,
		) bool {

			if utils.IsTypeAnyArrayType(senderType, ctx.TypeChecker) {
				ctx.ReportNode(receiverNode, buildUnsafeArrayPatternMessage(senderType))
				return false
			}

			if !checker.IsTupleType(senderType) {
				return true
			}

			tupleElements := checker.Checker_getTypeArguments(ctx.TypeChecker, senderType)

			checkArrayElement := func(receiverElement *ast.Node, receiverIndex int) bool {
				if receiverElement == nil {
					return false
				}
				if receiverIndex >= len(tupleElements) {
					return false
				}
				senderType := tupleElements[receiverIndex]

				if utils.IsTypeAnyType(senderType) {
					ctx.ReportNode(receiverElement, buildUnsafeArrayPatternFromTupleMessage(senderType))
					return true
				} else if ast.IsArrayBindingPattern(receiverElement) || ast.IsArrayLiteralExpression(receiverElement) {
					return checkArrayDestructure(
						receiverElement,
						senderType,
						senderNode,
					)
				} else if ast.IsObjectBindingPattern(receiverElement) || ast.IsObjectLiteralExpression(receiverElement) {
					return checkObjectDestructure(
						receiverElement,
						senderType,
						senderNode,
					)
				}

				return false
			}

			didReport := false
			if ast.IsArrayLiteralExpression(receiverNode) {
				for receiverIndex, receiverElement := range receiverNode.AsArrayLiteralExpression().Elements.Nodes {
					if ast.IsSpreadElement(receiverElement) {

						continue
					}

					if checkArrayElement(receiverElement, receiverIndex) {
						didReport = true
					}
				}
			} else if ast.IsArrayBindingPattern(receiverNode) {
				for receiverIndex, receiverElement := range receiverNode.AsBindingPattern().Elements.Nodes {
					elem := receiverElement.AsBindingElement()
					if elem.DotDotDotToken != nil {

						continue
					}

					if checkArrayElement(receiverElement.Name(), receiverIndex) {

						didReport = true
					}
				}
			}

			return didReport
		}

		checkArrayDestructureHelper := func(
			receiverNode *ast.Node,
			senderNode *ast.Node,
		) bool {
			if !ast.IsArrayBindingPattern(receiverNode) && !ast.IsArrayLiteralExpression(receiverNode) {
				return false
			}

			senderType := ctx.TypeChecker.GetTypeAtLocation(senderNode)

			return checkArrayDestructure(receiverNode, senderType, senderNode)
		}

		checkAssignment := func(
			receiverNode *ast.Node,
			senderNode *ast.Node,
			reportingNode *ast.Node,
			compType comparisonType,
		) bool {
			var receiverType *checker.Type
			if compType == comparisonTypeContextual {
				receiverType = utils.GetContextualType(ctx.TypeChecker, receiverNode)
			}
			if receiverType == nil {
				receiverType = ctx.TypeChecker.GetTypeAtLocation(receiverNode)
			}
			senderType := ctx.TypeChecker.GetTypeAtLocation(senderNode)

			if utils.IsTypeAnyType(senderType) {

				if utils.IsTypeUnknownType(receiverType) {
					return false
				}

				if !isNoImplicitThis {

					thisExpression := utils.GetThisExpression(senderNode)
					if thisExpression != nil && utils.IsTypeAnyType(utils.GetConstrainedTypeAtLocation(ctx.TypeChecker, thisExpression)) {
						ctx.ReportNode(reportingNode, buildAnyAssignmentThisMessage(senderType))
						return true
					}
				}

				ctx.ReportNode(reportingNode, buildAnyAssignmentMessage(senderType))
				return true
			}

			if compType == comparisonTypeNone {
				return false
			}

			receiver, sender, unsafe := utils.IsUnsafeAssignment(
				senderType,
				receiverType,
				ctx.TypeChecker,
				senderNode,
			)
			if !unsafe {
				return false
			}

			ctx.ReportNode(reportingNode, buildUnsafeAssignmentMessage(ctx.TypeChecker, sender, receiver))
			return true
		}

		getComparisonType := func(
			nodeWithTypeAnnotation *ast.Node,
		) comparisonType {
			if nodeWithTypeAnnotation.Type() != nil {

				return comparisonTypeBasic
			}

			return comparisonTypeNone
		}

		checkAssignmentFull := func(id *ast.Node, init *ast.Node, node *ast.Node) {
			if id == nil || init == nil {
				return
			}
			didReport := checkAssignment(
				id,
				init,
				node,

				comparisonTypeBasic,
			)

			if !didReport {
				didReport = checkArrayDestructureHelper(id, init)
			}
			if !didReport {
				checkObjectDestructureHelper(id, init)
			}
		}

		return rule.RuleListeners{

			ast.KindPropertyDeclaration: func(node *ast.Node) {
				initializer := node.Initializer()
				if initializer == nil {
					return
				}
				checkAssignment(node.Name(), initializer, node, getComparisonType(node))
			},

			ast.KindBinaryExpression: func(node *ast.Node) {
				if !ast.IsAssignmentExpression(node, true) {
					return
				}

				expr := node.AsBinaryExpression()
				checkAssignmentFull(expr.Left, expr.Right, node)
			},

			ast.KindBindingElement: func(node *ast.Node) {
				checkAssignmentFull(node.Name(), node.Initializer(), node)
			},

			ast.KindParameter: func(node *ast.Node) {
				checkAssignmentFull(node.Name(), node.Initializer(), node)
			},

			ast.KindShorthandPropertyAssignment: func(node *ast.Node) {
				assignment := node.AsShorthandPropertyAssignment()
				checkAssignmentFull(assignment.Name(), assignment.ObjectAssignmentInitializer, node)
			},

			ast.KindVariableDeclaration: func(node *ast.Node) {
				init := node.Initializer()
				if init == nil {
					return
				}

				id := node.Name()
				didReport := checkAssignment(
					id,
					init,
					node,
					getComparisonType(node),
				)

				if !didReport {
					didReport = checkArrayDestructureHelper(id, init)
				}
				if !didReport {
					checkObjectDestructureHelper(id, init)
				}
			},

			rule.ListenerOnNotAllowPattern(ast.KindObjectLiteralExpression): func(node *ast.Node) {
				for _, node := range node.AsObjectLiteralExpression().Properties.Nodes {
					var init *ast.Node
					if ast.IsPropertyAssignment(node) {
						init = node.Initializer()
					} else if ast.IsShorthandPropertyAssignment(node) {
						init = node.Name()
					} else {
						continue
					}

					if init == nil {
						return
					}
					init = ast.SkipParentheses(init)

					if ast.IsAssignmentExpression(init, false) {

						return
					}

					checkAssignment(node.Name(), init, node, comparisonTypeContextual)
				}
			},

			rule.ListenerOnNotAllowPattern(ast.KindArrayLiteralExpression): func(node *ast.Node) {
				for _, node := range node.AsArrayLiteralExpression().Elements.Nodes {
					if !ast.IsSpreadElement(node) {
						continue
					}

					restType := ctx.TypeChecker.GetTypeAtLocation(node.Expression())
					if utils.IsTypeAnyType(restType) || utils.IsTypeAnyArrayType(restType, ctx.TypeChecker) {
						ctx.ReportNode(node, buildUnsafeArraySpreadMessage(restType))
					}
				}
			},

			ast.KindJsxAttribute: func(node *ast.Node) {
				init := node.Initializer()
				if init == nil || init.Kind != ast.KindJsxExpression {
					return
				}

				expr := init.AsJsxExpression().Expression
				if expr == nil {
					return
				}

				checkAssignment(node.Name(), expr, expr, comparisonTypeContextual)
			},
		}
	},
})

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