Skip to content
This repository was archived by the owner on Jun 2, 2023. It is now read-only.

Commit 719ac45

Browse files
committed
Add -exclude flag and functionality
1 parent 5b29f57 commit 719ac45

File tree

6 files changed

+86
-28
lines changed

6 files changed

+86
-28
lines changed

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,27 @@ The `-blank` flag enables checking for assignments of errors to the
3737
blank identifier. It takes no arguments.
3838

3939

40-
## Whitelist
41-
errcheck has an internal whitelist for functions in the Go standard library that
40+
## Excluding functions
41+
42+
Use the `-exclude` flag to specify a path to a file containing a list of functions to
43+
be excluded.
44+
45+
errcheck -exclude errcheck_excludes.txt path/to/package
46+
47+
The file should contain one function signature per line. The format for function signatures is
48+
`package.FunctionName` while for methods it's `(package.Receiver).MethodName` for value receivers
49+
and `(*package.Receiver).MethodName` for pointer receivers.
50+
51+
An example of an exclude file is:
52+
53+
io/ioutil.ReadFile
54+
(*net/http.Client).Do
55+
56+
The exclude list is combined with an internal list for functions in the Go standard library that
4257
have an error return type but are documented to never return an error.
4358

44-
## Ignoring functions
59+
60+
### The deprecated method
4561

4662
The `-ignore` flag takes a comma-separated list of pairs of the form package:regex.
4763
For each package, the regex describes which functions to ignore within that package.

internal/errcheck/errcheck.go

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,30 @@ type Checker struct {
106106

107107
// If true, checking of of _test.go files is disabled
108108
WithoutTests bool
109+
110+
exclude map[string]bool
111+
}
112+
113+
func NewChecker() *Checker {
114+
c := Checker{}
115+
c.SetExclude(map[string]bool{})
116+
return &c
117+
}
118+
119+
func (c *Checker) SetExclude(l map[string]bool) {
120+
// Default exclude for stdlib functions
121+
c.exclude = map[string]bool{
122+
"math/rand.Read": true,
123+
"(*math/rand.Rand).Read": true,
124+
125+
"(*bytes.Buffer).Write": true,
126+
"(*bytes.Buffer).WriteByte": true,
127+
"(*bytes.Buffer).WriteRune": true,
128+
"(*bytes.Buffer).WriteString": true,
129+
}
130+
for k := range l {
131+
c.exclude[k] = true
132+
}
109133
}
110134

111135
func (c *Checker) logf(msg string, args ...interface{}) {
@@ -160,6 +184,7 @@ func (c *Checker) CheckPackages(paths ...string) error {
160184
blank: c.Blank,
161185
asserts: c.Asserts,
162186
lines: make(map[string][]string),
187+
exclude: c.exclude,
163188
errors: []UncheckedError{},
164189
}
165190

@@ -186,22 +211,12 @@ type visitor struct {
186211
blank bool
187212
asserts bool
188213
lines map[string][]string
214+
exclude map[string]bool
189215

190216
errors []UncheckedError
191217
}
192218

193-
func (v *visitor) builtinIgnoreCall(call *ast.CallExpr) bool {
194-
// TODO(dh): find a better name for this method
195-
ignored := map[string]bool{
196-
"math/rand.Read": true,
197-
"(*math/rand.Rand).Read": true,
198-
199-
"(*bytes.Buffer).Write": true,
200-
"(*bytes.Buffer).WriteByte": true,
201-
"(*bytes.Buffer).WriteRune": true,
202-
"(*bytes.Buffer).WriteString": true,
203-
}
204-
219+
func (v *visitor) excludeCall(call *ast.CallExpr) bool {
205220
sel, ok := call.Fun.(*ast.SelectorExpr)
206221
if !ok {
207222
return false
@@ -219,11 +234,11 @@ func (v *visitor) builtinIgnoreCall(call *ast.CallExpr) bool {
219234
// want to support vendored stdlib packages, we need to implement
220235
// FullName with our own logic.
221236
name := fn.FullName()
222-
return ignored[name]
237+
return v.exclude[name]
223238
}
224239

225240
func (v *visitor) ignoreCall(call *ast.CallExpr) bool {
226-
if v.builtinIgnoreCall(call) {
241+
if v.excludeCall(call) {
227242
return true
228243
}
229244

internal/errcheck/errcheck_test.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ func TestAll(t *testing.T) {
8989
test(t, CheckAsserts|CheckBlank)
9090
}
9191

92+
func TestWhitelist(t *testing.T) {
93+
94+
}
95+
9296
const testVendorMain = `
9397
package main
9498
@@ -152,9 +156,8 @@ func TestIgnore(t *testing.T) {
152156
}
153157

154158
for i, currCase := range cases {
155-
checker := &Checker{
156-
Ignore: currCase.ignore,
157-
}
159+
checker := NewChecker()
160+
checker.Ignore = currCase.ignore
158161
err := checker.CheckPackages(path.Join("github.com/kisielk/errcheck/internal/errcheck", testVendorDir))
159162

160163
if currCase.numExpectedErrs == 0 {
@@ -181,10 +184,9 @@ func test(t *testing.T, f flags) {
181184
asserts bool = f&CheckAsserts != 0
182185
blank bool = f&CheckBlank != 0
183186
)
184-
checker := &Checker{
185-
Asserts: asserts,
186-
Blank: blank,
187-
}
187+
checker := NewChecker()
188+
checker.Asserts = asserts
189+
checker.Blank = blank
188190
err := checker.CheckPackages(testPackage)
189191
uerr, ok := err.(*UncheckedErrors)
190192
if !ok {

main.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"bufio"
45
"flag"
56
"fmt"
67
"os"
@@ -102,7 +103,7 @@ func reportUncheckedErrors(e *errcheck.UncheckedErrors) {
102103
func mainCmd(args []string) int {
103104
runtime.GOMAXPROCS(runtime.NumCPU())
104105

105-
checker := &errcheck.Checker{}
106+
checker := errcheck.NewChecker()
106107
paths, err := parseFlags(checker, args)
107108
if err != exitCodeOk {
108109
return err
@@ -137,13 +138,34 @@ func parseFlags(checker *errcheck.Checker, args []string) ([]string, int) {
137138
ignore := ignoreFlag(map[string]*regexp.Regexp{
138139
"fmt": dotStar,
139140
})
140-
flags.Var(ignore, "ignore", "comma-separated list of pairs of the form pkg:regex\n"+
141-
" the regex is used to ignore names within pkg")
141+
flags.Var(ignore, "ignore", "[deprecated] comma-separated list of pairs of the form pkg:regex\n"+
142+
" the regex is used to ignore names within pkg.")
143+
144+
var excludeFile string
145+
flags.StringVar(&excludeFile, "exclude", "", "Path to a file containing a list of functions to exclude from checking")
142146

143147
if err := flags.Parse(args[1:]); err != nil {
144148
return nil, exitFatalError
145149
}
146150

151+
if excludeFile != "" {
152+
exclude := make(map[string]bool)
153+
fh, err := os.Open(excludeFile)
154+
if err != nil {
155+
fmt.Fprintf(os.Stderr, "Could not read exclude file: %s\n", err)
156+
return nil, exitFatalError
157+
}
158+
scanner := bufio.NewScanner(fh)
159+
for scanner.Scan() {
160+
exclude[scanner.Text()] = true
161+
}
162+
if err := scanner.Err(); err != nil {
163+
fmt.Fprintf(os.Stderr, "Could not read exclude file: %s\n", err)
164+
return nil, exitFatalError
165+
}
166+
checker.SetExclude(exclude)
167+
}
168+
147169
checker.Tags = tags
148170
for _, pkg := range strings.Split(*ignorePkg, ",") {
149171
if pkg != "" {

main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func TestMain(t *testing.T) {
5454
t.Errorf("Exit code is %d, expected %d", exitCode, exitUncheckedError)
5555
}
5656

57-
expectUnchecked := 14
57+
expectUnchecked := 15
5858
if got := strings.Count(out, "UNCHECKED"); got != expectUnchecked {
5959
t.Errorf("Got %d UNCHECKED errors, expected %d in:\n%s", got, expectUnchecked, out)
6060
}

testdata/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"bytes"
55
"fmt"
6+
"io/ioutil"
67
"math/rand"
78
mrand "math/rand"
89
)
@@ -136,4 +137,6 @@ func main() {
136137
b2.Write(nil)
137138
rand.Read(nil)
138139
mrand.Read(nil)
140+
141+
ioutil.ReadFile("main.go") // UNCHECKED
139142
}

0 commit comments

Comments
 (0)