Skip to content

Commit c1c14fd

Browse files
authored
Merge pull request #193 from kisielk/no-check-path
Remove CheckPaths
2 parents 720fbcb + c4c6bdd commit c1c14fd

File tree

3 files changed

+222
-189
lines changed

3 files changed

+222
-189
lines changed

errcheck/errcheck.go

Lines changed: 52 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ import (
1010
"go/types"
1111
"os"
1212
"regexp"
13-
"runtime"
1413
"sort"
1514
"strings"
16-
"sync"
1715

1816
"golang.org/x/tools/go/packages"
1917
)
@@ -75,39 +73,21 @@ type UncheckedError struct {
7573
FuncName string
7674
}
7775

78-
// UncheckedErrors is returned from the CheckPackage function if the package contains
76+
// Result is returned from the CheckPackage function if the package contains
7977
// 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.
8582
// 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
9884
}
9985

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
10787

10888
// 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]
11191

11292
pi, pj := ei.Pos, ej.Pos
11393

@@ -124,6 +104,40 @@ func (e byName) Less(i, j int) bool {
124104
return ei.Line < ej.Line
125105
}
126106

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+
127141
// Exclusions define symbols and language elements that will be not checked
128142
type Exclusions struct {
129143

@@ -176,23 +190,16 @@ type Checker struct {
176190

177191
// Tags are a list of build tags to use.
178192
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-
}
188193
}
189194

190195
// loadPackages is used for testing.
191196
var loadPackages = func(cfg *packages.Config, paths ...string) ([]*packages.Package, error) {
192197
return packages.Load(cfg, paths...)
193198
}
194199

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) {
196203
cfg := &packages.Config{
197204
Mode: packages.LoadAllSyntax,
198205
Tests: !c.Exclusions.TestFiles,
@@ -220,13 +227,13 @@ func (c *Checker) shouldSkipFile(file *ast.File) bool {
220227
return false
221228
}
222229

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 {
227235
excludedSymbols := map[string]bool{}
228236
for _, sym := range c.Exclusions.Symbols {
229-
c.logf("Excluding %v", sym)
230237
excludedSymbols[sym] = true
231238
}
232239

@@ -260,53 +267,7 @@ func (c *Checker) CheckPackage(pkg *packages.Package) []UncheckedError {
260267
}
261268
ast.Walk(v, astFile)
262269
}
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}
310271
}
311272

312273
// visitor implements the errcheck algorithm

0 commit comments

Comments
 (0)