github.com/inturn/pre-commit-gobuild@v1.0.12/hooks/go-errcheck/go-errcheck.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "flag" 6 "fmt" 7 "os" 8 "path/filepath" 9 "regexp" 10 "runtime" 11 "strings" 12 13 "github.com/inturn/pre-commit-gobuild/internal/errchecker" 14 ) 15 16 const ( 17 exitCodeOk int = iota 18 exitUncheckedError 19 exitFatalError 20 ) 21 22 var abspath bool 23 24 type ignoreFlag map[string]*regexp.Regexp 25 26 func main() { 27 os.Exit(mainCmd([]string{"errcheck", "-blank", "-asserts", "-ignoregenerated", "./..."})) 28 } 29 30 func (f ignoreFlag) String() string { 31 pairs := make([]string, 0, len(f)) 32 for pkg, re := range f { 33 prefix := "" 34 if pkg != "" { 35 prefix = pkg + ":" 36 } 37 pairs = append(pairs, prefix+re.String()) 38 } 39 return fmt.Sprintf("%q", strings.Join(pairs, ",")) 40 } 41 42 func (f ignoreFlag) Set(s string) error { 43 if s == "" { 44 return nil 45 } 46 for _, pair := range strings.Split(s, ",") { 47 colonIndex := strings.Index(pair, ":") 48 var pkg, re string 49 if colonIndex == -1 { 50 pkg = "" 51 re = pair 52 } else { 53 pkg = pair[:colonIndex] 54 re = pair[colonIndex+1:] 55 } 56 regex, err := regexp.Compile(re) 57 if err != nil { 58 return err 59 } 60 f[pkg] = regex 61 } 62 return nil 63 } 64 65 type tagsFlag []string 66 67 func (f *tagsFlag) String() string { 68 return fmt.Sprintf("%q", strings.Join(*f, " ")) 69 } 70 71 func (f *tagsFlag) Set(s string) error { 72 if s == "" { 73 return nil 74 } 75 tags := strings.Split(s, " ") 76 if tags == nil { 77 return nil 78 } 79 for _, tag := range tags { 80 if tag != "" { 81 *f = append(*f, tag) 82 } 83 } 84 return nil 85 } 86 87 var dotStar = regexp.MustCompile(".*") 88 89 func reportUncheckedErrors(e *errchecker.UncheckedErrors, verbose bool) { 90 wd, err := os.Getwd() 91 if err != nil { 92 wd = "" 93 } 94 for _, uncheckedError := range e.Errors { 95 pos := uncheckedError.Pos.String() 96 if !abspath { 97 newPos, err := filepath.Rel(wd, pos) 98 if err == nil { 99 pos = newPos 100 } 101 } 102 103 if verbose && uncheckedError.FuncName != "" { 104 fmt.Printf("%s:\t%s\t%s\n", pos, uncheckedError.FuncName, uncheckedError.Line) 105 } else { 106 fmt.Printf("%s:\t%s\n", pos, uncheckedError.Line) 107 } 108 } 109 } 110 111 func mainCmd(args []string) int { 112 runtime.GOMAXPROCS(runtime.NumCPU()) 113 114 checker := errchecker.NewChecker() 115 paths, err := parseFlags(checker, args) 116 if err != exitCodeOk { 117 return err 118 } 119 120 if err := checker.CheckPackages(paths...); err != nil { 121 if e, ok := err.(*errchecker.UncheckedErrors); ok { 122 reportUncheckedErrors(e, checker.Verbose) 123 return exitUncheckedError 124 } else if err == errchecker.ErrNoGoFiles { 125 fmt.Fprintln(os.Stderr, err) 126 return exitCodeOk 127 } 128 fmt.Fprintf(os.Stderr, "error: failed to check packages: %s\n", err) 129 return exitFatalError 130 } 131 return exitCodeOk 132 } 133 134 func parseFlags(checker *errchecker.Checker, args []string) ([]string, int) { 135 flags := flag.NewFlagSet(args[0], flag.ContinueOnError) 136 flags.BoolVar(&checker.Blank, "blank", false, "if true, check for errors assigned to blank identifier") 137 flags.BoolVar(&checker.Asserts, "asserts", false, "if true, check for ignored type assertion results") 138 flags.BoolVar(&checker.WithoutTests, "ignoretests", false, "if true, checking of _test.go files is disabled") 139 flags.BoolVar(&checker.WithoutGeneratedCode, "ignoregenerated", false, "if true, checking of files with generated code is disabled") 140 flags.BoolVar(&checker.Verbose, "verbose", false, "produce more verbose logging") 141 142 flags.BoolVar(&abspath, "abspath", false, "print absolute paths to files") 143 144 tags := tagsFlag{} 145 flags.Var(&tags, "tags", "space-separated list of build tags to include") 146 ignorePkg := flags.String("ignorepkg", "", "comma-separated list of package paths to ignore") 147 ignore := ignoreFlag(map[string]*regexp.Regexp{}) 148 flags.Var(ignore, "ignore", "[deprecated] comma-separated list of pairs of the form pkg:regex\n"+ 149 " the regex is used to ignore names within pkg.") 150 151 var excludeFile string 152 flags.StringVar(&excludeFile, "exclude", "", "Path to a file containing a list of functions to exclude from checking") 153 154 if err := flags.Parse(args[1:]); err != nil { 155 return nil, exitFatalError 156 } 157 158 if excludeFile != "" { 159 exclude := make(map[string]bool) 160 fh, err := os.Open(excludeFile) 161 if err != nil { 162 fmt.Fprintf(os.Stderr, "Could not read exclude file: %s\n", err) 163 return nil, exitFatalError 164 } 165 scanner := bufio.NewScanner(fh) 166 for scanner.Scan() { 167 name := scanner.Text() 168 exclude[name] = true 169 170 if checker.Verbose { 171 fmt.Printf("Excluding %s\n", name) 172 } 173 } 174 if err := scanner.Err(); err != nil { 175 fmt.Fprintf(os.Stderr, "Could not read exclude file: %s\n", err) 176 return nil, exitFatalError 177 } 178 checker.SetExclude(exclude) 179 } 180 181 checker.Tags = tags 182 for _, pkg := range strings.Split(*ignorePkg, ",") { 183 if pkg != "" { 184 ignore[pkg] = dotStar 185 } 186 } 187 checker.Ignore = ignore 188 189 paths := flags.Args() 190 if len(paths) == 0 { 191 paths = []string{"."} 192 } 193 return paths, exitCodeOk 194 }