mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-01-05 23:28:42 +00:00
c28f7cb29f
Initial part of #435
104 lines
2.5 KiB
Go
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))
|
|
}
|