@@ -10,10 +10,8 @@ import (
10
10
"go/types"
11
11
"os"
12
12
"regexp"
13
- "runtime"
14
13
"sort"
15
14
"strings"
16
- "sync"
17
15
18
16
"golang.org/x/tools/go/packages"
19
17
)
@@ -75,39 +73,21 @@ type UncheckedError struct {
75
73
FuncName string
76
74
}
77
75
78
- // UncheckedErrors is returned from the CheckPackage function if the package contains
76
+ // Result is returned from the CheckPackage function if the package contains
79
77
// any unchecked errors.
80
- // Errors should be appended using the Append method, which is safe to use concurrently.
81
- type UncheckedErrors struct {
82
- mu sync.Mutex
83
-
84
- // Errors is a list of all the unchecked errors in the package.
78
+ //
79
+ // Aggregation can be done using the Append method.
80
+ type Result struct {
81
+ // UncheckedErrors is a list of all the unchecked errors in the package.
85
82
// Printing an error reports its position within the file and the contents of the line.
86
- Errors []UncheckedError
87
- }
88
-
89
- // Append appends errors to e. It is goroutine-safe.
90
- func (e * UncheckedErrors ) Append (errors ... UncheckedError ) {
91
- e .mu .Lock ()
92
- defer e .mu .Unlock ()
93
- e .Errors = append (e .Errors , errors ... )
94
- }
95
-
96
- func (e * UncheckedErrors ) Error () string {
97
- return fmt .Sprintf ("%d unchecked errors" , len (e .Errors ))
83
+ UncheckedErrors []UncheckedError
98
84
}
99
85
100
- // Len is the number of elements in the collection.
101
- func (e * UncheckedErrors ) Len () int { return len (e .Errors ) }
102
-
103
- // Swap swaps the elements with indexes i and j.
104
- func (e * UncheckedErrors ) Swap (i , j int ) { e .Errors [i ], e .Errors [j ] = e .Errors [j ], e .Errors [i ] }
105
-
106
- type byName struct { * UncheckedErrors }
86
+ type byName []UncheckedError
107
87
108
88
// Less reports whether the element with index i should sort before the element with index j.
109
- func (e byName ) Less (i , j int ) bool {
110
- ei , ej := e . Errors [i ], e . Errors [j ]
89
+ func (b byName ) Less (i , j int ) bool {
90
+ ei , ej := b [i ], b [j ]
111
91
112
92
pi , pj := ei .Pos , ej .Pos
113
93
@@ -124,6 +104,40 @@ func (e byName) Less(i, j int) bool {
124
104
return ei .Line < ej .Line
125
105
}
126
106
107
+ func (b byName ) Swap (i , j int ) {
108
+ b [i ], b [j ] = b [j ], b [i ]
109
+ }
110
+
111
+ func (b byName ) Len () int {
112
+ return len (b )
113
+ }
114
+
115
+ // Append appends errors to e. Append does not do any duplicate checking.
116
+ func (r * Result ) Append (other * Result ) {
117
+ r .UncheckedErrors = append (r .UncheckedErrors , other .UncheckedErrors ... )
118
+ }
119
+
120
+ // Returns the unique errors that have been accumulated. Duplicates may occur
121
+ // when a file containing an unchecked error belongs to > 1 package.
122
+ //
123
+ // The method receiver remains unmodified after the call to Unique.
124
+ func (r * Result ) Unique () * Result {
125
+ result := make ([]UncheckedError , len (r .UncheckedErrors ))
126
+ copy (result , r .UncheckedErrors )
127
+ sort .Sort ((byName )(result ))
128
+ uniq := result [:0 ] // compact in-place
129
+ for i , err := range result {
130
+ if i == 0 || err != result [i - 1 ] {
131
+ uniq = append (uniq , err )
132
+ }
133
+ }
134
+ return & Result {UncheckedErrors : uniq }
135
+ }
136
+
137
+ func (r * Result ) Error () string {
138
+ return fmt .Sprintf ("%d unchecked errors" , len (r .UncheckedErrors ))
139
+ }
140
+
127
141
// Exclusions define symbols and language elements that will be not checked
128
142
type Exclusions struct {
129
143
@@ -176,23 +190,16 @@ type Checker struct {
176
190
177
191
// Tags are a list of build tags to use.
178
192
Tags []string
179
-
180
- // Verbose causes extra information to be output to stdout.
181
- Verbose bool
182
- }
183
-
184
- func (c * Checker ) logf (msg string , args ... interface {}) {
185
- if c .Verbose {
186
- fmt .Fprintf (os .Stderr , msg + "\n " , args ... )
187
- }
188
193
}
189
194
190
195
// loadPackages is used for testing.
191
196
var loadPackages = func (cfg * packages.Config , paths ... string ) ([]* packages.Package , error ) {
192
197
return packages .Load (cfg , paths ... )
193
198
}
194
199
195
- func (c * Checker ) load (paths ... string ) ([]* packages.Package , error ) {
200
+ // LoadPackages loads all the packages in all the paths provided. It uses the
201
+ // exclusions and build tags provided to by the user when loading the packages.
202
+ func (c * Checker ) LoadPackages (paths ... string ) ([]* packages.Package , error ) {
196
203
cfg := & packages.Config {
197
204
Mode : packages .LoadAllSyntax ,
198
205
Tests : ! c .Exclusions .TestFiles ,
@@ -220,13 +227,13 @@ func (c *Checker) shouldSkipFile(file *ast.File) bool {
220
227
return false
221
228
}
222
229
223
- // CheckPackage checks packages for errors.
224
- func (c * Checker ) CheckPackage (pkg * packages.Package ) []UncheckedError {
225
- c .logf ("Checking %s" , pkg .Types .Path ())
226
-
230
+ // CheckPackage checks packages for errors that have not been checked.
231
+ //
232
+ // It will exclude specific errors from analysis if the user has configured
233
+ // exclusions.
234
+ func (c * Checker ) CheckPackage (pkg * packages.Package ) * Result {
227
235
excludedSymbols := map [string ]bool {}
228
236
for _ , sym := range c .Exclusions .Symbols {
229
- c .logf ("Excluding %v" , sym )
230
237
excludedSymbols [sym ] = true
231
238
}
232
239
@@ -260,53 +267,7 @@ func (c *Checker) CheckPackage(pkg *packages.Package) []UncheckedError {
260
267
}
261
268
ast .Walk (v , astFile )
262
269
}
263
- return v .errors
264
- }
265
-
266
- // CheckPaths checks packages for errors.
267
- func (c * Checker ) CheckPaths (paths ... string ) error {
268
- pkgs , err := c .load (paths ... )
269
- if err != nil {
270
- return err
271
- }
272
- // Check for errors in the initial packages.
273
- work := make (chan * packages.Package , len (pkgs ))
274
- for _ , pkg := range pkgs {
275
- if len (pkg .Errors ) > 0 {
276
- return fmt .Errorf ("errors while loading package %s: %v" , pkg .ID , pkg .Errors )
277
- }
278
- work <- pkg
279
- }
280
- close (work )
281
-
282
- var wg sync.WaitGroup
283
- u := & UncheckedErrors {}
284
- for i := 0 ; i < runtime .NumCPU (); i ++ {
285
- wg .Add (1 )
286
-
287
- go func () {
288
- defer wg .Done ()
289
- for pkg := range work {
290
- u .Append (c .CheckPackage (pkg )... )
291
- }
292
- }()
293
- }
294
-
295
- wg .Wait ()
296
- if u .Len () > 0 {
297
- // Sort unchecked errors and remove duplicates. Duplicates may occur when a file
298
- // containing an unchecked error belongs to > 1 package.
299
- sort .Sort (byName {u })
300
- uniq := u .Errors [:0 ] // compact in-place
301
- for i , err := range u .Errors {
302
- if i == 0 || err != u .Errors [i - 1 ] {
303
- uniq = append (uniq , err )
304
- }
305
- }
306
- u .Errors = uniq
307
- return u
308
- }
309
- return nil
270
+ return & Result {UncheckedErrors : v .errors }
310
271
}
311
272
312
273
// visitor implements the errcheck algorithm
0 commit comments