package rule import ( "go/ast" "go/token" "github.com/mgechev/revive/lint" ) // UselessBreak lint rule. type UselessBreak struct{} // Apply applies the rule to given file. func (*UselessBreak) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) } astFile := file.AST w := &lintUselessBreak{onFailure, false} ast.Walk(w, astFile) return failures } // Name returns the rule name. func (*UselessBreak) Name() string { return "useless-break" } type lintUselessBreak struct { onFailure func(lint.Failure) inLoopBody bool } func (w *lintUselessBreak) Visit(node ast.Node) ast.Visitor { switch v := node.(type) { case *ast.ForStmt: w.inLoopBody = true ast.Walk(w, v.Body) w.inLoopBody = false return nil case *ast.RangeStmt: w.inLoopBody = true ast.Walk(w, v.Body) w.inLoopBody = false return nil case *ast.CommClause: for _, n := range v.Body { w.inspectCaseStatement(n) } return nil case *ast.CaseClause: for _, n := range v.Body { w.inspectCaseStatement(n) } return nil } return w } func (w *lintUselessBreak) inspectCaseStatement(n ast.Stmt) { switch s := n.(type) { case *ast.BranchStmt: if s.Tok != token.BREAK { return // not a break statement } if s.Label != nil { return // labeled break statement, usually affects a nesting loop } msg := "useless break in case clause" if w.inLoopBody { msg += " (WARN: this break statement affects this switch or select statement and not the loop enclosing it)" } w.onFailure(lint.Failure{ Confidence: 1, Node: s, Failure: msg, }) } }