complexity

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

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ComplexityRule = rule.Rule{
	Name: "complexity",
	Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
		threshold, isModified := parseOptions(options)

		counters := map[*ast.Node]int{}

		findOwner := func(n *ast.Node) *ast.Node {
			cur := n.Parent
			prev := n
			for cur != nil {
				if ast.IsFunctionLikeDeclaration(cur) {
					return cur
				}
				if cur.Kind == ast.KindClassStaticBlockDeclaration {
					return cur
				}
				if cur.Kind == ast.KindPropertyDeclaration {
					propDecl := cur.AsPropertyDeclaration()
					if propDecl != nil &&
						propDecl.Initializer != nil &&
						propDecl.Initializer == prev &&
						!ast.IsFunctionLikeDeclaration(propDecl.Initializer) {
						return cur
					}
				}
				prev = cur
				cur = cur.Parent
			}
			return nil
		}

		increment := func(node *ast.Node) {
			owner := findOwner(node)
			if owner == nil {
				return
			}
			counters[owner]++
		}

		startFunc := func(node *ast.Node) {
			counters[node] = 1
		}
		endFunc := func(node *ast.Node) {
			complexity, ok := counters[node]
			if !ok {
				return
			}
			delete(counters, node)
			if complexity > threshold {
				name := utils.UpperCaseFirstASCII(getFunctionNameWithKind(node))
				loc := utils.GetFunctionHeadLoc(ctx.SourceFile, node)
				ctx.ReportRange(loc, makeMessage(name, complexity, threshold))
			}
		}

		startStaticBlock := func(node *ast.Node) {
			counters[node] = 1
		}
		endStaticBlock := func(node *ast.Node) {
			complexity, ok := counters[node]
			if !ok {
				return
			}
			delete(counters, node)
			if complexity > threshold {

				trimmedStart := utils.TrimNodeTextRange(ctx.SourceFile, node).Pos()
				tokenRange := scanner.GetRangeOfTokenAtPosition(ctx.SourceFile, trimmedStart)
				ctx.ReportRange(tokenRange, makeMessage("Class static block", complexity, threshold))
			}
		}

		startProp := func(node *ast.Node) {
			propDecl := node.AsPropertyDeclaration()
			if propDecl == nil || propDecl.Initializer == nil ||
				ast.IsFunctionLikeDeclaration(propDecl.Initializer) {
				return
			}
			counters[node] = 1
		}
		endProp := func(node *ast.Node) {
			complexity, ok := counters[node]
			if !ok {
				return
			}
			delete(counters, node)
			if complexity > threshold {
				propDecl := node.AsPropertyDeclaration()
				if propDecl == nil || propDecl.Initializer == nil {
					return
				}
				loc := utils.TrimNodeTextRange(ctx.SourceFile, propDecl.Initializer)
				ctx.ReportRange(loc, makeMessage("Class field initializer", complexity, threshold))
			}
		}

		return rule.RuleListeners{
			ast.KindFunctionDeclaration:                              startFunc,
			rule.ListenerOnExit(ast.KindFunctionDeclaration):         endFunc,
			ast.KindFunctionExpression:                               startFunc,
			rule.ListenerOnExit(ast.KindFunctionExpression):          endFunc,
			ast.KindArrowFunction:                                    startFunc,
			rule.ListenerOnExit(ast.KindArrowFunction):               endFunc,
			ast.KindMethodDeclaration:                                startFunc,
			rule.ListenerOnExit(ast.KindMethodDeclaration):           endFunc,
			ast.KindGetAccessor:                                      startFunc,
			rule.ListenerOnExit(ast.KindGetAccessor):                 endFunc,
			ast.KindSetAccessor:                                      startFunc,
			rule.ListenerOnExit(ast.KindSetAccessor):                 endFunc,
			ast.KindConstructor:                                      startFunc,
			rule.ListenerOnExit(ast.KindConstructor):                 endFunc,
			ast.KindClassStaticBlockDeclaration:                      startStaticBlock,
			rule.ListenerOnExit(ast.KindClassStaticBlockDeclaration): endStaticBlock,
			ast.KindPropertyDeclaration:                              startProp,
			rule.ListenerOnExit(ast.KindPropertyDeclaration):         endProp,

			ast.KindCatchClause:           increment,
			ast.KindConditionalExpression: increment,
			ast.KindIfStatement:           increment,
			ast.KindWhileStatement:        increment,
			ast.KindDoStatement:           increment,
			ast.KindForStatement:          increment,
			ast.KindForInStatement:        increment,
			ast.KindForOfStatement:        increment,

			ast.KindBinaryExpression: func(node *ast.Node) {
				bin := node.AsBinaryExpression()
				if bin == nil || bin.OperatorToken == nil {
					return
				}
				switch bin.OperatorToken.Kind {
				case ast.KindAmpersandAmpersandToken,
					ast.KindBarBarToken,
					ast.KindQuestionQuestionToken,
					ast.KindAmpersandAmpersandEqualsToken,
					ast.KindBarBarEqualsToken,
					ast.KindQuestionQuestionEqualsToken:
					increment(node)
				}
			},

			ast.KindCaseClause: func(node *ast.Node) {
				if !isModified {
					increment(node)
				}
			},

			ast.KindSwitchStatement: func(node *ast.Node) {
				if isModified {
					increment(node)
				}
			},

			ast.KindPropertyAccessExpression: func(node *ast.Node) {
				pae := node.AsPropertyAccessExpression()
				if pae != nil && pae.QuestionDotToken != nil {
					increment(node)
				}
			},
			ast.KindElementAccessExpression: func(node *ast.Node) {
				eae := node.AsElementAccessExpression()
				if eae != nil && eae.QuestionDotToken != nil {
					increment(node)
				}
			},
			ast.KindCallExpression: func(node *ast.Node) {
				ce := node.AsCallExpression()
				if ce != nil && ce.QuestionDotToken != nil {
					increment(node)
				}
			},

			ast.KindParameter: func(node *ast.Node) {
				pd := node.AsParameterDeclaration()
				if pd != nil && pd.Initializer != nil {
					increment(node)
				}
			},
			ast.KindBindingElement: func(node *ast.Node) {
				be := node.AsBindingElement()
				if be != nil && be.Initializer != nil {
					increment(node)
				}
			},
		}
	},
}

ComplexityRule enforces a maximum cyclomatic complexity allowed in a program. https://eslint.org/docs/latest/rules/complexity

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