Skip to content

Commit

Permalink
Merge pull request #116 from kisielk/exclude
Browse files Browse the repository at this point in the history
Add -exclude flag and functionality
  • Loading branch information
kisielk committed Nov 18, 2016
2 parents 5b29f57 + 719ac45 commit db0ca22
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 28 deletions.
22 changes: 19 additions & 3 deletions README.md
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -37,11 +37,27 @@ The `-blank` flag enables checking for assignments of errors to the
blank identifier. It takes no arguments. blank identifier. It takes no arguments.




## Whitelist ## Excluding functions
errcheck has an internal whitelist for functions in the Go standard library that
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. 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. 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. For each package, the regex describes which functions to ignore within that package.
Expand Down
43 changes: 29 additions & 14 deletions internal/errcheck/errcheck.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -106,6 +106,30 @@ type Checker struct {


// If true, checking of of _test.go files is disabled // If true, checking of of _test.go files is disabled
WithoutTests bool 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{}) { func (c *Checker) logf(msg string, args ...interface{}) {
Expand Down Expand Up @@ -160,6 +184,7 @@ func (c *Checker) CheckPackages(paths ...string) error {
blank: c.Blank, blank: c.Blank,
asserts: c.Asserts, asserts: c.Asserts,
lines: make(map[string][]string), lines: make(map[string][]string),
exclude: c.exclude,
errors: []UncheckedError{}, errors: []UncheckedError{},
} }


Expand All @@ -186,22 +211,12 @@ type visitor struct {
blank bool blank bool
asserts bool asserts bool
lines map[string][]string lines map[string][]string
exclude map[string]bool


errors []UncheckedError errors []UncheckedError
} }


func (v *visitor) builtinIgnoreCall(call *ast.CallExpr) bool { func (v *visitor) excludeCall(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,
}

sel, ok := call.Fun.(*ast.SelectorExpr) sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok { if !ok {
return false return false
Expand All @@ -219,11 +234,11 @@ func (v *visitor) builtinIgnoreCall(call *ast.CallExpr) bool {
// want to support vendored stdlib packages, we need to implement // want to support vendored stdlib packages, we need to implement
// FullName with our own logic. // FullName with our own logic.
name := fn.FullName() name := fn.FullName()
return ignored[name] return v.exclude[name]
} }


func (v *visitor) ignoreCall(call *ast.CallExpr) bool { func (v *visitor) ignoreCall(call *ast.CallExpr) bool {
if v.builtinIgnoreCall(call) { if v.excludeCall(call) {
return true return true
} }


Expand Down
16 changes: 9 additions & 7 deletions internal/errcheck/errcheck_test.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ func TestAll(t *testing.T) {
test(t, CheckAsserts|CheckBlank) test(t, CheckAsserts|CheckBlank)
} }


func TestWhitelist(t *testing.T) {

}

const testVendorMain = ` const testVendorMain = `
package main package main
Expand Down Expand Up @@ -152,9 +156,8 @@ func TestIgnore(t *testing.T) {
} }


for i, currCase := range cases { for i, currCase := range cases {
checker := &Checker{ checker := NewChecker()
Ignore: currCase.ignore, checker.Ignore = currCase.ignore
}
err := checker.CheckPackages(path.Join("github.com/kisielk/errcheck/internal/errcheck", testVendorDir)) err := checker.CheckPackages(path.Join("github.com/kisielk/errcheck/internal/errcheck", testVendorDir))


if currCase.numExpectedErrs == 0 { if currCase.numExpectedErrs == 0 {
Expand All @@ -181,10 +184,9 @@ func test(t *testing.T, f flags) {
asserts bool = f&CheckAsserts != 0 asserts bool = f&CheckAsserts != 0
blank bool = f&CheckBlank != 0 blank bool = f&CheckBlank != 0
) )
checker := &Checker{ checker := NewChecker()
Asserts: asserts, checker.Asserts = asserts
Blank: blank, checker.Blank = blank
}
err := checker.CheckPackages(testPackage) err := checker.CheckPackages(testPackage)
uerr, ok := err.(*UncheckedErrors) uerr, ok := err.(*UncheckedErrors)
if !ok { if !ok {
Expand Down
28 changes: 25 additions & 3 deletions main.go
Original file line number Original file line Diff line number Diff line change
@@ -1,6 +1,7 @@
package main package main


import ( import (
"bufio"
"flag" "flag"
"fmt" "fmt"
"os" "os"
Expand Down Expand Up @@ -102,7 +103,7 @@ func reportUncheckedErrors(e *errcheck.UncheckedErrors) {
func mainCmd(args []string) int { func mainCmd(args []string) int {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())


checker := &errcheck.Checker{} checker := errcheck.NewChecker()
paths, err := parseFlags(checker, args) paths, err := parseFlags(checker, args)
if err != exitCodeOk { if err != exitCodeOk {
return err return err
Expand Down Expand Up @@ -137,13 +138,34 @@ func parseFlags(checker *errcheck.Checker, args []string) ([]string, int) {
ignore := ignoreFlag(map[string]*regexp.Regexp{ ignore := ignoreFlag(map[string]*regexp.Regexp{
"fmt": dotStar, "fmt": dotStar,
}) })
flags.Var(ignore, "ignore", "comma-separated list of pairs of the form pkg:regex\n"+ 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") " 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 { if err := flags.Parse(args[1:]); err != nil {
return nil, exitFatalError 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 checker.Tags = tags
for _, pkg := range strings.Split(*ignorePkg, ",") { for _, pkg := range strings.Split(*ignorePkg, ",") {
if pkg != "" { if pkg != "" {
Expand Down
2 changes: 1 addition & 1 deletion main_test.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func TestMain(t *testing.T) {
t.Errorf("Exit code is %d, expected %d", exitCode, exitUncheckedError) t.Errorf("Exit code is %d, expected %d", exitCode, exitUncheckedError)
} }


expectUnchecked := 14 expectUnchecked := 15
if got := strings.Count(out, "UNCHECKED"); got != expectUnchecked { if got := strings.Count(out, "UNCHECKED"); got != expectUnchecked {
t.Errorf("Got %d UNCHECKED errors, expected %d in:\n%s", got, expectUnchecked, out) t.Errorf("Got %d UNCHECKED errors, expected %d in:\n%s", got, expectUnchecked, out)
} }
Expand Down
3 changes: 3 additions & 0 deletions testdata/main.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil"
"math/rand" "math/rand"
mrand "math/rand" mrand "math/rand"
) )
Expand Down Expand Up @@ -136,4 +137,6 @@ func main() {
b2.Write(nil) b2.Write(nil)
rand.Read(nil) rand.Read(nil)
mrand.Read(nil) mrand.Read(nil)

ioutil.ReadFile("main.go") // UNCHECKED
} }

0 comments on commit db0ca22

Please sign in to comment.