golint.go raw

   1  // Copyright (c) 2013 The Go Authors. All rights reserved.
   2  //
   3  // Use of this source code is governed by a BSD-style
   4  // license that can be found in the LICENSE file or at
   5  // https://developers.google.com/open-source/licenses/bsd.
   6  
   7  // golint lints the Go source files named on its command line.
   8  package main
   9  
  10  import (
  11  	"flag"
  12  	"fmt"
  13  	"go/build"
  14  	"io/ioutil"
  15  	"os"
  16  	"path/filepath"
  17  	"strings"
  18  
  19  	"golang.org/x/lint"
  20  )
  21  
  22  var (
  23  	minConfidence = flag.Float64("min_confidence", 0.8, "minimum confidence of a problem to print it")
  24  	setExitStatus = flag.Bool("set_exit_status", false, "set exit status to 1 if any issues are found")
  25  	suggestions   int
  26  )
  27  
  28  func usage() {
  29  	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
  30  	fmt.Fprintf(os.Stderr, "\tgolint [flags] # runs on package in current directory\n")
  31  	fmt.Fprintf(os.Stderr, "\tgolint [flags] [packages]\n")
  32  	fmt.Fprintf(os.Stderr, "\tgolint [flags] [directories] # where a '/...' suffix includes all sub-directories\n")
  33  	fmt.Fprintf(os.Stderr, "\tgolint [flags] [files] # all must belong to a single package\n")
  34  	fmt.Fprintf(os.Stderr, "Flags:\n")
  35  	flag.PrintDefaults()
  36  }
  37  
  38  func main() {
  39  	flag.Usage = usage
  40  	flag.Parse()
  41  
  42  	if flag.NArg() == 0 {
  43  		lintDir(".")
  44  	} else {
  45  		// dirsRun, filesRun, and pkgsRun indicate whether golint is applied to
  46  		// directory, file or package targets. The distinction affects which
  47  		// checks are run. It is no valid to mix target types.
  48  		var dirsRun, filesRun, pkgsRun int
  49  		var args []string
  50  		for _, arg := range flag.Args() {
  51  			if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-len("/...")]) {
  52  				dirsRun = 1
  53  				for _, dirname := range allPackagesInFS(arg) {
  54  					args = append(args, dirname)
  55  				}
  56  			} else if isDir(arg) {
  57  				dirsRun = 1
  58  				args = append(args, arg)
  59  			} else if exists(arg) {
  60  				filesRun = 1
  61  				args = append(args, arg)
  62  			} else {
  63  				pkgsRun = 1
  64  				args = append(args, arg)
  65  			}
  66  		}
  67  
  68  		if dirsRun+filesRun+pkgsRun != 1 {
  69  			usage()
  70  			os.Exit(2)
  71  		}
  72  		switch {
  73  		case dirsRun == 1:
  74  			for _, dir := range args {
  75  				lintDir(dir)
  76  			}
  77  		case filesRun == 1:
  78  			lintFiles(args...)
  79  		case pkgsRun == 1:
  80  			for _, pkg := range importPaths(args) {
  81  				lintPackage(pkg)
  82  			}
  83  		}
  84  	}
  85  
  86  	if *setExitStatus && suggestions > 0 {
  87  		fmt.Fprintf(os.Stderr, "Found %d lint suggestions; failing.\n", suggestions)
  88  		os.Exit(1)
  89  	}
  90  }
  91  
  92  func isDir(filename string) bool {
  93  	fi, err := os.Stat(filename)
  94  	return err == nil && fi.IsDir()
  95  }
  96  
  97  func exists(filename string) bool {
  98  	_, err := os.Stat(filename)
  99  	return err == nil
 100  }
 101  
 102  func lintFiles(filenames ...string) {
 103  	files := make(map[string][]byte)
 104  	for _, filename := range filenames {
 105  		src, err := ioutil.ReadFile(filename)
 106  		if err != nil {
 107  			fmt.Fprintln(os.Stderr, err)
 108  			continue
 109  		}
 110  		files[filename] = src
 111  	}
 112  
 113  	l := new(lint.Linter)
 114  	ps, err := l.LintFiles(files)
 115  	if err != nil {
 116  		fmt.Fprintf(os.Stderr, "%v\n", err)
 117  		return
 118  	}
 119  	for _, p := range ps {
 120  		if p.Confidence >= *minConfidence {
 121  			fmt.Printf("%v: %s\n", p.Position, p.Text)
 122  			suggestions++
 123  		}
 124  	}
 125  }
 126  
 127  func lintDir(dirname string) {
 128  	pkg, err := build.ImportDir(dirname, 0)
 129  	lintImportedPackage(pkg, err)
 130  }
 131  
 132  func lintPackage(pkgname string) {
 133  	pkg, err := build.Import(pkgname, ".", 0)
 134  	lintImportedPackage(pkg, err)
 135  }
 136  
 137  func lintImportedPackage(pkg *build.Package, err error) {
 138  	if err != nil {
 139  		if _, nogo := err.(*build.NoGoError); nogo {
 140  			// Don't complain if the failure is due to no Go source files.
 141  			return
 142  		}
 143  		fmt.Fprintln(os.Stderr, err)
 144  		return
 145  	}
 146  
 147  	var files []string
 148  	files = append(files, pkg.GoFiles...)
 149  	files = append(files, pkg.CgoFiles...)
 150  	files = append(files, pkg.TestGoFiles...)
 151  	if pkg.Dir != "." {
 152  		for i, f := range files {
 153  			files[i] = filepath.Join(pkg.Dir, f)
 154  		}
 155  	}
 156  	// TODO(dsymonds): Do foo_test too (pkg.XTestGoFiles)
 157  
 158  	lintFiles(files...)
 159  }
 160