-
Notifications
You must be signed in to change notification settings - Fork 8
/
main.go
124 lines (105 loc) · 3.06 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package funlen
import (
"fmt"
"go/ast"
"go/token"
"reflect"
)
const (
defaultLineLimit = 60
defaultStmtLimit = 40
)
// Run runs this linter on the provided code
func Run(file *ast.File, fset *token.FileSet, lineLimit int, stmtLimit int, ignoreComments bool) []Message {
if lineLimit == 0 {
lineLimit = defaultLineLimit
}
if stmtLimit == 0 {
stmtLimit = defaultStmtLimit
}
cmap := ast.NewCommentMap(fset, file, file.Comments)
var msgs []Message
for _, f := range file.Decls {
decl, ok := f.(*ast.FuncDecl)
if !ok || decl.Body == nil { // decl.Body can be nil for e.g. cgo
continue
}
if stmtLimit > 0 {
if stmts := parseStmts(decl.Body.List); stmts > stmtLimit {
msgs = append(msgs, makeStmtMessage(fset, decl.Name, stmts, stmtLimit))
continue
}
}
if lineLimit > 0 {
if lines := getLines(fset, decl, cmap.Filter(decl), ignoreComments); lines > lineLimit {
msgs = append(msgs, makeLineMessage(fset, decl.Name, lines, lineLimit))
}
}
}
return msgs
}
// Message contains a message
type Message struct {
Pos token.Position
Message string
}
func makeLineMessage(fset *token.FileSet, funcInfo *ast.Ident, lines, lineLimit int) Message {
return Message{
fset.Position(funcInfo.Pos()),
fmt.Sprintf("Function '%s' is too long (%d > %d)\n", funcInfo.Name, lines, lineLimit),
}
}
func makeStmtMessage(fset *token.FileSet, funcInfo *ast.Ident, stmts, stmtLimit int) Message {
return Message{
fset.Position(funcInfo.Pos()),
fmt.Sprintf("Function '%s' has too many statements (%d > %d)\n", funcInfo.Name, stmts, stmtLimit),
}
}
func getLines(fset *token.FileSet, f *ast.FuncDecl, cmap ast.CommentMap, ignoreComments bool) int { // nolint: interfacer
var lineCount int
var commentCount int
lineCount = fset.Position(f.End()).Line - fset.Position(f.Pos()).Line - 1
if !ignoreComments {
return lineCount
}
for _, c := range cmap.Comments() {
// If the CommenGroup's lines are inside the function
// count how many comments are in the CommentGroup
if (fset.Position(c.Pos()).Line > fset.Position(f.Pos()).Line) &&
(fset.Position(c.End()).Line < fset.Position(f.End()).Line) {
commentCount += len(c.List)
}
}
return lineCount - commentCount
}
func parseStmts(s []ast.Stmt) (total int) {
for _, v := range s {
total++
switch stmt := v.(type) {
case *ast.BlockStmt:
total += parseStmts(stmt.List) - 1
case *ast.ForStmt, *ast.RangeStmt, *ast.IfStmt,
*ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt:
total += parseBodyListStmts(stmt)
case *ast.CaseClause:
total += parseStmts(stmt.Body)
case *ast.AssignStmt:
total += checkInlineFunc(stmt.Rhs[0])
case *ast.GoStmt:
total += checkInlineFunc(stmt.Call.Fun)
case *ast.DeferStmt:
total += checkInlineFunc(stmt.Call.Fun)
}
}
return
}
func checkInlineFunc(stmt ast.Expr) int {
if block, ok := stmt.(*ast.FuncLit); ok {
return parseStmts(block.Body.List)
}
return 0
}
func parseBodyListStmts(t interface{}) int {
i := reflect.ValueOf(t).Elem().FieldByName(`Body`).Elem().FieldByName(`List`).Interface()
return parseStmts(i.([]ast.Stmt))
}