diff --git a/README.md b/README.md index cdccd81..b4804c8 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ An example of an exclude file is: io/ioutil.ReadFile io.Copy(*bytes.Buffer) io.Copy(os.Stdout) + + // Sometimes we don't care if a HTTP request fails. (*net/http.Client).Do The exclude list is combined with an internal list for functions in the Go standard library that @@ -70,6 +72,7 @@ In this case, add this line to your exclude file: example.com/yourpkg/vendor/example.net/fmt2.Println ``` +Empty lines and lines starting with `//` are ignored. ### The deprecated method diff --git a/main.go b/main.go index 7481c9e..da37140 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,10 @@ package main import ( "bufio" + "bytes" "flag" "fmt" + "io/ioutil" "os" "path/filepath" "regexp" @@ -152,26 +154,17 @@ func parseFlags(checker *errcheck.Checker, args []string) ([]string, int) { } if excludeFile != "" { - exclude := make(map[string]bool) - fh, err := os.Open(excludeFile) + excludes, err := readExcludes(excludeFile) if err != nil { - fmt.Fprintf(os.Stderr, "Could not read exclude file: %s\n", err) + fmt.Fprintf(os.Stderr, "Could not read exclude file: %v\n", err) return nil, exitFatalError } - scanner := bufio.NewScanner(fh) - for scanner.Scan() { - name := scanner.Text() - exclude[name] = true - - if checker.Verbose { - fmt.Printf("Excluding %s\n", name) + if checker.Verbose { + for _, exclude := range excludes { + fmt.Printf("Excluding %v\n", exclude) } } - 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.SetExclude(excludes) } checker.Tags = tags @@ -189,6 +182,32 @@ func parseFlags(checker *errcheck.Checker, args []string) ([]string, int) { return paths, exitCodeOk } +// readExcludes reads an excludes file, a newline delimited file that lists +// patterns for which to allow unchecked errors. +func readExcludes(path string) (map[string]bool, error) { + excludes := map[string]bool{} + + buf, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + scanner := bufio.NewScanner(bytes.NewReader(buf)) + for scanner.Scan() { + name := scanner.Text() + // Skip comments and empty lines. + if strings.HasPrefix(name, "//") || name == "" { + continue + } + excludes[name] = true + } + if err := scanner.Err(); err != nil { + return nil, err + } + + return excludes, nil +} + func main() { os.Exit(mainCmd(os.Args)) } diff --git a/main_test.go b/main_test.go index 9f22933..99a0b85 100644 --- a/main_test.go +++ b/main_test.go @@ -4,6 +4,7 @@ import ( "bytes" "io" "os" + "reflect" "regexp" "strings" "testing" @@ -222,3 +223,36 @@ func TestParseFlags(t *testing.T) { } } } + +func TestReadExcludes(t *testing.T) { + expectedExcludes := map[string]bool{ + "hello()": true, + "world()": true, + } + t.Logf("expectedExcludes: %#v", expectedExcludes) + excludes, err := readExcludes("testdata/excludes.txt") + if err != nil { + t.Fatal(err) + } + t.Logf("excludes: %#v", excludes) + if !reflect.DeepEqual(expectedExcludes, excludes) { + t.Fatal("excludes did not match expectedExcludes") + } +} + +func TestReadEmptyExcludes(t *testing.T) { + excludes, err := readExcludes("testdata/empty_excludes.txt") + if err != nil { + t.Fatal(err) + } + if len(excludes) != 0 { + t.Fatalf("expected empty excludes, got %#v", excludes) + } +} + +func TestReadExcludesMissingFile(t *testing.T) { + _, err := readExcludes("testdata/missing_file") + if err == nil { + t.Fatal("expected non-nil err, got nil") + } +} diff --git a/testdata/empty_excludes.txt b/testdata/empty_excludes.txt new file mode 100644 index 0000000..e69de29 diff --git a/testdata/excludes.txt b/testdata/excludes.txt new file mode 100644 index 0000000..6a0b2dc --- /dev/null +++ b/testdata/excludes.txt @@ -0,0 +1,5 @@ +// comment +hello() + +//trickycomment() +world()