1
1
package wrapcheck
2
2
3
3
import (
4
+ "fmt"
4
5
"go/ast"
5
6
"go/token"
6
7
"go/types"
@@ -13,18 +14,16 @@ import (
13
14
"golang.org/x/tools/go/analysis"
14
15
)
15
16
16
- var (
17
- DefaultIgnoreSigs = []string {
18
- ".Errorf(" ,
19
- "errors.New(" ,
20
- "errors.Unwrap(" ,
21
- ".Wrap(" ,
22
- ".Wrapf(" ,
23
- ".WithMessage(" ,
24
- ".WithMessagef(" ,
25
- ".WithStack(" ,
26
- }
27
- )
17
+ var DefaultIgnoreSigs = []string {
18
+ ".Errorf(" ,
19
+ "errors.New(" ,
20
+ "errors.Unwrap(" ,
21
+ ".Wrap(" ,
22
+ ".Wrapf(" ,
23
+ ".WithMessage(" ,
24
+ ".WithMessagef(" ,
25
+ ".WithStack(" ,
26
+ }
28
27
29
28
// WrapcheckConfig is the set of configuration values which configure the
30
29
// behaviour of the linter.
@@ -91,7 +90,14 @@ func NewAnalyzer(cfg WrapcheckConfig) *analysis.Analyzer {
91
90
}
92
91
93
92
func run (cfg WrapcheckConfig ) func (* analysis.Pass ) (interface {}, error ) {
93
+ // Precompile the regexps, report the error
94
+ ignoreSigRegexp , err := compileRegexps (cfg .IgnoreSigRegexps )
95
+
94
96
return func (pass * analysis.Pass ) (interface {}, error ) {
97
+ if err != nil {
98
+ return nil , err
99
+ }
100
+
95
101
for _ , file := range pass .Files {
96
102
ast .Inspect (file , func (n ast.Node ) bool {
97
103
ret , ok := n .(* ast.ReturnStmt )
@@ -112,8 +118,9 @@ func run(cfg WrapcheckConfig) func(*analysis.Pass) (interface{}, error) {
112
118
// If the return type of the function is a single error. This will not
113
119
// match an error within multiple return values, for that, the below
114
120
// tuple check is required.
121
+
115
122
if isError (pass .TypesInfo .TypeOf (expr )) {
116
- reportUnwrapped (pass , retFn , retFn .Pos (), cfg )
123
+ reportUnwrapped (pass , retFn , retFn .Pos (), cfg , ignoreSigRegexp )
117
124
return true
118
125
}
119
126
@@ -131,7 +138,7 @@ func run(cfg WrapcheckConfig) func(*analysis.Pass) (interface{}, error) {
131
138
return true
132
139
}
133
140
if isError (v .Type ()) {
134
- reportUnwrapped (pass , retFn , expr .Pos (), cfg )
141
+ reportUnwrapped (pass , retFn , expr .Pos (), cfg , ignoreSigRegexp )
135
142
return true
136
143
}
137
144
}
@@ -146,9 +153,7 @@ func run(cfg WrapcheckConfig) func(*analysis.Pass) (interface{}, error) {
146
153
return true
147
154
}
148
155
149
- var (
150
- call * ast.CallExpr
151
- )
156
+ var call * ast.CallExpr
152
157
153
158
// Attempt to find the most recent short assign
154
159
if shortAss := prevErrAssign (pass , file , ident ); shortAss != nil {
@@ -195,7 +200,7 @@ func run(cfg WrapcheckConfig) func(*analysis.Pass) (interface{}, error) {
195
200
return true
196
201
}
197
202
198
- reportUnwrapped (pass , call , ident .NamePos , cfg )
203
+ reportUnwrapped (pass , call , ident .NamePos , cfg , ignoreSigRegexp )
199
204
}
200
205
201
206
return true
@@ -208,17 +213,18 @@ func run(cfg WrapcheckConfig) func(*analysis.Pass) (interface{}, error) {
208
213
209
214
// Report unwrapped takes a call expression and an identifier and reports
210
215
// if the call is unwrapped.
211
- func reportUnwrapped (pass * analysis.Pass , call * ast.CallExpr , tokenPos token.Pos , cfg WrapcheckConfig ) {
216
+ func reportUnwrapped (pass * analysis.Pass , call * ast.CallExpr , tokenPos token.Pos , cfg WrapcheckConfig , regexps [] * regexp. Regexp ) {
212
217
sel , ok := call .Fun .(* ast.SelectorExpr )
213
218
if ! ok {
214
219
return
215
220
}
216
221
217
222
// Check for ignored signatures
218
223
fnSig := pass .TypesInfo .ObjectOf (sel .Sel ).String ()
224
+
219
225
if contains (cfg .IgnoreSigs , fnSig ) {
220
226
return
221
- } else if containsMatch (cfg . IgnoreSigRegexps , fnSig ) {
227
+ } else if containsMatch (regexps , fnSig ) {
222
228
return
223
229
}
224
230
@@ -245,6 +251,9 @@ func isInterface(pass *analysis.Pass, sel *ast.SelectorExpr) bool {
245
251
return ok
246
252
}
247
253
254
+ // isFromotherPkg returns whether the function is defined in the pacakge
255
+ // currently under analysis or is considered external. It will ignore packages
256
+ // defined in config.IgnorePackageGlobs.
248
257
func isFromOtherPkg (pass * analysis.Pass , sel * ast.SelectorExpr , config WrapcheckConfig ) bool {
249
258
// The package of the function that we are calling which returns the error
250
259
fn := pass .TypesInfo .ObjectOf (sel .Sel )
@@ -331,14 +340,8 @@ func contains(slice []string, el string) bool {
331
340
return false
332
341
}
333
342
334
- func containsMatch (slice []string , el string ) bool {
335
- for _ , s := range slice {
336
- re , err := regexp .Compile (s )
337
- if err != nil {
338
- log .Printf ("unable to parse regexp: %s\n " , s )
339
- os .Exit (1 )
340
- }
341
-
343
+ func containsMatch (regexps []* regexp.Regexp , el string ) bool {
344
+ for _ , re := range regexps {
342
345
if re .MatchString (el ) {
343
346
return true
344
347
}
@@ -365,3 +368,19 @@ func isUnresolved(file *ast.File, ident *ast.Ident) bool {
365
368
366
369
return false
367
370
}
371
+
372
+ // compileRegexps compiles a set of regular expressions returning them for use,
373
+ // or the first encountered error due to an invalid expression.
374
+ func compileRegexps (regexps []string ) ([]* regexp.Regexp , error ) {
375
+ var compiledRegexps []* regexp.Regexp
376
+ for _ , reg := range regexps {
377
+ re , err := regexp .Compile (reg )
378
+ if err != nil {
379
+ return nil , fmt .Errorf ("unable to compile regexp %s: %v\n " , reg , err )
380
+ }
381
+
382
+ compiledRegexps = append (compiledRegexps , re )
383
+ }
384
+
385
+ return compiledRegexps , nil
386
+ }
0 commit comments