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.
5 // Package deepequalerrors defines an Analyzer that checks for the use
6 // of reflect.DeepEqual with error values.
7 package deepequalerrors
13 "golang.org/x/tools/go/analysis"
14 "golang.org/x/tools/go/analysis/passes/inspect"
15 "golang.org/x/tools/go/ast/inspector"
16 "golang.org/x/tools/go/types/typeutil"
19 const Doc = `check for calls of reflect.DeepEqual on error values
21 The deepequalerrors checker looks for calls of the form:
23 reflect.DeepEqual(err1, err2)
25 where err1 and err2 are errors. Using reflect.DeepEqual to compare
26 errors is discouraged.`
28 var Analyzer = &analysis.Analyzer{
29 Name: "deepequalerrors",
31 Requires: []*analysis.Analyzer{inspect.Analyzer},
35 func run(pass *analysis.Pass) (interface{}, error) {
36 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
38 nodeFilter := []ast.Node{
41 inspect.Preorder(nodeFilter, func(n ast.Node) {
42 call := n.(*ast.CallExpr)
43 fn, ok := typeutil.Callee(pass.TypesInfo, call).(*types.Func)
47 if fn.FullName() == "reflect.DeepEqual" && hasError(pass, call.Args[0]) && hasError(pass, call.Args[1]) {
48 pass.ReportRangef(call, "avoid using reflect.DeepEqual with errors")
54 var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
56 // hasError reports whether the type of e contains the type error.
57 // See containsError, below, for the meaning of "contains".
58 func hasError(pass *analysis.Pass, e ast.Expr) bool {
59 tv, ok := pass.TypesInfo.Types[e]
60 if !ok { // no type info, assume good
63 return containsError(tv.Type)
66 // Report whether any type that typ could store and that could be compared is the
67 // error type. This includes typ itself, as well as the types of struct field, slice
68 // and array elements, map keys and elements, and pointers. It does not include
69 // channel types (incomparable), arg and result types of a Signature (not stored), or
70 // methods of a named or interface type (not stored).
71 func containsError(typ types.Type) bool {
72 // Track types being processed, to avoid infinite recursion.
73 // Using types as keys here is OK because we are checking for the identical pointer, not
74 // type identity. See analysis/passes/printf/types.go.
75 inProgress := make(map[types.Type]bool)
77 var check func(t types.Type) bool
78 check = func(t types.Type) bool {
86 switch t := t.(type) {
88 return check(t.Elem())
90 return check(t.Elem())
92 return check(t.Elem())
94 return check(t.Key()) || check(t.Elem())
96 for i := 0; i < t.NumFields(); i++ {
97 if check(t.Field(i).Type()) {
102 return check(t.Underlying())
104 // We list the remaining valid type kinds for completeness.
106 case *types.Chan: // channels store values, but they are not comparable
107 case *types.Signature:
108 case *types.Tuple: // tuples are only part of signatures
109 case *types.Interface: