github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/deep-exit.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 7 "github.com/mgechev/revive/lint" 8 ) 9 10 // DeepExitRule lints program exit at functions other than main or init. 11 type DeepExitRule struct{} 12 13 // Apply applies the rule to given file. 14 func (r *DeepExitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 15 var failures []lint.Failure 16 onFailure := func(failure lint.Failure) { 17 failures = append(failures, failure) 18 } 19 20 var exitFunctions = map[string]map[string]bool{ 21 "os": {"Exit": true}, 22 "syscall": {"Exit": true}, 23 "log": { 24 "Fatal": true, 25 "Fatalf": true, 26 "Fatalln": true, 27 "Panic": true, 28 "Panicf": true, 29 "Panicln": true, 30 }, 31 } 32 33 w := lintDeepExit{onFailure, exitFunctions, file.IsTest()} 34 ast.Walk(w, file.AST) 35 return failures 36 } 37 38 // Name returns the rule name. 39 func (r *DeepExitRule) Name() string { 40 return "deep-exit" 41 } 42 43 type lintDeepExit struct { 44 onFailure func(lint.Failure) 45 exitFunctions map[string]map[string]bool 46 isTestFile bool 47 } 48 49 func (w lintDeepExit) Visit(node ast.Node) ast.Visitor { 50 if fd, ok := node.(*ast.FuncDecl); ok { 51 if w.mustIgnore(fd) { 52 return nil // skip analysis of this function 53 } 54 55 return w 56 } 57 58 se, ok := node.(*ast.ExprStmt) 59 if !ok { 60 return w 61 } 62 ce, ok := se.X.(*ast.CallExpr) 63 if !ok { 64 return w 65 } 66 67 fc, ok := ce.Fun.(*ast.SelectorExpr) 68 if !ok { 69 return w 70 } 71 id, ok := fc.X.(*ast.Ident) 72 if !ok { 73 return w 74 } 75 76 fn := fc.Sel.Name 77 pkg := id.Name 78 if w.exitFunctions[pkg] != nil && w.exitFunctions[pkg][fn] { // it's a call to an exit function 79 w.onFailure(lint.Failure{ 80 Confidence: 1, 81 Node: ce, 82 Category: "bad practice", 83 Failure: fmt.Sprintf("calls to %s.%s only in main() or init() functions", pkg, fn), 84 }) 85 } 86 87 return w 88 } 89 90 func (w *lintDeepExit) mustIgnore(fd *ast.FuncDecl) bool { 91 fn := fd.Name.Name 92 93 return fn == "init" || fn == "main" || (w.isTestFile && fn == "TestMain") 94 }