golang.org/x/tools@v0.21.0/go/analysis/passes/deepequalerrors/deepequalerrors.go (about) 1 // Copyright 2019 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 // Package deepequalerrors defines an Analyzer that checks for the use 6 // of reflect.DeepEqual with error values. 7 package deepequalerrors 8 9 import ( 10 "go/ast" 11 "go/types" 12 13 "golang.org/x/tools/go/analysis" 14 "golang.org/x/tools/go/analysis/passes/inspect" 15 "golang.org/x/tools/go/analysis/passes/internal/analysisutil" 16 "golang.org/x/tools/go/ast/inspector" 17 "golang.org/x/tools/go/types/typeutil" 18 "golang.org/x/tools/internal/aliases" 19 ) 20 21 const Doc = `check for calls of reflect.DeepEqual on error values 22 23 The deepequalerrors checker looks for calls of the form: 24 25 reflect.DeepEqual(err1, err2) 26 27 where err1 and err2 are errors. Using reflect.DeepEqual to compare 28 errors is discouraged.` 29 30 var Analyzer = &analysis.Analyzer{ 31 Name: "deepequalerrors", 32 Doc: Doc, 33 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/deepequalerrors", 34 Requires: []*analysis.Analyzer{inspect.Analyzer}, 35 Run: run, 36 } 37 38 func run(pass *analysis.Pass) (interface{}, error) { 39 if !analysisutil.Imports(pass.Pkg, "reflect") { 40 return nil, nil // doesn't directly import reflect 41 } 42 43 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 44 45 nodeFilter := []ast.Node{ 46 (*ast.CallExpr)(nil), 47 } 48 inspect.Preorder(nodeFilter, func(n ast.Node) { 49 call := n.(*ast.CallExpr) 50 fn, _ := typeutil.Callee(pass.TypesInfo, call).(*types.Func) 51 if analysisutil.IsFunctionNamed(fn, "reflect", "DeepEqual") && hasError(pass, call.Args[0]) && hasError(pass, call.Args[1]) { 52 pass.ReportRangef(call, "avoid using reflect.DeepEqual with errors") 53 } 54 }) 55 return nil, nil 56 } 57 58 var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) 59 60 // hasError reports whether the type of e contains the type error. 61 // See containsError, below, for the meaning of "contains". 62 func hasError(pass *analysis.Pass, e ast.Expr) bool { 63 tv, ok := pass.TypesInfo.Types[e] 64 if !ok { // no type info, assume good 65 return false 66 } 67 return containsError(tv.Type) 68 } 69 70 // Report whether any type that typ could store and that could be compared is the 71 // error type. This includes typ itself, as well as the types of struct field, slice 72 // and array elements, map keys and elements, and pointers. It does not include 73 // channel types (incomparable), arg and result types of a Signature (not stored), or 74 // methods of a named or interface type (not stored). 75 func containsError(typ types.Type) bool { 76 // Track types being processed, to avoid infinite recursion. 77 // Using types as keys here is OK because we are checking for the identical pointer, not 78 // type identity. See analysis/passes/printf/types.go. 79 inProgress := make(map[types.Type]bool) 80 81 var check func(t types.Type) bool 82 check = func(t types.Type) bool { 83 if t == errorType { 84 return true 85 } 86 if inProgress[t] { 87 return false 88 } 89 inProgress[t] = true 90 switch t := t.(type) { 91 case *types.Pointer: 92 return check(t.Elem()) 93 case *types.Slice: 94 return check(t.Elem()) 95 case *types.Array: 96 return check(t.Elem()) 97 case *types.Map: 98 return check(t.Key()) || check(t.Elem()) 99 case *types.Struct: 100 for i := 0; i < t.NumFields(); i++ { 101 if check(t.Field(i).Type()) { 102 return true 103 } 104 } 105 case *types.Named, *aliases.Alias: 106 return check(t.Underlying()) 107 108 // We list the remaining valid type kinds for completeness. 109 case *types.Basic: 110 case *types.Chan: // channels store values, but they are not comparable 111 case *types.Signature: 112 case *types.Tuple: // tuples are only part of signatures 113 case *types.Interface: 114 } 115 return false 116 } 117 118 return check(typ) 119 }