no_extra_bind

package
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: May 14, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var NoExtraBindRule = rule.Rule{
	Name: "no-extra-bind",
	Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
		type scopeInfo struct {
			match     *bindMatch
			thisFound bool
			upper     *scopeInfo
		}

		var scope *scopeInfo

		getBindMatch := func(funcNode *ast.Node) *bindMatch {
			current := funcNode.Parent
			for current != nil && current.Kind == ast.KindParenthesizedExpression {
				current = current.Parent
			}
			if current == nil {
				return nil
			}

			var memberNode *ast.Node
			switch current.Kind {
			case ast.KindPropertyAccessExpression:
				propAccess := current.AsPropertyAccessExpression()
				if propAccess == nil || propAccess.Name() == nil {
					return nil
				}
				if propAccess.Name().Text() != "bind" {
					return nil
				}
				memberNode = current
			case ast.KindElementAccessExpression:
				elemAccess := current.AsElementAccessExpression()
				if elemAccess == nil || elemAccess.ArgumentExpression == nil {
					return nil
				}
				arg := elemAccess.ArgumentExpression
				if arg.Kind == ast.KindStringLiteral && arg.Text() == "bind" {
					memberNode = current
				} else if arg.Kind == ast.KindNoSubstitutionTemplateLiteral && arg.Text() == "bind" {
					memberNode = current
				} else {
					return nil
				}
			default:
				return nil
			}

			callParent := memberNode.Parent
			for callParent != nil && callParent.Kind == ast.KindParenthesizedExpression {
				callParent = callParent.Parent
			}
			if callParent == nil || callParent.Kind != ast.KindCallExpression {
				return nil
			}

			callExpr := callParent.AsCallExpression()
			if callExpr == nil {
				return nil
			}

			callee := callExpr.Expression
			for callee != nil && callee.Kind == ast.KindParenthesizedExpression {
				callee = callee.AsParenthesizedExpression().Expression
			}
			if callee != memberNode {
				return nil
			}

			argCount := 0
			if callExpr.Arguments != nil {
				argCount = len(callExpr.Arguments.Nodes)
			}
			if argCount != 1 {
				return nil
			}

			firstArg := callExpr.Arguments.Nodes[0]
			if firstArg.Kind == ast.KindSpreadElement {
				return nil
			}

			return &bindMatch{callNode: callParent, memberNode: memberNode}
		}

		msg := rule.RuleMessage{
			Id:          "unexpected",
			Description: "The function binding is unnecessary.",
		}

		buildFixes := func(m *bindMatch) []rule.RuleFix {
			callExpr := m.callNode.AsCallExpression()
			firstArg := callExpr.Arguments.Nodes[0]
			if !isSideEffectFree(firstArg) {
				return nil
			}

			// The expression to keep (left side of the member access)
			var keepExpr *ast.Node
			switch m.memberNode.Kind {
			case ast.KindPropertyAccessExpression:
				keepExpr = m.memberNode.AsPropertyAccessExpression().Expression
			case ast.KindElementAccessExpression:
				keepExpr = m.memberNode.AsElementAccessExpression().Expression
			}
			if keepExpr == nil {
				return nil
			}

			sourceText := ctx.SourceFile.Text()
			tokenStart := scanner.SkipTrivia(sourceText, keepExpr.End())

			fix1 := rule.RuleFixRemoveRange(core.NewTextRange(tokenStart, m.memberNode.End()))

			fix2 := rule.RuleFixRemoveRange(core.NewTextRange(callExpr.Expression.End(), m.callNode.End()))
			return []rule.RuleFix{fix1, fix2}
		}

		report := func(m *bindMatch) {
			if fixes := buildFixes(m); len(fixes) > 0 {
				ctx.ReportNodeWithFixes(m.callNode, msg, fixes...)
			} else {
				ctx.ReportNode(m.callNode, msg)
			}
		}

		enterFunction := func(node *ast.Node) {
			scope = &scopeInfo{
				match:     getBindMatch(node),
				thisFound: false,
				upper:     scope,
			}
		}

		exitFunction := func(node *ast.Node) {
			if scope != nil {
				if scope.match != nil && !scope.thisFound {
					report(scope.match)
				}
				scope = scope.upper
			}
		}

		enterThisScope := func(node *ast.Node) {
			scope = &scopeInfo{
				match:     nil,
				thisFound: false,
				upper:     scope,
			}
		}

		exitThisScope := func(node *ast.Node) {
			if scope != nil {
				scope = scope.upper
			}
		}

		return rule.RuleListeners{
			ast.KindFunctionExpression:                       enterFunction,
			rule.ListenerOnExit(ast.KindFunctionExpression):  exitFunction,
			ast.KindFunctionDeclaration:                      enterFunction,
			rule.ListenerOnExit(ast.KindFunctionDeclaration): exitFunction,

			ast.KindMethodDeclaration:                      enterThisScope,
			rule.ListenerOnExit(ast.KindMethodDeclaration): exitThisScope,
			ast.KindGetAccessor:                            enterThisScope,
			rule.ListenerOnExit(ast.KindGetAccessor):       exitThisScope,
			ast.KindSetAccessor:                            enterThisScope,
			rule.ListenerOnExit(ast.KindSetAccessor):       exitThisScope,
			ast.KindConstructor:                            enterThisScope,
			rule.ListenerOnExit(ast.KindConstructor):       exitThisScope,

			ast.KindArrowFunction: func(node *ast.Node) {
				if m := getBindMatch(node); m != nil {
					report(m)
				}
			},

			ast.KindThisKeyword: func(node *ast.Node) {
				if scope == nil {
					return
				}
				target := scope
				if isThisInComputedPropertyName(node) && scope.upper != nil {
					target = scope.upper
				}
				target.thisFound = true
			},
		}
	},
}

https://eslint.org/docs/latest/rules/no-extra-bind

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