github.com/gamechanger/errcheck@v0.0.0-20190204213044-2847c07a5541/main.go (about)

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