github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/unreachable-code.go (about) 1 package rule 2 3 import ( 4 "go/ast" 5 6 "github.com/mgechev/revive/lint" 7 ) 8 9 // UnreachableCodeRule lints unreachable code. 10 type UnreachableCodeRule struct{} 11 12 // Apply applies the rule to given file. 13 func (r *UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 14 var failures []lint.Failure 15 onFailure := func(failure lint.Failure) { 16 failures = append(failures, failure) 17 } 18 19 var branchingFunctions = map[string]map[string]bool{ 20 "os": {"Exit": true}, 21 "log": { 22 "Fatal": true, 23 "Fatalf": true, 24 "Fatalln": true, 25 "Panic": true, 26 "Panicf": true, 27 "Panicln": true, 28 }, 29 } 30 31 w := lintUnreachableCode{onFailure, branchingFunctions} 32 ast.Walk(w, file.AST) 33 return failures 34 } 35 36 // Name returns the rule name. 37 func (r *UnreachableCodeRule) Name() string { 38 return "unreachable-code" 39 } 40 41 type lintUnreachableCode struct { 42 onFailure func(lint.Failure) 43 branchingFunctions map[string]map[string]bool 44 } 45 46 func (w lintUnreachableCode) Visit(node ast.Node) ast.Visitor { 47 blk, ok := node.(*ast.BlockStmt) 48 if !ok { 49 return w 50 } 51 52 if len(blk.List) < 2 { 53 return w 54 } 55 loop: 56 for i, stmt := range blk.List[:len(blk.List)-1] { 57 // println("iterating ", len(blk.List)) 58 next := blk.List[i+1] 59 if _, ok := next.(*ast.LabeledStmt); ok { 60 continue // skip if next statement is labeled 61 } 62 63 switch s := stmt.(type) { 64 case *ast.ReturnStmt: 65 w.onFailure(newUnreachableCodeFailure(s)) 66 break loop 67 case *ast.BranchStmt: 68 token := s.Tok.String() 69 if token != "fallthrough" { 70 w.onFailure(newUnreachableCodeFailure(s)) 71 break loop 72 } 73 case *ast.ExprStmt: 74 ce, ok := s.X.(*ast.CallExpr) 75 if !ok { 76 continue 77 } 78 // it's a function call 79 fc, ok := ce.Fun.(*ast.SelectorExpr) 80 if !ok { 81 continue 82 } 83 84 id, ok := fc.X.(*ast.Ident) 85 86 if !ok { 87 continue 88 } 89 fn := fc.Sel.Name 90 pkg := id.Name 91 if !w.branchingFunctions[pkg][fn] { // it isn't a call to a branching function 92 continue 93 } 94 95 if _, ok := next.(*ast.ReturnStmt); ok { // return statement needed to satisfy function signature 96 continue 97 } 98 99 w.onFailure(newUnreachableCodeFailure(s)) 100 break loop 101 } 102 } 103 104 return w 105 } 106 107 func newUnreachableCodeFailure(node ast.Node) lint.Failure { 108 return lint.Failure{ 109 Confidence: 1, 110 Node: node, 111 Category: "logic", 112 Failure: "unreachable code after this statement", 113 } 114 }