github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/unhandled-error.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/types" 7 8 "github.com/mgechev/revive/lint" 9 ) 10 11 // UnhandledErrorRule lints given else constructs. 12 type UnhandledErrorRule struct{} 13 14 type ignoreListType map[string]struct{} 15 16 // Apply applies the rule to given file. 17 func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { 18 var failures []lint.Failure 19 20 ignoreList := make(ignoreListType, len(args)) 21 22 for _, arg := range args { 23 argStr, ok := arg.(string) 24 if !ok { 25 panic(fmt.Sprintf("Invalid argument to the unhandled-error rule. Expecting a string, got %T", arg)) 26 } 27 28 ignoreList[argStr] = struct{}{} 29 } 30 31 walker := &lintUnhandledErrors{ 32 ignoreList: ignoreList, 33 pkg: file.Pkg, 34 onFailure: func(failure lint.Failure) { 35 failures = append(failures, failure) 36 }, 37 } 38 39 file.Pkg.TypeCheck() 40 ast.Walk(walker, file.AST) 41 42 return failures 43 } 44 45 // Name returns the rule name. 46 func (r *UnhandledErrorRule) Name() string { 47 return "unhandled-error" 48 } 49 50 type lintUnhandledErrors struct { 51 ignoreList ignoreListType 52 pkg *lint.Package 53 onFailure func(lint.Failure) 54 } 55 56 // Visit looks for statements that are function calls. 57 // If the called function returns a value of type error a failure will be created. 58 func (w *lintUnhandledErrors) Visit(node ast.Node) ast.Visitor { 59 switch n := node.(type) { 60 case *ast.ExprStmt: 61 fCall, ok := n.X.(*ast.CallExpr) 62 if !ok { 63 return nil // not a function call 64 } 65 66 funcType := w.pkg.TypeOf(fCall) 67 if funcType == nil { 68 return nil // skip, type info not available 69 } 70 71 switch t := funcType.(type) { 72 case *types.Named: 73 if !w.isTypeError(t) { 74 return nil // func call does not return an error 75 } 76 77 w.addFailure(fCall) 78 default: 79 retTypes, ok := funcType.Underlying().(*types.Tuple) 80 if !ok { 81 return nil // skip, unable to retrieve return type of the called function 82 } 83 84 if w.returnsAnError(retTypes) { 85 w.addFailure(fCall) 86 } 87 } 88 } 89 return w 90 } 91 92 func (w *lintUnhandledErrors) addFailure(n *ast.CallExpr) { 93 funcName := gofmt(n.Fun) 94 if _, mustIgnore := w.ignoreList[funcName]; mustIgnore { 95 return 96 } 97 98 w.onFailure(lint.Failure{ 99 Category: "bad practice", 100 Confidence: 1, 101 Node: n, 102 Failure: fmt.Sprintf("Unhandled error in call to function %v", funcName), 103 }) 104 } 105 106 func (*lintUnhandledErrors) isTypeError(t *types.Named) bool { 107 const errorTypeName = "_.error" 108 109 return t.Obj().Id() == errorTypeName 110 } 111 112 func (w *lintUnhandledErrors) returnsAnError(tt *types.Tuple) bool { 113 for i := 0; i < tt.Len(); i++ { 114 nt, ok := tt.At(i).Type().(*types.Named) 115 if ok && w.isTypeError(nt) { 116 return true 117 } 118 } 119 return false 120 }