1
0
mirror of https://github.com/mgechev/revive.git synced 2024-12-04 10:24:49 +02:00
revive/rule/unreachable-code.go
Abirdcfly fcc59adb72
add testing.FailNow and related function to unreachable check (#711)
Signed-off-by: Abirdcfly <fp544037857@gmail.com>
2022-07-20 15:41:31 -07:00

123 lines
2.5 KiB
Go

package rule
import (
"go/ast"
"github.com/mgechev/revive/lint"
)
// UnreachableCodeRule lints unreachable code.
type UnreachableCodeRule struct{}
// Apply applies the rule to given file.
func (*UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
var failures []lint.Failure
onFailure := func(failure lint.Failure) {
failures = append(failures, failure)
}
testingFunctions := map[string]bool{
"Fatal": true,
"Fatalf": true,
"FailNow": true,
}
branchingFunctions := map[string]map[string]bool{
"os": {"Exit": true},
"log": {
"Fatal": true,
"Fatalf": true,
"Fatalln": true,
"Panic": true,
"Panicf": true,
"Panicln": true,
},
"t": testingFunctions,
"b": testingFunctions,
"f": testingFunctions,
}
w := lintUnreachableCode{onFailure, branchingFunctions}
ast.Walk(w, file.AST)
return failures
}
// Name returns the rule name.
func (*UnreachableCodeRule) Name() string {
return "unreachable-code"
}
type lintUnreachableCode struct {
onFailure func(lint.Failure)
branchingFunctions map[string]map[string]bool
}
func (w lintUnreachableCode) Visit(node ast.Node) ast.Visitor {
blk, ok := node.(*ast.BlockStmt)
if !ok {
return w
}
if len(blk.List) < 2 {
return w
}
loop:
for i, stmt := range blk.List[:len(blk.List)-1] {
// println("iterating ", len(blk.List))
next := blk.List[i+1]
if _, ok := next.(*ast.LabeledStmt); ok {
continue // skip if next statement is labeled
}
switch s := stmt.(type) {
case *ast.ReturnStmt:
w.onFailure(newUnreachableCodeFailure(s))
break loop
case *ast.BranchStmt:
token := s.Tok.String()
if token != "fallthrough" {
w.onFailure(newUnreachableCodeFailure(s))
break loop
}
case *ast.ExprStmt:
ce, ok := s.X.(*ast.CallExpr)
if !ok {
continue
}
// it's a function call
fc, ok := ce.Fun.(*ast.SelectorExpr)
if !ok {
continue
}
id, ok := fc.X.(*ast.Ident)
if !ok {
continue
}
fn := fc.Sel.Name
pkg := id.Name
if !w.branchingFunctions[pkg][fn] { // it isn't a call to a branching function
continue
}
if _, ok := next.(*ast.ReturnStmt); ok { // return statement needed to satisfy function signature
continue
}
w.onFailure(newUnreachableCodeFailure(s))
break loop
}
}
return w
}
func newUnreachableCodeFailure(node ast.Node) lint.Failure {
return lint.Failure{
Confidence: 1,
Node: node,
Category: "logic",
Failure: "unreachable code after this statement",
}
}