@@ -21,32 +21,43 @@ import (
21
21
"fmt"
22
22
"go/ast"
23
23
"go/format"
24
- "go/token"
25
24
"go/types"
26
25
27
26
"golang.org/x/tools/go/analysis"
28
27
)
29
28
30
- // visitCallBasic checks for errors.Is(x, y) and errors.As(x, y).
31
- func (v Visitor ) visitCallBasic (x * ast.CallExpr ) bool {
32
- if len (x .Args ) != 2 { //nolint:mnd
33
- return true
34
- }
29
+ // visitCallValue checks for encoding/json#Decoder.Decode, json.Unmarshal, errors.Is and errors.As.
30
+ func (v Visitor ) visitCallBasic (x * ast.CallExpr ) bool { //nolint:cyclop
35
31
fun , ok := unwrap (x .Fun ).(* ast.SelectorExpr )
36
- if ! ok || ! v . isErrors ( fun . X ) {
32
+ if ! ok {
37
33
return true
38
34
}
39
35
40
- if fun .Sel .Name == "As" { // Do not report pointers in errors.As(..., ...).
41
- return false
36
+ funType := v .TypesInfo .Types [fun .X ]
37
+ if funType .IsValue () && funType .Type .String () == "*encoding/json.Decoder" && fun .Sel .Name == "Decode" {
38
+ return false // Do not report pointers in json.Decoder#Decode
42
39
}
43
40
44
- if fun .Sel .Name != "Is" {
41
+ switch path , ok2 := v .pathOf (fun .X ); {
42
+ case ! ok2 :
43
+ return true
44
+
45
+ case path == "encoding/json" && fun .Sel .Name == "Unmarshal" :
46
+ return false // Do not report pointers in json.Unmarshal(..., ...).
47
+
48
+ case len (x .Args ) != 2 || path != "errors" && path != "golang.org/x/exp/errors" :
45
49
return true
46
- }
47
50
48
- // Delegate errors.Is(..., ...) to visitCmp for further analysis of the comparison.
49
- return v .visitCmp (x , x .Args [0 ], x .Args [1 ])
51
+ case fun .Sel .Name == "As" :
52
+ return false // Do not report pointers in errors.As(..., ...)
53
+
54
+ case fun .Sel .Name == "Is" :
55
+ // Delegate errors.Is(..., ...) to visitCmp for further analysis of the comparison.
56
+ return v .visitCmp (x , x .Args [0 ], x .Args [1 ])
57
+
58
+ default :
59
+ return true
60
+ }
50
61
}
51
62
52
63
// visitCall checks for type casts T(x), errors.Is(x, y), errors.As(x, y) and new(T).
@@ -67,20 +78,18 @@ func (v Visitor) visitCall(x *ast.CallExpr) bool {
67
78
}
68
79
69
80
// isErrors checks whether the expression is a package specifier for errors or golang.org/x/exp/errors.
70
- func (v Visitor ) isErrors (x ast.Expr ) bool {
81
+ func (v Visitor ) pathOf (x ast.Expr ) ( string , bool ) {
71
82
id , ok := x .(* ast.Ident )
72
83
if ! ok {
73
- return false
84
+ return "" , false
74
85
}
75
86
76
87
pkg , ok := v .TypesInfo .Uses [id ].(* types.PkgName )
77
88
if ! ok {
78
- return false
89
+ return "" , false
79
90
}
80
91
81
- path := pkg .Imported ().Path ()
82
-
83
- return path == "errors" || path == "golang.org/x/exp/errors"
92
+ return pkg .Imported ().Path (), true
84
93
}
85
94
86
95
// visitCast is called for type casts T(nil).
@@ -97,7 +106,7 @@ func (v Visitor) visitCast(t types.Type, x *ast.CallExpr) bool {
97
106
message := fmt .Sprintf ("cast of nil to pointer to zero-size variable of type %q" , elem )
98
107
var fixes []analysis.SuggestedFix
99
108
if s , ok2 := unwrap (x .Fun ).(* ast.StarExpr ); ok2 {
100
- fixes = makePure (x , s .X )
109
+ fixes = v . makePure (x , s .X )
101
110
}
102
111
103
112
v .report (x , message , fixes )
@@ -117,12 +126,12 @@ func (v Visitor) visitNew(x *ast.CallExpr) bool {
117
126
118
127
arg := x .Args [0 ] // new(arg).
119
128
argType := v .TypesInfo .Types [arg ].Type
120
- if ! v .isZeroSizedType (argType ) {
129
+ if ! v .zeroSizedType (argType ) {
121
130
return true
122
131
}
123
132
124
133
message := fmt .Sprintf ("new called on zero-sized type %q" , argType )
125
- fixes := makePure (x , arg )
134
+ fixes := v . makePure (x , arg )
126
135
v .report (x , message , fixes )
127
136
128
137
return false
@@ -143,9 +152,9 @@ func unwrap(e ast.Expr) ast.Expr {
143
152
}
144
153
145
154
// makePure adds a suggested fix from (*T)(nil) or new(T) to T{}.
146
- func makePure (n ast.Node , x ast.Expr ) []analysis.SuggestedFix {
155
+ func ( v Visitor ) makePure (n ast.Node , x ast.Expr ) []analysis.SuggestedFix {
147
156
var buf bytes.Buffer
148
- if err := format .Node (& buf , token . NewFileSet () , x ); err != nil {
157
+ if err := format .Node (& buf , v . Fset , x ); err != nil {
149
158
return nil
150
159
}
151
160
buf .WriteString ("{}" )
0 commit comments