返回 DeepSeek-Reasonix
complexity.go
根目录 / tools / repolint / complexity.go
1 package main
2
3 import (
4 "fmt"
5 "go/ast"
6 "go/token"
7 )
8
9 const (
10 maxFuncLines = 120
11 maxComplexity = 30
12 )
13
14 func checkComplexity(s *sourceFile) []Finding {
15 if s.isTest() {
16 return nil
17 }
18 var out []Finding
19 for _, decl := range s.file.Decls {
20 fn, ok := decl.(*ast.FuncDecl)
21 if !ok || fn.Body == nil {
22 continue
23 }
24 line := s.line(fn.Pos())
25 if n := s.line(fn.Body.Rbrace) - s.line(fn.Body.Lbrace); n > maxFuncLines {
26 out = append(out, Finding{s.rel, line, ruleFuncSize,
27 fmt.Sprintf("%s is %d lines, over the %d-line limit", funcName(fn), n, maxFuncLines), n - maxFuncLines})
28 }
29 if c := cyclomatic(fn.Body); c > maxComplexity {
30 out = append(out, Finding{s.rel, line, ruleComplexity,
31 fmt.Sprintf("%s has cyclomatic complexity %d, over %d", funcName(fn), c, maxComplexity), c - maxComplexity})
32 }
33 }
34 return out
35 }
36
37 func funcName(fn *ast.FuncDecl) string {
38 if fn.Recv == nil || len(fn.Recv.List) == 0 {
39 return fn.Name.Name
40 }
41 return receiverName(fn.Recv.List[0].Type) + "." + fn.Name.Name
42 }
43
44 func receiverName(expr ast.Expr) string {
45 switch t := expr.(type) {
46 case *ast.StarExpr:
47 return receiverName(t.X)
48 case *ast.IndexExpr:
49 return receiverName(t.X)
50 case *ast.IndexListExpr:
51 return receiverName(t.X)
52 case *ast.Ident:
53 return t.Name
54 }
55 return "?"
56 }
57
58 // One plus every branch point, the standard cyclomatic count. A bare `default`
59 // adds no branch, so only case clauses that actually match are counted.
60 func cyclomatic(body *ast.BlockStmt) int {
61 count := 1
62 ast.Inspect(body, func(n ast.Node) bool {
63 switch node := n.(type) {
64 case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt:
65 count++
66 case *ast.CaseClause:
67 if len(node.List) > 0 {
68 count++
69 }
70 case *ast.CommClause:
71 if node.Comm != nil {
72 count++
73 }
74 case *ast.BinaryExpr:
75 if node.Op == token.LAND || node.Op == token.LOR {
76 count++
77 }
78 }
79 return true
80 })
81 return count
82 }
83
83 lines GO