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