Permalink
Browse files

Add -exclude flag and functionality

  • Loading branch information...
1 parent 5b29f57 commit 719ac455570b5e9f44e5dbf9a16c68ce911ba04b @kisielk committed Nov 18, 2016
Showing with 86 additions and 28 deletions.
  1. +19 −3 README.md
  2. +29 −14 internal/errcheck/errcheck.go
  3. +9 −7 internal/errcheck/errcheck_test.go
  4. +25 −3 main.go
  5. +1 −1 main_test.go
  6. +3 −0 testdata/main.go
View
@@ -37,11 +37,27 @@ The `-blank` flag enables checking for assignments of errors to the
blank identifier. It takes no arguments.
-## Whitelist
-errcheck has an internal whitelist for functions in the Go standard library that
+## Excluding functions
+
+Use the `-exclude` flag to specify a path to a file containing a list of functions to
+be excluded.
+
+ errcheck -exclude errcheck_excludes.txt path/to/package
+
+The file should contain one function signature per line. The format for function signatures is
+`package.FunctionName` while for methods it's `(package.Receiver).MethodName` for value receivers
+and `(*package.Receiver).MethodName` for pointer receivers.
+
+An example of an exclude file is:
+
+ io/ioutil.ReadFile
+ (*net/http.Client).Do
+
+The exclude list is combined with an internal list for functions in the Go standard library that
have an error return type but are documented to never return an error.
-## Ignoring functions
+
+### The deprecated method
The `-ignore` flag takes a comma-separated list of pairs of the form package:regex.
For each package, the regex describes which functions to ignore within that package.
@@ -106,6 +106,30 @@ type Checker struct {
// If true, checking of of _test.go files is disabled
WithoutTests bool
+
+ exclude map[string]bool
+}
+
+func NewChecker() *Checker {
+ c := Checker{}
+ c.SetExclude(map[string]bool{})
+ return &c
+}
+
+func (c *Checker) SetExclude(l map[string]bool) {
+ // Default exclude for stdlib functions
+ c.exclude = map[string]bool{
+ "math/rand.Read": true,
+ "(*math/rand.Rand).Read": true,
+
+ "(*bytes.Buffer).Write": true,
+ "(*bytes.Buffer).WriteByte": true,
+ "(*bytes.Buffer).WriteRune": true,
+ "(*bytes.Buffer).WriteString": true,
+ }
+ for k := range l {
+ c.exclude[k] = true
+ }
}
func (c *Checker) logf(msg string, args ...interface{}) {
@@ -160,6 +184,7 @@ func (c *Checker) CheckPackages(paths ...string) error {
blank: c.Blank,
asserts: c.Asserts,
lines: make(map[string][]string),
+ exclude: c.exclude,
errors: []UncheckedError{},
}
@@ -186,22 +211,12 @@ type visitor struct {
blank bool
asserts bool
lines map[string][]string
+ exclude map[string]bool
errors []UncheckedError
}
-func (v *visitor) builtinIgnoreCall(call *ast.CallExpr) bool {
- // TODO(dh): find a better name for this method
- ignored := map[string]bool{
- "math/rand.Read": true,
- "(*math/rand.Rand).Read": true,
-
- "(*bytes.Buffer).Write": true,
- "(*bytes.Buffer).WriteByte": true,
- "(*bytes.Buffer).WriteRune": true,
- "(*bytes.Buffer).WriteString": true,
- }
-
+func (v *visitor) excludeCall(call *ast.CallExpr) bool {
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return false
@@ -219,11 +234,11 @@ func (v *visitor) builtinIgnoreCall(call *ast.CallExpr) bool {
// want to support vendored stdlib packages, we need to implement
// FullName with our own logic.
name := fn.FullName()
- return ignored[name]
+ return v.exclude[name]
}
func (v *visitor) ignoreCall(call *ast.CallExpr) bool {
- if v.builtinIgnoreCall(call) {
+ if v.excludeCall(call) {
return true
}
@@ -89,6 +89,10 @@ func TestAll(t *testing.T) {
test(t, CheckAsserts|CheckBlank)
}
+func TestWhitelist(t *testing.T) {
+
+}
+
const testVendorMain = `
package main
@@ -152,9 +156,8 @@ func TestIgnore(t *testing.T) {
}
for i, currCase := range cases {
- checker := &Checker{
- Ignore: currCase.ignore,
- }
+ checker := NewChecker()
+ checker.Ignore = currCase.ignore
err := checker.CheckPackages(path.Join("github.com/kisielk/errcheck/internal/errcheck", testVendorDir))
if currCase.numExpectedErrs == 0 {
@@ -181,10 +184,9 @@ func test(t *testing.T, f flags) {
asserts bool = f&CheckAsserts != 0
blank bool = f&CheckBlank != 0
)
- checker := &Checker{
- Asserts: asserts,
- Blank: blank,
- }
+ checker := NewChecker()
+ checker.Asserts = asserts
+ checker.Blank = blank
err := checker.CheckPackages(testPackage)
uerr, ok := err.(*UncheckedErrors)
if !ok {
View
@@ -1,6 +1,7 @@
package main
import (
+ "bufio"
"flag"
"fmt"
"os"
@@ -102,7 +103,7 @@ func reportUncheckedErrors(e *errcheck.UncheckedErrors) {
func mainCmd(args []string) int {
runtime.GOMAXPROCS(runtime.NumCPU())
- checker := &errcheck.Checker{}
+ checker := errcheck.NewChecker()
paths, err := parseFlags(checker, args)
if err != exitCodeOk {
return err
@@ -137,13 +138,34 @@ func parseFlags(checker *errcheck.Checker, args []string) ([]string, int) {
ignore := ignoreFlag(map[string]*regexp.Regexp{
"fmt": dotStar,
})
- flags.Var(ignore, "ignore", "comma-separated list of pairs of the form pkg:regex\n"+
- " the regex is used to ignore names within pkg")
+ flags.Var(ignore, "ignore", "[deprecated] comma-separated list of pairs of the form pkg:regex\n"+
+ " the regex is used to ignore names within pkg.")
+
+ var excludeFile string
+ flags.StringVar(&excludeFile, "exclude", "", "Path to a file containing a list of functions to exclude from checking")
if err := flags.Parse(args[1:]); err != nil {
return nil, exitFatalError
}
+ if excludeFile != "" {
+ exclude := make(map[string]bool)
+ fh, err := os.Open(excludeFile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Could not read exclude file: %s\n", err)
+ return nil, exitFatalError
+ }
+ scanner := bufio.NewScanner(fh)
+ for scanner.Scan() {
+ exclude[scanner.Text()] = true
+ }
+ if err := scanner.Err(); err != nil {
+ fmt.Fprintf(os.Stderr, "Could not read exclude file: %s\n", err)
+ return nil, exitFatalError
+ }
+ checker.SetExclude(exclude)
+ }
+
checker.Tags = tags
for _, pkg := range strings.Split(*ignorePkg, ",") {
if pkg != "" {
View
@@ -54,7 +54,7 @@ func TestMain(t *testing.T) {
t.Errorf("Exit code is %d, expected %d", exitCode, exitUncheckedError)
}
- expectUnchecked := 14
+ expectUnchecked := 15
if got := strings.Count(out, "UNCHECKED"); got != expectUnchecked {
t.Errorf("Got %d UNCHECKED errors, expected %d in:\n%s", got, expectUnchecked, out)
}
View
@@ -3,6 +3,7 @@ package main
import (
"bytes"
"fmt"
+ "io/ioutil"
"math/rand"
mrand "math/rand"
)
@@ -136,4 +137,6 @@ func main() {
b2.Write(nil)
rand.Read(nil)
mrand.Read(nil)
+
+ ioutil.ReadFile("main.go") // UNCHECKED
}

0 comments on commit 719ac45

Please sign in to comment.