woodpecker/vendor/github.com/ultraware/funlen/main.go
Lukas c28f7cb29f
Add golangci-lint (#502)
Initial part of #435
2021-11-14 21:01:54 +01:00

104 lines
2.5 KiB
Go

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, stmtLimit int) []Message {
if lineLimit == 0 {
lineLimit = defaultLineLimit
}
if stmtLimit == 0 {
stmtLimit = defaultStmtLimit
}
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); 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) int { // nolint: interfacer
return fset.Position(f.End()).Line - fset.Position(f.Pos()).Line - 1
}
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))
}