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  }