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