GoPLS Viewer

Home|gopls/go/analysis/passes/deepequalerrors/deepequalerrors.go
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.
7package deepequalerrors
8
9import (
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/ast/inspector"
16    "golang.org/x/tools/go/types/typeutil"
17)
18
19const Doc = `check for calls of reflect.DeepEqual on error values
20
21The deepequalerrors checker looks for calls of the form:
22
23    reflect.DeepEqual(err1, err2)
24
25where err1 and err2 are errors. Using reflect.DeepEqual to compare
26errors is discouraged.`
27
28var Analyzer = &analysis.Analyzer{
29    Name:     "deepequalerrors",
30    Doc:      Doc,
31    Requires: []*analysis.Analyzer{inspect.Analyzer},
32    Run:      run,
33}
34
35func run(pass *analysis.Pass) (interface{}, error) {
36    inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
37
38    nodeFilter := []ast.Node{
39        (*ast.CallExpr)(nil),
40    }
41    inspect.Preorder(nodeFilter, func(n ast.Node) {
42        call := n.(*ast.CallExpr)
43        fnok := typeutil.Callee(pass.TypesInfocall).(*types.Func)
44        if !ok {
45            return
46        }
47        if fn.FullName() == "reflect.DeepEqual" && hasError(passcall.Args[0]) && hasError(passcall.Args[1]) {
48            pass.ReportRangef(call"avoid using reflect.DeepEqual with errors")
49        }
50    })
51    return nilnil
52}
53
54var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
55
56// hasError reports whether the type of e contains the type error.
57// See containsError, below, for the meaning of "contains".
58func hasError(pass *analysis.Passe ast.Exprbool {
59    tvok := pass.TypesInfo.Types[e]
60    if !ok { // no type info, assume good
61        return false
62    }
63    return containsError(tv.Type)
64}
65
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).
71func containsError(typ types.Typebool {
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)
76
77    var check func(t types.Typebool
78    check = func(t types.Typebool {
79        if t == errorType {
80            return true
81        }
82        if inProgress[t] {
83            return false
84        }
85        inProgress[t] = true
86        switch t := t.(type) {
87        case *types.Pointer:
88            return check(t.Elem())
89        case *types.Slice:
90            return check(t.Elem())
91        case *types.Array:
92            return check(t.Elem())
93        case *types.Map:
94            return check(t.Key()) || check(t.Elem())
95        case *types.Struct:
96            for i := 0i < t.NumFields(); i++ {
97                if check(t.Field(i).Type()) {
98                    return true
99                }
100            }
101        case *types.Named:
102            return check(t.Underlying())
103
104        // We list the remaining valid type kinds for completeness.
105        case *types.Basic:
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:
110        }
111        return false
112    }
113
114    return check(typ)
115}
116
MembersX
Doc
containsError.typ
containsError.inProgress
ast
analysis
inspect
run.pass
run.nodeFilter
hasError.pass
hasError.e
containsError.BlockStmt.BlockStmt.i
types
containsError
typeutil
run
hasError
inspector
Members
X