| // Copyright (c) 2013 The Go Authors. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd. |
| |
| // golint lints the Go source files named on its command line. |
| package main |
| |
| import ( |
| "flag" |
| "fmt" |
| "go/build" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "strings" |
| |
| "golang.org/x/lint" |
| ) |
| |
| var ( |
| minConfidence = flag.Float64("min_confidence", 0.8, "minimum confidence of a problem to print it") |
| setExitStatus = flag.Bool("set_exit_status", false, "set exit status to 1 if any issues are found") |
| suggestions int |
| ) |
| |
| func usage() { |
| fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) |
| fmt.Fprintf(os.Stderr, "\tgolint [flags] # runs on package in current directory\n") |
| fmt.Fprintf(os.Stderr, "\tgolint [flags] [packages]\n") |
| fmt.Fprintf(os.Stderr, "\tgolint [flags] [directories] # where a '/...' suffix includes all sub-directories\n") |
| fmt.Fprintf(os.Stderr, "\tgolint [flags] [files] # all must belong to a single package\n") |
| fmt.Fprintf(os.Stderr, "Flags:\n") |
| flag.PrintDefaults() |
| } |
| |
| func main() { |
| flag.Usage = usage |
| flag.Parse() |
| |
| if flag.NArg() == 0 { |
| lintDir(".") |
| } else { |
| // dirsRun, filesRun, and pkgsRun indicate whether golint is applied to |
| // directory, file or package targets. The distinction affects which |
| // checks are run. It is no valid to mix target types. |
| var dirsRun, filesRun, pkgsRun int |
| var args []string |
| for _, arg := range flag.Args() { |
| if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-len("/...")]) { |
| dirsRun = 1 |
| for _, dirname := range allPackagesInFS(arg) { |
| args = append(args, dirname) |
| } |
| } else if isDir(arg) { |
| dirsRun = 1 |
| args = append(args, arg) |
| } else if exists(arg) { |
| filesRun = 1 |
| args = append(args, arg) |
| } else { |
| pkgsRun = 1 |
| args = append(args, arg) |
| } |
| } |
| |
| if dirsRun+filesRun+pkgsRun != 1 { |
| usage() |
| os.Exit(2) |
| } |
| switch { |
| case dirsRun == 1: |
| for _, dir := range args { |
| lintDir(dir) |
| } |
| case filesRun == 1: |
| lintFiles(args...) |
| case pkgsRun == 1: |
| for _, pkg := range importPaths(args) { |
| lintPackage(pkg) |
| } |
| } |
| } |
| |
| if *setExitStatus && suggestions > 0 { |
| fmt.Fprintf(os.Stderr, "Found %d lint suggestions; failing.\n", suggestions) |
| os.Exit(1) |
| } |
| } |
| |
| func isDir(filename string) bool { |
| fi, err := os.Stat(filename) |
| return err == nil && fi.IsDir() |
| } |
| |
| func exists(filename string) bool { |
| _, err := os.Stat(filename) |
| return err == nil |
| } |
| |
| func lintFiles(filenames ...string) { |
| files := make(map[string][]byte) |
| for _, filename := range filenames { |
| src, err := ioutil.ReadFile(filename) |
| if err != nil { |
| fmt.Fprintln(os.Stderr, err) |
| continue |
| } |
| files[filename] = src |
| } |
| |
| l := new(lint.Linter) |
| ps, err := l.LintFiles(files) |
| if err != nil { |
| fmt.Fprintf(os.Stderr, "%v\n", err) |
| return |
| } |
| for _, p := range ps { |
| if p.Confidence >= *minConfidence { |
| fmt.Printf("%v: %s\n", p.Position, p.Text) |
| suggestions++ |
| } |
| } |
| } |
| |
| func lintDir(dirname string) { |
| pkg, err := build.ImportDir(dirname, 0) |
| lintImportedPackage(pkg, err) |
| } |
| |
| func lintPackage(pkgname string) { |
| pkg, err := build.Import(pkgname, ".", 0) |
| lintImportedPackage(pkg, err) |
| } |
| |
| func lintImportedPackage(pkg *build.Package, err error) { |
| if err != nil { |
| if _, nogo := err.(*build.NoGoError); nogo { |
| // Don't complain if the failure is due to no Go source files. |
| return |
| } |
| fmt.Fprintln(os.Stderr, err) |
| return |
| } |
| |
| var files []string |
| files = append(files, pkg.GoFiles...) |
| files = append(files, pkg.CgoFiles...) |
| files = append(files, pkg.TestGoFiles...) |
| if pkg.Dir != "." { |
| for i, f := range files { |
| files[i] = filepath.Join(pkg.Dir, f) |
| } |
| } |
| // TODO(dsymonds): Do foo_test too (pkg.XTestGoFiles) |
| |
| lintFiles(files...) |
| } |