github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/analysis/passes/errorsas/errorsas.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // The errorsas package defines an Analyzer that checks that the second argument to 6 // errors.As is a pointer to a type implementing error. 7 package errorsas 8 9 import ( 10 "errors" 11 "go/ast" 12 "go/types" 13 14 "golang.org/x/tools/go/analysis" 15 "golang.org/x/tools/go/analysis/passes/inspect" 16 "golang.org/x/tools/go/ast/inspector" 17 "golang.org/x/tools/go/types/typeutil" 18 ) 19 20 const Doc = `report passing non-pointer or non-error values to errors.As 21 22 The errorsas analysis reports calls to errors.As where the type 23 of the second argument is not a pointer to a type implementing error.` 24 25 var Analyzer = &analysis.Analyzer{ 26 Name: "errorsas", 27 Doc: Doc, 28 Requires: []*analysis.Analyzer{inspect.Analyzer}, 29 Run: run, 30 } 31 32 func run(pass *analysis.Pass) (interface{}, error) { 33 switch pass.Pkg.Path() { 34 case "errors", "errors_test": 35 // These packages know how to use their own APIs. 36 // Sometimes they are testing what happens to incorrect programs. 37 return nil, nil 38 } 39 40 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 41 42 nodeFilter := []ast.Node{ 43 (*ast.CallExpr)(nil), 44 } 45 inspect.Preorder(nodeFilter, func(n ast.Node) { 46 call := n.(*ast.CallExpr) 47 fn := typeutil.StaticCallee(pass.TypesInfo, call) 48 if fn == nil { 49 return // not a static call 50 } 51 if len(call.Args) < 2 { 52 return // not enough arguments, e.g. called with return values of another function 53 } 54 if fn.FullName() != "errors.As" { 55 return 56 } 57 if err := checkAsTarget(pass, call.Args[1]); err != nil { 58 pass.ReportRangef(call, "%v", err) 59 } 60 }) 61 return nil, nil 62 } 63 64 var errorType = types.Universe.Lookup("error").Type() 65 66 // pointerToInterfaceOrError reports whether the type of e is a pointer to an interface or a type implementing error, 67 // or is the empty interface. 68 69 // checkAsTarget reports an error if the second argument to errors.As is invalid. 70 func checkAsTarget(pass *analysis.Pass, e ast.Expr) error { 71 t := pass.TypesInfo.Types[e].Type 72 if it, ok := t.Underlying().(*types.Interface); ok && it.NumMethods() == 0 { 73 // A target of interface{} is always allowed, since it often indicates 74 // a value forwarded from another source. 75 return nil 76 } 77 pt, ok := t.Underlying().(*types.Pointer) 78 if !ok { 79 return errors.New("second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type") 80 } 81 if pt.Elem() == errorType { 82 return errors.New("second argument to errors.As should not be *error") 83 } 84 _, ok = pt.Elem().Underlying().(*types.Interface) 85 if ok || types.Implements(pt.Elem(), errorType.Underlying().(*types.Interface)) { 86 return nil 87 } 88 return errors.New("second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type") 89 }