23
25
var verbose = flag.Bool("v", false, "verbose")
28
// Flags to control which checks to perform. "all" is set to true here, and disabled later if
29
// a flag is set explicitly.
30
var report = map[string]*bool{
31
"all": flag.Bool("all", true, "check everything; disabled if any explicit check is requested"),
32
"asmdecl": flag.Bool("asmdecl", false, "check assembly against Go declarations"),
33
"assign": flag.Bool("assign", false, "check for useless assignments"),
34
"atomic": flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"),
35
"buildtags": flag.Bool("buildtags", false, "check that +build tags are valid"),
36
"composites": flag.Bool("composites", false, "check that composite literals used type-tagged elements"),
37
"methods": flag.Bool("methods", false, "check that canonically named methods are canonically defined"),
38
"printf": flag.Bool("printf", false, "check printf-like invocations"),
39
"rangeloops": flag.Bool("rangeloops", false, "check that range loop variables are used correctly"),
40
"structtags": flag.Bool("structtags", false, "check that struct field tags have canonical format"),
41
"unreachable": flag.Bool("unreachable", false, "check for unreachable code"),
44
// vet tells whether to report errors for the named check, a flag name.
45
func vet(name string) bool {
46
return *report["all"] || *report[name]
26
49
// setExit sets the value for os.Exit when it is called, later. It
27
50
// remembers the highest value.
28
51
func setExit(err int) {
76
112
if flag.NArg() == 0 {
77
doFile("stdin", os.Stdin)
117
for _, name := range flag.Args() {
118
// Is it a directory?
119
fi, err := os.Stat(name)
121
warnf("error walking tree: %s", err)
79
134
for _, name := range flag.Args() {
81
if fi, err := os.Stat(name); err == nil && fi.IsDir() {
139
doPackage(flag.Args())
91
// doFile analyzes one file. If the reader is nil, the source code is read from the
93
func doFile(name string, reader io.Reader) {
143
// prefixDirectory places the directory name on the beginning of each name in the list.
144
func prefixDirectory(directory string, names []string) {
145
if directory != "." {
146
for i, name := range names {
147
names[i] = filepath.Join(directory, name)
152
// doPackageDir analyzes the single package found in the directory, if there is one,
153
// plus a test package, if there is one.
154
func doPackageDir(directory string) {
155
pkg, err := build.Default.ImportDir(directory, 0)
157
// If it's just that there are no go source files, that's fine.
158
if _, nogo := err.(*build.NoGoError); nogo {
161
// Non-fatal: we are doing a recursive walk and there may be other directories.
162
warnf("cannot process directory %s: %s", directory, err)
166
names = append(names, pkg.GoFiles...)
167
names = append(names, pkg.CgoFiles...)
168
names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
169
names = append(names, pkg.SFiles...)
170
prefixDirectory(directory, names)
172
// Is there also a "foo_test" package? If so, do that one as well.
173
if len(pkg.XTestGoFiles) > 0 {
174
names = pkg.XTestGoFiles
175
prefixDirectory(directory, names)
180
type Package struct {
181
types map[ast.Expr]Type
182
values map[ast.Expr]interface{}
186
// doPackage analyzes the single package constructed from the named files.
187
func doPackage(names []string) {
189
var astFiles []*ast.File
94
190
fs := token.NewFileSet()
95
parsedFile, err := parser.ParseFile(fs, name, reader, 0)
97
errorf("%s: %s", name, err)
100
file := &File{fset: fs, file: parsedFile}
101
file.walkFile(name, parsedFile)
191
for _, name := range names {
192
f, err := os.Open(name)
194
// Warn but continue to next package.
195
warnf("%s: %s", name, err)
199
data, err := ioutil.ReadAll(f)
201
warnf("%s: %s", name, err)
204
checkBuildTag(name, data)
205
var parsedFile *ast.File
206
if strings.HasSuffix(name, ".go") {
207
parsedFile, err = parser.ParseFile(fs, name, bytes.NewReader(data), 0)
209
warnf("%s: %s", name, err)
212
astFiles = append(astFiles, parsedFile)
214
files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
218
// Type check the package.
219
err := pkg.check(fs, astFiles)
220
if err != nil && *verbose {
223
for _, file := range files {
225
if file.file != nil {
226
file.walkFile(file.name, file.file)
104
232
func visit(path string, f os.FileInfo, err error) error {
106
errorf("walk error: %s", err)
234
warnf("walk error: %s", err)
237
// One package per directory. Ignore the files themselves.
109
if !f.IsDir() && strings.HasSuffix(path, ".go") {
115
// walkDir recursively walks the tree looking for .go files.
245
func (pkg *Package) hasFileWithSuffix(suffix string) bool {
246
for _, f := range pkg.files {
247
if strings.HasSuffix(f.name, suffix) {
254
// walkDir recursively walks the tree looking for Go packages.
116
255
func walkDir(root string) {
117
256
filepath.Walk(root, visit)
120
// error formats the error to standard error, adding program
121
// identification and a newline
259
// errorf formats the error to standard error, adding program
260
// identification and a newline, and exits.
122
261
func errorf(format string, args ...interface{}) {
123
262
fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
266
// warnf formats the error to standard error, adding program
267
// identification and a newline, but does not exit.
268
func warnf(format string, args ...interface{}) {
269
fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
127
273
// Println is fmt.Println guarded by -v.
301
func (f *File) loc(pos token.Pos) string {
302
if pos == token.NoPos {
305
// Do not print columns. Because the pos often points to the start of an
306
// expression instead of the inner part with the actual error, the
307
// precision can mislead.
308
posn := f.fset.Position(pos)
309
return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line)
155
312
// Warn reports an error but does not set the exit code.
156
313
func (f *File) Warn(pos token.Pos, args ...interface{}) {
157
loc := f.fset.Position(pos).String() + ": "
158
fmt.Fprint(os.Stderr, loc+fmt.Sprintln(args...))
314
fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...))
161
317
// Warnf reports a formatted error but does not set the exit code.
162
318
func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
163
loc := f.fset.Position(pos).String() + ": "
164
fmt.Fprintf(os.Stderr, loc+format+"\n", args...)
319
fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...)
167
322
// walkFile walks the file's tree.