~ubuntu-branches/ubuntu/vivid/golang/vivid

« back to all changes in this revision

Viewing changes to src/cmd/vet/main.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-20 14:06:23 UTC
  • mfrom: (14.1.23 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130820140623-b414jfxi3m0qkmrq
Tags: 2:1.1.2-2ubuntu1
* Merge from Debian unstable (LP: #1211749, #1202027). Remaining changes:
  - 016-armhf-elf-header.patch: Use correct ELF header for armhf binaries.
  - d/control,control.cross: Update Breaks/Replaces for Ubuntu
    versions to ensure smooth upgrades, regenerate control file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
        "flag"
12
12
        "fmt"
13
13
        "go/ast"
 
14
        "go/build"
14
15
        "go/parser"
 
16
        "go/printer"
15
17
        "go/token"
16
 
        "io"
 
18
        "io/ioutil"
17
19
        "os"
18
20
        "path/filepath"
19
21
        "strconv"
23
25
var verbose = flag.Bool("v", false, "verbose")
24
26
var exitCode = 0
25
27
 
 
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"),
 
42
}
 
43
 
 
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]
 
47
}
 
48
 
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) {
34
57
// Usage is a replacement usage function for the flags package.
35
58
func Usage() {
36
59
        fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
 
60
        fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
 
61
        fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
37
62
        flag.PrintDefaults()
38
63
        os.Exit(2)
39
64
}
41
66
// File is a wrapper for the state of a file used in the parser.
42
67
// The parse tree walkers are all methods of this type.
43
68
type File struct {
44
 
        fset *token.FileSet
45
 
        file *ast.File
46
 
        b    bytes.Buffer // for use by methods
 
69
        pkg     *Package
 
70
        fset    *token.FileSet
 
71
        name    string
 
72
        content []byte
 
73
        file    *ast.File
 
74
        b       bytes.Buffer // for use by methods
47
75
}
48
76
 
49
77
func main() {
50
78
        flag.Usage = Usage
51
79
        flag.Parse()
52
80
 
 
81
        // If a check is named explicitly, turn off the 'all' flag.
 
82
        for name, ptr := range report {
 
83
                if name != "all" && *ptr {
 
84
                        *report["all"] = false
 
85
                        break
 
86
                }
 
87
        }
 
88
 
53
89
        if *printfuncs != "" {
54
90
                for _, name := range strings.Split(*printfuncs, ",") {
55
91
                        if len(name) == 0 {
74
110
        }
75
111
 
76
112
        if flag.NArg() == 0 {
77
 
                doFile("stdin", os.Stdin)
78
 
        } else {
 
113
                Usage()
 
114
        }
 
115
        dirs := false
 
116
        files := false
 
117
        for _, name := range flag.Args() {
 
118
                // Is it a directory?
 
119
                fi, err := os.Stat(name)
 
120
                if err != nil {
 
121
                        warnf("error walking tree: %s", err)
 
122
                        continue
 
123
                }
 
124
                if fi.IsDir() {
 
125
                        dirs = true
 
126
                } else {
 
127
                        files = true
 
128
                }
 
129
        }
 
130
        if dirs && files {
 
131
                Usage()
 
132
        }
 
133
        if dirs {
79
134
                for _, name := range flag.Args() {
80
 
                        // Is it a directory?
81
 
                        if fi, err := os.Stat(name); err == nil && fi.IsDir() {
82
 
                                walkDir(name)
83
 
                        } else {
84
 
                                doFile(name, nil)
85
 
                        }
 
135
                        walkDir(name)
86
136
                }
 
137
                return
87
138
        }
 
139
        doPackage(flag.Args())
88
140
        os.Exit(exitCode)
89
141
}
90
142
 
91
 
// doFile analyzes one file.  If the reader is nil, the source code is read from the
92
 
// named file.
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)
 
148
                }
 
149
        }
 
150
}
 
151
 
 
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)
 
156
        if err != nil {
 
157
                // If it's just that there are no go source files, that's fine.
 
158
                if _, nogo := err.(*build.NoGoError); nogo {
 
159
                        return
 
160
                }
 
161
                // Non-fatal: we are doing a recursive walk and there may be other directories.
 
162
                warnf("cannot process directory %s: %s", directory, err)
 
163
                return
 
164
        }
 
165
        var names []string
 
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)
 
171
        doPackage(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)
 
176
                doPackage(names)
 
177
        }
 
178
}
 
179
 
 
180
type Package struct {
 
181
        types  map[ast.Expr]Type
 
182
        values map[ast.Expr]interface{}
 
183
        files  []*File
 
184
}
 
185
 
 
186
// doPackage analyzes the single package constructed from the named files.
 
187
func doPackage(names []string) {
 
188
        var files []*File
 
189
        var astFiles []*ast.File
94
190
        fs := token.NewFileSet()
95
 
        parsedFile, err := parser.ParseFile(fs, name, reader, 0)
96
 
        if err != nil {
97
 
                errorf("%s: %s", name, err)
98
 
                return
99
 
        }
100
 
        file := &File{fset: fs, file: parsedFile}
101
 
        file.walkFile(name, parsedFile)
 
191
        for _, name := range names {
 
192
                f, err := os.Open(name)
 
193
                if err != nil {
 
194
                        // Warn but continue to next package.
 
195
                        warnf("%s: %s", name, err)
 
196
                        return
 
197
                }
 
198
                defer f.Close()
 
199
                data, err := ioutil.ReadAll(f)
 
200
                if err != nil {
 
201
                        warnf("%s: %s", name, err)
 
202
                        return
 
203
                }
 
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)
 
208
                        if err != nil {
 
209
                                warnf("%s: %s", name, err)
 
210
                                return
 
211
                        }
 
212
                        astFiles = append(astFiles, parsedFile)
 
213
                }
 
214
                files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
 
215
        }
 
216
        pkg := new(Package)
 
217
        pkg.files = files
 
218
        // Type check the package.
 
219
        err := pkg.check(fs, astFiles)
 
220
        if err != nil && *verbose {
 
221
                warnf("%s", err)
 
222
        }
 
223
        for _, file := range files {
 
224
                file.pkg = pkg
 
225
                if file.file != nil {
 
226
                        file.walkFile(file.name, file.file)
 
227
                }
 
228
        }
 
229
        asmCheck(pkg)
102
230
}
103
231
 
104
232
func visit(path string, f os.FileInfo, err error) error {
105
233
        if err != nil {
106
 
                errorf("walk error: %s", err)
 
234
                warnf("walk error: %s", err)
 
235
                return err
 
236
        }
 
237
        // One package per directory. Ignore the files themselves.
 
238
        if !f.IsDir() {
107
239
                return nil
108
240
        }
109
 
        if !f.IsDir() && strings.HasSuffix(path, ".go") {
110
 
                doFile(path, nil)
111
 
        }
 
241
        doPackageDir(path)
112
242
        return nil
113
243
}
114
244
 
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) {
 
248
                        return true
 
249
                }
 
250
        }
 
251
        return false
 
252
}
 
253
 
 
254
// walkDir recursively walks the tree looking for Go packages.
116
255
func walkDir(root string) {
117
256
        filepath.Walk(root, visit)
118
257
}
119
258
 
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...)
124
 
        setExit(2)
 
263
        os.Exit(2)
 
264
}
 
265
 
 
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...)
 
270
        setExit(1)
125
271
}
126
272
 
127
273
// Println is fmt.Println guarded by -v.
152
298
        setExit(1)
153
299
}
154
300
 
 
301
func (f *File) loc(pos token.Pos) string {
 
302
        if pos == token.NoPos {
 
303
                return ""
 
304
        }
 
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)
 
310
}
 
311
 
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...))
159
315
}
160
316
 
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...)
165
320
}
166
321
 
167
322
// walkFile walks the file's tree.
173
328
// Visit implements the ast.Visitor interface.
174
329
func (f *File) Visit(node ast.Node) ast.Visitor {
175
330
        switch n := node.(type) {
 
331
        case *ast.AssignStmt:
 
332
                f.walkAssignStmt(n)
176
333
        case *ast.CallExpr:
177
334
                f.walkCallExpr(n)
178
335
        case *ast.CompositeLit:
180
337
        case *ast.Field:
181
338
                f.walkFieldTag(n)
182
339
        case *ast.FuncDecl:
183
 
                f.walkMethodDecl(n)
 
340
                f.walkFuncDecl(n)
 
341
        case *ast.FuncLit:
 
342
                f.walkFuncLit(n)
184
343
        case *ast.InterfaceType:
185
344
                f.walkInterfaceType(n)
 
345
        case *ast.RangeStmt:
 
346
                f.walkRangeStmt(n)
186
347
        }
187
348
        return f
188
349
}
189
350
 
 
351
// walkAssignStmt walks an assignment statement
 
352
func (f *File) walkAssignStmt(stmt *ast.AssignStmt) {
 
353
        f.checkAssignStmt(stmt)
 
354
        f.checkAtomicAssignment(stmt)
 
355
}
 
356
 
190
357
// walkCall walks a call expression.
191
358
func (f *File) walkCall(call *ast.CallExpr, name string) {
192
359
        f.checkFmtPrintfCall(call, name)
193
360
}
194
361
 
 
362
// walkCallExpr walks a call expression.
 
363
func (f *File) walkCallExpr(call *ast.CallExpr) {
 
364
        switch x := call.Fun.(type) {
 
365
        case *ast.Ident:
 
366
                f.walkCall(call, x.Name)
 
367
        case *ast.SelectorExpr:
 
368
                f.walkCall(call, x.Sel.Name)
 
369
        }
 
370
}
 
371
 
195
372
// walkCompositeLit walks a composite literal.
196
373
func (f *File) walkCompositeLit(c *ast.CompositeLit) {
197
374
        f.checkUntaggedLiteral(c)
205
382
        f.checkCanonicalFieldTag(field)
206
383
}
207
384
 
208
 
// walkMethodDecl walks the method's signature.
 
385
// walkMethod walks the method's signature.
209
386
func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) {
210
387
        f.checkCanonicalMethod(id, t)
211
388
}
212
389
 
213
 
// walkMethodDecl walks the method signature in the declaration.
214
 
func (f *File) walkMethodDecl(d *ast.FuncDecl) {
215
 
        if d.Recv == nil {
216
 
                // not a method
217
 
                return
 
390
// walkFuncDecl walks a function declaration.
 
391
func (f *File) walkFuncDecl(d *ast.FuncDecl) {
 
392
        f.checkUnreachable(d.Body)
 
393
        if d.Recv != nil {
 
394
                f.walkMethod(d.Name, d.Type)
218
395
        }
219
 
        f.walkMethod(d.Name, d.Type)
 
396
}
 
397
 
 
398
// walkFuncLit walks a function literal.
 
399
func (f *File) walkFuncLit(x *ast.FuncLit) {
 
400
        f.checkUnreachable(x.Body)
220
401
}
221
402
 
222
403
// walkInterfaceType walks the method signatures of an interface.
228
409
        }
229
410
}
230
411
 
231
 
// walkCallExpr walks a call expression.
232
 
func (f *File) walkCallExpr(call *ast.CallExpr) {
233
 
        switch x := call.Fun.(type) {
234
 
        case *ast.Ident:
235
 
                f.walkCall(call, x.Name)
236
 
        case *ast.SelectorExpr:
237
 
                f.walkCall(call, x.Sel.Name)
238
 
        }
 
412
// walkRangeStmt walks a range statement.
 
413
func (f *File) walkRangeStmt(n *ast.RangeStmt) {
 
414
        checkRangeLoop(f, n)
 
415
}
 
416
 
 
417
// gofmt returns a string representation of the expression.
 
418
func (f *File) gofmt(x ast.Expr) string {
 
419
        f.b.Reset()
 
420
        printer.Fprint(&f.b, f.fset, x)
 
421
        return f.b.String()
239
422
}