1package checkers 2 3import ( 4 "go/ast" 5 6 "github.com/go-critic/go-critic/checkers/internal/astwalk" 7 "github.com/go-critic/go-critic/framework/linter" 8) 9 10func init() { 11 var info linter.CheckerInfo 12 info.Name = "nestingReduce" 13 info.Tags = []string{"style", "opinionated", "experimental"} 14 info.Params = linter.CheckerParams{ 15 "bodyWidth": { 16 Value: 5, 17 Usage: "min number of statements inside a branch to trigger a warning", 18 }, 19 } 20 info.Summary = "Finds where nesting level could be reduced" 21 info.Before = ` 22for _, v := range a { 23 if v.Bool { 24 body() 25 } 26}` 27 info.After = ` 28for _, v := range a { 29 if !v.Bool { 30 continue 31 } 32 body() 33}` 34 35 collection.AddChecker(&info, func(ctx *linter.CheckerContext) (linter.FileWalker, error) { 36 c := &nestingReduceChecker{ctx: ctx} 37 c.bodyWidth = info.Params.Int("bodyWidth") 38 return astwalk.WalkerForStmt(c), nil 39 }) 40} 41 42type nestingReduceChecker struct { 43 astwalk.WalkHandler 44 ctx *linter.CheckerContext 45 46 bodyWidth int 47} 48 49func (c *nestingReduceChecker) VisitStmt(stmt ast.Stmt) { 50 switch stmt := stmt.(type) { 51 case *ast.ForStmt: 52 c.checkLoopBody(stmt.Body.List) 53 case *ast.RangeStmt: 54 c.checkLoopBody(stmt.Body.List) 55 } 56} 57 58func (c *nestingReduceChecker) checkLoopBody(body []ast.Stmt) { 59 if len(body) != 1 { 60 return 61 } 62 stmt, ok := body[0].(*ast.IfStmt) 63 if !ok { 64 return 65 } 66 if len(stmt.Body.List) >= c.bodyWidth && stmt.Else == nil { 67 c.warnLoop(stmt) 68 } 69} 70 71func (c *nestingReduceChecker) warnLoop(cause ast.Node) { 72 c.ctx.Warn(cause, "invert if cond, replace body with `continue`, move old body after the statement") 73} 74