]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/vet: move cmd/vet into std repo
authorRobert Griesemer <gri@golang.org>
Thu, 4 Jun 2015 19:54:58 +0000 (12:54 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 4 Jun 2015 21:22:13 +0000 (21:22 +0000)
cp -r x/tools/cmd/vet cmd/vet without any changes.
The next change will adjust the source to use std
repo go/types and friends.

This may (temporarily) break the build; the next
commit (immediately following) will fix it. We do
it in two commits so that we can see the manual
changes.

Change-Id: Ic45dab7066f13923e21f8c61200c8c3fd447b171
Reviewed-on: https://go-review.googlesource.com/10694
Reviewed-by: Rob Pike <r@golang.org>
47 files changed:
src/cmd/vet/asmdecl.go [new file with mode: 0644]
src/cmd/vet/assign.go [new file with mode: 0644]
src/cmd/vet/atomic.go [new file with mode: 0644]
src/cmd/vet/bool.go [new file with mode: 0644]
src/cmd/vet/buildtag.go [new file with mode: 0644]
src/cmd/vet/composite.go [new file with mode: 0644]
src/cmd/vet/copylock.go [new file with mode: 0644]
src/cmd/vet/deadcode.go [new file with mode: 0644]
src/cmd/vet/doc.go [new file with mode: 0644]
src/cmd/vet/main.go [new file with mode: 0644]
src/cmd/vet/method.go [new file with mode: 0644]
src/cmd/vet/nilfunc.go [new file with mode: 0644]
src/cmd/vet/print.go [new file with mode: 0644]
src/cmd/vet/rangeloop.go [new file with mode: 0644]
src/cmd/vet/shadow.go [new file with mode: 0644]
src/cmd/vet/shift.go [new file with mode: 0644]
src/cmd/vet/structtag.go [new file with mode: 0644]
src/cmd/vet/testdata/asm.go [new file with mode: 0644]
src/cmd/vet/testdata/asm1.s [new file with mode: 0644]
src/cmd/vet/testdata/asm2.s [new file with mode: 0644]
src/cmd/vet/testdata/asm3.s [new file with mode: 0644]
src/cmd/vet/testdata/asm4.s [new file with mode: 0644]
src/cmd/vet/testdata/assign.go [new file with mode: 0644]
src/cmd/vet/testdata/atomic.go [new file with mode: 0644]
src/cmd/vet/testdata/bool.go [new file with mode: 0644]
src/cmd/vet/testdata/buildtag.go [new file with mode: 0644]
src/cmd/vet/testdata/buildtag_bad.go [new file with mode: 0644]
src/cmd/vet/testdata/composite.go [new file with mode: 0644]
src/cmd/vet/testdata/copylock_func.go [new file with mode: 0644]
src/cmd/vet/testdata/copylock_range.go [new file with mode: 0644]
src/cmd/vet/testdata/deadcode.go [new file with mode: 0644]
src/cmd/vet/testdata/method.go [new file with mode: 0644]
src/cmd/vet/testdata/nilfunc.go [new file with mode: 0644]
src/cmd/vet/testdata/print.go [new file with mode: 0644]
src/cmd/vet/testdata/rangeloop.go [new file with mode: 0644]
src/cmd/vet/testdata/shadow.go [new file with mode: 0644]
src/cmd/vet/testdata/shift.go [new file with mode: 0644]
src/cmd/vet/testdata/structtag.go [new file with mode: 0644]
src/cmd/vet/testdata/tagtest/file1.go [new file with mode: 0644]
src/cmd/vet/testdata/tagtest/file2.go [new file with mode: 0644]
src/cmd/vet/testdata/unsafeptr.go [new file with mode: 0644]
src/cmd/vet/testdata/unused.go [new file with mode: 0644]
src/cmd/vet/types.go [new file with mode: 0644]
src/cmd/vet/unsafeptr.go [new file with mode: 0644]
src/cmd/vet/unused.go [new file with mode: 0644]
src/cmd/vet/vet_test.go [new file with mode: 0644]
src/cmd/vet/whitelist/whitelist.go [new file with mode: 0644]

diff --git a/src/cmd/vet/asmdecl.go b/src/cmd/vet/asmdecl.go
new file mode 100644 (file)
index 0000000..6bdfdbf
--- /dev/null
@@ -0,0 +1,662 @@
+// Copyright 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.
+
+// Identify mismatches between assembly files and Go func declarations.
+
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/token"
+       "regexp"
+       "strconv"
+       "strings"
+)
+
+// 'kind' is a kind of assembly variable.
+// The kinds 1, 2, 4, 8 stand for values of that size.
+type asmKind int
+
+// These special kinds are not valid sizes.
+const (
+       asmString asmKind = 100 + iota
+       asmSlice
+       asmInterface
+       asmEmptyInterface
+)
+
+// An asmArch describes assembly parameters for an architecture
+type asmArch struct {
+       name      string
+       ptrSize   int
+       intSize   int
+       maxAlign  int
+       bigEndian bool
+       stack     string
+       lr        bool
+}
+
+// An asmFunc describes the expected variables for a function on a given architecture.
+type asmFunc struct {
+       arch        *asmArch
+       size        int // size of all arguments
+       vars        map[string]*asmVar
+       varByOffset map[int]*asmVar
+}
+
+// An asmVar describes a single assembly variable.
+type asmVar struct {
+       name  string
+       kind  asmKind
+       typ   string
+       off   int
+       size  int
+       inner []*asmVar
+}
+
+var (
+       asmArch386       = asmArch{"386", 4, 4, 4, false, "SP", false}
+       asmArchArm       = asmArch{"arm", 4, 4, 4, false, "R13", true}
+       asmArchArm64     = asmArch{"arm64", 8, 8, 8, false, "RSP", true}
+       asmArchAmd64     = asmArch{"amd64", 8, 8, 8, false, "SP", false}
+       asmArchAmd64p32  = asmArch{"amd64p32", 4, 4, 8, false, "SP", false}
+       asmArchPower64   = asmArch{"power64", 8, 8, 8, true, "R1", true}
+       asmArchPower64LE = asmArch{"power64le", 8, 8, 8, false, "R1", true}
+
+       arches = []*asmArch{
+               &asmArch386,
+               &asmArchArm,
+               &asmArchArm64,
+               &asmArchAmd64,
+               &asmArchAmd64p32,
+               &asmArchPower64,
+               &asmArchPower64LE,
+       }
+)
+
+var (
+       re           = regexp.MustCompile
+       asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
+       asmTEXT      = re(`\bTEXT\b.*ยท([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`)
+       asmDATA      = re(`\b(DATA|GLOBL)\b`)
+       asmNamedFP   = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
+       asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`)
+       asmSP        = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`)
+       asmOpcode    = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
+       power64Suff  = re(`([BHWD])(ZU|Z|U|BR)?$`)
+)
+
+func asmCheck(pkg *Package) {
+       if !vet("asmdecl") {
+               return
+       }
+
+       // No work if no assembly files.
+       if !pkg.hasFileWithSuffix(".s") {
+               return
+       }
+
+       // Gather declarations. knownFunc[name][arch] is func description.
+       knownFunc := make(map[string]map[string]*asmFunc)
+
+       for _, f := range pkg.files {
+               if f.file != nil {
+                       for _, decl := range f.file.Decls {
+                               if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
+                                       knownFunc[decl.Name.Name] = f.asmParseDecl(decl)
+                               }
+                       }
+               }
+       }
+
+Files:
+       for _, f := range pkg.files {
+               if !strings.HasSuffix(f.name, ".s") {
+                       continue
+               }
+               Println("Checking file", f.name)
+
+               // Determine architecture from file name if possible.
+               var arch string
+               var archDef *asmArch
+               for _, a := range arches {
+                       if strings.HasSuffix(f.name, "_"+a.name+".s") {
+                               arch = a.name
+                               archDef = a
+                               break
+                       }
+               }
+
+               lines := strings.SplitAfter(string(f.content), "\n")
+               var (
+                       fn                 *asmFunc
+                       fnName             string
+                       localSize, argSize int
+                       wroteSP            bool
+                       haveRetArg         bool
+                       retLine            []int
+               )
+
+               flushRet := func() {
+                       if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
+                               v := fn.vars["ret"]
+                               for _, line := range retLine {
+                                       f.Badf(token.NoPos, "%s:%d: [%s] %s: RET without writing to %d-byte ret+%d(FP)", f.name, line, arch, fnName, v.size, v.off)
+                               }
+                       }
+                       retLine = nil
+               }
+               for lineno, line := range lines {
+                       lineno++
+
+                       badf := func(format string, args ...interface{}) {
+                               f.Badf(token.NoPos, "%s:%d: [%s] %s: %s", f.name, lineno, arch, fnName, fmt.Sprintf(format, args...))
+                       }
+
+                       if arch == "" {
+                               // Determine architecture from +build line if possible.
+                               if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
+                               Fields:
+                                       for _, fld := range strings.Fields(m[1]) {
+                                               for _, a := range arches {
+                                                       if a.name == fld {
+                                                               arch = a.name
+                                                               archDef = a
+                                                               break Fields
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       if m := asmTEXT.FindStringSubmatch(line); m != nil {
+                               flushRet()
+                               if arch == "" {
+                                       f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
+                                       continue Files
+                               }
+                               fnName = m[1]
+                               fn = knownFunc[m[1]][arch]
+                               if fn != nil {
+                                       size, _ := strconv.Atoi(m[4])
+                                       if size != fn.size && (m[2] != "7" && !strings.Contains(m[2], "NOSPLIT") || size != 0) {
+                                               badf("wrong argument size %d; expected $...-%d", size, fn.size)
+                                       }
+                               }
+                               localSize, _ = strconv.Atoi(m[3])
+                               localSize += archDef.intSize
+                               if archDef.lr {
+                                       // Account for caller's saved LR
+                                       localSize += archDef.intSize
+                               }
+                               argSize, _ = strconv.Atoi(m[4])
+                               if fn == nil && !strings.Contains(fnName, "<>") {
+                                       badf("function %s missing Go declaration", fnName)
+                               }
+                               wroteSP = false
+                               haveRetArg = false
+                               continue
+                       } else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") {
+                               // function, but not visible from Go (didn't match asmTEXT), so stop checking
+                               flushRet()
+                               fn = nil
+                               fnName = ""
+                               continue
+                       }
+
+                       if strings.Contains(line, "RET") {
+                               retLine = append(retLine, lineno)
+                       }
+
+                       if fnName == "" {
+                               continue
+                       }
+
+                       if asmDATA.FindStringSubmatch(line) != nil {
+                               fn = nil
+                       }
+
+                       if archDef == nil {
+                               continue
+                       }
+
+                       if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) {
+                               wroteSP = true
+                               continue
+                       }
+
+                       for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
+                               if m[3] != archDef.stack || wroteSP {
+                                       continue
+                               }
+                               off := 0
+                               if m[1] != "" {
+                                       off, _ = strconv.Atoi(m[2])
+                               }
+                               if off >= localSize {
+                                       if fn != nil {
+                                               v := fn.varByOffset[off-localSize]
+                                               if v != nil {
+                                                       badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize)
+                                                       continue
+                                               }
+                                       }
+                                       if off >= localSize+argSize {
+                                               badf("use of %s points beyond argument frame", m[1])
+                                               continue
+                                       }
+                                       badf("use of %s to access argument frame", m[1])
+                               }
+                       }
+
+                       if fn == nil {
+                               continue
+                       }
+
+                       for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
+                               off, _ := strconv.Atoi(m[2])
+                               v := fn.varByOffset[off]
+                               if v != nil {
+                                       badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off)
+                               } else {
+                                       badf("use of unnamed argument %s", m[1])
+                               }
+                       }
+
+                       for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
+                               name := m[1]
+                               off := 0
+                               if m[2] != "" {
+                                       off, _ = strconv.Atoi(m[2])
+                               }
+                               if name == "ret" || strings.HasPrefix(name, "ret_") {
+                                       haveRetArg = true
+                               }
+                               v := fn.vars[name]
+                               if v == nil {
+                                       // Allow argframe+0(FP).
+                                       if name == "argframe" && off == 0 {
+                                               continue
+                                       }
+                                       v = fn.varByOffset[off]
+                                       if v != nil {
+                                               badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
+                                       } else {
+                                               badf("unknown variable %s", name)
+                                       }
+                                       continue
+                               }
+                               asmCheckVar(badf, fn, line, m[0], off, v)
+                       }
+               }
+               flushRet()
+       }
+}
+
+// asmParseDecl parses a function decl for expected assembly variables.
+func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc {
+       var (
+               arch   *asmArch
+               fn     *asmFunc
+               offset int
+               failed bool
+       )
+
+       addVar := func(outer string, v asmVar) {
+               if vo := fn.vars[outer]; vo != nil {
+                       vo.inner = append(vo.inner, &v)
+               }
+               fn.vars[v.name] = &v
+               for i := 0; i < v.size; i++ {
+                       fn.varByOffset[v.off+i] = &v
+               }
+       }
+
+       addParams := func(list []*ast.Field) {
+               for i, fld := range list {
+                       // Determine alignment, size, and kind of type in declaration.
+                       var align, size int
+                       var kind asmKind
+                       names := fld.Names
+                       typ := f.gofmt(fld.Type)
+                       switch t := fld.Type.(type) {
+                       default:
+                               switch typ {
+                               default:
+                                       f.Warnf(fld.Type.Pos(), "unknown assembly argument type %s", typ)
+                                       failed = true
+                                       return
+                               case "int8", "uint8", "byte", "bool":
+                                       size = 1
+                               case "int16", "uint16":
+                                       size = 2
+                               case "int32", "uint32", "float32":
+                                       size = 4
+                               case "int64", "uint64", "float64":
+                                       align = arch.maxAlign
+                                       size = 8
+                               case "int", "uint":
+                                       size = arch.intSize
+                               case "uintptr", "iword", "Word", "Errno", "unsafe.Pointer":
+                                       size = arch.ptrSize
+                               case "string", "ErrorString":
+                                       size = arch.ptrSize * 2
+                                       align = arch.ptrSize
+                                       kind = asmString
+                               }
+                       case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.StarExpr:
+                               size = arch.ptrSize
+                       case *ast.InterfaceType:
+                               align = arch.ptrSize
+                               size = 2 * arch.ptrSize
+                               if len(t.Methods.List) > 0 {
+                                       kind = asmInterface
+                               } else {
+                                       kind = asmEmptyInterface
+                               }
+                       case *ast.ArrayType:
+                               if t.Len == nil {
+                                       size = arch.ptrSize + 2*arch.intSize
+                                       align = arch.ptrSize
+                                       kind = asmSlice
+                                       break
+                               }
+                               f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
+                               failed = true
+                       case *ast.StructType:
+                               f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
+                               failed = true
+                       }
+                       if align == 0 {
+                               align = size
+                       }
+                       if kind == 0 {
+                               kind = asmKind(size)
+                       }
+                       offset += -offset & (align - 1)
+
+                       // Create variable for each name being declared with this type.
+                       if len(names) == 0 {
+                               name := "unnamed"
+                               if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 && &list[0] == &decl.Type.Results.List[0] && i == 0 {
+                                       // Assume assembly will refer to single unnamed result as r.
+                                       name = "ret"
+                               }
+                               names = []*ast.Ident{{Name: name}}
+                       }
+                       for _, id := range names {
+                               name := id.Name
+                               addVar("", asmVar{
+                                       name: name,
+                                       kind: kind,
+                                       typ:  typ,
+                                       off:  offset,
+                                       size: size,
+                               })
+                               switch kind {
+                               case 8:
+                                       if arch.ptrSize == 4 {
+                                               w1, w2 := "lo", "hi"
+                                               if arch.bigEndian {
+                                                       w1, w2 = w2, w1
+                                               }
+                                               addVar(name, asmVar{
+                                                       name: name + "_" + w1,
+                                                       kind: 4,
+                                                       typ:  "half " + typ,
+                                                       off:  offset,
+                                                       size: 4,
+                                               })
+                                               addVar(name, asmVar{
+                                                       name: name + "_" + w2,
+                                                       kind: 4,
+                                                       typ:  "half " + typ,
+                                                       off:  offset + 4,
+                                                       size: 4,
+                                               })
+                                       }
+
+                               case asmEmptyInterface:
+                                       addVar(name, asmVar{
+                                               name: name + "_type",
+                                               kind: asmKind(arch.ptrSize),
+                                               typ:  "interface type",
+                                               off:  offset,
+                                               size: arch.ptrSize,
+                                       })
+                                       addVar(name, asmVar{
+                                               name: name + "_data",
+                                               kind: asmKind(arch.ptrSize),
+                                               typ:  "interface data",
+                                               off:  offset + arch.ptrSize,
+                                               size: arch.ptrSize,
+                                       })
+
+                               case asmInterface:
+                                       addVar(name, asmVar{
+                                               name: name + "_itable",
+                                               kind: asmKind(arch.ptrSize),
+                                               typ:  "interface itable",
+                                               off:  offset,
+                                               size: arch.ptrSize,
+                                       })
+                                       addVar(name, asmVar{
+                                               name: name + "_data",
+                                               kind: asmKind(arch.ptrSize),
+                                               typ:  "interface data",
+                                               off:  offset + arch.ptrSize,
+                                               size: arch.ptrSize,
+                                       })
+
+                               case asmSlice:
+                                       addVar(name, asmVar{
+                                               name: name + "_base",
+                                               kind: asmKind(arch.ptrSize),
+                                               typ:  "slice base",
+                                               off:  offset,
+                                               size: arch.ptrSize,
+                                       })
+                                       addVar(name, asmVar{
+                                               name: name + "_len",
+                                               kind: asmKind(arch.intSize),
+                                               typ:  "slice len",
+                                               off:  offset + arch.ptrSize,
+                                               size: arch.intSize,
+                                       })
+                                       addVar(name, asmVar{
+                                               name: name + "_cap",
+                                               kind: asmKind(arch.intSize),
+                                               typ:  "slice cap",
+                                               off:  offset + arch.ptrSize + arch.intSize,
+                                               size: arch.intSize,
+                                       })
+
+                               case asmString:
+                                       addVar(name, asmVar{
+                                               name: name + "_base",
+                                               kind: asmKind(arch.ptrSize),
+                                               typ:  "string base",
+                                               off:  offset,
+                                               size: arch.ptrSize,
+                                       })
+                                       addVar(name, asmVar{
+                                               name: name + "_len",
+                                               kind: asmKind(arch.intSize),
+                                               typ:  "string len",
+                                               off:  offset + arch.ptrSize,
+                                               size: arch.intSize,
+                                       })
+                               }
+                               offset += size
+                       }
+               }
+       }
+
+       m := make(map[string]*asmFunc)
+       for _, arch = range arches {
+               fn = &asmFunc{
+                       arch:        arch,
+                       vars:        make(map[string]*asmVar),
+                       varByOffset: make(map[int]*asmVar),
+               }
+               offset = 0
+               addParams(decl.Type.Params.List)
+               if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
+                       offset += -offset & (arch.maxAlign - 1)
+                       addParams(decl.Type.Results.List)
+               }
+               fn.size = offset
+               m[arch.name] = fn
+       }
+
+       if failed {
+               return nil
+       }
+       return m
+}
+
+// asmCheckVar checks a single variable reference.
+func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
+       m := asmOpcode.FindStringSubmatch(line)
+       if m == nil {
+               if !strings.HasPrefix(strings.TrimSpace(line), "//") {
+                       badf("cannot find assembly opcode")
+               }
+               return
+       }
+
+       // Determine operand sizes from instruction.
+       // Typically the suffix suffices, but there are exceptions.
+       var src, dst, kind asmKind
+       op := m[1]
+       switch fn.arch.name + "." + op {
+       case "386.FMOVLP":
+               src, dst = 8, 4
+       case "arm.MOVD":
+               src = 8
+       case "arm.MOVW":
+               src = 4
+       case "arm.MOVH", "arm.MOVHU":
+               src = 2
+       case "arm.MOVB", "arm.MOVBU":
+               src = 1
+       // LEA* opcodes don't really read the second arg.
+       // They just take the address of it.
+       case "386.LEAL":
+               dst = 4
+       case "amd64.LEAQ":
+               dst = 8
+       case "amd64p32.LEAL":
+               dst = 4
+       default:
+               switch fn.arch.name {
+               case "386", "amd64":
+                       if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
+                               // FMOVDP, FXCHD, etc
+                               src = 8
+                               break
+                       }
+                       if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
+                               // FMOVFP, FXCHF, etc
+                               src = 4
+                               break
+                       }
+                       if strings.HasSuffix(op, "SD") {
+                               // MOVSD, SQRTSD, etc
+                               src = 8
+                               break
+                       }
+                       if strings.HasSuffix(op, "SS") {
+                               // MOVSS, SQRTSS, etc
+                               src = 4
+                               break
+                       }
+                       if strings.HasPrefix(op, "SET") {
+                               // SETEQ, etc
+                               src = 1
+                               break
+                       }
+                       switch op[len(op)-1] {
+                       case 'B':
+                               src = 1
+                       case 'W':
+                               src = 2
+                       case 'L':
+                               src = 4
+                       case 'D', 'Q':
+                               src = 8
+                       }
+               case "power64", "power64le":
+                       // Strip standard suffixes to reveal size letter.
+                       m := power64Suff.FindStringSubmatch(op)
+                       if m != nil {
+                               switch m[1][0] {
+                               case 'B':
+                                       src = 1
+                               case 'H':
+                                       src = 2
+                               case 'W':
+                                       src = 4
+                               case 'D':
+                                       src = 8
+                               }
+                       }
+               }
+       }
+       if dst == 0 {
+               dst = src
+       }
+
+       // Determine whether the match we're holding
+       // is the first or second argument.
+       if strings.Index(line, expr) > strings.Index(line, ",") {
+               kind = dst
+       } else {
+               kind = src
+       }
+
+       vk := v.kind
+       vt := v.typ
+       switch vk {
+       case asmInterface, asmEmptyInterface, asmString, asmSlice:
+               // allow reference to first word (pointer)
+               vk = v.inner[0].kind
+               vt = v.inner[0].typ
+       }
+
+       if off != v.off {
+               var inner bytes.Buffer
+               for i, vi := range v.inner {
+                       if len(v.inner) > 1 {
+                               fmt.Fprintf(&inner, ",")
+                       }
+                       fmt.Fprintf(&inner, " ")
+                       if i == len(v.inner)-1 {
+                               fmt.Fprintf(&inner, "or ")
+                       }
+                       fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
+               }
+               badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
+               return
+       }
+       if kind != 0 && kind != vk {
+               var inner bytes.Buffer
+               if len(v.inner) > 0 {
+                       fmt.Fprintf(&inner, " containing")
+                       for i, vi := range v.inner {
+                               if i > 0 && len(v.inner) > 2 {
+                                       fmt.Fprintf(&inner, ",")
+                               }
+                               fmt.Fprintf(&inner, " ")
+                               if i > 0 && i == len(v.inner)-1 {
+                                       fmt.Fprintf(&inner, "and ")
+                               }
+                               fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
+                       }
+               }
+               badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vk, inner.String())
+       }
+}
diff --git a/src/cmd/vet/assign.go b/src/cmd/vet/assign.go
new file mode 100644 (file)
index 0000000..54c1ae1
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 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.
+
+/*
+This file contains the code to check for useless assignments.
+*/
+
+package main
+
+import (
+       "go/ast"
+       "go/token"
+       "reflect"
+)
+
+func init() {
+       register("assign",
+               "check for useless assignments",
+               checkAssignStmt,
+               assignStmt)
+}
+
+// TODO: should also check for assignments to struct fields inside methods
+// that are on T instead of *T.
+
+// checkAssignStmt checks for assignments of the form "<expr> = <expr>".
+// These are almost always useless, and even when they aren't they are usually a mistake.
+func checkAssignStmt(f *File, node ast.Node) {
+       stmt := node.(*ast.AssignStmt)
+       if stmt.Tok != token.ASSIGN {
+               return // ignore :=
+       }
+       if len(stmt.Lhs) != len(stmt.Rhs) {
+               // If LHS and RHS have different cardinality, they can't be the same.
+               return
+       }
+       for i, lhs := range stmt.Lhs {
+               rhs := stmt.Rhs[i]
+               if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
+                       continue // short-circuit the heavy-weight gofmt check
+               }
+               le := f.gofmt(lhs)
+               re := f.gofmt(rhs)
+               if le == re {
+                       f.Badf(stmt.Pos(), "self-assignment of %s to %s", re, le)
+               }
+       }
+}
diff --git a/src/cmd/vet/atomic.go b/src/cmd/vet/atomic.go
new file mode 100644 (file)
index 0000000..c084f13
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright 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.
+
+package main
+
+import (
+       "go/ast"
+       "go/token"
+)
+
+func init() {
+       register("atomic",
+               "check for common mistaken usages of the sync/atomic package",
+               checkAtomicAssignment,
+               assignStmt)
+}
+
+// checkAtomicAssignment walks the assignment statement checking for common
+// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1)
+func checkAtomicAssignment(f *File, node ast.Node) {
+       n := node.(*ast.AssignStmt)
+       if len(n.Lhs) != len(n.Rhs) {
+               return
+       }
+
+       for i, right := range n.Rhs {
+               call, ok := right.(*ast.CallExpr)
+               if !ok {
+                       continue
+               }
+               sel, ok := call.Fun.(*ast.SelectorExpr)
+               if !ok {
+                       continue
+               }
+               pkg, ok := sel.X.(*ast.Ident)
+               if !ok || pkg.Name != "atomic" {
+                       continue
+               }
+
+               switch sel.Sel.Name {
+               case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
+                       f.checkAtomicAddAssignment(n.Lhs[i], call)
+               }
+       }
+}
+
+// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value
+// to the same variable being used in the operation
+func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) {
+       if len(call.Args) != 2 {
+               return
+       }
+       arg := call.Args[0]
+       broken := false
+
+       if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
+               broken = f.gofmt(left) == f.gofmt(uarg.X)
+       } else if star, ok := left.(*ast.StarExpr); ok {
+               broken = f.gofmt(star.X) == f.gofmt(arg)
+       }
+
+       if broken {
+               f.Bad(left.Pos(), "direct assignment to atomic value")
+       }
+}
diff --git a/src/cmd/vet/bool.go b/src/cmd/vet/bool.go
new file mode 100644 (file)
index 0000000..07c2a93
--- /dev/null
@@ -0,0 +1,186 @@
+// Copyright 2014 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.
+
+// This file contains boolean condition tests.
+
+package main
+
+import (
+       "go/ast"
+       "go/token"
+)
+
+func init() {
+       register("bool",
+               "check for mistakes involving boolean operators",
+               checkBool,
+               binaryExpr)
+}
+
+func checkBool(f *File, n ast.Node) {
+       e := n.(*ast.BinaryExpr)
+
+       var op boolOp
+       switch e.Op {
+       case token.LOR:
+               op = or
+       case token.LAND:
+               op = and
+       default:
+               return
+       }
+
+       comm := op.commutativeSets(e)
+       for _, exprs := range comm {
+               op.checkRedundant(f, exprs)
+               op.checkSuspect(f, exprs)
+       }
+}
+
+type boolOp struct {
+       name  string
+       tok   token.Token // token corresponding to this operator
+       badEq token.Token // token corresponding to the equality test that should not be used with this operator
+}
+
+var (
+       or  = boolOp{"or", token.LOR, token.NEQ}
+       and = boolOp{"and", token.LAND, token.EQL}
+)
+
+// commutativeSets returns all side effect free sets of
+// expressions in e that are connected by op.
+// For example, given 'a || b || f() || c || d' with the or op,
+// commutativeSets returns {{b, a}, {d, c}}.
+func (op boolOp) commutativeSets(e *ast.BinaryExpr) [][]ast.Expr {
+       exprs := op.split(e)
+
+       // Partition the slice of expressions into commutative sets.
+       i := 0
+       var sets [][]ast.Expr
+       for j := 0; j <= len(exprs); j++ {
+               if j == len(exprs) || hasSideEffects(exprs[j]) {
+                       if i < j {
+                               sets = append(sets, exprs[i:j])
+                       }
+                       i = j + 1
+               }
+       }
+
+       return sets
+}
+
+// checkRedundant checks for expressions of the form
+//   e && e
+//   e || e
+// Exprs must contain only side effect free expressions.
+func (op boolOp) checkRedundant(f *File, exprs []ast.Expr) {
+       seen := make(map[string]bool)
+       for _, e := range exprs {
+               efmt := f.gofmt(e)
+               if seen[efmt] {
+                       f.Badf(e.Pos(), "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt)
+               } else {
+                       seen[efmt] = true
+               }
+       }
+}
+
+// checkSuspect checks for expressions of the form
+//   x != c1 || x != c2
+//   x == c1 && x == c2
+// where c1 and c2 are constant expressions.
+// If c1 and c2 are the same then it's redundant;
+// if c1 and c2 are different then it's always true or always false.
+// Exprs must contain only side effect free expressions.
+func (op boolOp) checkSuspect(f *File, exprs []ast.Expr) {
+       // seen maps from expressions 'x' to equality expressions 'x != c'.
+       seen := make(map[string]string)
+
+       for _, e := range exprs {
+               bin, ok := e.(*ast.BinaryExpr)
+               if !ok || bin.Op != op.badEq {
+                       continue
+               }
+
+               // In order to avoid false positives, restrict to cases
+               // in which one of the operands is constant. We're then
+               // interested in the other operand.
+               // In the rare case in which both operands are constant
+               // (e.g. runtime.GOOS and "windows"), we'll only catch
+               // mistakes if the LHS is repeated, which is how most
+               // code is written.
+               var x ast.Expr
+               switch {
+               case f.pkg.types[bin.Y].Value != nil:
+                       x = bin.X
+               case f.pkg.types[bin.X].Value != nil:
+                       x = bin.Y
+               default:
+                       continue
+               }
+
+               // e is of the form 'x != c' or 'x == c'.
+               xfmt := f.gofmt(x)
+               efmt := f.gofmt(e)
+               if prev, found := seen[xfmt]; found {
+                       // checkRedundant handles the case in which efmt == prev.
+                       if efmt != prev {
+                               f.Badf(e.Pos(), "suspect %s: %s %s %s", op.name, efmt, op.tok, prev)
+                       }
+               } else {
+                       seen[xfmt] = efmt
+               }
+       }
+}
+
+// hasSideEffects reports whether evaluation of e has side effects.
+func hasSideEffects(e ast.Expr) bool {
+       safe := true
+       ast.Inspect(e, func(node ast.Node) bool {
+               switch n := node.(type) {
+               // Using CallExpr here will catch conversions
+               // as well as function and method invocations.
+               // We'll live with the false negatives for now.
+               case *ast.CallExpr:
+                       safe = false
+                       return false
+               case *ast.UnaryExpr:
+                       if n.Op == token.ARROW {
+                               safe = false
+                               return false
+                       }
+               }
+               return true
+       })
+       return !safe
+}
+
+// split returns a slice of all subexpressions in e that are connected by op.
+// For example, given 'a || (b || c) || d' with the or op,
+// split returns []{d, c, b, a}.
+func (op boolOp) split(e ast.Expr) (exprs []ast.Expr) {
+       for {
+               e = unparen(e)
+               if b, ok := e.(*ast.BinaryExpr); ok && b.Op == op.tok {
+                       exprs = append(exprs, op.split(b.Y)...)
+                       e = b.X
+               } else {
+                       exprs = append(exprs, e)
+                       break
+               }
+       }
+       return
+}
+
+// unparen returns e with any enclosing parentheses stripped.
+func unparen(e ast.Expr) ast.Expr {
+       for {
+               p, ok := e.(*ast.ParenExpr)
+               if !ok {
+                       return e
+               }
+               e = p.X
+       }
+}
diff --git a/src/cmd/vet/buildtag.go b/src/cmd/vet/buildtag.go
new file mode 100644 (file)
index 0000000..2d86edf
--- /dev/null
@@ -0,0 +1,91 @@
+// Copyright 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.
+
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "os"
+       "strings"
+       "unicode"
+)
+
+var (
+       nl         = []byte("\n")
+       slashSlash = []byte("//")
+       plusBuild  = []byte("+build")
+)
+
+// checkBuildTag checks that build tags are in the correct location and well-formed.
+func checkBuildTag(name string, data []byte) {
+       if !vet("buildtags") {
+               return
+       }
+       lines := bytes.SplitAfter(data, nl)
+
+       // Determine cutpoint where +build comments are no longer valid.
+       // They are valid in leading // comments in the file followed by
+       // a blank line.
+       var cutoff int
+       for i, line := range lines {
+               line = bytes.TrimSpace(line)
+               if len(line) == 0 {
+                       cutoff = i
+                       continue
+               }
+               if bytes.HasPrefix(line, slashSlash) {
+                       continue
+               }
+               break
+       }
+
+       for i, line := range lines {
+               line = bytes.TrimSpace(line)
+               if !bytes.HasPrefix(line, slashSlash) {
+                       continue
+               }
+               text := bytes.TrimSpace(line[2:])
+               if bytes.HasPrefix(text, plusBuild) {
+                       fields := bytes.Fields(text)
+                       if !bytes.Equal(fields[0], plusBuild) {
+                               // Comment is something like +buildasdf not +build.
+                               fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
+                               continue
+                       }
+                       if i >= cutoff {
+                               fmt.Fprintf(os.Stderr, "%s:%d: +build comment must appear before package clause and be followed by a blank line\n", name, i+1)
+                               setExit(1)
+                               continue
+                       }
+                       // Check arguments.
+               Args:
+                       for _, arg := range fields[1:] {
+                               for _, elem := range strings.Split(string(arg), ",") {
+                                       if strings.HasPrefix(elem, "!!") {
+                                               fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg)
+                                               setExit(1)
+                                               break Args
+                                       }
+                                       if strings.HasPrefix(elem, "!") {
+                                               elem = elem[1:]
+                                       }
+                                       for _, c := range elem {
+                                               if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
+                                                       fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg)
+                                                       setExit(1)
+                                                       break Args
+                                               }
+                                       }
+                               }
+                       }
+                       continue
+               }
+               // Comment with +build but not at beginning.
+               if bytes.Contains(line, plusBuild) && i < cutoff {
+                       fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
+                       continue
+               }
+       }
+}
diff --git a/src/cmd/vet/composite.go b/src/cmd/vet/composite.go
new file mode 100644 (file)
index 0000000..0c3f916
--- /dev/null
@@ -0,0 +1,125 @@
+// Copyright 2012 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.
+
+// This file contains the test for unkeyed struct literals.
+
+package main
+
+import (
+       "flag"
+       "go/ast"
+       "strings"
+
+       "golang.org/x/tools/cmd/vet/whitelist"
+)
+
+var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only")
+
+func init() {
+       register("composites",
+               "check that composite literals used field-keyed elements",
+               checkUnkeyedLiteral,
+               compositeLit)
+}
+
+// checkUnkeyedLiteral checks if a composite literal is a struct literal with
+// unkeyed fields.
+func checkUnkeyedLiteral(f *File, node ast.Node) {
+       c := node.(*ast.CompositeLit)
+       typ := c.Type
+       for {
+               if typ1, ok := c.Type.(*ast.ParenExpr); ok {
+                       typ = typ1
+                       continue
+               }
+               break
+       }
+
+       switch typ.(type) {
+       case *ast.ArrayType:
+               return
+       case *ast.MapType:
+               return
+       case *ast.StructType:
+               return // a literal struct type does not need to use keys
+       case *ast.Ident:
+               // A simple type name like t or T does not need keys either,
+               // since it is almost certainly declared in the current package.
+               // (The exception is names being used via import . "pkg", but
+               // those are already breaking the Go 1 compatibility promise,
+               // so not reporting potential additional breakage seems okay.)
+               return
+       }
+
+       // Otherwise the type is a selector like pkg.Name.
+       // We only care if pkg.Name is a struct, not if it's a map, array, or slice.
+       isStruct, typeString := f.pkg.isStruct(c)
+       if !isStruct {
+               return
+       }
+
+       if typeString == "" { // isStruct doesn't know
+               typeString = f.gofmt(typ)
+       }
+
+       // It's a struct, or we can't tell it's not a struct because we don't have types.
+
+       // Check if the CompositeLit contains an unkeyed field.
+       allKeyValue := true
+       for _, e := range c.Elts {
+               if _, ok := e.(*ast.KeyValueExpr); !ok {
+                       allKeyValue = false
+                       break
+               }
+       }
+       if allKeyValue {
+               return
+       }
+
+       // Check that the CompositeLit's type has the form pkg.Typ.
+       s, ok := c.Type.(*ast.SelectorExpr)
+       if !ok {
+               return
+       }
+       pkg, ok := s.X.(*ast.Ident)
+       if !ok {
+               return
+       }
+
+       // Convert the package name to an import path, and compare to a whitelist.
+       path := pkgPath(f, pkg.Name)
+       if path == "" {
+               f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name)
+               return
+       }
+       typeName := path + "." + s.Sel.Name
+       if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] {
+               return
+       }
+
+       f.Bad(c.Pos(), typeString+" composite literal uses unkeyed fields")
+}
+
+// pkgPath returns the import path "image/png" for the package name "png".
+//
+// This is based purely on syntax and convention, and not on the imported
+// package's contents. It will be incorrect if a package name differs from the
+// leaf element of the import path, or if the package was a dot import.
+func pkgPath(f *File, pkgName string) (path string) {
+       for _, x := range f.file.Imports {
+               s := strings.Trim(x.Path.Value, `"`)
+               if x.Name != nil {
+                       // Catch `import pkgName "foo/bar"`.
+                       if x.Name.Name == pkgName {
+                               return s
+                       }
+               } else {
+                       // Catch `import "pkgName"` or `import "foo/bar/pkgName"`.
+                       if s == pkgName || strings.HasSuffix(s, "/"+pkgName) {
+                               return s
+                       }
+               }
+       }
+       return ""
+}
diff --git a/src/cmd/vet/copylock.go b/src/cmd/vet/copylock.go
new file mode 100644 (file)
index 0000000..e8a6820
--- /dev/null
@@ -0,0 +1,155 @@
+// Copyright 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.
+
+// This file contains the code to check that locks are not passed by value.
+
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/token"
+
+       "golang.org/x/tools/go/types"
+)
+
+func init() {
+       register("copylocks",
+               "check that locks are not passed by value",
+               checkCopyLocks,
+               funcDecl, rangeStmt)
+}
+
+// checkCopyLocks checks whether node might
+// inadvertently copy a lock.
+func checkCopyLocks(f *File, node ast.Node) {
+       switch node := node.(type) {
+       case *ast.RangeStmt:
+               checkCopyLocksRange(f, node)
+       case *ast.FuncDecl:
+               checkCopyLocksFunc(f, node)
+       }
+}
+
+// checkCopyLocksFunc checks whether a function might
+// inadvertently copy a lock, by checking whether
+// its receiver, parameters, or return values
+// are locks.
+func checkCopyLocksFunc(f *File, d *ast.FuncDecl) {
+       if d.Recv != nil && len(d.Recv.List) > 0 {
+               expr := d.Recv.List[0].Type
+               if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
+                       f.Badf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path)
+               }
+       }
+
+       if d.Type.Params != nil {
+               for _, field := range d.Type.Params.List {
+                       expr := field.Type
+                       if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
+                               f.Badf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path)
+                       }
+               }
+       }
+
+       if d.Type.Results != nil {
+               for _, field := range d.Type.Results.List {
+                       expr := field.Type
+                       if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
+                               f.Badf(expr.Pos(), "%s returns Lock by value: %v", d.Name.Name, path)
+                       }
+               }
+       }
+}
+
+// checkCopyLocksRange checks whether a range statement
+// might inadvertently copy a lock by checking whether
+// any of the range variables are locks.
+func checkCopyLocksRange(f *File, r *ast.RangeStmt) {
+       checkCopyLocksRangeVar(f, r.Tok, r.Key)
+       checkCopyLocksRangeVar(f, r.Tok, r.Value)
+}
+
+func checkCopyLocksRangeVar(f *File, rtok token.Token, e ast.Expr) {
+       if e == nil {
+               return
+       }
+       id, isId := e.(*ast.Ident)
+       if isId && id.Name == "_" {
+               return
+       }
+
+       var typ types.Type
+       if rtok == token.DEFINE {
+               if !isId {
+                       return
+               }
+               obj := f.pkg.defs[id]
+               if obj == nil {
+                       return
+               }
+               typ = obj.Type()
+       } else {
+               typ = f.pkg.types[e].Type
+       }
+
+       if typ == nil {
+               return
+       }
+       if path := lockPath(f.pkg.typesPkg, typ); path != nil {
+               f.Badf(e.Pos(), "range var %s copies Lock: %v", f.gofmt(e), path)
+       }
+}
+
+type typePath []types.Type
+
+// String pretty-prints a typePath.
+func (path typePath) String() string {
+       n := len(path)
+       var buf bytes.Buffer
+       for i := range path {
+               if i > 0 {
+                       fmt.Fprint(&buf, " contains ")
+               }
+               // The human-readable path is in reverse order, outermost to innermost.
+               fmt.Fprint(&buf, path[n-i-1].String())
+       }
+       return buf.String()
+}
+
+// lockPath returns a typePath describing the location of a lock value
+// contained in typ. If there is no contained lock, it returns nil.
+func lockPath(tpkg *types.Package, typ types.Type) typePath {
+       if typ == nil {
+               return nil
+       }
+
+       // We're only interested in the case in which the underlying
+       // type is a struct. (Interfaces and pointers are safe to copy.)
+       styp, ok := typ.Underlying().(*types.Struct)
+       if !ok {
+               return nil
+       }
+
+       // We're looking for cases in which a reference to this type
+       // can be locked, but a value cannot. This differentiates
+       // embedded interfaces from embedded values.
+       if plock := types.NewMethodSet(types.NewPointer(typ)).Lookup(tpkg, "Lock"); plock != nil {
+               if lock := types.NewMethodSet(typ).Lookup(tpkg, "Lock"); lock == nil {
+                       return []types.Type{typ}
+               }
+       }
+
+       nfields := styp.NumFields()
+       for i := 0; i < nfields; i++ {
+               ftyp := styp.Field(i).Type()
+               subpath := lockPath(tpkg, ftyp)
+               if subpath != nil {
+                       return append(subpath, typ)
+               }
+       }
+
+       return nil
+}
diff --git a/src/cmd/vet/deadcode.go b/src/cmd/vet/deadcode.go
new file mode 100644 (file)
index 0000000..3b306c2
--- /dev/null
@@ -0,0 +1,296 @@
+// Copyright 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.
+
+// Check for syntactically unreachable code.
+
+package main
+
+import (
+       "go/ast"
+       "go/token"
+)
+
+func init() {
+       register("unreachable",
+               "check for unreachable code",
+               checkUnreachable,
+               funcDecl, funcLit)
+}
+
+type deadState struct {
+       f           *File
+       hasBreak    map[ast.Stmt]bool
+       hasGoto     map[string]bool
+       labels      map[string]ast.Stmt
+       breakTarget ast.Stmt
+
+       reachable bool
+}
+
+// checkUnreachable checks a function body for dead code.
+func checkUnreachable(f *File, node ast.Node) {
+       var body *ast.BlockStmt
+       switch n := node.(type) {
+       case *ast.FuncDecl:
+               body = n.Body
+       case *ast.FuncLit:
+               body = n.Body
+       }
+       if body == nil {
+               return
+       }
+
+       d := &deadState{
+               f:        f,
+               hasBreak: make(map[ast.Stmt]bool),
+               hasGoto:  make(map[string]bool),
+               labels:   make(map[string]ast.Stmt),
+       }
+
+       d.findLabels(body)
+
+       d.reachable = true
+       d.findDead(body)
+}
+
+// findLabels gathers information about the labels defined and used by stmt
+// and about which statements break, whether a label is involved or not.
+func (d *deadState) findLabels(stmt ast.Stmt) {
+       switch x := stmt.(type) {
+       default:
+               d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x)
+
+       case *ast.AssignStmt,
+               *ast.BadStmt,
+               *ast.DeclStmt,
+               *ast.DeferStmt,
+               *ast.EmptyStmt,
+               *ast.ExprStmt,
+               *ast.GoStmt,
+               *ast.IncDecStmt,
+               *ast.ReturnStmt,
+               *ast.SendStmt:
+               // no statements inside
+
+       case *ast.BlockStmt:
+               for _, stmt := range x.List {
+                       d.findLabels(stmt)
+               }
+
+       case *ast.BranchStmt:
+               switch x.Tok {
+               case token.GOTO:
+                       if x.Label != nil {
+                               d.hasGoto[x.Label.Name] = true
+                       }
+
+               case token.BREAK:
+                       stmt := d.breakTarget
+                       if x.Label != nil {
+                               stmt = d.labels[x.Label.Name]
+                       }
+                       if stmt != nil {
+                               d.hasBreak[stmt] = true
+                       }
+               }
+
+       case *ast.IfStmt:
+               d.findLabels(x.Body)
+               if x.Else != nil {
+                       d.findLabels(x.Else)
+               }
+
+       case *ast.LabeledStmt:
+               d.labels[x.Label.Name] = x.Stmt
+               d.findLabels(x.Stmt)
+
+       // These cases are all the same, but the x.Body only works
+       // when the specific type of x is known, so the cases cannot
+       // be merged.
+       case *ast.ForStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.RangeStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.SelectStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.SwitchStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.TypeSwitchStmt:
+               outer := d.breakTarget
+               d.breakTarget = x
+               d.findLabels(x.Body)
+               d.breakTarget = outer
+
+       case *ast.CommClause:
+               for _, stmt := range x.Body {
+                       d.findLabels(stmt)
+               }
+
+       case *ast.CaseClause:
+               for _, stmt := range x.Body {
+                       d.findLabels(stmt)
+               }
+       }
+}
+
+// findDead walks the statement looking for dead code.
+// If d.reachable is false on entry, stmt itself is dead.
+// When findDead returns, d.reachable tells whether the
+// statement following stmt is reachable.
+func (d *deadState) findDead(stmt ast.Stmt) {
+       // Is this a labeled goto target?
+       // If so, assume it is reachable due to the goto.
+       // This is slightly conservative, in that we don't
+       // check that the goto is reachable, so
+       //      L: goto L
+       // will not provoke a warning.
+       // But it's good enough.
+       if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] {
+               d.reachable = true
+       }
+
+       if !d.reachable {
+               switch stmt.(type) {
+               case *ast.EmptyStmt:
+                       // do not warn about unreachable empty statements
+               default:
+                       d.f.Bad(stmt.Pos(), "unreachable code")
+                       d.reachable = true // silence error about next statement
+               }
+       }
+
+       switch x := stmt.(type) {
+       default:
+               d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x)
+
+       case *ast.AssignStmt,
+               *ast.BadStmt,
+               *ast.DeclStmt,
+               *ast.DeferStmt,
+               *ast.EmptyStmt,
+               *ast.GoStmt,
+               *ast.IncDecStmt,
+               *ast.SendStmt:
+               // no control flow
+
+       case *ast.BlockStmt:
+               for _, stmt := range x.List {
+                       d.findDead(stmt)
+               }
+
+       case *ast.BranchStmt:
+               switch x.Tok {
+               case token.BREAK, token.GOTO, token.FALLTHROUGH:
+                       d.reachable = false
+               case token.CONTINUE:
+                       // NOTE: We accept "continue" statements as terminating.
+                       // They are not necessary in the spec definition of terminating,
+                       // because a continue statement cannot be the final statement
+                       // before a return. But for the more general problem of syntactically
+                       // identifying dead code, continue redirects control flow just
+                       // like the other terminating statements.
+                       d.reachable = false
+               }
+
+       case *ast.ExprStmt:
+               // Call to panic?
+               call, ok := x.X.(*ast.CallExpr)
+               if ok {
+                       name, ok := call.Fun.(*ast.Ident)
+                       if ok && name.Name == "panic" && name.Obj == nil {
+                               d.reachable = false
+                       }
+               }
+
+       case *ast.ForStmt:
+               d.findDead(x.Body)
+               d.reachable = x.Cond != nil || d.hasBreak[x]
+
+       case *ast.IfStmt:
+               d.findDead(x.Body)
+               if x.Else != nil {
+                       r := d.reachable
+                       d.reachable = true
+                       d.findDead(x.Else)
+                       d.reachable = d.reachable || r
+               } else {
+                       // might not have executed if statement
+                       d.reachable = true
+               }
+
+       case *ast.LabeledStmt:
+               d.findDead(x.Stmt)
+
+       case *ast.RangeStmt:
+               d.findDead(x.Body)
+               d.reachable = true
+
+       case *ast.ReturnStmt:
+               d.reachable = false
+
+       case *ast.SelectStmt:
+               // NOTE: Unlike switch and type switch below, we don't care
+               // whether a select has a default, because a select without a
+               // default blocks until one of the cases can run. That's different
+               // from a switch without a default, which behaves like it has
+               // a default with an empty body.
+               anyReachable := false
+               for _, comm := range x.Body.List {
+                       d.reachable = true
+                       for _, stmt := range comm.(*ast.CommClause).Body {
+                               d.findDead(stmt)
+                       }
+                       anyReachable = anyReachable || d.reachable
+               }
+               d.reachable = anyReachable || d.hasBreak[x]
+
+       case *ast.SwitchStmt:
+               anyReachable := false
+               hasDefault := false
+               for _, cas := range x.Body.List {
+                       cc := cas.(*ast.CaseClause)
+                       if cc.List == nil {
+                               hasDefault = true
+                       }
+                       d.reachable = true
+                       for _, stmt := range cc.Body {
+                               d.findDead(stmt)
+                       }
+                       anyReachable = anyReachable || d.reachable
+               }
+               d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
+
+       case *ast.TypeSwitchStmt:
+               anyReachable := false
+               hasDefault := false
+               for _, cas := range x.Body.List {
+                       cc := cas.(*ast.CaseClause)
+                       if cc.List == nil {
+                               hasDefault = true
+                       }
+                       d.reachable = true
+                       for _, stmt := range cc.Body {
+                               d.findDead(stmt)
+                       }
+                       anyReachable = anyReachable || d.reachable
+               }
+               d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
+       }
+}
diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go
new file mode 100644 (file)
index 0000000..a19de3f
--- /dev/null
@@ -0,0 +1,190 @@
+// Copyright 2010 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.
+
+/*
+
+Vet examines Go source code and reports suspicious constructs, such as Printf
+calls whose arguments do not align with the format string. Vet uses heuristics
+that do not guarantee all reports are genuine problems, but it can find errors
+not caught by the compilers.
+
+It can be invoked three ways:
+
+By package, from the go tool:
+       go vet package/path/name
+vets the package whose path is provided.
+
+By files:
+       go tool vet source/directory/*.go
+vets the files named, all of which must be in the same package.
+
+By directory:
+       go tool vet source/directory
+recursively descends the directory, vetting each package it finds.
+
+Vet's exit code is 2 for erroneous invocation of the tool, 1 if a
+problem was reported, and 0 otherwise. Note that the tool does not
+check every possible problem and depends on unreliable heuristics
+so it should be used as guidance only, not as a firm indicator of
+program correctness.
+
+By default all checks are performed. If any flags are explicitly set
+to true, only those tests are run. Conversely, if any flag is
+explicitly set to false, only those tests are disabled.
+Thus -printf=true runs the printf check, -printf=false runs all checks
+except the printf check.
+
+Available checks:
+
+Printf family
+
+Flag: -printf
+
+Suspicious calls to functions in the Printf family, including any functions
+with these names, disregarding case:
+       Print Printf Println
+       Fprint Fprintf Fprintln
+       Sprint Sprintf Sprintln
+       Error Errorf
+       Fatal Fatalf
+       Log Logf
+       Panic Panicf Panicln
+If the function name ends with an 'f', the function is assumed to take
+a format descriptor string in the manner of fmt.Printf. If not, vet
+complains about arguments that look like format descriptor strings.
+
+It also checks for errors such as using a Writer as the first argument of
+Printf.
+
+Methods
+
+Flag: -methods
+
+Non-standard signatures for methods with familiar names, including:
+       Format GobEncode GobDecode MarshalJSON MarshalXML
+       Peek ReadByte ReadFrom ReadRune Scan Seek
+       UnmarshalJSON UnreadByte UnreadRune WriteByte
+       WriteTo
+
+Struct tags
+
+Flag: -structtags
+
+Struct tags that do not follow the format understood by reflect.StructTag.Get.
+Well-known encoding struct tags (json, xml) used with unexported fields.
+
+Unkeyed composite literals
+
+Flag: -composites
+
+Composite struct literals that do not use the field-keyed syntax.
+
+Assembly declarations
+
+Flag: -asmdecl
+
+Mismatches between assembly files and Go function declarations.
+
+Useless assignments
+
+Flag: -assign
+
+Check for useless assignments.
+
+Atomic mistakes
+
+Flag: -atomic
+
+Common mistaken usages of the sync/atomic package.
+
+Boolean conditions
+
+Flag: -bool
+
+Mistakes involving boolean operators.
+
+Build tags
+
+Flag: -buildtags
+
+Badly formed or misplaced +build tags.
+
+Copying locks
+
+Flag: -copylocks
+
+Locks that are erroneously passed by value.
+
+Nil function comparison
+
+Flag: -nilfunc
+
+Comparisons between functions and nil.
+
+Range loop variables
+
+Flag: -rangeloops
+
+Incorrect uses of range loop variables in closures.
+
+Unreachable code
+
+Flag: -unreachable
+
+Unreachable code.
+
+Shadowed variables
+
+Flag: -shadow=false (experimental; must be set explicitly)
+
+Variables that may have been unintentionally shadowed.
+
+Misuse of unsafe Pointers
+
+Flag: -unsafeptr
+
+Likely incorrect uses of unsafe.Pointer to convert integers to pointers.
+A conversion from uintptr to unsafe.Pointer is invalid if it implies that
+there is a uintptr-typed word in memory that holds a pointer value,
+because that word will be invisible to stack copying and to the garbage
+collector.
+
+Unused result of certain function calls
+
+Flag: -unusedresult
+
+Calls to well-known functions and methods that return a value that is
+discarded.  By default, this includes functions like fmt.Errorf and
+fmt.Sprintf and methods like String and Error. The flags -unusedfuncs
+and -unusedstringmethods control the set.
+
+Shifts
+
+Flag: -shift
+
+Shifts equal to or longer than the variable's length.
+
+Other flags
+
+These flags configure the behavior of vet:
+
+       -all (default true)
+               Check everything; disabled if any explicit check is requested.
+       -v
+               Verbose mode
+       -printfuncs
+               A comma-separated list of print-like functions to supplement
+               the standard list.  Each entry is in the form Name:N where N
+               is the zero-based argument position of the first argument
+               involved in the print: either the format or the first print
+               argument for non-formatted prints.  For example,
+               if you have Warn and Warnf functions that take an
+               io.Writer as their first argument, like Fprintf,
+                       -printfuncs=Warn:1,Warnf:1
+       -shadowstrict
+               Whether to be strict about shadowing; can be noisy.
+       -test
+               For testing only: sets -all and -shadow.
+*/
+package main // import "golang.org/x/tools/cmd/vet"
diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go
new file mode 100644 (file)
index 0000000..e4b6877
--- /dev/null
@@ -0,0 +1,488 @@
+// Copyright 2010 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.
+
+// Vet is a simple checker for static errors in Go source code.
+// See doc.go for more information.
+package main
+
+import (
+       "bytes"
+       "flag"
+       "fmt"
+       "go/ast"
+       "go/build"
+       "go/parser"
+       "go/printer"
+       "go/token"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "strconv"
+       "strings"
+
+       _ "golang.org/x/tools/go/gcimporter"
+       "golang.org/x/tools/go/types"
+)
+
+var (
+       verbose  = flag.Bool("v", false, "verbose")
+       testFlag = flag.Bool("test", false, "for testing only: sets -all and -shadow")
+       tags     = flag.String("tags", "", "comma-separated list of build tags to apply when parsing")
+       tagList  = []string{} // exploded version of tags flag; set in main
+)
+
+var exitCode = 0
+
+// "all" is here only for the appearance of backwards compatibility.
+// It has no effect; the triState flags do the work.
+var all = flag.Bool("all", true, "check everything; disabled if any explicit check is requested")
+
+// Flags to control which individual checks to perform.
+var report = map[string]*triState{
+       // Only unusual checks are written here.
+       // Most checks that operate during the AST walk are added by register.
+       "asmdecl":   triStateFlag("asmdecl", unset, "check assembly against Go declarations"),
+       "buildtags": triStateFlag("buildtags", unset, "check that +build tags are valid"),
+}
+
+// experimental records the flags enabling experimental features. These must be
+// requested explicitly; they are not enabled by -all.
+var experimental = map[string]bool{}
+
+// setTrueCount record how many flags are explicitly set to true.
+var setTrueCount int
+
+// A triState is a boolean that knows whether it has been set to either true or false.
+// It is used to identify if a flag appears; the standard boolean flag cannot
+// distinguish missing from unset. It also satisfies flag.Value.
+type triState int
+
+const (
+       unset triState = iota
+       setTrue
+       setFalse
+)
+
+func triStateFlag(name string, value triState, usage string) *triState {
+       flag.Var(&value, name, usage)
+       return &value
+}
+
+// triState implements flag.Value, flag.Getter, and flag.boolFlag.
+// They work like boolean flags: we can say vet -printf as well as vet -printf=true
+func (ts *triState) Get() interface{} {
+       return *ts == setTrue
+}
+
+func (ts triState) isTrue() bool {
+       return ts == setTrue
+}
+
+func (ts *triState) Set(value string) error {
+       b, err := strconv.ParseBool(value)
+       if err != nil {
+               return err
+       }
+       if b {
+               *ts = setTrue
+               setTrueCount++
+       } else {
+               *ts = setFalse
+       }
+       return nil
+}
+
+func (ts *triState) String() string {
+       switch *ts {
+       case unset:
+               return "unset"
+       case setTrue:
+               return "true"
+       case setFalse:
+               return "false"
+       }
+       panic("not reached")
+}
+
+func (ts triState) IsBoolFlag() bool {
+       return true
+}
+
+// vet tells whether to report errors for the named check, a flag name.
+func vet(name string) bool {
+       if *testFlag {
+               return true
+       }
+       return report[name].isTrue()
+}
+
+// setExit sets the value for os.Exit when it is called, later.  It
+// remembers the highest value.
+func setExit(err int) {
+       if err > exitCode {
+               exitCode = err
+       }
+}
+
+var (
+       // Each of these vars has a corresponding case in (*File).Visit.
+       assignStmt    *ast.AssignStmt
+       binaryExpr    *ast.BinaryExpr
+       callExpr      *ast.CallExpr
+       compositeLit  *ast.CompositeLit
+       exprStmt      *ast.ExprStmt
+       field         *ast.Field
+       funcDecl      *ast.FuncDecl
+       funcLit       *ast.FuncLit
+       genDecl       *ast.GenDecl
+       interfaceType *ast.InterfaceType
+       rangeStmt     *ast.RangeStmt
+
+       // checkers is a two-level map.
+       // The outer level is keyed by a nil pointer, one of the AST vars above.
+       // The inner level is keyed by checker name.
+       checkers = make(map[ast.Node]map[string]func(*File, ast.Node))
+)
+
+func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) {
+       report[name] = triStateFlag(name, unset, usage)
+       for _, typ := range types {
+               m := checkers[typ]
+               if m == nil {
+                       m = make(map[string]func(*File, ast.Node))
+                       checkers[typ] = m
+               }
+               m[name] = fn
+       }
+}
+
+// Usage is a replacement usage function for the flags package.
+func Usage() {
+       fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+       fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
+       fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
+       fmt.Fprintf(os.Stderr, "For more information run\n")
+       fmt.Fprintf(os.Stderr, "\tgodoc golang.org/x/tools/cmd/vet\n\n")
+       fmt.Fprintf(os.Stderr, "Flags:\n")
+       flag.PrintDefaults()
+       os.Exit(2)
+}
+
+// File is a wrapper for the state of a file used in the parser.
+// The parse tree walkers are all methods of this type.
+type File struct {
+       pkg     *Package
+       fset    *token.FileSet
+       name    string
+       content []byte
+       file    *ast.File
+       b       bytes.Buffer // for use by methods
+
+       // The objects that are receivers of a "String() string" method.
+       // This is used by the recursiveStringer method in print.go.
+       stringers map[*ast.Object]bool
+
+       // Registered checkers to run.
+       checkers map[ast.Node][]func(*File, ast.Node)
+}
+
+func main() {
+       flag.Usage = Usage
+       flag.Parse()
+
+       // If any flag is set, we run only those checks requested.
+       // If no flags are set true, set all the non-experimental ones not explicitly set (in effect, set the "-all" flag).
+       if setTrueCount == 0 {
+               for name, setting := range report {
+                       if *setting == unset && !experimental[name] {
+                               *setting = setTrue
+                       }
+               }
+       }
+
+       tagList = strings.Split(*tags, ",")
+
+       initPrintFlags()
+       initUnusedFlags()
+
+       if flag.NArg() == 0 {
+               Usage()
+       }
+       dirs := false
+       files := false
+       for _, name := range flag.Args() {
+               // Is it a directory?
+               fi, err := os.Stat(name)
+               if err != nil {
+                       warnf("error walking tree: %s", err)
+                       continue
+               }
+               if fi.IsDir() {
+                       dirs = true
+               } else {
+                       files = true
+               }
+       }
+       if dirs && files {
+               Usage()
+       }
+       if dirs {
+               for _, name := range flag.Args() {
+                       walkDir(name)
+               }
+               os.Exit(exitCode)
+       }
+       if !doPackage(".", flag.Args()) {
+               warnf("no files checked")
+       }
+       os.Exit(exitCode)
+}
+
+// prefixDirectory places the directory name on the beginning of each name in the list.
+func prefixDirectory(directory string, names []string) {
+       if directory != "." {
+               for i, name := range names {
+                       names[i] = filepath.Join(directory, name)
+               }
+       }
+}
+
+// doPackageDir analyzes the single package found in the directory, if there is one,
+// plus a test package, if there is one.
+func doPackageDir(directory string) {
+       context := build.Default
+       if len(context.BuildTags) != 0 {
+               warnf("build tags %s previously set", context.BuildTags)
+       }
+       context.BuildTags = append(tagList, context.BuildTags...)
+
+       pkg, err := context.ImportDir(directory, 0)
+       if err != nil {
+               // If it's just that there are no go source files, that's fine.
+               if _, nogo := err.(*build.NoGoError); nogo {
+                       return
+               }
+               // Non-fatal: we are doing a recursive walk and there may be other directories.
+               warnf("cannot process directory %s: %s", directory, err)
+               return
+       }
+       var names []string
+       names = append(names, pkg.GoFiles...)
+       names = append(names, pkg.CgoFiles...)
+       names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
+       names = append(names, pkg.SFiles...)
+       prefixDirectory(directory, names)
+       doPackage(directory, names)
+       // Is there also a "foo_test" package? If so, do that one as well.
+       if len(pkg.XTestGoFiles) > 0 {
+               names = pkg.XTestGoFiles
+               prefixDirectory(directory, names)
+               doPackage(directory, names)
+       }
+}
+
+type Package struct {
+       path      string
+       defs      map[*ast.Ident]types.Object
+       uses      map[*ast.Ident]types.Object
+       selectors map[*ast.SelectorExpr]*types.Selection
+       types     map[ast.Expr]types.TypeAndValue
+       spans     map[types.Object]Span
+       files     []*File
+       typesPkg  *types.Package
+}
+
+// doPackage analyzes the single package constructed from the named files.
+// It returns whether any files were checked.
+func doPackage(directory string, names []string) bool {
+       var files []*File
+       var astFiles []*ast.File
+       fs := token.NewFileSet()
+       for _, name := range names {
+               data, err := ioutil.ReadFile(name)
+               if err != nil {
+                       // Warn but continue to next package.
+                       warnf("%s: %s", name, err)
+                       return false
+               }
+               checkBuildTag(name, data)
+               var parsedFile *ast.File
+               if strings.HasSuffix(name, ".go") {
+                       parsedFile, err = parser.ParseFile(fs, name, data, 0)
+                       if err != nil {
+                               warnf("%s: %s", name, err)
+                               return false
+                       }
+                       astFiles = append(astFiles, parsedFile)
+               }
+               files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
+       }
+       if len(astFiles) == 0 {
+               return false
+       }
+       pkg := new(Package)
+       pkg.path = astFiles[0].Name.Name
+       pkg.files = files
+       // Type check the package.
+       err := pkg.check(fs, astFiles)
+       if err != nil && *verbose {
+               warnf("%s", err)
+       }
+
+       // Check.
+       chk := make(map[ast.Node][]func(*File, ast.Node))
+       for typ, set := range checkers {
+               for name, fn := range set {
+                       if vet(name) {
+                               chk[typ] = append(chk[typ], fn)
+                       }
+               }
+       }
+       for _, file := range files {
+               file.pkg = pkg
+               file.checkers = chk
+               if file.file != nil {
+                       file.walkFile(file.name, file.file)
+               }
+       }
+       asmCheck(pkg)
+       return true
+}
+
+func visit(path string, f os.FileInfo, err error) error {
+       if err != nil {
+               warnf("walk error: %s", err)
+               return err
+       }
+       // One package per directory. Ignore the files themselves.
+       if !f.IsDir() {
+               return nil
+       }
+       doPackageDir(path)
+       return nil
+}
+
+func (pkg *Package) hasFileWithSuffix(suffix string) bool {
+       for _, f := range pkg.files {
+               if strings.HasSuffix(f.name, suffix) {
+                       return true
+               }
+       }
+       return false
+}
+
+// walkDir recursively walks the tree looking for Go packages.
+func walkDir(root string) {
+       filepath.Walk(root, visit)
+}
+
+// errorf formats the error to standard error, adding program
+// identification and a newline, and exits.
+func errorf(format string, args ...interface{}) {
+       fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
+       os.Exit(2)
+}
+
+// warnf formats the error to standard error, adding program
+// identification and a newline, but does not exit.
+func warnf(format string, args ...interface{}) {
+       fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
+       setExit(1)
+}
+
+// Println is fmt.Println guarded by -v.
+func Println(args ...interface{}) {
+       if !*verbose {
+               return
+       }
+       fmt.Println(args...)
+}
+
+// Printf is fmt.Printf guarded by -v.
+func Printf(format string, args ...interface{}) {
+       if !*verbose {
+               return
+       }
+       fmt.Printf(format+"\n", args...)
+}
+
+// Bad reports an error and sets the exit code..
+func (f *File) Bad(pos token.Pos, args ...interface{}) {
+       f.Warn(pos, args...)
+       setExit(1)
+}
+
+// Badf reports a formatted error and sets the exit code.
+func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
+       f.Warnf(pos, format, args...)
+       setExit(1)
+}
+
+// loc returns a formatted representation of the position.
+func (f *File) loc(pos token.Pos) string {
+       if pos == token.NoPos {
+               return ""
+       }
+       // Do not print columns. Because the pos often points to the start of an
+       // expression instead of the inner part with the actual error, the
+       // precision can mislead.
+       posn := f.fset.Position(pos)
+       return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line)
+}
+
+// Warn reports an error but does not set the exit code.
+func (f *File) Warn(pos token.Pos, args ...interface{}) {
+       fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...))
+}
+
+// Warnf reports a formatted error but does not set the exit code.
+func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
+       fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...)
+}
+
+// walkFile walks the file's tree.
+func (f *File) walkFile(name string, file *ast.File) {
+       Println("Checking file", name)
+       ast.Walk(f, file)
+}
+
+// Visit implements the ast.Visitor interface.
+func (f *File) Visit(node ast.Node) ast.Visitor {
+       var key ast.Node
+       switch node.(type) {
+       case *ast.AssignStmt:
+               key = assignStmt
+       case *ast.BinaryExpr:
+               key = binaryExpr
+       case *ast.CallExpr:
+               key = callExpr
+       case *ast.CompositeLit:
+               key = compositeLit
+       case *ast.ExprStmt:
+               key = exprStmt
+       case *ast.Field:
+               key = field
+       case *ast.FuncDecl:
+               key = funcDecl
+       case *ast.FuncLit:
+               key = funcLit
+       case *ast.GenDecl:
+               key = genDecl
+       case *ast.InterfaceType:
+               key = interfaceType
+       case *ast.RangeStmt:
+               key = rangeStmt
+       }
+       for _, fn := range f.checkers[key] {
+               fn(f, node)
+       }
+       return f
+}
+
+// gofmt returns a string representation of the expression.
+func (f *File) gofmt(x ast.Expr) string {
+       f.b.Reset()
+       printer.Fprint(&f.b, f.fset, x)
+       return f.b.String()
+}
diff --git a/src/cmd/vet/method.go b/src/cmd/vet/method.go
new file mode 100644 (file)
index 0000000..00949df
--- /dev/null
@@ -0,0 +1,182 @@
+// Copyright 2010 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.
+
+// This file contains the code to check canonical methods.
+
+package main
+
+import (
+       "fmt"
+       "go/ast"
+       "go/printer"
+       "strings"
+)
+
+func init() {
+       register("methods",
+               "check that canonically named methods are canonically defined",
+               checkCanonicalMethod,
+               funcDecl, interfaceType)
+}
+
+type MethodSig struct {
+       args    []string
+       results []string
+}
+
+// canonicalMethods lists the input and output types for Go methods
+// that are checked using dynamic interface checks.  Because the
+// checks are dynamic, such methods would not cause a compile error
+// if they have the wrong signature: instead the dynamic check would
+// fail, sometimes mysteriously.  If a method is found with a name listed
+// here but not the input/output types listed here, vet complains.
+//
+// A few of the canonical methods have very common names.
+// For example, a type might implement a Scan method that
+// has nothing to do with fmt.Scanner, but we still want to check
+// the methods that are intended to implement fmt.Scanner.
+// To do that, the arguments that have a = prefix are treated as
+// signals that the canonical meaning is intended: if a Scan
+// method doesn't have a fmt.ScanState as its first argument,
+// we let it go.  But if it does have a fmt.ScanState, then the
+// rest has to match.
+var canonicalMethods = map[string]MethodSig{
+       // "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
+       "Format":        {[]string{"=fmt.State", "rune"}, []string{}},                      // fmt.Formatter
+       "GobDecode":     {[]string{"[]byte"}, []string{"error"}},                           // gob.GobDecoder
+       "GobEncode":     {[]string{}, []string{"[]byte", "error"}},                         // gob.GobEncoder
+       "MarshalJSON":   {[]string{}, []string{"[]byte", "error"}},                         // json.Marshaler
+       "MarshalXML":    {[]string{"*xml.Encoder", "xml.StartElement"}, []string{"error"}}, // xml.Marshaler
+       "Peek":          {[]string{"=int"}, []string{"[]byte", "error"}},                   // image.reader (matching bufio.Reader)
+       "ReadByte":      {[]string{}, []string{"byte", "error"}},                           // io.ByteReader
+       "ReadFrom":      {[]string{"=io.Reader"}, []string{"int64", "error"}},              // io.ReaderFrom
+       "ReadRune":      {[]string{}, []string{"rune", "int", "error"}},                    // io.RuneReader
+       "Scan":          {[]string{"=fmt.ScanState", "rune"}, []string{"error"}},           // fmt.Scanner
+       "Seek":          {[]string{"=int64", "int"}, []string{"int64", "error"}},           // io.Seeker
+       "UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}},                           // json.Unmarshaler
+       "UnmarshalXML":  {[]string{"*xml.Decoder", "xml.StartElement"}, []string{"error"}}, // xml.Unmarshaler
+       "UnreadByte":    {[]string{}, []string{"error"}},
+       "UnreadRune":    {[]string{}, []string{"error"}},
+       "WriteByte":     {[]string{"byte"}, []string{"error"}},                // jpeg.writer (matching bufio.Writer)
+       "WriteTo":       {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo
+}
+
+func checkCanonicalMethod(f *File, node ast.Node) {
+       switch n := node.(type) {
+       case *ast.FuncDecl:
+               if n.Recv != nil {
+                       canonicalMethod(f, n.Name, n.Type)
+               }
+       case *ast.InterfaceType:
+               for _, field := range n.Methods.List {
+                       for _, id := range field.Names {
+                               canonicalMethod(f, id, field.Type.(*ast.FuncType))
+                       }
+               }
+       }
+}
+
+func canonicalMethod(f *File, id *ast.Ident, t *ast.FuncType) {
+       // Expected input/output.
+       expect, ok := canonicalMethods[id.Name]
+       if !ok {
+               return
+       }
+
+       // Actual input/output
+       args := typeFlatten(t.Params.List)
+       var results []ast.Expr
+       if t.Results != nil {
+               results = typeFlatten(t.Results.List)
+       }
+
+       // Do the =s (if any) all match?
+       if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") {
+               return
+       }
+
+       // Everything must match.
+       if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") {
+               expectFmt := id.Name + "(" + argjoin(expect.args) + ")"
+               if len(expect.results) == 1 {
+                       expectFmt += " " + argjoin(expect.results)
+               } else if len(expect.results) > 1 {
+                       expectFmt += " (" + argjoin(expect.results) + ")"
+               }
+
+               f.b.Reset()
+               if err := printer.Fprint(&f.b, f.fset, t); err != nil {
+                       fmt.Fprintf(&f.b, "<%s>", err)
+               }
+               actual := f.b.String()
+               actual = strings.TrimPrefix(actual, "func")
+               actual = id.Name + actual
+
+               f.Badf(id.Pos(), "method %s should have signature %s", actual, expectFmt)
+       }
+}
+
+func argjoin(x []string) string {
+       y := make([]string, len(x))
+       for i, s := range x {
+               if s[0] == '=' {
+                       s = s[1:]
+               }
+               y[i] = s
+       }
+       return strings.Join(y, ", ")
+}
+
+// Turn parameter list into slice of types
+// (in the ast, types are Exprs).
+// Have to handle f(int, bool) and f(x, y, z int)
+// so not a simple 1-to-1 conversion.
+func typeFlatten(l []*ast.Field) []ast.Expr {
+       var t []ast.Expr
+       for _, f := range l {
+               if len(f.Names) == 0 {
+                       t = append(t, f.Type)
+                       continue
+               }
+               for _ = range f.Names {
+                       t = append(t, f.Type)
+               }
+       }
+       return t
+}
+
+// Does each type in expect with the given prefix match the corresponding type in actual?
+func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bool {
+       for i, x := range expect {
+               if !strings.HasPrefix(x, prefix) {
+                       continue
+               }
+               if i >= len(actual) {
+                       return false
+               }
+               if !f.matchParamType(x, actual[i]) {
+                       return false
+               }
+       }
+       if prefix == "" && len(actual) > len(expect) {
+               return false
+       }
+       return true
+}
+
+// Does this one type match?
+func (f *File) matchParamType(expect string, actual ast.Expr) bool {
+       if strings.HasPrefix(expect, "=") {
+               expect = expect[1:]
+       }
+       // Strip package name if we're in that package.
+       if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' {
+               expect = expect[n+1:]
+       }
+
+       // Overkill but easy.
+       f.b.Reset()
+       printer.Fprint(&f.b, f.fset, actual)
+       return f.b.String() == expect
+}
diff --git a/src/cmd/vet/nilfunc.go b/src/cmd/vet/nilfunc.go
new file mode 100644 (file)
index 0000000..fa1bac7
--- /dev/null
@@ -0,0 +1,68 @@
+// Copyright 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.
+
+/*
+This file contains the code to check for useless function comparisons.
+A useless comparison is one like f == nil as opposed to f() == nil.
+*/
+
+package main
+
+import (
+       "go/ast"
+       "go/token"
+
+       "golang.org/x/tools/go/types"
+)
+
+func init() {
+       register("nilfunc",
+               "check for comparisons between functions and nil",
+               checkNilFuncComparison,
+               binaryExpr)
+}
+
+func checkNilFuncComparison(f *File, node ast.Node) {
+       e := node.(*ast.BinaryExpr)
+
+       // Only want == or != comparisons.
+       if e.Op != token.EQL && e.Op != token.NEQ {
+               return
+       }
+
+       // Only want comparisons with a nil identifier on one side.
+       var e2 ast.Expr
+       switch {
+       case f.isNil(e.X):
+               e2 = e.Y
+       case f.isNil(e.Y):
+               e2 = e.X
+       default:
+               return
+       }
+
+       // Only want identifiers or selector expressions.
+       var obj types.Object
+       switch v := e2.(type) {
+       case *ast.Ident:
+               obj = f.pkg.uses[v]
+       case *ast.SelectorExpr:
+               obj = f.pkg.uses[v.Sel]
+       default:
+               return
+       }
+
+       // Only want functions.
+       if _, ok := obj.(*types.Func); !ok {
+               return
+       }
+
+       f.Badf(e.Pos(), "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ)
+}
+
+// isNil reports whether the provided expression is the built-in nil
+// identifier.
+func (f *File) isNil(e ast.Expr) bool {
+       return f.pkg.types[e].Type == types.Typ[types.UntypedNil]
+}
diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go
new file mode 100644 (file)
index 0000000..b20d935
--- /dev/null
@@ -0,0 +1,587 @@
+// Copyright 2010 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.
+
+// This file contains the printf-checker.
+
+package main
+
+import (
+       "bytes"
+       "flag"
+       "go/ast"
+       "go/token"
+       "strconv"
+       "strings"
+       "unicode/utf8"
+
+       "golang.org/x/tools/go/exact"
+       "golang.org/x/tools/go/types"
+)
+
+var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check")
+
+func init() {
+       register("printf",
+               "check printf-like invocations",
+               checkFmtPrintfCall,
+               funcDecl, callExpr)
+}
+
+func initPrintFlags() {
+       if *printfuncs == "" {
+               return
+       }
+       for _, name := range strings.Split(*printfuncs, ",") {
+               if len(name) == 0 {
+                       flag.Usage()
+               }
+               skip := 0
+               if colon := strings.LastIndex(name, ":"); colon > 0 {
+                       var err error
+                       skip, err = strconv.Atoi(name[colon+1:])
+                       if err != nil {
+                               errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
+                       }
+                       name = name[:colon]
+               }
+               name = strings.ToLower(name)
+               if name[len(name)-1] == 'f' {
+                       printfList[name] = skip
+               } else {
+                       printList[name] = skip
+               }
+       }
+}
+
+// printfList records the formatted-print functions. The value is the location
+// of the format parameter. Names are lower-cased so the lookup is
+// case insensitive.
+var printfList = map[string]int{
+       "errorf":  0,
+       "fatalf":  0,
+       "fprintf": 1,
+       "logf":    0,
+       "panicf":  0,
+       "printf":  0,
+       "sprintf": 0,
+}
+
+// printList records the unformatted-print functions. The value is the location
+// of the first parameter to be printed.  Names are lower-cased so the lookup is
+// case insensitive.
+var printList = map[string]int{
+       "error":  0,
+       "fatal":  0,
+       "fprint": 1, "fprintln": 1,
+       "log":   0,
+       "panic": 0, "panicln": 0,
+       "print": 0, "println": 0,
+       "sprint": 0, "sprintln": 0,
+}
+
+// checkCall triggers the print-specific checks if the call invokes a print function.
+func checkFmtPrintfCall(f *File, node ast.Node) {
+       if d, ok := node.(*ast.FuncDecl); ok && isStringer(f, d) {
+               // Remember we saw this.
+               if f.stringers == nil {
+                       f.stringers = make(map[*ast.Object]bool)
+               }
+               if l := d.Recv.List; len(l) == 1 {
+                       if n := l[0].Names; len(n) == 1 {
+                               f.stringers[n[0].Obj] = true
+                       }
+               }
+               return
+       }
+
+       call, ok := node.(*ast.CallExpr)
+       if !ok {
+               return
+       }
+       var Name string
+       switch x := call.Fun.(type) {
+       case *ast.Ident:
+               Name = x.Name
+       case *ast.SelectorExpr:
+               Name = x.Sel.Name
+       default:
+               return
+       }
+
+       name := strings.ToLower(Name)
+       if skip, ok := printfList[name]; ok {
+               f.checkPrintf(call, Name, skip)
+               return
+       }
+       if skip, ok := printList[name]; ok {
+               f.checkPrint(call, Name, skip)
+               return
+       }
+}
+
+// isStringer returns true if the provided declaration is a "String() string"
+// method, an implementation of fmt.Stringer.
+func isStringer(f *File, d *ast.FuncDecl) bool {
+       return d.Recv != nil && d.Name.Name == "String" && d.Type.Results != nil &&
+               len(d.Type.Params.List) == 0 && len(d.Type.Results.List) == 1 &&
+               f.pkg.types[d.Type.Results.List[0].Type].Type == types.Typ[types.String]
+}
+
+// formatState holds the parsed representation of a printf directive such as "%3.*[4]d".
+// It is constructed by parsePrintfVerb.
+type formatState struct {
+       verb     rune   // the format verb: 'd' for "%d"
+       format   string // the full format directive from % through verb, "%.3d".
+       name     string // Printf, Sprintf etc.
+       flags    []byte // the list of # + etc.
+       argNums  []int  // the successive argument numbers that are consumed, adjusted to refer to actual arg in call
+       indexed  bool   // whether an indexing expression appears: %[1]d.
+       firstArg int    // Index of first argument after the format in the Printf call.
+       // Used only during parse.
+       file         *File
+       call         *ast.CallExpr
+       argNum       int  // Which argument we're expecting to format now.
+       indexPending bool // Whether we have an indexed argument that has not resolved.
+       nbytes       int  // number of bytes of the format string consumed.
+}
+
+// checkPrintf checks a call to a formatted print routine such as Printf.
+// call.Args[formatIndex] is (well, should be) the format argument.
+func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) {
+       if formatIndex >= len(call.Args) {
+               f.Bad(call.Pos(), "too few arguments in call to", name)
+               return
+       }
+       lit := f.pkg.types[call.Args[formatIndex]].Value
+       if lit == nil {
+               if *verbose {
+                       f.Warn(call.Pos(), "can't check non-constant format in call to", name)
+               }
+               return
+       }
+       if lit.Kind() != exact.String {
+               f.Badf(call.Pos(), "constant %v not a string in call to %s", lit, name)
+               return
+       }
+       format := exact.StringVal(lit)
+       firstArg := formatIndex + 1 // Arguments are immediately after format string.
+       if !strings.Contains(format, "%") {
+               if len(call.Args) > firstArg {
+                       f.Badf(call.Pos(), "no formatting directive in %s call", name)
+               }
+               return
+       }
+       // Hard part: check formats against args.
+       argNum := firstArg
+       indexed := false
+       for i, w := 0, 0; i < len(format); i += w {
+               w = 1
+               if format[i] == '%' {
+                       state := f.parsePrintfVerb(call, name, format[i:], firstArg, argNum)
+                       if state == nil {
+                               return
+                       }
+                       w = len(state.format)
+                       if state.indexed {
+                               indexed = true
+                       }
+                       if !f.okPrintfArg(call, state) { // One error per format is enough.
+                               return
+                       }
+                       if len(state.argNums) > 0 {
+                               // Continue with the next sequential argument.
+                               argNum = state.argNums[len(state.argNums)-1] + 1
+                       }
+               }
+       }
+       // Dotdotdot is hard.
+       if call.Ellipsis.IsValid() && argNum >= len(call.Args)-1 {
+               return
+       }
+       // If the arguments were direct indexed, we assume the programmer knows what's up.
+       // Otherwise, there should be no leftover arguments.
+       if !indexed && argNum != len(call.Args) {
+               expect := argNum - firstArg
+               numArgs := len(call.Args) - firstArg
+               f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs)
+       }
+}
+
+// parseFlags accepts any printf flags.
+func (s *formatState) parseFlags() {
+       for s.nbytes < len(s.format) {
+               switch c := s.format[s.nbytes]; c {
+               case '#', '0', '+', '-', ' ':
+                       s.flags = append(s.flags, c)
+                       s.nbytes++
+               default:
+                       return
+               }
+       }
+}
+
+// scanNum advances through a decimal number if present.
+func (s *formatState) scanNum() {
+       for ; s.nbytes < len(s.format); s.nbytes++ {
+               c := s.format[s.nbytes]
+               if c < '0' || '9' < c {
+                       return
+               }
+       }
+}
+
+// parseIndex scans an index expression. It returns false if there is a syntax error.
+func (s *formatState) parseIndex() bool {
+       if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' {
+               return true
+       }
+       // Argument index present.
+       s.indexed = true
+       s.nbytes++ // skip '['
+       start := s.nbytes
+       s.scanNum()
+       if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' {
+               s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index")
+               return false
+       }
+       arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
+       if err != nil {
+               s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index: %s", err)
+               return false
+       }
+       s.nbytes++ // skip ']'
+       arg := int(arg32)
+       arg += s.firstArg - 1 // We want to zero-index the actual arguments.
+       s.argNum = arg
+       s.indexPending = true
+       return true
+}
+
+// parseNum scans a width or precision (or *). It returns false if there's a bad index expression.
+func (s *formatState) parseNum() bool {
+       if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' {
+               if s.indexPending { // Absorb it.
+                       s.indexPending = false
+               }
+               s.nbytes++
+               s.argNums = append(s.argNums, s.argNum)
+               s.argNum++
+       } else {
+               s.scanNum()
+       }
+       return true
+}
+
+// parsePrecision scans for a precision. It returns false if there's a bad index expression.
+func (s *formatState) parsePrecision() bool {
+       // If there's a period, there may be a precision.
+       if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' {
+               s.flags = append(s.flags, '.') // Treat precision as a flag.
+               s.nbytes++
+               if !s.parseIndex() {
+                       return false
+               }
+               if !s.parseNum() {
+                       return false
+               }
+       }
+       return true
+}
+
+// parsePrintfVerb looks the formatting directive that begins the format string
+// and returns a formatState that encodes what the directive wants, without looking
+// at the actual arguments present in the call. The result is nil if there is an error.
+func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState {
+       state := &formatState{
+               format:   format,
+               name:     name,
+               flags:    make([]byte, 0, 5),
+               argNum:   argNum,
+               argNums:  make([]int, 0, 1),
+               nbytes:   1, // There's guaranteed to be a percent sign.
+               indexed:  false,
+               firstArg: firstArg,
+               file:     f,
+               call:     call,
+       }
+       // There may be flags.
+       state.parseFlags()
+       indexPending := false
+       // There may be an index.
+       if !state.parseIndex() {
+               return nil
+       }
+       // There may be a width.
+       if !state.parseNum() {
+               return nil
+       }
+       // There may be a precision.
+       if !state.parsePrecision() {
+               return nil
+       }
+       // Now a verb, possibly prefixed by an index (which we may already have).
+       if !indexPending && !state.parseIndex() {
+               return nil
+       }
+       if state.nbytes == len(state.format) {
+               f.Badf(call.Pos(), "missing verb at end of format string in %s call", name)
+               return nil
+       }
+       verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:])
+       state.verb = verb
+       state.nbytes += w
+       if verb != '%' {
+               state.argNums = append(state.argNums, state.argNum)
+       }
+       state.format = state.format[:state.nbytes]
+       return state
+}
+
+// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
+type printfArgType int
+
+const (
+       argBool printfArgType = 1 << iota
+       argInt
+       argRune
+       argString
+       argFloat
+       argComplex
+       argPointer
+       anyType printfArgType = ^0
+)
+
+type printVerb struct {
+       verb  rune   // User may provide verb through Formatter; could be a rune.
+       flags string // known flags are all ASCII
+       typ   printfArgType
+}
+
+// Common flag sets for printf verbs.
+const (
+       noFlag       = ""
+       numFlag      = " -+.0"
+       sharpNumFlag = " -+.0#"
+       allFlags     = " -+.0#"
+)
+
+// printVerbs identifies which flags are known to printf for each verb.
+// TODO: A type that implements Formatter may do what it wants, and vet
+// will complain incorrectly.
+var printVerbs = []printVerb{
+       // '-' is a width modifier, always valid.
+       // '.' is a precision for float, max width for strings.
+       // '+' is required sign for numbers, Go format for %v.
+       // '#' is alternate format for several verbs.
+       // ' ' is spacer for numbers
+       {'%', noFlag, 0},
+       {'b', numFlag, argInt | argFloat | argComplex},
+       {'c', "-", argRune | argInt},
+       {'d', numFlag, argInt},
+       {'e', numFlag, argFloat | argComplex},
+       {'E', numFlag, argFloat | argComplex},
+       {'f', numFlag, argFloat | argComplex},
+       {'F', numFlag, argFloat | argComplex},
+       {'g', numFlag, argFloat | argComplex},
+       {'G', numFlag, argFloat | argComplex},
+       {'o', sharpNumFlag, argInt},
+       {'p', "-#", argPointer},
+       {'q', " -+.0#", argRune | argInt | argString},
+       {'s', " -+.0", argString},
+       {'t', "-", argBool},
+       {'T', "-", anyType},
+       {'U', "-#", argRune | argInt},
+       {'v', allFlags, anyType},
+       {'x', sharpNumFlag, argRune | argInt | argString},
+       {'X', sharpNumFlag, argRune | argInt | argString},
+}
+
+// okPrintfArg compares the formatState to the arguments actually present,
+// reporting any discrepancies it can discern. If the final argument is ellipsissed,
+// there's little it can do for that.
+func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
+       var v printVerb
+       found := false
+       // Linear scan is fast enough for a small list.
+       for _, v = range printVerbs {
+               if v.verb == state.verb {
+                       found = true
+                       break
+               }
+       }
+       if !found {
+               f.Badf(call.Pos(), "unrecognized printf verb %q", state.verb)
+               return false
+       }
+       for _, flag := range state.flags {
+               if !strings.ContainsRune(v.flags, rune(flag)) {
+                       f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", state.verb, flag)
+                       return false
+               }
+       }
+       // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all
+       // but the final arg must be an integer.
+       trueArgs := 1
+       if state.verb == '%' {
+               trueArgs = 0
+       }
+       nargs := len(state.argNums)
+       for i := 0; i < nargs-trueArgs; i++ {
+               argNum := state.argNums[i]
+               if !f.argCanBeChecked(call, i, true, state) {
+                       return
+               }
+               arg := call.Args[argNum]
+               if !f.matchArgType(argInt, nil, arg) {
+                       f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(arg))
+                       return false
+               }
+       }
+       if state.verb == '%' {
+               return true
+       }
+       argNum := state.argNums[len(state.argNums)-1]
+       if !f.argCanBeChecked(call, len(state.argNums)-1, false, state) {
+               return false
+       }
+       arg := call.Args[argNum]
+       if !f.matchArgType(v.typ, nil, arg) {
+               typeString := ""
+               if typ := f.pkg.types[arg].Type; typ != nil {
+                       typeString = typ.String()
+               }
+               f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), state.verb, typeString)
+               return false
+       }
+       if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && f.recursiveStringer(arg) {
+               f.Badf(call.Pos(), "arg %s for printf causes recursive call to String method", f.gofmt(arg))
+               return false
+       }
+       return true
+}
+
+// recursiveStringer reports whether the provided argument is r or &r for the
+// fmt.Stringer receiver identifier r.
+func (f *File) recursiveStringer(e ast.Expr) bool {
+       if len(f.stringers) == 0 {
+               return false
+       }
+       var obj *ast.Object
+       switch e := e.(type) {
+       case *ast.Ident:
+               obj = e.Obj
+       case *ast.UnaryExpr:
+               if id, ok := e.X.(*ast.Ident); ok && e.Op == token.AND {
+                       obj = id.Obj
+               }
+       }
+
+       // It's unlikely to be a recursive stringer if it has a Format method.
+       if typ := f.pkg.types[e].Type; typ != nil {
+               // Not a perfect match; see issue 6259.
+               if f.hasMethod(typ, "Format") {
+                       return false
+               }
+       }
+
+       // We compare the underlying Object, which checks that the identifier
+       // is the one we declared as the receiver for the String method in
+       // which this printf appears.
+       return f.stringers[obj]
+}
+
+// argCanBeChecked reports whether the specified argument is statically present;
+// it may be beyond the list of arguments or in a terminal slice... argument, which
+// means we can't see it.
+func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, isStar bool, state *formatState) bool {
+       argNum := state.argNums[formatArg]
+       if argNum < 0 {
+               // Shouldn't happen, so catch it with prejudice.
+               panic("negative arg num")
+       }
+       if argNum == 0 {
+               f.Badf(call.Pos(), `index value [0] for %s("%s"); indexes start at 1`, state.name, state.format)
+               return false
+       }
+       if argNum < len(call.Args)-1 {
+               return true // Always OK.
+       }
+       if call.Ellipsis.IsValid() {
+               return false // We just can't tell; there could be many more arguments.
+       }
+       if argNum < len(call.Args) {
+               return true
+       }
+       // There are bad indexes in the format or there are fewer arguments than the format needs.
+       // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
+       arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed.
+       f.Badf(call.Pos(), `missing argument for %s("%s"): format reads arg %d, have only %d args`, state.name, state.format, arg, len(call.Args)-state.firstArg)
+       return false
+}
+
+// checkPrint checks a call to an unformatted print routine such as Println.
+// call.Args[firstArg] is the first argument to be printed.
+func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) {
+       isLn := strings.HasSuffix(name, "ln")
+       isF := strings.HasPrefix(name, "F")
+       args := call.Args
+       if name == "Log" && len(args) > 0 {
+               // Special case: Don't complain about math.Log or cmplx.Log.
+               // Not strictly necessary because the only complaint likely is for Log("%d")
+               // but it feels wrong to check that math.Log is a good print function.
+               if sel, ok := args[0].(*ast.SelectorExpr); ok {
+                       if x, ok := sel.X.(*ast.Ident); ok {
+                               if x.Name == "math" || x.Name == "cmplx" {
+                                       return
+                               }
+                       }
+               }
+       }
+       // check for Println(os.Stderr, ...)
+       if firstArg == 0 && !isF && len(args) > 0 {
+               if sel, ok := args[0].(*ast.SelectorExpr); ok {
+                       if x, ok := sel.X.(*ast.Ident); ok {
+                               if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
+                                       f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name)
+                               }
+                       }
+               }
+       }
+       if len(args) <= firstArg {
+               // If we have a call to a method called Error that satisfies the Error interface,
+               // then it's ok. Otherwise it's something like (*T).Error from the testing package
+               // and we need to check it.
+               if name == "Error" && f.isErrorMethodCall(call) {
+                       return
+               }
+               // If it's an Error call now, it's probably for printing errors.
+               if !isLn {
+                       // Check the signature to be sure: there are niladic functions called "error".
+                       if firstArg != 0 || f.numArgsInSignature(call) != firstArg {
+                               f.Badf(call.Pos(), "no args in %s call", name)
+                       }
+               }
+               return
+       }
+       arg := args[firstArg]
+       if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
+               if strings.Contains(lit.Value, "%") {
+                       f.Badf(call.Pos(), "possible formatting directive in %s call", name)
+               }
+       }
+       if isLn {
+               // The last item, if a string, should not have a newline.
+               arg = args[len(call.Args)-1]
+               if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
+                       if strings.HasSuffix(lit.Value, `\n"`) {
+                               f.Badf(call.Pos(), "%s call ends with newline", name)
+                       }
+               }
+       }
+       for _, arg := range args {
+               if f.recursiveStringer(arg) {
+                       f.Badf(call.Pos(), "arg %s for print causes recursive call to String method", f.gofmt(arg))
+               }
+       }
+}
diff --git a/src/cmd/vet/rangeloop.go b/src/cmd/vet/rangeloop.go
new file mode 100644 (file)
index 0000000..96e2ca8
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright 2012 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.
+
+/*
+This file contains the code to check range loop variables bound inside function
+literals that are deferred or launched in new goroutines. We only check
+instances where the defer or go statement is the last statement in the loop
+body, as otherwise we would need whole program analysis.
+
+For example:
+
+       for i, v := range s {
+               go func() {
+                       println(i, v) // not what you might expect
+               }()
+       }
+
+See: http://golang.org/doc/go_faq.html#closures_and_goroutines
+*/
+
+package main
+
+import "go/ast"
+
+func init() {
+       register("rangeloops",
+               "check that range loop variables are used correctly",
+               checkRangeLoop,
+               rangeStmt)
+}
+
+// checkRangeLoop walks the body of the provided range statement, checking if
+// its index or value variables are used unsafely inside goroutines or deferred
+// function literals.
+func checkRangeLoop(f *File, node ast.Node) {
+       n := node.(*ast.RangeStmt)
+       key, _ := n.Key.(*ast.Ident)
+       val, _ := n.Value.(*ast.Ident)
+       if key == nil && val == nil {
+               return
+       }
+       sl := n.Body.List
+       if len(sl) == 0 {
+               return
+       }
+       var last *ast.CallExpr
+       switch s := sl[len(sl)-1].(type) {
+       case *ast.GoStmt:
+               last = s.Call
+       case *ast.DeferStmt:
+               last = s.Call
+       default:
+               return
+       }
+       lit, ok := last.Fun.(*ast.FuncLit)
+       if !ok {
+               return
+       }
+       ast.Inspect(lit.Body, func(n ast.Node) bool {
+               id, ok := n.(*ast.Ident)
+               if !ok || id.Obj == nil {
+                       return true
+               }
+               if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj {
+                       f.Bad(id.Pos(), "range variable", id.Name, "captured by func literal")
+               }
+               return true
+       })
+}
diff --git a/src/cmd/vet/shadow.go b/src/cmd/vet/shadow.go
new file mode 100644 (file)
index 0000000..34e5db9
--- /dev/null
@@ -0,0 +1,245 @@
+// Copyright 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.
+
+/*
+This file contains the code to check for shadowed variables.
+A shadowed variable is a variable declared in an inner scope
+with the same name and type as a variable in an outer scope,
+and where the outer variable is mentioned after the inner one
+is declared.
+
+(This definition can be refined; the module generates too many
+false positives and is not yet enabled by default.)
+
+For example:
+
+       func BadRead(f *os.File, buf []byte) error {
+               var err error
+               for {
+                       n, err := f.Read(buf) // shadows the function variable 'err'
+                       if err != nil {
+                               break // causes return of wrong value
+                       }
+                       foo(buf)
+               }
+               return err
+       }
+
+*/
+
+package main
+
+import (
+       "flag"
+       "go/ast"
+       "go/token"
+
+       "golang.org/x/tools/go/types"
+)
+
+var strictShadowing = flag.Bool("shadowstrict", false, "whether to be strict about shadowing; can be noisy")
+
+func init() {
+       register("shadow",
+               "check for shadowed variables (experimental; must be set explicitly)",
+               checkShadow,
+               assignStmt, genDecl)
+       experimental["shadow"] = true
+}
+
+func checkShadow(f *File, node ast.Node) {
+       switch n := node.(type) {
+       case *ast.AssignStmt:
+               checkShadowAssignment(f, n)
+       case *ast.GenDecl:
+               checkShadowDecl(f, n)
+       }
+}
+
+// Span stores the minimum range of byte positions in the file in which a
+// given variable (types.Object) is mentioned. It is lexically defined: it spans
+// from the beginning of its first mention to the end of its last mention.
+// A variable is considered shadowed (if *strictShadowing is off) only if the
+// shadowing variable is declared within the span of the shadowed variable.
+// In other words, if a variable is shadowed but not used after the shadowed
+// variable is declared, it is inconsequential and not worth complaining about.
+// This simple check dramatically reduces the nuisance rate for the shadowing
+// check, at least until something cleverer comes along.
+//
+// One wrinkle: A "naked return" is a silent use of a variable that the Span
+// will not capture, but the compilers catch naked returns of shadowed
+// variables so we don't need to.
+//
+// Cases this gets wrong (TODO):
+// - If a for loop's continuation statement mentions a variable redeclared in
+// the block, we should complain about it but don't.
+// - A variable declared inside a function literal can falsely be identified
+// as shadowing a variable in the outer function.
+//
+type Span struct {
+       min token.Pos
+       max token.Pos
+}
+
+// contains reports whether the position is inside the span.
+func (s Span) contains(pos token.Pos) bool {
+       return s.min <= pos && pos < s.max
+}
+
+// growSpan expands the span for the object to contain the instance represented
+// by the identifier.
+func (pkg *Package) growSpan(ident *ast.Ident, obj types.Object) {
+       if *strictShadowing {
+               return // No need
+       }
+       pos := ident.Pos()
+       end := ident.End()
+       span, ok := pkg.spans[obj]
+       if ok {
+               if span.min > pos {
+                       span.min = pos
+               }
+               if span.max < end {
+                       span.max = end
+               }
+       } else {
+               span = Span{pos, end}
+       }
+       pkg.spans[obj] = span
+}
+
+// checkShadowAssignment checks for shadowing in a short variable declaration.
+func checkShadowAssignment(f *File, a *ast.AssignStmt) {
+       if a.Tok != token.DEFINE {
+               return
+       }
+       if f.idiomaticShortRedecl(a) {
+               return
+       }
+       for _, expr := range a.Lhs {
+               ident, ok := expr.(*ast.Ident)
+               if !ok {
+                       f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier")
+                       return
+               }
+               checkShadowing(f, ident)
+       }
+}
+
+// idiomaticShortRedecl reports whether this short declaration can be ignored for
+// the purposes of shadowing, that is, that any redeclarations it contains are deliberate.
+func (f *File) idiomaticShortRedecl(a *ast.AssignStmt) bool {
+       // Don't complain about deliberate redeclarations of the form
+       //      i := i
+       // Such constructs are idiomatic in range loops to create a new variable
+       // for each iteration. Another example is
+       //      switch n := n.(type)
+       if len(a.Rhs) != len(a.Lhs) {
+               return false
+       }
+       // We know it's an assignment, so the LHS must be all identifiers. (We check anyway.)
+       for i, expr := range a.Lhs {
+               lhs, ok := expr.(*ast.Ident)
+               if !ok {
+                       f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier")
+                       return true // Don't do any more processing.
+               }
+               switch rhs := a.Rhs[i].(type) {
+               case *ast.Ident:
+                       if lhs.Name != rhs.Name {
+                               return false
+                       }
+               case *ast.TypeAssertExpr:
+                       if id, ok := rhs.X.(*ast.Ident); ok {
+                               if lhs.Name != id.Name {
+                                       return false
+                               }
+                       }
+               }
+       }
+       return true
+}
+
+// idiomaticRedecl reports whether this declaration spec can be ignored for
+// the purposes of shadowing, that is, that any redeclarations it contains are deliberate.
+func (f *File) idiomaticRedecl(d *ast.ValueSpec) bool {
+       // Don't complain about deliberate redeclarations of the form
+       //      var i, j = i, j
+       if len(d.Names) != len(d.Values) {
+               return false
+       }
+       for i, lhs := range d.Names {
+               if rhs, ok := d.Values[i].(*ast.Ident); ok {
+                       if lhs.Name != rhs.Name {
+                               return false
+                       }
+               }
+       }
+       return true
+}
+
+// checkShadowDecl checks for shadowing in a general variable declaration.
+func checkShadowDecl(f *File, d *ast.GenDecl) {
+       if d.Tok != token.VAR {
+               return
+       }
+       for _, spec := range d.Specs {
+               valueSpec, ok := spec.(*ast.ValueSpec)
+               if !ok {
+                       f.Badf(spec.Pos(), "invalid AST: var GenDecl not ValueSpec")
+                       return
+               }
+               // Don't complain about deliberate redeclarations of the form
+               //      var i = i
+               if f.idiomaticRedecl(valueSpec) {
+                       return
+               }
+               for _, ident := range valueSpec.Names {
+                       checkShadowing(f, ident)
+               }
+       }
+}
+
+// checkShadowing checks whether the identifier shadows an identifier in an outer scope.
+func checkShadowing(f *File, ident *ast.Ident) {
+       if ident.Name == "_" {
+               // Can't shadow the blank identifier.
+               return
+       }
+       obj := f.pkg.defs[ident]
+       if obj == nil {
+               return
+       }
+       // obj.Parent.Parent is the surrounding scope. If we can find another declaration
+       // starting from there, we have a shadowed identifier.
+       _, shadowed := obj.Parent().Parent().LookupParent(obj.Name())
+       if shadowed == nil {
+               return
+       }
+       // Don't complain if it's shadowing a universe-declared identifier; that's fine.
+       if shadowed.Parent() == types.Universe {
+               return
+       }
+       if *strictShadowing {
+               // The shadowed identifier must appear before this one to be an instance of shadowing.
+               if shadowed.Pos() > ident.Pos() {
+                       return
+               }
+       } else {
+               // Don't complain if the span of validity of the shadowed identifier doesn't include
+               // the shadowing identifier.
+               span, ok := f.pkg.spans[shadowed]
+               if !ok {
+                       f.Badf(ident.Pos(), "internal error: no range for %s", ident.Name)
+                       return
+               }
+               if !span.contains(ident.Pos()) {
+                       return
+               }
+       }
+       // Don't complain if the types differ: that implies the programmer really wants two different things.
+       if types.Identical(obj.Type(), shadowed.Type()) {
+               f.Badf(ident.Pos(), "declaration of %s shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos()))
+       }
+}
diff --git a/src/cmd/vet/shift.go b/src/cmd/vet/shift.go
new file mode 100644 (file)
index 0000000..2385c23
--- /dev/null
@@ -0,0 +1,83 @@
+// Copyright 2014 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.
+
+/*
+This file contains the code to check for suspicious shifts.
+*/
+
+package main
+
+import (
+       "go/ast"
+       "go/token"
+
+       "golang.org/x/tools/go/exact"
+       "golang.org/x/tools/go/types"
+)
+
+func init() {
+       register("shift",
+               "check for useless shifts",
+               checkShift,
+               binaryExpr, assignStmt)
+}
+
+func checkShift(f *File, node ast.Node) {
+       switch node := node.(type) {
+       case *ast.BinaryExpr:
+               if node.Op == token.SHL || node.Op == token.SHR {
+                       checkLongShift(f, node, node.X, node.Y)
+               }
+       case *ast.AssignStmt:
+               if len(node.Lhs) != 1 || len(node.Rhs) != 1 {
+                       return
+               }
+               if node.Tok == token.SHL_ASSIGN || node.Tok == token.SHR_ASSIGN {
+                       checkLongShift(f, node, node.Lhs[0], node.Rhs[0])
+               }
+       }
+}
+
+// checkLongShift checks if shift or shift-assign operations shift by more than
+// the length of the underlying variable.
+func checkLongShift(f *File, node ast.Node, x, y ast.Expr) {
+       v := f.pkg.types[y].Value
+       if v == nil {
+               return
+       }
+       amt, ok := exact.Int64Val(v)
+       if !ok {
+               return
+       }
+       t := f.pkg.types[x].Type
+       if t == nil {
+               return
+       }
+       b, ok := t.Underlying().(*types.Basic)
+       if !ok {
+               return
+       }
+       var size int64
+       var msg string
+       switch b.Kind() {
+       case types.Uint8, types.Int8:
+               size = 8
+       case types.Uint16, types.Int16:
+               size = 16
+       case types.Uint32, types.Int32:
+               size = 32
+       case types.Uint64, types.Int64:
+               size = 64
+       case types.Int, types.Uint, types.Uintptr:
+               // These types may be as small as 32 bits, but no smaller.
+               size = 32
+               msg = "might be "
+       default:
+               return
+       }
+       if amt >= size {
+               ident := f.gofmt(x)
+               f.Badf(node.Pos(), "%s %stoo small for shift of %d", ident, msg, amt)
+       }
+}
diff --git a/src/cmd/vet/structtag.go b/src/cmd/vet/structtag.go
new file mode 100644 (file)
index 0000000..e8164a4
--- /dev/null
@@ -0,0 +1,122 @@
+// Copyright 2010 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.
+
+// This file contains the test for canonical struct tags.
+
+package main
+
+import (
+       "errors"
+       "go/ast"
+       "reflect"
+       "strconv"
+)
+
+func init() {
+       register("structtags",
+               "check that struct field tags have canonical format and apply to exported fields as needed",
+               checkCanonicalFieldTag,
+               field)
+}
+
+// checkCanonicalFieldTag checks a struct field tag.
+func checkCanonicalFieldTag(f *File, node ast.Node) {
+       field := node.(*ast.Field)
+       if field.Tag == nil {
+               return
+       }
+
+       tag, err := strconv.Unquote(field.Tag.Value)
+       if err != nil {
+               f.Badf(field.Pos(), "unable to read struct tag %s", field.Tag.Value)
+               return
+       }
+
+       if err := validateStructTag(tag); err != nil {
+               f.Badf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get: %s", field.Tag.Value, err)
+       }
+
+       // Check for use of json or xml tags with unexported fields.
+
+       // Embedded struct. Nothing to do for now, but that
+       // may change, depending on what happens with issue 7363.
+       if len(field.Names) == 0 {
+               return
+       }
+
+       if field.Names[0].IsExported() {
+               return
+       }
+
+       st := reflect.StructTag(tag)
+       for _, enc := range [...]string{"json", "xml"} {
+               if st.Get(enc) != "" {
+                       f.Badf(field.Pos(), "struct field %s has %s tag but is not exported", field.Names[0].Name, enc)
+                       return
+               }
+       }
+}
+
+var (
+       errTagSyntax      = errors.New("bad syntax for struct tag pair")
+       errTagKeySyntax   = errors.New("bad syntax for struct tag key")
+       errTagValueSyntax = errors.New("bad syntax for struct tag value")
+)
+
+// validateStructTag parses the struct tag and returns an error if it is not
+// in the canonical format, which is a space-separated list of key:"value"
+// settings. The value may contain spaces.
+func validateStructTag(tag string) error {
+       // This code is based on the StructTag.Get code in package reflect.
+
+       for tag != "" {
+               // Skip leading space.
+               i := 0
+               for i < len(tag) && tag[i] == ' ' {
+                       i++
+               }
+               tag = tag[i:]
+               if tag == "" {
+                       break
+               }
+
+               // Scan to colon. A space, a quote or a control character is a syntax error.
+               // Strictly speaking, control chars include the range [0x7f, 0x9f], not just
+               // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
+               // as it is simpler to inspect the tag's bytes than the tag's runes.
+               i = 0
+               for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
+                       i++
+               }
+               if i == 0 {
+                       return errTagKeySyntax
+               }
+               if i+1 >= len(tag) || tag[i] != ':' {
+                       return errTagSyntax
+               }
+               if tag[i+1] != '"' {
+                       return errTagValueSyntax
+               }
+               tag = tag[i+1:]
+
+               // Scan quoted string to find value.
+               i = 1
+               for i < len(tag) && tag[i] != '"' {
+                       if tag[i] == '\\' {
+                               i++
+                       }
+                       i++
+               }
+               if i >= len(tag) {
+                       return errTagValueSyntax
+               }
+               qvalue := string(tag[:i+1])
+               tag = tag[i+1:]
+
+               if _, err := strconv.Unquote(qvalue); err != nil {
+                       return errTagValueSyntax
+               }
+       }
+       return nil
+}
diff --git a/src/cmd/vet/testdata/asm.go b/src/cmd/vet/testdata/asm.go
new file mode 100644 (file)
index 0000000..9a3d531
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2010 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.
+
+// +build ignore
+
+// This file contains declarations to test the assembly in test_asm.s.
+
+package testdata
+
+func arg1(x int8, y uint8)
+func arg2(x int16, y uint16)
+func arg4(x int32, y uint32)
+func arg8(x int64, y uint64)
+func argint(x int, y uint)
+func argptr(x *byte, y *byte, c chan int, m map[int]int, f func())
+func argstring(x, y string)
+func argslice(x, y []string)
+func argiface(x interface{}, y interface {
+       m()
+})
+func returnint() int
+func returnbyte(x int) byte
+func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte)
+func returnintmissing() int
+func leaf(x, y int) int
+
+func noprof(x int)
+func dupok(x int)
+func nosplit(x int)
+func rodata(x int)
+func noptr(x int)
+func wrapper(x int)
diff --git a/src/cmd/vet/testdata/asm1.s b/src/cmd/vet/testdata/asm1.s
new file mode 100644 (file)
index 0000000..62f423c
--- /dev/null
@@ -0,0 +1,254 @@
+// Copyright 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.
+
+// +build amd64
+// +build vet_test
+
+TEXT ยทarg1(SB),0,$0-2
+       MOVB    x+0(FP), AX
+       // MOVB x+0(FP), AX // commented out instructions used to panic
+       MOVB    y+1(FP), BX
+       MOVW    x+0(FP), AX // ERROR "\[amd64\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
+       MOVW    y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value"
+       MOVL    y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value"
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value"
+       MOVQ    y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value"
+       MOVB    x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+       MOVB    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+       TESTB   x+0(FP), AX
+       TESTB   y+1(FP), BX
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value"
+       TESTW   y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value"
+       TESTL   x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value"
+       TESTL   y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value"
+       TESTQ   x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value"
+       TESTQ   y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value"
+       TESTB   x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+       TESTB   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+       MOVB    8(SP), AX // ERROR "8\(SP\) should be x\+0\(FP\)"
+       MOVB    9(SP), AX // ERROR "9\(SP\) should be y\+1\(FP\)"
+       MOVB    10(SP), AX // ERROR "use of 10\(SP\) points beyond argument frame"
+       RET
+
+TEXT ยทarg2(SB),0,$0-4
+       MOVB    x+0(FP), AX // ERROR "arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
+       MOVB    y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
+       MOVW    x+0(FP), AX
+       MOVW    y+2(FP), BX
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value"
+       MOVL    y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value"
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value"
+       MOVQ    y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value"
+       MOVW    x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+       MOVW    y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value"
+       TESTB   y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value"
+       TESTW   x+0(FP), AX
+       TESTW   y+2(FP), BX
+       TESTL   x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value"
+       TESTL   y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value"
+       TESTQ   x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value"
+       TESTQ   y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value"
+       TESTW   x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+       TESTW   y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+       RET
+
+TEXT ยทarg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
+       MOVB    y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value"
+       MOVW    y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value"
+       MOVL    x+0(FP), AX
+       MOVL    y+4(FP), AX
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value"
+       MOVQ    y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value"
+       MOVL    x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       MOVL    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value"
+       TESTB   y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value"
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value"
+       TESTW   y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value"
+       TESTL   x+0(FP), AX
+       TESTL   y+4(FP), AX
+       TESTQ   x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value"
+       TESTQ   y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value"
+       TESTL   x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       TESTL   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       RET
+
+TEXT ยทarg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
+       MOVB    y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
+       MOVW    y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value"
+       MOVL    y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value"
+       MOVQ    x+0(FP), AX
+       MOVQ    y+8(FP), AX
+       MOVQ    x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+       MOVQ    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value"
+       TESTB   y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value"
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value"
+       TESTW   y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value"
+       TESTL   x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value"
+       TESTL   y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value"
+       TESTQ   x+0(FP), AX
+       TESTQ   y+8(FP), AX
+       TESTQ   x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+       TESTQ   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+       RET
+
+TEXT ยทargint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
+       MOVB    y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
+       MOVW    y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int is 8-byte value"
+       MOVL    y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint is 8-byte value"
+       MOVQ    x+0(FP), AX
+       MOVQ    y+8(FP), AX
+       MOVQ    x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+       MOVQ    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 8-byte value"
+       TESTB   y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint is 8-byte value"
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 8-byte value"
+       TESTW   y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint is 8-byte value"
+       TESTL   x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int is 8-byte value"
+       TESTL   y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint is 8-byte value"
+       TESTQ   x+0(FP), AX
+       TESTQ   y+8(FP), AX
+       TESTQ   x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+       TESTQ   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+       RET
+
+TEXT ยทargptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
+       MOVB    y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
+       MOVW    y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); \*byte is 8-byte value"
+       MOVL    y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); \*byte is 8-byte value"
+       MOVQ    x+0(FP), AX
+       MOVQ    y+8(FP), AX
+       MOVQ    x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+       MOVQ    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 8-byte value"
+       TESTB   y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); \*byte is 8-byte value"
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 8-byte value"
+       TESTW   y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); \*byte is 8-byte value"
+       TESTL   x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); \*byte is 8-byte value"
+       TESTL   y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); \*byte is 8-byte value"
+       TESTQ   x+0(FP), AX
+       TESTQ   y+8(FP), AX
+       TESTQ   x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+       TESTQ   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+       MOVL    c+16(FP), AX // ERROR "invalid MOVL of c\+16\(FP\); chan int is 8-byte value"
+       MOVL    m+24(FP), AX // ERROR "invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value"
+       MOVL    f+32(FP), AX // ERROR "invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value"
+       RET
+
+TEXT ยทargstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); string base is 8-byte value"
+       MOVQ    x+0(FP), AX
+       MOVW    x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
+       MOVL    x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); string base is 8-byte value"
+       MOVQ    x_base+0(FP), AX
+       MOVW    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+       MOVL    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+       MOVQ    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+       MOVW    x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
+       MOVL    x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); string len is 8-byte value"
+       MOVQ    x_len+8(FP), AX
+       MOVQ    y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
+       MOVQ    y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)"
+       RET
+
+TEXT ยทargslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); slice base is 8-byte value"
+       MOVQ    x+0(FP), AX
+       MOVW    x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
+       MOVL    x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value"
+       MOVQ    x_base+0(FP), AX
+       MOVW    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+       MOVL    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+       MOVQ    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+       MOVW    x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
+       MOVL    x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value"
+       MOVQ    x_len+8(FP), AX
+       MOVW    x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+       MOVL    x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+       MOVQ    x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+       MOVW    x_cap+16(FP), AX // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
+       MOVL    x_cap+16(FP), AX // ERROR "invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value"
+       MOVQ    x_cap+16(FP), AX
+       MOVQ    y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
+       MOVQ    y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
+       MOVQ    y_cap+16(FP), AX // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
+       RET
+
+TEXT ยทargiface(SB),0,$0-32
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); interface type is 8-byte value"
+       MOVQ    x+0(FP), AX
+       MOVW    x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
+       MOVL    x_type+0(FP), AX // ERROR "invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value"
+       MOVQ    x_type+0(FP), AX
+       MOVQ    x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
+       MOVQ    x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
+       MOVW    x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+       MOVL    x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+       MOVQ    x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+       MOVW    x_data+8(FP), AX // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
+       MOVL    x_data+8(FP), AX // ERROR "invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value"
+       MOVQ    x_data+8(FP), AX
+       MOVW    y+16(FP), AX // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
+       MOVL    y+16(FP), AX // ERROR "invalid MOVL of y\+16\(FP\); interface itable is 8-byte value"
+       MOVQ    y+16(FP), AX
+       MOVW    y_itable+16(FP), AX // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
+       MOVL    y_itable+16(FP), AX // ERROR "invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value"
+       MOVQ    y_itable+16(FP), AX
+       MOVQ    y_type+16(FP), AX // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
+       MOVW    y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+       MOVL    y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+       MOVQ    y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+       MOVW    y_data+24(FP), AX // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
+       MOVL    y_data+24(FP), AX // ERROR "invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value"
+       MOVQ    y_data+24(FP), AX
+       RET
+
+TEXT ยทreturnint(SB),0,$0-8
+       MOVB    AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
+       MOVW    AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
+       MOVL    AX, ret+0(FP) // ERROR "invalid MOVL of ret\+0\(FP\); int is 8-byte value"
+       MOVQ    AX, ret+0(FP)
+       MOVQ    AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
+       MOVQ    AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
+       RET
+
+TEXT ยทreturnbyte(SB),0,$0-9
+       MOVQ    x+0(FP), AX
+       MOVB    AX, ret+8(FP)
+       MOVW    AX, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
+       MOVL    AX, ret+8(FP) // ERROR "invalid MOVL of ret\+8\(FP\); byte is 1-byte value"
+       MOVQ    AX, ret+8(FP) // ERROR "invalid MOVQ of ret\+8\(FP\); byte is 1-byte value"
+       MOVB    AX, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
+       RET
+
+TEXT ยทreturnnamed(SB),0,$0-41
+       MOVB    x+0(FP), AX
+       MOVQ    AX, r1+8(FP)
+       MOVW    AX, r2+16(FP)
+       MOVQ    AX, r3+24(FP)
+       MOVQ    AX, r3_base+24(FP)
+       MOVQ    AX, r3_len+32(FP)
+       MOVB    AX, r4+40(FP)
+       MOVL    AX, r1+8(FP) // ERROR "invalid MOVL of r1\+8\(FP\); int is 8-byte value"
+       RET
+
+TEXT ยทreturnintmissing(SB),0,$0-8
+       RET // ERROR "RET without writing to 8-byte ret\+0\(FP\)"
diff --git a/src/cmd/vet/testdata/asm2.s b/src/cmd/vet/testdata/asm2.s
new file mode 100644 (file)
index 0000000..c33c02a
--- /dev/null
@@ -0,0 +1,257 @@
+// Copyright 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.
+
+// +build 386
+// +build vet_test
+
+TEXT ยทarg1(SB),0,$0-2
+       MOVB    x+0(FP), AX
+       MOVB    y+1(FP), BX
+       MOVW    x+0(FP), AX // ERROR "\[386\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
+       MOVW    y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value"
+       MOVL    y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value"
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value"
+       MOVQ    y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value"
+       MOVB    x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+       MOVB    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+       TESTB   x+0(FP), AX
+       TESTB   y+1(FP), BX
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value"
+       TESTW   y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value"
+       TESTL   x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value"
+       TESTL   y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value"
+       TESTQ   x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value"
+       TESTQ   y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value"
+       TESTB   x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+       TESTB   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+       MOVB    4(SP), AX // ERROR "4\(SP\) should be x\+0\(FP\)"
+       MOVB    5(SP), AX // ERROR "5\(SP\) should be y\+1\(FP\)"
+       MOVB    6(SP), AX // ERROR "use of 6\(SP\) points beyond argument frame"
+       RET
+
+TEXT ยทarg2(SB),0,$0-4
+       MOVB    x+0(FP), AX // ERROR "arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
+       MOVB    y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
+       MOVW    x+0(FP), AX
+       MOVW    y+2(FP), BX
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value"
+       MOVL    y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value"
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value"
+       MOVQ    y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value"
+       MOVW    x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+       MOVW    y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value"
+       TESTB   y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value"
+       TESTW   x+0(FP), AX
+       TESTW   y+2(FP), BX
+       TESTL   x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value"
+       TESTL   y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value"
+       TESTQ   x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value"
+       TESTQ   y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value"
+       TESTW   x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+       TESTW   y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+       RET
+
+TEXT ยทarg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
+       MOVB    y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value"
+       MOVW    y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value"
+       MOVL    x+0(FP), AX
+       MOVL    y+4(FP), AX
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value"
+       MOVQ    y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value"
+       MOVL    x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       MOVL    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value"
+       TESTB   y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value"
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value"
+       TESTW   y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value"
+       TESTL   x+0(FP), AX
+       TESTL   y+4(FP), AX
+       TESTQ   x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value"
+       TESTQ   y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value"
+       TESTL   x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       TESTL   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       RET
+
+TEXT ยทarg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
+       MOVB    y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
+       MOVW    y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
+       MOVL    x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
+       MOVL    x_lo+0(FP), AX
+       MOVL    x_hi+4(FP), AX
+       MOVL    y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
+       MOVL    y_lo+8(FP), AX
+       MOVL    y_hi+12(FP), AX
+       MOVQ    x+0(FP), AX
+       MOVQ    y+8(FP), AX
+       MOVQ    x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+       MOVQ    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value"
+       TESTB   y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value"
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value"
+       TESTW   y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value"
+       TESTL   x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
+       TESTL   y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
+       TESTQ   x+0(FP), AX
+       TESTQ   y+8(FP), AX
+       TESTQ   x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+       TESTQ   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+       RET
+
+TEXT ยทargint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value"
+       MOVB    y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 4-byte value"
+       MOVW    y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint is 4-byte value"
+       MOVL    x+0(FP), AX
+       MOVL    y+4(FP), AX
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int is 4-byte value"
+       MOVQ    y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint is 4-byte value"
+       MOVQ    x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       MOVQ    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 4-byte value"
+       TESTB   y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint is 4-byte value"
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 4-byte value"
+       TESTW   y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint is 4-byte value"
+       TESTL   x+0(FP), AX
+       TESTL   y+4(FP), AX
+       TESTQ   x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int is 4-byte value"
+       TESTQ   y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint is 4-byte value"
+       TESTQ   x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       TESTQ   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       RET
+
+TEXT ยทargptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value"
+       MOVB    y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 4-byte value"
+       MOVW    y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); \*byte is 4-byte value"
+       MOVL    x+0(FP), AX
+       MOVL    y+4(FP), AX
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); \*byte is 4-byte value"
+       MOVQ    y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); \*byte is 4-byte value"
+       MOVQ    x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       MOVQ    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       TESTB   x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 4-byte value"
+       TESTB   y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); \*byte is 4-byte value"
+       TESTW   x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 4-byte value"
+       TESTW   y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); \*byte is 4-byte value"
+       TESTL   x+0(FP), AX
+       TESTL   y+4(FP), AX
+       TESTQ   x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); \*byte is 4-byte value"
+       TESTQ   y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); \*byte is 4-byte value"
+       TESTQ   x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       TESTQ   y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       MOVW    c+8(FP), AX // ERROR "invalid MOVW of c\+8\(FP\); chan int is 4-byte value"
+       MOVW    m+12(FP), AX // ERROR "invalid MOVW of m\+12\(FP\); map\[int\]int is 4-byte value"
+       MOVW    f+16(FP), AX // ERROR "invalid MOVW of f\+16\(FP\); func\(\) is 4-byte value"
+       RET
+
+TEXT ยทargstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 4-byte value"
+       MOVL    x+0(FP), AX
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); string base is 4-byte value"
+       MOVW    x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 4-byte value"
+       MOVL    x_base+0(FP), AX
+       MOVQ    x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); string base is 4-byte value"
+       MOVW    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVL    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVQ    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVW    x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); string len is 4-byte value"
+       MOVL    x_len+4(FP), AX
+       MOVQ    x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); string len is 4-byte value"
+       MOVQ    y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)"
+       MOVQ    y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)"
+       RET
+
+TEXT ยทargslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 4-byte value"
+       MOVL    x+0(FP), AX
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); slice base is 4-byte value"
+       MOVW    x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 4-byte value"
+       MOVL    x_base+0(FP), AX
+       MOVQ    x_base+0(FP), AX // ERROR "invalid MOVQ of x_base\+0\(FP\); slice base is 4-byte value"
+       MOVW    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVL    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVQ    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVW    x_len+4(FP), AX // ERROR "invalid MOVW of x_len\+4\(FP\); slice len is 4-byte value"
+       MOVL    x_len+4(FP), AX
+       MOVQ    x_len+4(FP), AX // ERROR "invalid MOVQ of x_len\+4\(FP\); slice len is 4-byte value"
+       MOVW    x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
+       MOVL    x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
+       MOVQ    x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
+       MOVW    x_cap+8(FP), AX // ERROR "invalid MOVW of x_cap\+8\(FP\); slice cap is 4-byte value"
+       MOVL    x_cap+8(FP), AX
+       MOVQ    x_cap+8(FP), AX // ERROR "invalid MOVQ of x_cap\+8\(FP\); slice cap is 4-byte value"
+       MOVQ    y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)"
+       MOVQ    y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)"
+       MOVQ    y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)"
+       RET
+
+TEXT ยทargiface(SB),0,$0-16
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 4-byte value"
+       MOVL    x+0(FP), AX
+       MOVQ    x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); interface type is 4-byte value"
+       MOVW    x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 4-byte value"
+       MOVL    x_type+0(FP), AX
+       MOVQ    x_type+0(FP), AX // ERROR "invalid MOVQ of x_type\+0\(FP\); interface type is 4-byte value"
+       MOVQ    x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
+       MOVQ    x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
+       MOVW    x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
+       MOVL    x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
+       MOVQ    x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
+       MOVW    x_data+4(FP), AX // ERROR "invalid MOVW of x_data\+4\(FP\); interface data is 4-byte value"
+       MOVL    x_data+4(FP), AX
+       MOVQ    x_data+4(FP), AX // ERROR "invalid MOVQ of x_data\+4\(FP\); interface data is 4-byte value"
+       MOVW    y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); interface itable is 4-byte value"
+       MOVL    y+8(FP), AX
+       MOVQ    y+8(FP), AX // ERROR "invalid MOVQ of y\+8\(FP\); interface itable is 4-byte value"
+       MOVW    y_itable+8(FP), AX // ERROR "invalid MOVW of y_itable\+8\(FP\); interface itable is 4-byte value"
+       MOVL    y_itable+8(FP), AX
+       MOVQ    y_itable+8(FP), AX // ERROR "invalid MOVQ of y_itable\+8\(FP\); interface itable is 4-byte value"
+       MOVQ    y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)"
+       MOVW    y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
+       MOVL    y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
+       MOVQ    y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
+       MOVW    y_data+12(FP), AX // ERROR "invalid MOVW of y_data\+12\(FP\); interface data is 4-byte value"
+       MOVL    y_data+12(FP), AX
+       MOVQ    y_data+12(FP), AX // ERROR "invalid MOVQ of y_data\+12\(FP\); interface data is 4-byte value"
+       RET
+
+TEXT ยทreturnint(SB),0,$0-4
+       MOVB    AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value"
+       MOVW    AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 4-byte value"
+       MOVL    AX, ret+0(FP)
+       MOVQ    AX, ret+0(FP) // ERROR "invalid MOVQ of ret\+0\(FP\); int is 4-byte value"
+       MOVQ    AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
+       MOVQ    AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
+       RET
+
+TEXT ยทreturnbyte(SB),0,$0-5
+       MOVL    x+0(FP), AX
+       MOVB    AX, ret+4(FP)
+       MOVW    AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
+       MOVL    AX, ret+4(FP) // ERROR "invalid MOVL of ret\+4\(FP\); byte is 1-byte value"
+       MOVQ    AX, ret+4(FP) // ERROR "invalid MOVQ of ret\+4\(FP\); byte is 1-byte value"
+       MOVB    AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
+       RET
+
+TEXT ยทreturnnamed(SB),0,$0-21
+       MOVB    x+0(FP), AX
+       MOVL    AX, r1+4(FP)
+       MOVW    AX, r2+8(FP)
+       MOVL    AX, r3+12(FP)
+       MOVL    AX, r3_base+12(FP)
+       MOVL    AX, r3_len+16(FP)
+       MOVB    AX, r4+20(FP)
+       MOVQ    AX, r1+4(FP) // ERROR "invalid MOVQ of r1\+4\(FP\); int is 4-byte value"
+       RET
+
+TEXT ยทreturnintmissing(SB),0,$0-4
+       RET // ERROR "RET without writing to 4-byte ret\+0\(FP\)"
diff --git a/src/cmd/vet/testdata/asm3.s b/src/cmd/vet/testdata/asm3.s
new file mode 100644 (file)
index 0000000..3d69356
--- /dev/null
@@ -0,0 +1,178 @@
+// Copyright 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.
+
+// +build arm
+// +build vet_test
+
+TEXT ยทarg1(SB),0,$0-2
+       MOVB    x+0(FP), AX
+       MOVB    y+1(FP), BX
+       MOVH    x+0(FP), AX // ERROR "\[arm\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
+       MOVH    y+1(FP), AX // ERROR "invalid MOVH of y\+1\(FP\); uint8 is 1-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
+       MOVW    y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
+       MOVB    x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+       MOVB    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+       MOVB    8(R13), AX // ERROR "8\(R13\) should be x\+0\(FP\)"
+       MOVB    9(R13), AX // ERROR "9\(R13\) should be y\+1\(FP\)"
+       MOVB    10(R13), AX // ERROR "use of 10\(R13\) points beyond argument frame"
+       RET
+
+TEXT ยทarg2(SB),0,$0-4
+       MOVB    x+0(FP), AX // ERROR "arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
+       MOVB    y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
+       MOVH    x+0(FP), AX
+       MOVH    y+2(FP), BX
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int16 is 2-byte value"
+       MOVW    y+2(FP), AX // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
+       MOVH    x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+       MOVH    y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+       RET
+
+TEXT ยทarg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
+       MOVB    y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
+       MOVH    x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
+       MOVH    y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
+       MOVW    x+0(FP), AX
+       MOVW    y+4(FP), AX
+       MOVW    x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       MOVW    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       RET
+
+TEXT ยทarg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
+       MOVB    y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
+       MOVH    x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
+       MOVH    y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
+       MOVW    x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
+       MOVW    x_lo+0(FP), AX
+       MOVW    x_hi+4(FP), AX
+       MOVW    y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
+       MOVW    y_lo+8(FP), AX
+       MOVW    y_hi+12(FP), AX
+       MOVQ    x+0(FP), AX
+       MOVQ    y+8(FP), AX
+       MOVQ    x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+       MOVQ    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+       RET
+
+TEXT ยทargint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value"
+       MOVB    y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value"
+       MOVH    x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int is 4-byte value"
+       MOVH    y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint is 4-byte value"
+       MOVW    x+0(FP), AX
+       MOVW    y+4(FP), AX
+       MOVQ    x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       MOVQ    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       RET
+
+TEXT ยทargptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20"
+       MOVB    x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value"
+       MOVB    y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value"
+       MOVH    x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 4-byte value"
+       MOVH    y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); \*byte is 4-byte value"
+       MOVW    x+0(FP), AX
+       MOVW    y+4(FP), AX
+       MOVQ    x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+       MOVQ    y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+       MOVH    c+8(FP), AX // ERROR "invalid MOVH of c\+8\(FP\); chan int is 4-byte value"
+       MOVH    m+12(FP), AX // ERROR "invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value"
+       MOVH    f+16(FP), AX // ERROR "invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value"
+       RET
+
+TEXT ยทargstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16"
+       MOVH    x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); string base is 4-byte value"
+       MOVW    x+0(FP), AX
+       MOVH    x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 4-byte value"
+       MOVW    x_base+0(FP), AX
+       MOVH    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVW    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVQ    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVH    x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); string len is 4-byte value"
+       MOVW    x_len+4(FP), AX
+       MOVQ    y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)"
+       MOVQ    y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)"
+       RET
+
+TEXT ยทargslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
+       MOVH    x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); slice base is 4-byte value"
+       MOVW    x+0(FP), AX
+       MOVH    x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value"
+       MOVW    x_base+0(FP), AX
+       MOVH    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVW    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVQ    x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+       MOVH    x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value"
+       MOVW    x_len+4(FP), AX
+       MOVH    x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
+       MOVW    x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
+       MOVQ    x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
+       MOVH    x_cap+8(FP), AX // ERROR "invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value"
+       MOVW    x_cap+8(FP), AX
+       MOVQ    y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)"
+       MOVQ    y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)"
+       MOVQ    y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)"
+       RET
+
+TEXT ยทargiface(SB),0,$0-16
+       MOVH    x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); interface type is 4-byte value"
+       MOVW    x+0(FP), AX
+       MOVH    x_type+0(FP), AX // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value"
+       MOVW    x_type+0(FP), AX
+       MOVQ    x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
+       MOVQ    x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
+       MOVH    x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
+       MOVW    x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
+       MOVQ    x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
+       MOVH    x_data+4(FP), AX // ERROR "invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value"
+       MOVW    x_data+4(FP), AX
+       MOVH    y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); interface itable is 4-byte value"
+       MOVW    y+8(FP), AX
+       MOVH    y_itable+8(FP), AX // ERROR "invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value"
+       MOVW    y_itable+8(FP), AX
+       MOVQ    y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)"
+       MOVH    y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
+       MOVW    y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
+       MOVQ    y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
+       MOVH    y_data+12(FP), AX // ERROR "invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value"
+       MOVW    y_data+12(FP), AX
+       RET
+
+TEXT ยทreturnint(SB),0,$0-4
+       MOVB    AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value"
+       MOVH    AX, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 4-byte value"
+       MOVW    AX, ret+0(FP)
+       MOVQ    AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
+       MOVQ    AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
+       RET
+
+TEXT ยทreturnbyte(SB),0,$0-5
+       MOVW    x+0(FP), AX
+       MOVB    AX, ret+4(FP)
+       MOVH    AX, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value"
+       MOVW    AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
+       MOVB    AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
+       RET
+
+TEXT ยทreturnnamed(SB),0,$0-21
+       MOVB    x+0(FP), AX
+       MOVW    AX, r1+4(FP)
+       MOVH    AX, r2+8(FP)
+       MOVW    AX, r3+12(FP)
+       MOVW    AX, r3_base+12(FP)
+       MOVW    AX, r3_len+16(FP)
+       MOVB    AX, r4+20(FP)
+       MOVB    AX, r1+4(FP) // ERROR "invalid MOVB of r1\+4\(FP\); int is 4-byte value"
+       RET
+
+TEXT ยทreturnintmissing(SB),0,$0-4
+       RET // ERROR "RET without writing to 4-byte ret\+0\(FP\)"
+
+TEXT ยทleaf(SB),0,$-4-12
+       MOVW    x+0(FP), AX
+       MOVW    y+4(FP), AX
+       MOVW    AX, ret+8(FP)
+       RET
diff --git a/src/cmd/vet/testdata/asm4.s b/src/cmd/vet/testdata/asm4.s
new file mode 100644 (file)
index 0000000..044b050
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 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.
+
+// +build amd64
+// +build vet_test
+
+// Test cases for symbolic NOSPLIT etc. on TEXT symbols.
+
+TEXT ยทnoprof(SB),NOPROF,$0-8
+       RET
+
+TEXT ยทdupok(SB),DUPOK,$0-8
+       RET
+
+TEXT ยทnosplit(SB),NOSPLIT,$0
+       RET
+
+TEXT ยทrodata(SB),RODATA,$0-8
+       RET
+
+TEXT ยทnoptr(SB),NOPTR|NOSPLIT,$0
+       RET
+
+TEXT ยทwrapper(SB),WRAPPER,$0-8
+       RET
diff --git a/src/cmd/vet/testdata/assign.go b/src/cmd/vet/testdata/assign.go
new file mode 100644 (file)
index 0000000..32ba868
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 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.
+
+// This file contains tests for the useless-assignment checker.
+
+package testdata
+
+type ST struct {
+       x int
+}
+
+func (s *ST) SetX(x int) {
+       // Accidental self-assignment; it should be "s.x = x"
+       x = x // ERROR "self-assignment of x to x"
+       // Another mistake
+       s.x = s.x // ERROR "self-assignment of s.x to s.x"
+}
diff --git a/src/cmd/vet/testdata/atomic.go b/src/cmd/vet/testdata/atomic.go
new file mode 100644 (file)
index 0000000..1ba261d
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 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.
+
+// This file contains tests for the atomic checker.
+
+package testdata
+
+import (
+       "sync/atomic"
+)
+
+type Counter uint64
+
+func AtomicTests() {
+       x := uint64(1)
+       x = atomic.AddUint64(&x, 1)        // ERROR "direct assignment to atomic value"
+       _, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
+       x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value"
+
+       y := &x
+       *y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value"
+
+       var su struct{ Counter uint64 }
+       su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value"
+       z1 := atomic.AddUint64(&su.Counter, 1)
+       _ = z1 // Avoid err "z declared and not used"
+
+       var sp struct{ Counter *uint64 }
+       *sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value"
+       z2 := atomic.AddUint64(sp.Counter, 1)
+       _ = z2 // Avoid err "z declared and not used"
+
+       au := []uint64{10, 20}
+       au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value"
+       au[1] = atomic.AddUint64(&au[0], 1)
+
+       ap := []*uint64{&au[0], &au[1]}
+       *ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value"
+       *ap[1] = atomic.AddUint64(ap[0], 1)
+
+       x = atomic.AddUint64() // Used to make vet crash; now silently ignored.
+}
diff --git a/src/cmd/vet/testdata/bool.go b/src/cmd/vet/testdata/bool.go
new file mode 100644 (file)
index 0000000..af6cc01
--- /dev/null
@@ -0,0 +1,113 @@
+// Copyright 2014 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.
+
+// This file contains tests for the bool checker.
+
+package testdata
+
+import "io"
+
+func RatherStupidConditions() {
+       var f, g func() int
+       if f() == 0 || f() == 0 { // OK f might have side effects
+       }
+       if v, w := f(), g(); v == w || v == w { // ERROR "redundant or: v == w || v == w"
+       }
+       _ = f == nil || f == nil // ERROR "redundant or: f == nil || f == nil"
+
+       _ = i == byte(1) || i == byte(1) // TODO conversions are treated as if they may have side effects
+
+       var c chan int
+       _ = 0 == <-c || 0 == <-c                                  // OK subsequent receives may yield different values
+       for i, j := <-c, <-c; i == j || i == j; i, j = <-c, <-c { // ERROR "redundant or: i == j || i == j"
+       }
+
+       var i, j, k int
+       _ = i+1 == 1 || i+1 == 1         // ERROR "redundant or: i\+1 == 1 || i\+1 == 1"
+       _ = i == 1 || j+1 == i || i == 1 // ERROR "redundant or: i == 1 || i == 1"
+
+       _ = i == 1 || i == 1 || f() == 1 // ERROR "redundant or: i == 1 || i == 1"
+       _ = i == 1 || f() == 1 || i == 1 // OK f may alter i as a side effect
+       _ = f() == 1 || i == 1 || i == 1 // ERROR "redundant or: i == 1 || i == 1"
+
+       // Test partition edge cases
+       _ = f() == 1 || i == 1 || i == 1 || j == 1 // ERROR "redundant or: i == 1 || i == 1"
+       _ = f() == 1 || j == 1 || i == 1 || i == 1 // ERROR "redundant or: i == 1 || i == 1"
+       _ = i == 1 || f() == 1 || i == 1 || i == 1 // ERROR "redundant or: i == 1 || i == 1"
+       _ = i == 1 || i == 1 || f() == 1 || i == 1 // ERROR "redundant or: i == 1 || i == 1"
+       _ = i == 1 || i == 1 || j == 1 || f() == 1 // ERROR "redundant or: i == 1 || i == 1"
+       _ = j == 1 || i == 1 || i == 1 || f() == 1 // ERROR "redundant or: i == 1 || i == 1"
+       _ = i == 1 || f() == 1 || f() == 1 || i == 1
+
+       _ = i == 1 || (i == 1 || i == 2)             // ERROR "redundant or: i == 1 || i == 1"
+       _ = i == 1 || (f() == 1 || i == 1)           // OK f may alter i as a side effect
+       _ = i == 1 || (i == 1 || f() == 1)           // ERROR "redundant or: i == 1 || i == 1"
+       _ = i == 1 || (i == 2 || (i == 1 || i == 3)) // ERROR "redundant or: i == 1 || i == 1"
+
+       var a, b bool
+       _ = i == 1 || (a || (i == 1 || b)) // ERROR "redundant or: i == 1 || i == 1"
+
+       // Check that all redundant ors are flagged
+       _ = j == 0 ||
+               i == 1 ||
+               f() == 1 ||
+               j == 0 || // ERROR "redundant or: j == 0 || j == 0"
+               i == 1 || // ERROR "redundant or: i == 1 || i == 1"
+               i == 1 || // ERROR "redundant or: i == 1 || i == 1"
+               i == 1 ||
+               j == 0 ||
+               k == 0
+
+       _ = i == 1*2*3 || i == 1*2*3 // ERROR "redundant or: i == 1\*2\*3 || i == 1\*2\*3"
+
+       // These test that redundant, suspect expressions do not trigger multiple errors.
+       _ = i != 0 || i != 0 // ERROR "redundant or: i != 0 || i != 0"
+       _ = i == 0 && i == 0 // ERROR "redundant and: i == 0 && i == 0"
+
+       // and is dual to or; check the basics and
+       // let the or tests pull the rest of the weight.
+       _ = 0 != <-c && 0 != <-c         // OK subsequent receives may yield different values
+       _ = f() != 0 && f() != 0         // OK f might have side effects
+       _ = f != nil && f != nil         // ERROR "redundant and: f != nil && f != nil"
+       _ = i != 1 && i != 1 && f() != 1 // ERROR "redundant and: i != 1 && i != 1"
+       _ = i != 1 && f() != 1 && i != 1 // OK f may alter i as a side effect
+       _ = f() != 1 && i != 1 && i != 1 // ERROR "redundant and: i != 1 && i != 1"
+}
+
+func RoyallySuspectConditions() {
+       var i, j int
+
+       _ = i == 0 || i == 1 // OK
+       _ = i != 0 || i != 1 // ERROR "suspect or: i != 0 || i != 1"
+       _ = i != 0 || 1 != i // ERROR "suspect or: i != 0 || 1 != i"
+       _ = 0 != i || 1 != i // ERROR "suspect or: 0 != i || 1 != i"
+       _ = 0 != i || i != 1 // ERROR "suspect or: 0 != i || i != 1"
+
+       _ = (0 != i) || i != 1 // ERROR "suspect or: 0 != i || i != 1"
+
+       _ = i+3 != 7 || j+5 == 0 || i+3 != 9 // ERROR "suspect or: i\+3 != 7 || i\+3 != 9"
+
+       _ = i != 0 || j == 0 || i != 1 // ERROR "suspect or: i != 0 || i != 1"
+
+       _ = i != 0 || i != 1<<4 // ERROR "suspect or: i != 0 || i != 1<<4"
+
+       _ = i != 0 || j != 0
+       _ = 0 != i || 0 != j
+
+       var s string
+       _ = s != "one" || s != "the other" // ERROR "suspect or: s != .one. || s != .the other."
+
+       _ = "et" != "alii" || "et" != "cetera"         // ERROR "suspect or: .et. != .alii. || .et. != .cetera."
+       _ = "me gustas" != "tu" || "le gustas" != "tu" // OK we could catch this case, but it's not worth the code
+
+       var err error
+       _ = err != nil || err != io.EOF // TODO catch this case?
+
+       // Sanity check and.
+       _ = i != 0 && i != 1 // OK
+       _ = i == 0 && i == 1 // ERROR "suspect and: i == 0 && i == 1"
+       _ = i == 0 && 1 == i // ERROR "suspect and: i == 0 && 1 == i"
+       _ = 0 == i && 1 == i // ERROR "suspect and: 0 == i && 1 == i"
+       _ = 0 == i && i == 1 // ERROR "suspect and: 0 == i && i == 1"
+}
diff --git a/src/cmd/vet/testdata/buildtag.go b/src/cmd/vet/testdata/buildtag.go
new file mode 100644 (file)
index 0000000..eb36fd3
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 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.
+
+// This file contains tests for the buildtag checker.
+
+// +builder // ERROR "possible malformed \+build comment"
+// +build !ignore
+
+package testdata
+
+// +build toolate // ERROR "build comment must appear before package clause and be followed by a blank line"
+
+var _ = 3
diff --git a/src/cmd/vet/testdata/buildtag_bad.go b/src/cmd/vet/testdata/buildtag_bad.go
new file mode 100644 (file)
index 0000000..fbe10cf
--- /dev/null
@@ -0,0 +1,15 @@
+// This file contains misplaced or malformed build constraints.
+// The Go tool will skip it, because the constraints are invalid.
+// It serves only to test the tag checker during make test.
+
+// Mention +build // ERROR "possible malformed \+build comment"
+
+// +build !!bang // ERROR "invalid double negative in build constraint"
+// +build @#$ // ERROR "invalid non-alphanumeric build constraint"
+
+// +build toolate // ERROR "build comment must appear before package clause and be followed by a blank line"
+package bad
+
+// This is package 'bad' rather than 'main' so the erroneous build
+// tag doesn't end up looking like a package doc for the vet command
+// when examined by godoc.
diff --git a/src/cmd/vet/testdata/composite.go b/src/cmd/vet/testdata/composite.go
new file mode 100644 (file)
index 0000000..69e7d7c
--- /dev/null
@@ -0,0 +1,63 @@
+// Copyright 2012 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.
+
+// This file contains tests for the untagged struct literal checker.
+
+// This file contains the test for untagged struct literals.
+
+package testdata
+
+import (
+       "flag"
+       "go/scanner"
+)
+
+var Okay1 = []string{
+       "Name",
+       "Usage",
+       "DefValue",
+}
+
+var Okay2 = map[string]bool{
+       "Name":     true,
+       "Usage":    true,
+       "DefValue": true,
+}
+
+var Okay3 = struct {
+       X string
+       Y string
+       Z string
+}{
+       "Name",
+       "Usage",
+       "DefValue",
+}
+
+type MyStruct struct {
+       X string
+       Y string
+       Z string
+}
+
+var Okay4 = MyStruct{
+       "Name",
+       "Usage",
+       "DefValue",
+}
+
+// Testing is awkward because we need to reference things from a separate package
+// to trigger the warnings.
+
+var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "unkeyed fields"
+       "Name",
+       "Usage",
+       nil, // Value
+       "DefValue",
+}
+
+// Used to test the check for slices and arrays: If that test is disabled and
+// vet is run with --compositewhitelist=false, this line triggers an error.
+// Clumsy but sufficient.
+var scannerErrorListTest = scanner.ErrorList{nil, nil}
diff --git a/src/cmd/vet/testdata/copylock_func.go b/src/cmd/vet/testdata/copylock_func.go
new file mode 100644 (file)
index 0000000..108c044
--- /dev/null
@@ -0,0 +1,90 @@
+// Copyright 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.
+
+// This file contains tests for the copylock checker's
+// function declaration analysis.
+
+package testdata
+
+import "sync"
+
+func OkFunc(*sync.Mutex) {}
+func BadFunc(sync.Mutex) {} // ERROR "BadFunc passes Lock by value: sync.Mutex"
+func OkRet() *sync.Mutex {}
+func BadRet() sync.Mutex {} // ERROR "BadRet returns Lock by value: sync.Mutex"
+
+type EmbeddedRWMutex struct {
+       sync.RWMutex
+}
+
+func (*EmbeddedRWMutex) OkMeth() {}
+func (EmbeddedRWMutex) BadMeth() {} // ERROR "BadMeth passes Lock by value: testdata.EmbeddedRWMutex"
+func OkFunc(e *EmbeddedRWMutex)  {}
+func BadFunc(EmbeddedRWMutex)    {} // ERROR "BadFunc passes Lock by value: testdata.EmbeddedRWMutex"
+func OkRet() *EmbeddedRWMutex    {}
+func BadRet() EmbeddedRWMutex    {} // ERROR "BadRet returns Lock by value: testdata.EmbeddedRWMutex"
+
+type FieldMutex struct {
+       s sync.Mutex
+}
+
+func (*FieldMutex) OkMeth()   {}
+func (FieldMutex) BadMeth()   {} // ERROR "BadMeth passes Lock by value: testdata.FieldMutex contains sync.Mutex"
+func OkFunc(*FieldMutex)      {}
+func BadFunc(FieldMutex, int) {} // ERROR "BadFunc passes Lock by value: testdata.FieldMutex contains sync.Mutex"
+
+type L0 struct {
+       L1
+}
+
+type L1 struct {
+       l L2
+}
+
+type L2 struct {
+       sync.Mutex
+}
+
+func (*L0) Ok() {}
+func (L0) Bad() {} // ERROR "Bad passes Lock by value: testdata.L0 contains testdata.L1 contains testdata.L2"
+
+type EmbeddedMutexPointer struct {
+       s *sync.Mutex // safe to copy this pointer
+}
+
+func (*EmbeddedMutexPointer) Ok()      {}
+func (EmbeddedMutexPointer) AlsoOk()   {}
+func StillOk(EmbeddedMutexPointer)     {}
+func LookinGood() EmbeddedMutexPointer {}
+
+type EmbeddedLocker struct {
+       sync.Locker // safe to copy interface values
+}
+
+func (*EmbeddedLocker) Ok()    {}
+func (EmbeddedLocker) AlsoOk() {}
+
+type CustomLock struct{}
+
+func (*CustomLock) Lock()   {}
+func (*CustomLock) Unlock() {}
+
+func Ok(*CustomLock) {}
+func Bad(CustomLock) {} // ERROR "Bad passes Lock by value: testdata.CustomLock"
+
+// TODO: Unfortunate cases
+
+// Non-ideal error message:
+// Since we're looking for Lock methods, sync.Once's underlying
+// sync.Mutex gets called out, but without any reference to the sync.Once.
+type LocalOnce sync.Once
+
+func (LocalOnce) Bad() {} // ERROR "Bad passes Lock by value: testdata.LocalOnce contains sync.Mutex"
+
+// False negative:
+// LocalMutex doesn't have a Lock method.
+// Nevertheless, it is probably a bad idea to pass it by value.
+type LocalMutex sync.Mutex
+
+func (LocalMutex) Bad() {} // WANTED: An error here :(
diff --git a/src/cmd/vet/testdata/copylock_range.go b/src/cmd/vet/testdata/copylock_range.go
new file mode 100644 (file)
index 0000000..f95b025
--- /dev/null
@@ -0,0 +1,67 @@
+// Copyright 2014 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.
+
+// This file contains tests for the copylock checker's
+// range statement analysis.
+
+package testdata
+
+import "sync"
+
+func rangeMutex() {
+       var mu sync.Mutex
+       var i int
+
+       var s []sync.Mutex
+       for range s {
+       }
+       for i = range s {
+       }
+       for i := range s {
+       }
+       for i, _ = range s {
+       }
+       for i, _ := range s {
+       }
+       for _, mu = range s { // ERROR "range var mu copies Lock: sync.Mutex"
+       }
+       for _, m := range s { // ERROR "range var m copies Lock: sync.Mutex"
+       }
+       for i, mu = range s { // ERROR "range var mu copies Lock: sync.Mutex"
+       }
+       for i, m := range s { // ERROR "range var m copies Lock: sync.Mutex"
+       }
+
+       var a [3]sync.Mutex
+       for _, m := range a { // ERROR "range var m copies Lock: sync.Mutex"
+       }
+
+       var m map[sync.Mutex]sync.Mutex
+       for k := range m { // ERROR "range var k copies Lock: sync.Mutex"
+       }
+       for mu, _ = range m { // ERROR "range var mu copies Lock: sync.Mutex"
+       }
+       for k, _ := range m { // ERROR "range var k copies Lock: sync.Mutex"
+       }
+       for _, mu = range m { // ERROR "range var mu copies Lock: sync.Mutex"
+       }
+       for _, v := range m { // ERROR "range var v copies Lock: sync.Mutex"
+       }
+
+       var c chan sync.Mutex
+       for range c {
+       }
+       for mu = range c { // ERROR "range var mu copies Lock: sync.Mutex"
+       }
+       for v := range c { // ERROR "range var v copies Lock: sync.Mutex"
+       }
+
+       // Test non-idents in range variables
+       var t struct {
+               i  int
+               mu sync.Mutex
+       }
+       for t.i, t.mu = range s { // ERROR "range var t.mu copies Lock: sync.Mutex"
+       }
+}
diff --git a/src/cmd/vet/testdata/deadcode.go b/src/cmd/vet/testdata/deadcode.go
new file mode 100644 (file)
index 0000000..5370bc3
--- /dev/null
@@ -0,0 +1,2125 @@
+// Copyright 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.
+
+// +build ignore
+
+// This file contains tests for the dead code checker.
+
+package testdata
+
+type T int
+
+var x interface{}
+var c chan int
+
+func external() int // ok
+
+func _() int {
+}
+
+func _() int {
+       print(1)
+}
+
+func _() int {
+       print(1)
+       return 2
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+L:
+       print(1)
+       goto L
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       panic(2)
+       println() // ERROR "unreachable code"
+}
+
+// but only builtin panic
+func _() int {
+       var panic = func(int) {}
+       print(1)
+       panic(2)
+       println() // ok
+}
+
+func _() int {
+       {
+               print(1)
+               return 2
+               println() // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+       {
+               print(1)
+               return 2
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+L:
+       {
+               print(1)
+               goto L
+               println() // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+L:
+       {
+               print(1)
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       {
+               panic(2)
+       }
+}
+
+func _() int {
+       print(1)
+       {
+               panic(2)
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       {
+               panic(2)
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       return 2
+       { // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+L:
+       print(1)
+       goto L
+       { // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       panic(2)
+       { // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       {
+               print(1)
+               return 2
+               { // ERROR "unreachable code"
+               }
+       }
+}
+
+func _() int {
+L:
+       {
+               print(1)
+               goto L
+               { // ERROR "unreachable code"
+               }
+       }
+}
+
+func _() int {
+       print(1)
+       {
+               panic(2)
+               { // ERROR "unreachable code"
+               }
+       }
+}
+
+func _() int {
+       {
+               print(1)
+               return 2
+       }
+       { // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+L:
+       {
+               print(1)
+               goto L
+       }
+       { // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       {
+               panic(2)
+       }
+       { // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       if x == nil {
+               panic(2)
+       } else {
+               panic(3)
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+L:
+       print(1)
+       if x == nil {
+               panic(2)
+       } else {
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+L:
+       print(1)
+       if x == nil {
+               panic(2)
+       } else if x == 1 {
+               return 0
+       } else if x != 2 {
+               panic(3)
+       } else {
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+// if-else chain missing final else is not okay, even if the
+// conditions cover every possible case.
+
+func _() int {
+       print(1)
+       if x == nil {
+               panic(2)
+       } else if x != nil {
+               panic(3)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       if x == nil {
+               panic(2)
+       }
+       println() // ok
+}
+
+func _() int {
+L:
+       print(1)
+       if x == nil {
+               panic(2)
+       } else if x == 1 {
+               return 0
+       } else if x != 1 {
+               panic(3)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       for {
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       for {
+               for {
+                       break
+               }
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       for {
+               for {
+                       break
+                       println() // ERROR "unreachable code"
+               }
+       }
+}
+
+func _() int {
+       for {
+               for {
+                       continue
+                       println() // ERROR "unreachable code"
+               }
+       }
+}
+
+func _() int {
+       for {
+       L:
+               for {
+                       break L
+               }
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       for {
+               break
+       }
+       println() // ok
+}
+
+func _() int {
+       for {
+               for {
+               }
+               break // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+L:
+       for {
+               for {
+                       break L
+               }
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       for x == nil {
+       }
+       println() // ok
+}
+
+func _() int {
+       for x == nil {
+               for {
+                       break
+               }
+       }
+       println() // ok
+}
+
+func _() int {
+       for x == nil {
+       L:
+               for {
+                       break L
+               }
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       for true {
+       }
+       println() // ok
+}
+
+func _() int {
+       for true {
+               for {
+                       break
+               }
+       }
+       println() // ok
+}
+
+func _() int {
+       for true {
+       L:
+               for {
+                       break L
+               }
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       select {}
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               for {
+               }
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               for {
+               }
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+L:
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               println() // ERROR "unreachable code"
+       case c <- 1:
+               print(2)
+               goto L
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+L:
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       case c <- 1:
+               print(2)
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               println() // ERROR "unreachable code"
+       default:
+               select {}
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       default:
+               select {}
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+       }
+       println() // ok
+}
+
+func _() int {
+L:
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               goto L // ERROR "unreachable code"
+       case c <- 1:
+               print(2)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       default:
+               print(2)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       select {
+       default:
+               break
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               break // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+L:
+       select {
+       case <-c:
+               print(2)
+               for {
+                       break L
+               }
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+L:
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       case c <- 1:
+               print(2)
+               break L
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       select {
+       case <-c:
+               print(1)
+               panic("abc")
+       default:
+               select {}
+               break // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+               println() // ERROR "unreachable code"
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+       default:
+               return 4
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       switch x {
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       case 1:
+               print(2)
+               panic(3)
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       switch x {
+       default:
+               return 4
+       case 1:
+               print(2)
+               panic(3)
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               fallthrough
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               fallthrough
+       default:
+               return 4
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       switch {
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+       case 2:
+               return 4
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x {
+       case 2:
+               return 4
+       case 1:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               fallthrough
+       case 2:
+               return 4
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+L:
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+               break L // ERROR "unreachable code"
+       default:
+               return 4
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x {
+       default:
+               return 4
+               break // ERROR "unreachable code"
+       case 1:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+L:
+       switch x {
+       case 1:
+               print(2)
+               for {
+                       break L
+               }
+       default:
+               return 4
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+               println() // ERROR "unreachable code"
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+       default:
+               return 4
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       case int:
+               print(2)
+               panic(3)
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       default:
+               return 4
+       case int:
+               print(2)
+               panic(3)
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               fallthrough
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               fallthrough
+       default:
+               return 4
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       print(1)
+       switch {
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+       case float64:
+               return 4
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       case float64:
+               return 4
+       case int:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               fallthrough
+       case float64:
+               return 4
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+L:
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+               break L // ERROR "unreachable code"
+       default:
+               return 4
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+       switch x.(type) {
+       default:
+               return 4
+               break // ERROR "unreachable code"
+       case int:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+func _() int {
+       print(1)
+L:
+       switch x.(type) {
+       case int:
+               print(2)
+               for {
+                       break L
+               }
+       default:
+               return 4
+       }
+       println() // ok
+}
+
+// again, but without the leading print(1).
+// testing that everything works when the terminating statement is first.
+
+func _() int {
+       println() // ok
+}
+
+func _() int {
+       return 2
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+L:
+       goto L
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       panic(2)
+       println() // ERROR "unreachable code"
+}
+
+// but only builtin panic
+func _() int {
+       var panic = func(int) {}
+       panic(2)
+       println() // ok
+}
+
+func _() int {
+       {
+               return 2
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       {
+               return 2
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+L:
+       {
+               goto L
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+L:
+       {
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       {
+               panic(2)
+               println() // ERROR "unreachable code"
+       }
+}
+
+func _() int {
+       {
+               panic(2)
+       }
+       println() // ERROR "unreachable code"
+}
+
+func _() int {
+       return 2
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+L:
+       goto L
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+       panic(2)
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+       {
+               return 2
+               { // ERROR "unreachable code"
+               }
+       }
+       println() // ok
+}
+
+func _() int {
+L:
+       {
+               goto L
+               { // ERROR "unreachable code"
+               }
+       }
+       println() // ok
+}
+
+func _() int {
+       {
+               panic(2)
+               { // ERROR "unreachable code"
+               }
+       }
+       println() // ok
+}
+
+func _() int {
+       {
+               return 2
+       }
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+L:
+       {
+               goto L
+       }
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+func _() int {
+       {
+               panic(2)
+       }
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+// again, with func literals
+
+var _ = func() int {
+}
+
+var _ = func() int {
+       print(1)
+}
+
+var _ = func() int {
+       print(1)
+       return 2
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+L:
+       print(1)
+       goto L
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       panic(2)
+       println() // ERROR "unreachable code"
+}
+
+// but only builtin panic
+var _ = func() int {
+       var panic = func(int) {}
+       print(1)
+       panic(2)
+       println() // ok
+}
+
+var _ = func() int {
+       {
+               print(1)
+               return 2
+               println() // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       {
+               print(1)
+               return 2
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+L:
+       {
+               print(1)
+               goto L
+               println() // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+L:
+       {
+               print(1)
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       {
+               panic(2)
+       }
+}
+
+var _ = func() int {
+       print(1)
+       {
+               panic(2)
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       {
+               panic(2)
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       return 2
+       { // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+L:
+       print(1)
+       goto L
+       { // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       panic(2)
+       { // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       {
+               print(1)
+               return 2
+               { // ERROR "unreachable code"
+               }
+       }
+}
+
+var _ = func() int {
+L:
+       {
+               print(1)
+               goto L
+               { // ERROR "unreachable code"
+               }
+       }
+}
+
+var _ = func() int {
+       print(1)
+       {
+               panic(2)
+               { // ERROR "unreachable code"
+               }
+       }
+}
+
+var _ = func() int {
+       {
+               print(1)
+               return 2
+       }
+       { // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+L:
+       {
+               print(1)
+               goto L
+       }
+       { // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       {
+               panic(2)
+       }
+       { // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       if x == nil {
+               panic(2)
+       } else {
+               panic(3)
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+L:
+       print(1)
+       if x == nil {
+               panic(2)
+       } else {
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+L:
+       print(1)
+       if x == nil {
+               panic(2)
+       } else if x == 1 {
+               return 0
+       } else if x != 2 {
+               panic(3)
+       } else {
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+// if-else chain missing final else is not okay, even if the
+// conditions cover every possible case.
+
+var _ = func() int {
+       print(1)
+       if x == nil {
+               panic(2)
+       } else if x != nil {
+               panic(3)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       if x == nil {
+               panic(2)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+L:
+       print(1)
+       if x == nil {
+               panic(2)
+       } else if x == 1 {
+               return 0
+       } else if x != 1 {
+               panic(3)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       for {
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       for {
+               for {
+                       break
+               }
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       for {
+               for {
+                       break
+                       println() // ERROR "unreachable code"
+               }
+       }
+}
+
+var _ = func() int {
+       for {
+               for {
+                       continue
+                       println() // ERROR "unreachable code"
+               }
+       }
+}
+
+var _ = func() int {
+       for {
+       L:
+               for {
+                       break L
+               }
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       for {
+               break
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       for {
+               for {
+               }
+               break // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+L:
+       for {
+               for {
+                       break L
+               }
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       for x == nil {
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       for x == nil {
+               for {
+                       break
+               }
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       for x == nil {
+       L:
+               for {
+                       break L
+               }
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       for true {
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       for true {
+               for {
+                       break
+               }
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       for true {
+       L:
+               for {
+                       break L
+               }
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       select {}
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               for {
+               }
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               for {
+               }
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+L:
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               println() // ERROR "unreachable code"
+       case c <- 1:
+               print(2)
+               goto L
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+L:
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       case c <- 1:
+               print(2)
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               println() // ERROR "unreachable code"
+       default:
+               select {}
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       default:
+               select {}
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+L:
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               goto L // ERROR "unreachable code"
+       case c <- 1:
+               print(2)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       default:
+               print(2)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       default:
+               break
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+               break // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+L:
+       select {
+       case <-c:
+               print(2)
+               for {
+                       break L
+               }
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+L:
+       select {
+       case <-c:
+               print(2)
+               panic("abc")
+       case c <- 1:
+               print(2)
+               break L
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       select {
+       case <-c:
+               print(1)
+               panic("abc")
+       default:
+               select {}
+               break // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+               println() // ERROR "unreachable code"
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+       default:
+               return 4
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       case 1:
+               print(2)
+               panic(3)
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       default:
+               return 4
+       case 1:
+               print(2)
+               panic(3)
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               fallthrough
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               fallthrough
+       default:
+               return 4
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       switch {
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+       case 2:
+               return 4
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       case 2:
+               return 4
+       case 1:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               fallthrough
+       case 2:
+               return 4
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+L:
+       switch x {
+       case 1:
+               print(2)
+               panic(3)
+               break L // ERROR "unreachable code"
+       default:
+               return 4
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x {
+       default:
+               return 4
+               break // ERROR "unreachable code"
+       case 1:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+L:
+       switch x {
+       case 1:
+               print(2)
+               for {
+                       break L
+               }
+       default:
+               return 4
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+               println() // ERROR "unreachable code"
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+       default:
+               return 4
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       case int:
+               print(2)
+               panic(3)
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       default:
+               return 4
+       case int:
+               print(2)
+               panic(3)
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               fallthrough
+       default:
+               return 4
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               fallthrough
+       default:
+               return 4
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       print(1)
+       switch {
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+       case float64:
+               return 4
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       case float64:
+               return 4
+       case int:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               fallthrough
+       case float64:
+               return 4
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+L:
+       switch x.(type) {
+       case int:
+               print(2)
+               panic(3)
+               break L // ERROR "unreachable code"
+       default:
+               return 4
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+       switch x.(type) {
+       default:
+               return 4
+               break // ERROR "unreachable code"
+       case int:
+               print(2)
+               panic(3)
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       print(1)
+L:
+       switch x.(type) {
+       case int:
+               print(2)
+               for {
+                       break L
+               }
+       default:
+               return 4
+       }
+       println() // ok
+}
+
+// again, but without the leading print(1).
+// testing that everything works when the terminating statement is first.
+
+var _ = func() int {
+       println() // ok
+}
+
+var _ = func() int {
+       return 2
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+L:
+       goto L
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       panic(2)
+       println() // ERROR "unreachable code"
+}
+
+// but only builtin panic
+var _ = func() int {
+       var panic = func(int) {}
+       panic(2)
+       println() // ok
+}
+
+var _ = func() int {
+       {
+               return 2
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       {
+               return 2
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+L:
+       {
+               goto L
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+L:
+       {
+               goto L
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       {
+               panic(2)
+               println() // ERROR "unreachable code"
+       }
+}
+
+var _ = func() int {
+       {
+               panic(2)
+       }
+       println() // ERROR "unreachable code"
+}
+
+var _ = func() int {
+       return 2
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+L:
+       goto L
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       panic(2)
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       {
+               return 2
+               { // ERROR "unreachable code"
+               }
+       }
+       println() // ok
+}
+
+var _ = func() int {
+L:
+       {
+               goto L
+               { // ERROR "unreachable code"
+               }
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       {
+               panic(2)
+               { // ERROR "unreachable code"
+               }
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       {
+               return 2
+       }
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+L:
+       {
+               goto L
+       }
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() int {
+       {
+               panic(2)
+       }
+       { // ERROR "unreachable code"
+       }
+       println() // ok
+}
+
+var _ = func() {
+       // goto without label used to panic
+       goto
+}
diff --git a/src/cmd/vet/testdata/method.go b/src/cmd/vet/testdata/method.go
new file mode 100644 (file)
index 0000000..52b500d
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2010 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.
+
+// This file contains tests for the canonical method checker.
+
+// This file contains the code to check canonical methods.
+
+package testdata
+
+import (
+       "fmt"
+)
+
+type MethodTest int
+
+func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan"
+}
+
+type MethodTestInterface interface {
+       ReadByte() byte // ERROR "should have signature ReadByte"
+}
diff --git a/src/cmd/vet/testdata/nilfunc.go b/src/cmd/vet/testdata/nilfunc.go
new file mode 100644 (file)
index 0000000..2ce7bc8
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 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.
+
+package testdata
+
+func F() {}
+
+type T struct {
+       F func()
+}
+
+func (T) M() {}
+
+var Fv = F
+
+func Comparison() {
+       var t T
+       var fn func()
+       if fn == nil || Fv == nil || t.F == nil {
+               // no error; these func vars or fields may be nil
+       }
+       if F == nil { // ERROR "comparison of function F == nil is always false"
+               panic("can't happen")
+       }
+       if t.M == nil { // ERROR "comparison of function M == nil is always false"
+               panic("can't happen")
+       }
+       if F != nil { // ERROR "comparison of function F != nil is always true"
+               if t.M != nil { // ERROR "comparison of function M != nil is always true"
+                       return
+               }
+       }
+       panic("can't happen")
+}
diff --git a/src/cmd/vet/testdata/print.go b/src/cmd/vet/testdata/print.go
new file mode 100644 (file)
index 0000000..3390a31
--- /dev/null
@@ -0,0 +1,342 @@
+// Copyright 2010 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.
+
+// This file contains tests for the printf checker.
+
+package testdata
+
+import (
+       "fmt"
+       "math"
+       "os"
+       "unsafe" // just for test case printing unsafe.Pointer
+)
+
+func UnsafePointerPrintfTest() {
+       var up unsafe.Pointer
+       fmt.Printf("%p, %x %X", up, up, up)
+}
+
+// Error methods that do not satisfy the Error interface and should be checked.
+type errorTest1 int
+
+func (errorTest1) Error(...interface{}) string {
+       return "hi"
+}
+
+type errorTest2 int // Analogous to testing's *T type.
+func (errorTest2) Error(...interface{}) {
+}
+
+type errorTest3 int
+
+func (errorTest3) Error() { // No return value.
+}
+
+type errorTest4 int
+
+func (errorTest4) Error() int { // Different return type.
+       return 3
+}
+
+type errorTest5 int
+
+func (errorTest5) error() { // niladic; don't complain if no args (was bug)
+}
+
+// This function never executes, but it serves as a simple test for the program.
+// Test with make test.
+func PrintfTests() {
+       var b bool
+       var i int
+       var r rune
+       var s string
+       var x float64
+       var p *int
+       var imap map[int]int
+       var fslice []float64
+       var c complex64
+       // Some good format/argtypes
+       fmt.Printf("")
+       fmt.Printf("%b %b %b", 3, i, x)
+       fmt.Printf("%c %c %c %c", 3, i, 'x', r)
+       fmt.Printf("%d %d %d", 3, i, imap)
+       fmt.Printf("%e %e %e %e", 3e9, x, fslice, c)
+       fmt.Printf("%E %E %E %E", 3e9, x, fslice, c)
+       fmt.Printf("%f %f %f %f", 3e9, x, fslice, c)
+       fmt.Printf("%F %F %F %F", 3e9, x, fslice, c)
+       fmt.Printf("%g %g %g %g", 3e9, x, fslice, c)
+       fmt.Printf("%G %G %G %G", 3e9, x, fslice, c)
+       fmt.Printf("%b %b %b %b", 3e9, x, fslice, c)
+       fmt.Printf("%o %o", 3, i)
+       fmt.Printf("%p %p", p, nil)
+       fmt.Printf("%q %q %q %q", 3, i, 'x', r)
+       fmt.Printf("%s %s %s", "hi", s, []byte{65})
+       fmt.Printf("%t %t", true, b)
+       fmt.Printf("%T %T", 3, i)
+       fmt.Printf("%U %U", 3, i)
+       fmt.Printf("%v %v", 3, i)
+       fmt.Printf("%x %x %x %x", 3, i, "hi", s)
+       fmt.Printf("%X %X %X %X", 3, i, "hi", s)
+       fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
+       fmt.Printf("%s", &stringerv)
+       fmt.Printf("%v", &stringerv)
+       fmt.Printf("%T", &stringerv)
+       fmt.Printf("%v", notstringerv)
+       fmt.Printf("%T", notstringerv)
+       fmt.Printf("%q", stringerarrayv)
+       fmt.Printf("%v", stringerarrayv)
+       fmt.Printf("%s", stringerarrayv)
+       fmt.Printf("%v", notstringerarrayv)
+       fmt.Printf("%T", notstringerarrayv)
+       fmt.Printf("%d", new(Formatter))
+       fmt.Printf("%*%", 2)               // Ridiculous but allowed.
+       fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say.
+
+       fmt.Printf("%g", 1+2i)
+       // Some bad format/argTypes
+       fmt.Printf("%b", "hi")                     // ERROR "arg .hi. for printf verb %b of wrong type"
+       fmt.Printf("%t", c)                        // ERROR "arg c for printf verb %t of wrong type"
+       fmt.Printf("%t", 1+2i)                     // ERROR "arg 1 \+ 2i for printf verb %t of wrong type"
+       fmt.Printf("%c", 2.3)                      // ERROR "arg 2.3 for printf verb %c of wrong type"
+       fmt.Printf("%d", 2.3)                      // ERROR "arg 2.3 for printf verb %d of wrong type"
+       fmt.Printf("%e", "hi")                     // ERROR "arg .hi. for printf verb %e of wrong type"
+       fmt.Printf("%E", true)                     // ERROR "arg true for printf verb %E of wrong type"
+       fmt.Printf("%f", "hi")                     // ERROR "arg .hi. for printf verb %f of wrong type"
+       fmt.Printf("%F", 'x')                      // ERROR "arg 'x' for printf verb %F of wrong type"
+       fmt.Printf("%g", "hi")                     // ERROR "arg .hi. for printf verb %g of wrong type"
+       fmt.Printf("%g", imap)                     // ERROR "arg imap for printf verb %g of wrong type"
+       fmt.Printf("%G", i)                        // ERROR "arg i for printf verb %G of wrong type"
+       fmt.Printf("%o", x)                        // ERROR "arg x for printf verb %o of wrong type"
+       fmt.Printf("%p", 23)                       // ERROR "arg 23 for printf verb %p of wrong type"
+       fmt.Printf("%q", x)                        // ERROR "arg x for printf verb %q of wrong type"
+       fmt.Printf("%s", b)                        // ERROR "arg b for printf verb %s of wrong type"
+       fmt.Printf("%s", byte(65))                 // ERROR "arg byte\(65\) for printf verb %s of wrong type"
+       fmt.Printf("%t", 23)                       // ERROR "arg 23 for printf verb %t of wrong type"
+       fmt.Printf("%U", x)                        // ERROR "arg x for printf verb %U of wrong type"
+       fmt.Printf("%x", nil)                      // ERROR "arg nil for printf verb %x of wrong type"
+       fmt.Printf("%X", 2.3)                      // ERROR "arg 2.3 for printf verb %X of wrong type"
+       fmt.Printf("%s", stringerv)                // ERROR "arg stringerv for printf verb %s of wrong type"
+       fmt.Printf("%t", stringerv)                // ERROR "arg stringerv for printf verb %t of wrong type"
+       fmt.Printf("%q", notstringerv)             // ERROR "arg notstringerv for printf verb %q of wrong type"
+       fmt.Printf("%t", notstringerv)             // ERROR "arg notstringerv for printf verb %t of wrong type"
+       fmt.Printf("%t", stringerarrayv)           // ERROR "arg stringerarrayv for printf verb %t of wrong type"
+       fmt.Printf("%t", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %t of wrong type"
+       fmt.Printf("%q", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %q of wrong type"
+       fmt.Printf("%d", Formatter(true))          // correct (the type is responsible for formatting)
+       fmt.Printf("%s", nonemptyinterface)        // correct (the dynamic type of nonemptyinterface may be a stringer)
+       fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
+       fmt.Println()                              // not an error
+       fmt.Println("%s", "hi")                    // ERROR "possible formatting directive in Println call"
+       fmt.Printf("%s", "hi", 3)                  // ERROR "wrong number of args for format in Printf call"
+       _ = fmt.Sprintf("%"+("s"), "hi", 3)        // ERROR "wrong number of args for format in Sprintf call"
+       fmt.Printf("%s%%%d", "hi", 3)              // correct
+       fmt.Printf("%08s", "woo")                  // correct
+       fmt.Printf("% 8s", "woo")                  // correct
+       fmt.Printf("%.*d", 3, 3)                   // correct
+       fmt.Printf("%.*d", 3, 3, 3, 3)             // ERROR "wrong number of args for format in Printf call.*4 args"
+       fmt.Printf("%.*d", "hi", 3)                // ERROR "arg .hi. for \* in printf format not of type int"
+       fmt.Printf("%.*d", i, 3)                   // correct
+       fmt.Printf("%.*d", s, 3)                   // ERROR "arg s for \* in printf format not of type int"
+       fmt.Printf("%*%", 0.22)                    // ERROR "arg 0.22 for \* in printf format not of type int"
+       fmt.Printf("%q %q", multi()...)            // ok
+       fmt.Printf("%#q", `blah`)                  // ok
+       printf("now is the time", "buddy")         // ERROR "no formatting directive"
+       Printf("now is the time", "buddy")         // ERROR "no formatting directive"
+       Printf("hi")                               // ok
+       const format = "%s %s\n"
+       Printf(format, "hi", "there")
+       Printf(format, "hi")              // ERROR "missing argument for Printf..%s..: format reads arg 2, have only 1"
+       Printf("%s %d %.3v %q", "str", 4) // ERROR "missing argument for Printf..%.3v..: format reads arg 3, have only 2"
+       f := new(stringer)
+       f.Warn(0, "%s", "hello", 3)  // ERROR "possible formatting directive in Warn call"
+       f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call"
+       f.Warnf(0, "%r", "hello")    // ERROR "unrecognized printf verb"
+       f.Warnf(0, "%#s", "hello")   // ERROR "unrecognized printf flag"
+       Printf("d%", 2)              // ERROR "missing verb at end of format string in Printf call"
+       Printf("%d", percentDV)
+       Printf("%d", &percentDV)
+       Printf("%d", notPercentDV)  // ERROR "arg notPercentDV for printf verb %d of wrong type"
+       Printf("%d", &notPercentDV) // ERROR "arg &notPercentDV for printf verb %d of wrong type"
+       Printf("%p", &notPercentDV) // Works regardless: we print it as a pointer.
+       Printf("%s", percentSV)
+       Printf("%s", &percentSV)
+       // Good argument reorderings.
+       Printf("%[1]d", 3)
+       Printf("%[1]*d", 3, 1)
+       Printf("%[2]*[1]d", 1, 3)
+       Printf("%[2]*.[1]*[3]d", 2, 3, 4)
+       fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.
+       // Bad argument reorderings.
+       Printf("%[xd", 3)                    // ERROR "illegal syntax for printf argument index"
+       Printf("%[x]d", 3)                   // ERROR "illegal syntax for printf argument index"
+       Printf("%[3]*s", "hi", 2)            // ERROR "missing argument for Printf.* reads arg 3, have only 2"
+       _ = fmt.Sprintf("%[3]d", 2)          // ERROR "missing argument for Sprintf.* reads arg 3, have only 1"
+       Printf("%[2]*.[1]*[3]d", 2, "hi", 4) // ERROR "arg .hi. for \* in printf format not of type int"
+       Printf("%[0]s", "arg1")              // ERROR "index value \[0\] for Printf.*; indexes start at 1"
+       Printf("%[0]d", 1)                   // ERROR "index value \[0\] for Printf.*; indexes start at 1"
+       // Something that satisfies the error interface.
+       var e error
+       fmt.Println(e.Error()) // ok
+       // Something that looks like an error interface but isn't, such as the (*T).Error method
+       // in the testing package.
+       var et1 errorTest1
+       fmt.Println(et1.Error())        // ERROR "no args in Error call"
+       fmt.Println(et1.Error("hi"))    // ok
+       fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call"
+       var et2 errorTest2
+       et2.Error()        // ERROR "no args in Error call"
+       et2.Error("hi")    // ok, not an error method.
+       et2.Error("%d", 3) // ERROR "possible formatting directive in Error call"
+       var et3 errorTest3
+       et3.Error() // ok, not an error method.
+       var et4 errorTest4
+       et4.Error() // ok, not an error method.
+       var et5 errorTest5
+       et5.error() // ok, not an error method.
+       // Bug: used to recur forever.
+       Printf("%p %x", recursiveStructV, recursiveStructV.next)
+       Printf("%p %x", recursiveStruct1V, recursiveStruct1V.next)
+       Printf("%p %x", recursiveSliceV, recursiveSliceV)
+       Printf("%p %x", recursiveMapV, recursiveMapV)
+       // Special handling for Log.
+       math.Log(3)  // OK
+       Log(3)       // OK
+       Log("%d", 3) // ERROR "possible formatting directive in Log call"
+       Logf("%d", 3)
+       Logf("%d", "hi") // ERROR "arg .hi. for printf verb %d of wrong type: untyped string"
+
+}
+
+// Printf is used by the test so we must declare it.
+func Printf(format string, args ...interface{}) {
+       panic("don't call - testing only")
+}
+
+// printf is used by the test so we must declare it.
+func printf(format string, args ...interface{}) {
+       panic("don't call - testing only")
+}
+
+// multi is used by the test.
+func multi() []interface{} {
+       panic("don't call - testing only")
+}
+
+type stringer float64
+
+var stringerv stringer
+
+func (*stringer) String() string {
+       return "string"
+}
+
+func (*stringer) Warn(int, ...interface{}) string {
+       return "warn"
+}
+
+func (*stringer) Warnf(int, string, ...interface{}) string {
+       return "warnf"
+}
+
+type notstringer struct {
+       f float64
+}
+
+var notstringerv notstringer
+
+type stringerarray [4]float64
+
+func (stringerarray) String() string {
+       return "string"
+}
+
+var stringerarrayv stringerarray
+
+type notstringerarray [4]float64
+
+var notstringerarrayv notstringerarray
+
+var nonemptyinterface = interface {
+       f()
+}(nil)
+
+// A data type we can print with "%d".
+type percentDStruct struct {
+       a int
+       b []byte
+       c *float64
+}
+
+var percentDV percentDStruct
+
+// A data type we cannot print correctly with "%d".
+type notPercentDStruct struct {
+       a int
+       b []byte
+       c bool
+}
+
+var notPercentDV notPercentDStruct
+
+// A data type we can print with "%s".
+type percentSStruct struct {
+       a string
+       b []byte
+       c stringerarray
+}
+
+var percentSV percentSStruct
+
+type recursiveStringer int
+
+func (s recursiveStringer) String() string {
+       _ = fmt.Sprintf("%d", s)
+       _ = fmt.Sprintf("%#v", s)
+       _ = fmt.Sprintf("%v", s)  // ERROR "arg s for printf causes recursive call to String method"
+       _ = fmt.Sprintf("%v", &s) // ERROR "arg &s for printf causes recursive call to String method"
+       _ = fmt.Sprintf("%T", s)  // ok; does not recursively call String
+       return fmt.Sprintln(s)    // ERROR "arg s for print causes recursive call to String method"
+}
+
+type recursivePtrStringer int
+
+func (p *recursivePtrStringer) String() string {
+       _ = fmt.Sprintf("%v", *p)
+       return fmt.Sprintln(p) // ERROR "arg p for print causes recursive call to String method"
+}
+
+type Formatter bool
+
+func (*Formatter) Format(fmt.State, rune) {
+}
+
+type RecursiveSlice []RecursiveSlice
+
+var recursiveSliceV = &RecursiveSlice{}
+
+type RecursiveMap map[int]RecursiveMap
+
+var recursiveMapV = make(RecursiveMap)
+
+type RecursiveStruct struct {
+       next *RecursiveStruct
+}
+
+var recursiveStructV = &RecursiveStruct{}
+
+type RecursiveStruct1 struct {
+       next *Recursive2Struct
+}
+
+type RecursiveStruct2 struct {
+       next *Recursive1Struct
+}
+
+var recursiveStruct1V = &RecursiveStruct1{}
+
+// Fix for issue 7149: Missing return type on String method caused fault.
+func (int) String() {
+       return ""
+}
diff --git a/src/cmd/vet/testdata/rangeloop.go b/src/cmd/vet/testdata/rangeloop.go
new file mode 100644 (file)
index 0000000..37b5940
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright 2012 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.
+
+// This file contains tests for the rangeloop checker.
+
+package testdata
+
+func RangeLoopTests() {
+       var s []int
+       for i, v := range s {
+               go func() {
+                       println(i) // ERROR "range variable i captured by func literal"
+                       println(v) // ERROR "range variable v captured by func literal"
+               }()
+       }
+       for i, v := range s {
+               defer func() {
+                       println(i) // ERROR "range variable i captured by func literal"
+                       println(v) // ERROR "range variable v captured by func literal"
+               }()
+       }
+       for i := range s {
+               go func() {
+                       println(i) // ERROR "range variable i captured by func literal"
+               }()
+       }
+       for _, v := range s {
+               go func() {
+                       println(v) // ERROR "range variable v captured by func literal"
+               }()
+       }
+       for i, v := range s {
+               go func() {
+                       println(i, v)
+               }()
+               println("unfortunately, we don't catch the error above because of this statement")
+       }
+       for i, v := range s {
+               go func(i, v int) {
+                       println(i, v)
+               }(i, v)
+       }
+       for i, v := range s {
+               i, v := i, v
+               go func() {
+                       println(i, v)
+               }()
+       }
+       // If the key of the range statement is not an identifier
+       // the code should not panic (it used to).
+       var x [2]int
+       var f int
+       for x[0], f = range s {
+               go func() {
+                       _ = f // ERROR "range variable f captured by func literal"
+               }()
+       }
+}
diff --git a/src/cmd/vet/testdata/shadow.go b/src/cmd/vet/testdata/shadow.go
new file mode 100644 (file)
index 0000000..34a6806
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright 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.
+
+// This file contains tests for the shadowed variable checker.
+// Some of these errors are caught by the compiler (shadowed return parameters for example)
+// but are nonetheless useful tests.
+
+package testdata
+
+import "os"
+
+func ShadowRead(f *os.File, buf []byte) (err error) {
+       var x int
+       if f != nil {
+               err := 3 // OK - different type.
+               _ = err
+       }
+       if f != nil {
+               _, err := f.Read(buf) // ERROR "declaration of err shadows declaration at testdata/shadow.go:13"
+               if err != nil {
+                       return err
+               }
+               i := 3 // OK
+               _ = i
+       }
+       if f != nil {
+               var _, err = f.Read(buf) // ERROR "declaration of err shadows declaration at testdata/shadow.go:13"
+               if err != nil {
+                       return err
+               }
+       }
+       for i := 0; i < 10; i++ {
+               i := i // OK: obviously intentional idiomatic redeclaration
+               go func() {
+                       println(i)
+               }()
+       }
+       var shadowTemp interface{}
+       switch shadowTemp := shadowTemp.(type) { // OK: obviously intentional idiomatic redeclaration
+       case int:
+               println("OK")
+               _ = shadowTemp
+       }
+       if shadowTemp := shadowTemp; true { // OK: obviously intentional idiomatic redeclaration
+               var f *os.File // OK because f is not mentioned later in the function.
+               // The declaration of x is a shadow because x is mentioned below.
+               var x int // ERROR "declaration of x shadows declaration at testdata/shadow.go:14"
+               _, _, _ = x, f, shadowTemp
+       }
+       // Use a couple of variables to trigger shadowing errors.
+       _, _ = err, x
+       return
+}
diff --git a/src/cmd/vet/testdata/shift.go b/src/cmd/vet/testdata/shift.go
new file mode 100644 (file)
index 0000000..6624f09
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright 2014 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.
+
+// This file contains tests for the suspicious shift checker.
+
+package testdata
+
+func ShiftTest() {
+       var i8 int8
+       _ = i8 << 7
+       _ = (i8 + 1) << 8 // ERROR "\(i8 \+ 1\) too small for shift of 8"
+       _ = i8 << (7 + 1) // ERROR "i8 too small for shift of 8"
+       _ = i8 >> 8       // ERROR "i8 too small for shift of 8"
+       i8 <<= 8          // ERROR "i8 too small for shift of 8"
+       i8 >>= 8          // ERROR "i8 too small for shift of 8"
+       var i16 int16
+       _ = i16 << 15
+       _ = i16 << 16 // ERROR "i16 too small for shift of 16"
+       _ = i16 >> 16 // ERROR "i16 too small for shift of 16"
+       i16 <<= 16    // ERROR "i16 too small for shift of 16"
+       i16 >>= 16    // ERROR "i16 too small for shift of 16"
+       var i32 int32
+       _ = i32 << 31
+       _ = i32 << 32 // ERROR "i32 too small for shift of 32"
+       _ = i32 >> 32 // ERROR "i32 too small for shift of 32"
+       i32 <<= 32    // ERROR "i32 too small for shift of 32"
+       i32 >>= 32    // ERROR "i32 too small for shift of 32"
+       var i64 int64
+       _ = i64 << 63
+       _ = i64 << 64 // ERROR "i64 too small for shift of 64"
+       _ = i64 >> 64 // ERROR "i64 too small for shift of 64"
+       i64 <<= 64    // ERROR "i64 too small for shift of 64"
+       i64 >>= 64    // ERROR "i64 too small for shift of 64"
+       var u8 uint8
+       _ = u8 << 7
+       _ = u8 << 8 // ERROR "u8 too small for shift of 8"
+       _ = u8 >> 8 // ERROR "u8 too small for shift of 8"
+       u8 <<= 8    // ERROR "u8 too small for shift of 8"
+       u8 >>= 8    // ERROR "u8 too small for shift of 8"
+       var u16 uint16
+       _ = u16 << 15
+       _ = u16 << 16 // ERROR "u16 too small for shift of 16"
+       _ = u16 >> 16 // ERROR "u16 too small for shift of 16"
+       u16 <<= 16    // ERROR "u16 too small for shift of 16"
+       u16 >>= 16    // ERROR "u16 too small for shift of 16"
+       var u32 uint32
+       _ = u32 << 31
+       _ = u32 << 32 // ERROR "u32 too small for shift of 32"
+       _ = u32 >> 32 // ERROR "u32 too small for shift of 32"
+       u32 <<= 32    // ERROR "u32 too small for shift of 32"
+       u32 >>= 32    // ERROR "u32 too small for shift of 32"
+       var u64 uint64
+       _ = u64 << 63
+       _ = u64 << 64  // ERROR "u64 too small for shift of 64"
+       _ = u64 >> 64  // ERROR "u64 too small for shift of 64"
+       u64 <<= 64     // ERROR "u64 too small for shift of 64"
+       u64 >>= 64     // ERROR "u64 too small for shift of 64"
+       _ = u64 << u64 // Non-constant shifts should succeed.
+       var i int
+       _ = i << 31
+       _ = i << 32 // ERROR "i might be too small for shift of 32"
+       _ = i >> 32 // ERROR "i might be too small for shift of 32"
+       i <<= 32    // ERROR "i might be too small for shift of 32"
+       i >>= 32    // ERROR "i might be too small for shift of 32"
+       var u uint
+       _ = u << 31
+       _ = u << 32 // ERROR "u might be too small for shift of 32"
+       _ = u >> 32 // ERROR "u might be too small for shift of 32"
+       u <<= 32    // ERROR "u might be too small for shift of 32"
+       u >>= 32    // ERROR "u might be too small for shift of 32"
+       var p uintptr
+       _ = p << 31
+       _ = p << 32 // ERROR "p might be too small for shift of 32"
+       _ = p >> 32 // ERROR "p might be too small for shift of 32"
+       p <<= 32    // ERROR "p might be too small for shift of 32"
+       p >>= 32    // ERROR "p might be too small for shift of 32"
+}
diff --git a/src/cmd/vet/testdata/structtag.go b/src/cmd/vet/testdata/structtag.go
new file mode 100644 (file)
index 0000000..6878f56
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright 2010 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.
+
+// This file contains the test for canonical struct tags.
+
+package testdata
+
+type StructTagTest struct {
+       A   int "hello"            // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag pair"
+       B   int "\tx:\"y\""        // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag key"
+       C   int "x:\"y\"\tx:\"y\"" // ERROR "not compatible with reflect.StructTag.Get"
+       D   int "x:`y`"            // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value"
+       E   int "ct\brl:\"char\""  // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag pair"
+       F   int `:"emptykey"`      // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag key"
+       G   int `x:"noEndQuote`    // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value"
+       H   int `x:"trunc\x0"`     // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value"
+       OK0 int `x:"y" u:"v" w:""`
+       OK1 int `x:"y:z" u:"v" w:""` // note multiple colons.
+       OK2 int "k0:\"values contain spaces\" k1:\"literal\ttabs\" k2:\"and\\tescaped\\tabs\""
+       OK3 int `under_scores:"and" CAPS:"ARE_OK"`
+}
+
+type UnexportedEncodingTagTest struct {
+       x int `json:"xx"` // ERROR "struct field x has json tag but is not exported"
+       y int `xml:"yy"`  // ERROR "struct field y has xml tag but is not exported"
+       z int
+       A int `json:"aa" xml:"bb"`
+}
+
+type unexp struct{}
+
+type JSONEmbeddedField struct {
+       UnexportedEncodingTagTest `is:"embedded"`
+       unexp                     `is:"embedded,notexported" json:"unexp"` // OK for now, see issue 7363
+}
diff --git a/src/cmd/vet/testdata/tagtest/file1.go b/src/cmd/vet/testdata/tagtest/file1.go
new file mode 100644 (file)
index 0000000..22a1509
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright 2015 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.
+
+// +build testtag
+
+package main
+
+func main() {
+}
diff --git a/src/cmd/vet/testdata/tagtest/file2.go b/src/cmd/vet/testdata/tagtest/file2.go
new file mode 100644 (file)
index 0000000..ba7dd91
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright 2015 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.
+
+// +build !testtag
+
+package main
+
+func ignore() {
+}
diff --git a/src/cmd/vet/testdata/unsafeptr.go b/src/cmd/vet/testdata/unsafeptr.go
new file mode 100644 (file)
index 0000000..8f64030
--- /dev/null
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+package testdata
+
+import (
+       "reflect"
+       "unsafe"
+)
+
+func f() {
+       var x unsafe.Pointer
+       var y uintptr
+       x = unsafe.Pointer(y) // ERROR "possible misuse of unsafe.Pointer"
+       y = uintptr(x)
+
+       // only allowed pointer arithmetic is ptr +/- num.
+       // num+ptr is technically okay but still flagged: write ptr+num instead.
+       x = unsafe.Pointer(uintptr(x) + 1)
+       x = unsafe.Pointer(1 + uintptr(x))          // ERROR "possible misuse of unsafe.Pointer"
+       x = unsafe.Pointer(uintptr(x) + uintptr(x)) // ERROR "possible misuse of unsafe.Pointer"
+       x = unsafe.Pointer(uintptr(x) - 1)
+       x = unsafe.Pointer(1 - uintptr(x)) // ERROR "possible misuse of unsafe.Pointer"
+
+       // certain uses of reflect are okay
+       var v reflect.Value
+       x = unsafe.Pointer(v.Pointer())
+       x = unsafe.Pointer(v.UnsafeAddr())
+       var s1 *reflect.StringHeader
+       x = unsafe.Pointer(s1.Data)
+       var s2 *reflect.SliceHeader
+       x = unsafe.Pointer(s2.Data)
+       var s3 reflect.StringHeader
+       x = unsafe.Pointer(s3.Data) // ERROR "possible misuse of unsafe.Pointer"
+       var s4 reflect.SliceHeader
+       x = unsafe.Pointer(s4.Data) // ERROR "possible misuse of unsafe.Pointer"
+
+       // but only in reflect
+       var vv V
+       x = unsafe.Pointer(vv.Pointer())    // ERROR "possible misuse of unsafe.Pointer"
+       x = unsafe.Pointer(vv.UnsafeAddr()) // ERROR "possible misuse of unsafe.Pointer"
+       var ss1 *StringHeader
+       x = unsafe.Pointer(ss1.Data) // ERROR "possible misuse of unsafe.Pointer"
+       var ss2 *SliceHeader
+       x = unsafe.Pointer(ss2.Data) // ERROR "possible misuse of unsafe.Pointer"
+
+}
+
+type V interface {
+       Pointer() uintptr
+       UnsafeAddr() uintptr
+}
+
+type StringHeader struct {
+       Data uintptr
+}
+
+type SliceHeader struct {
+       Data uintptr
+}
diff --git a/src/cmd/vet/testdata/unused.go b/src/cmd/vet/testdata/unused.go
new file mode 100644 (file)
index 0000000..d50f659
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2015 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.
+
+// This file contains tests for the unusedresult checker.
+
+package testdata
+
+import (
+       "bytes"
+       "errors"
+       "fmt"
+)
+
+func _() {
+       fmt.Errorf("") // ERROR "result of fmt.Errorf call not used"
+       _ = fmt.Errorf("")
+
+       errors.New("") // ERROR "result of errors.New call not used"
+
+       err := errors.New("")
+       err.Error() // ERROR "result of \(error\).Error call not used"
+
+       var buf bytes.Buffer
+       buf.String() // ERROR "result of \(bytes.Buffer\).String call not used"
+
+       fmt.Sprint("")  // ERROR "result of fmt.Sprint call not used"
+       fmt.Sprintf("") // ERROR "result of fmt.Sprintf call not used"
+}
diff --git a/src/cmd/vet/types.go b/src/cmd/vet/types.go
new file mode 100644 (file)
index 0000000..89e9989
--- /dev/null
@@ -0,0 +1,366 @@
+// Copyright 2010 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.
+
+// This file contains the pieces of the tool that use typechecking from the go/types package.
+
+package main
+
+import (
+       "go/ast"
+       "go/token"
+
+       "golang.org/x/tools/go/types"
+)
+
+// imports is the canonical map of imported packages we need for typechecking.
+// It is created during initialization.
+var imports = make(map[string]*types.Package)
+
+var (
+       stringerMethodType = types.New("func() string")
+       errorType          = types.New("error").Underlying().(*types.Interface)
+       stringerType       = types.New("interface{ String() string }").(*types.Interface)
+       formatterType      *types.Interface
+)
+
+func init() {
+       typ := importType("fmt", "Formatter")
+       if typ != nil {
+               formatterType = typ.Underlying().(*types.Interface)
+       }
+}
+
+// importType returns the type denoted by the qualified identifier
+// path.name, and adds the respective package to the imports map
+// as a side effect.
+func importType(path, name string) types.Type {
+       pkg, err := types.DefaultImport(imports, path)
+       if err != nil {
+               // This can happen if fmt hasn't been compiled yet.
+               // Since nothing uses formatterType anyway, don't complain.
+               //warnf("import failed: %v", err)
+               return nil
+       }
+       if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok {
+               return obj.Type()
+       }
+       warnf("invalid type name %q", name)
+       return nil
+}
+
+func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
+       pkg.defs = make(map[*ast.Ident]types.Object)
+       pkg.uses = make(map[*ast.Ident]types.Object)
+       pkg.selectors = make(map[*ast.SelectorExpr]*types.Selection)
+       pkg.spans = make(map[types.Object]Span)
+       pkg.types = make(map[ast.Expr]types.TypeAndValue)
+       config := types.Config{
+               // We provide the same packages map for all imports to ensure
+               // that everybody sees identical packages for the given paths.
+               Packages: imports,
+               // By providing a Config with our own error function, it will continue
+               // past the first error. There is no need for that function to do anything.
+               Error: func(error) {},
+       }
+       info := &types.Info{
+               Selections: pkg.selectors,
+               Types:      pkg.types,
+               Defs:       pkg.defs,
+               Uses:       pkg.uses,
+       }
+       typesPkg, err := config.Check(pkg.path, fs, astFiles, info)
+       pkg.typesPkg = typesPkg
+       // update spans
+       for id, obj := range pkg.defs {
+               pkg.growSpan(id, obj)
+       }
+       for id, obj := range pkg.uses {
+               pkg.growSpan(id, obj)
+       }
+       return err
+}
+
+// isStruct reports whether the composite literal c is a struct.
+// If it is not (probably a struct), it returns a printable form of the type.
+func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
+       // Check that the CompositeLit's type is a slice or array (which needs no field keys), if possible.
+       typ := pkg.types[c].Type
+       // If it's a named type, pull out the underlying type. If it's not, the Underlying
+       // method returns the type itself.
+       actual := typ
+       if actual != nil {
+               actual = actual.Underlying()
+       }
+       if actual == nil {
+               // No type information available. Assume true, so we do the check.
+               return true, ""
+       }
+       switch actual.(type) {
+       case *types.Struct:
+               return true, typ.String()
+       default:
+               return false, ""
+       }
+}
+
+// matchArgType reports an error if printf verb t is not appropriate
+// for operand arg.
+//
+// typ is used only for recursive calls; external callers must supply nil.
+//
+// (Recursion arises from the compound types {map,chan,slice} which
+// may be printed with %d etc. if that is appropriate for their element
+// types.)
+func (f *File) matchArgType(t printfArgType, typ types.Type, arg ast.Expr) bool {
+       return f.matchArgTypeInternal(t, typ, arg, make(map[types.Type]bool))
+}
+
+// matchArgTypeInternal is the internal version of matchArgType. It carries a map
+// remembering what types are in progress so we don't recur when faced with recursive
+// types or mutually recursive types.
+func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
+       // %v, %T accept any argument type.
+       if t == anyType {
+               return true
+       }
+       if typ == nil {
+               // external call
+               typ = f.pkg.types[arg].Type
+               if typ == nil {
+                       return true // probably a type check problem
+               }
+       }
+       // If the type implements fmt.Formatter, we have nothing to check.
+       // But (see issue 6259) that's not easy to verify, so instead we see
+       // if its method set contains a Format function. We could do better,
+       // even now, but we don't need to be 100% accurate. Wait for 6259 to
+       // be fixed instead. TODO.
+       if f.hasMethod(typ, "Format") {
+               return true
+       }
+       // If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
+       if t&argString != 0 {
+               if types.AssertableTo(errorType, typ) || types.AssertableTo(stringerType, typ) {
+                       return true
+               }
+       }
+
+       typ = typ.Underlying()
+       if inProgress[typ] {
+               // We're already looking at this type. The call that started it will take care of it.
+               return true
+       }
+       inProgress[typ] = true
+
+       switch typ := typ.(type) {
+       case *types.Signature:
+               return t&argPointer != 0
+
+       case *types.Map:
+               // Recur: map[int]int matches %d.
+               return t&argPointer != 0 ||
+                       (f.matchArgTypeInternal(t, typ.Key(), arg, inProgress) && f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress))
+
+       case *types.Chan:
+               return t&argPointer != 0
+
+       case *types.Array:
+               // Same as slice.
+               if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
+                       return true // %s matches []byte
+               }
+               // Recur: []int matches %d.
+               return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress)
+
+       case *types.Slice:
+               // Same as array.
+               if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
+                       return true // %s matches []byte
+               }
+               // Recur: []int matches %d. But watch out for
+               //      type T []T
+               // If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below.
+               return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress)
+
+       case *types.Pointer:
+               // Ugly, but dealing with an edge case: a known pointer to an invalid type,
+               // probably something from a failed import.
+               if typ.Elem().String() == "invalid type" {
+                       if *verbose {
+                               f.Warnf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", f.gofmt(arg))
+                       }
+                       return true // special case
+               }
+               // If it's actually a pointer with %p, it prints as one.
+               if t == argPointer {
+                       return true
+               }
+               // If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct.
+               if str, ok := typ.Elem().Underlying().(*types.Struct); ok {
+                       return f.matchStructArgType(t, str, arg, inProgress)
+               }
+               // The rest can print with %p as pointers, or as integers with %x etc.
+               return t&(argInt|argPointer) != 0
+
+       case *types.Struct:
+               return f.matchStructArgType(t, typ, arg, inProgress)
+
+       case *types.Interface:
+               // If the static type of the argument is empty interface, there's little we can do.
+               // Example:
+               //      func f(x interface{}) { fmt.Printf("%s", x) }
+               // Whether x is valid for %s depends on the type of the argument to f. One day
+               // we will be able to do better. For now, we assume that empty interface is OK
+               // but non-empty interfaces, with Stringer and Error handled above, are errors.
+               return typ.NumMethods() == 0
+
+       case *types.Basic:
+               switch typ.Kind() {
+               case types.UntypedBool,
+                       types.Bool:
+                       return t&argBool != 0
+
+               case types.UntypedInt,
+                       types.Int,
+                       types.Int8,
+                       types.Int16,
+                       types.Int32,
+                       types.Int64,
+                       types.Uint,
+                       types.Uint8,
+                       types.Uint16,
+                       types.Uint32,
+                       types.Uint64,
+                       types.Uintptr:
+                       return t&argInt != 0
+
+               case types.UntypedFloat,
+                       types.Float32,
+                       types.Float64:
+                       return t&argFloat != 0
+
+               case types.UntypedComplex,
+                       types.Complex64,
+                       types.Complex128:
+                       return t&argComplex != 0
+
+               case types.UntypedString,
+                       types.String:
+                       return t&argString != 0
+
+               case types.UnsafePointer:
+                       return t&(argPointer|argInt) != 0
+
+               case types.UntypedRune:
+                       return t&(argInt|argRune) != 0
+
+               case types.UntypedNil:
+                       return t&argPointer != 0 // TODO?
+
+               case types.Invalid:
+                       if *verbose {
+                               f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg))
+                       }
+                       return true // Probably a type check problem.
+               }
+               panic("unreachable")
+       }
+
+       return false
+}
+
+// hasBasicType reports whether x's type is a types.Basic with the given kind.
+func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool {
+       t := f.pkg.types[x].Type
+       if t != nil {
+               t = t.Underlying()
+       }
+       b, ok := t.(*types.Basic)
+       return ok && b.Kind() == kind
+}
+
+// matchStructArgType reports whether all the elements of the struct match the expected
+// type. For instance, with "%d" all the elements must be printable with the "%d" format.
+func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
+       for i := 0; i < typ.NumFields(); i++ {
+               if !f.matchArgTypeInternal(t, typ.Field(i).Type(), arg, inProgress) {
+                       return false
+               }
+       }
+       return true
+}
+
+// numArgsInSignature tells how many formal arguments the function type
+// being called has.
+func (f *File) numArgsInSignature(call *ast.CallExpr) int {
+       // Check the type of the function or method declaration
+       typ := f.pkg.types[call.Fun].Type
+       if typ == nil {
+               return 0
+       }
+       // The type must be a signature, but be sure for safety.
+       sig, ok := typ.(*types.Signature)
+       if !ok {
+               return 0
+       }
+       return sig.Params().Len()
+}
+
+// isErrorMethodCall reports whether the call is of a method with signature
+//     func Error() string
+// where "string" is the universe's string type. We know the method is called "Error".
+func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
+       typ := f.pkg.types[call].Type
+       if typ != nil {
+               // We know it's called "Error", so just check the function signature.
+               return types.Identical(f.pkg.types[call.Fun].Type, stringerMethodType)
+       }
+       // Without types, we can still check by hand.
+       // Is it a selector expression? Otherwise it's a function call, not a method call.
+       sel, ok := call.Fun.(*ast.SelectorExpr)
+       if !ok {
+               return false
+       }
+       // The package is type-checked, so if there are no arguments, we're done.
+       if len(call.Args) > 0 {
+               return false
+       }
+       // Check the type of the method declaration
+       typ = f.pkg.types[sel].Type
+       if typ == nil {
+               return false
+       }
+       // The type must be a signature, but be sure for safety.
+       sig, ok := typ.(*types.Signature)
+       if !ok {
+               return false
+       }
+       // There must be a receiver for it to be a method call. Otherwise it is
+       // a function, not something that satisfies the error interface.
+       if sig.Recv() == nil {
+               return false
+       }
+       // There must be no arguments. Already verified by type checking, but be thorough.
+       if sig.Params().Len() > 0 {
+               return false
+       }
+       // Finally the real questions.
+       // There must be one result.
+       if sig.Results().Len() != 1 {
+               return false
+       }
+       // It must have return type "string" from the universe.
+       return sig.Results().At(0).Type() == types.Typ[types.String]
+}
+
+// hasMethod reports whether the type contains a method with the given name.
+// It is part of the workaround for Formatters and should be deleted when
+// that workaround is no longer necessary.
+// TODO: This could be better once issue 6259 is fixed.
+func (f *File) hasMethod(typ types.Type, name string) bool {
+       // assume we have an addressable variable of type typ
+       obj, _, _ := types.LookupFieldOrMethod(typ, true, f.pkg.typesPkg, name)
+       _, ok := obj.(*types.Func)
+       return ok
+}
diff --git a/src/cmd/vet/unsafeptr.go b/src/cmd/vet/unsafeptr.go
new file mode 100644 (file)
index 0000000..ca15f72
--- /dev/null
@@ -0,0 +1,98 @@
+// Copyright 2014 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.
+
+// Check for invalid uintptr -> unsafe.Pointer conversions.
+
+package main
+
+import (
+       "go/ast"
+       "go/token"
+
+       "golang.org/x/tools/go/types"
+)
+
+func init() {
+       register("unsafeptr",
+               "check for misuse of unsafe.Pointer",
+               checkUnsafePointer,
+               callExpr)
+}
+
+func checkUnsafePointer(f *File, node ast.Node) {
+       x := node.(*ast.CallExpr)
+       if len(x.Args) != 1 {
+               return
+       }
+       if f.hasBasicType(x.Fun, types.UnsafePointer) && f.hasBasicType(x.Args[0], types.Uintptr) && !f.isSafeUintptr(x.Args[0]) {
+               f.Badf(x.Pos(), "possible misuse of unsafe.Pointer")
+       }
+}
+
+// isSafeUintptr reports whether x - already known to be a uintptr -
+// is safe to convert to unsafe.Pointer. It is safe if x is itself derived
+// directly from an unsafe.Pointer via conversion and pointer arithmetic
+// or if x is the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr
+// or obtained from the Data field of a *reflect.SliceHeader or *reflect.StringHeader.
+func (f *File) isSafeUintptr(x ast.Expr) bool {
+       switch x := x.(type) {
+       case *ast.ParenExpr:
+               return f.isSafeUintptr(x.X)
+
+       case *ast.SelectorExpr:
+               switch x.Sel.Name {
+               case "Data":
+                       // reflect.SliceHeader and reflect.StringHeader are okay,
+                       // but only if they are pointing at a real slice or string.
+                       // It's not okay to do:
+                       //      var x SliceHeader
+                       //      x.Data = uintptr(unsafe.Pointer(...))
+                       //      ... use x ...
+                       //      p := unsafe.Pointer(x.Data)
+                       // because in the middle the garbage collector doesn't
+                       // see x.Data as a pointer and so x.Data may be dangling
+                       // by the time we get to the conversion at the end.
+                       // For now approximate by saying that *Header is okay
+                       // but Header is not.
+                       pt, ok := f.pkg.types[x.X].Type.(*types.Pointer)
+                       if ok {
+                               t, ok := pt.Elem().(*types.Named)
+                               if ok && t.Obj().Pkg().Path() == "reflect" {
+                                       switch t.Obj().Name() {
+                                       case "StringHeader", "SliceHeader":
+                                               return true
+                                       }
+                               }
+                       }
+               }
+
+       case *ast.CallExpr:
+               switch len(x.Args) {
+               case 0:
+                       // maybe call to reflect.Value.Pointer or reflect.Value.UnsafeAddr.
+                       sel, ok := x.Fun.(*ast.SelectorExpr)
+                       if !ok {
+                               break
+                       }
+                       switch sel.Sel.Name {
+                       case "Pointer", "UnsafeAddr":
+                               t, ok := f.pkg.types[sel.X].Type.(*types.Named)
+                               if ok && t.Obj().Pkg().Path() == "reflect" && t.Obj().Name() == "Value" {
+                                       return true
+                               }
+                       }
+
+               case 1:
+                       // maybe conversion of uintptr to unsafe.Pointer
+                       return f.hasBasicType(x.Fun, types.Uintptr) && f.hasBasicType(x.Args[0], types.UnsafePointer)
+               }
+
+       case *ast.BinaryExpr:
+               switch x.Op {
+               case token.ADD, token.SUB:
+                       return f.isSafeUintptr(x.X) && !f.isSafeUintptr(x.Y)
+               }
+       }
+       return false
+}
diff --git a/src/cmd/vet/unused.go b/src/cmd/vet/unused.go
new file mode 100644 (file)
index 0000000..db988fe
--- /dev/null
@@ -0,0 +1,94 @@
+// Copyright 2015 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.
+
+// This file defines the check for unused results of calls to certain
+// pure functions.
+
+package main
+
+import (
+       "flag"
+       "go/ast"
+       "go/token"
+       "strings"
+
+       "golang.org/x/tools/go/types"
+)
+
+var unusedFuncsFlag = flag.String("unusedfuncs",
+       "errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse",
+       "comma-separated list of functions whose results must be used")
+
+var unusedStringMethodsFlag = flag.String("unusedstringmethods",
+       "Error,String",
+       "comma-separated list of names of methods of type func() string whose results must be used")
+
+func init() {
+       register("unusedresult",
+               "check for unused result of calls to functions in -unusedfuncs list and methods in -unusedstringmethods list",
+               checkUnusedResult,
+               exprStmt)
+}
+
+// func() string
+var sigNoArgsStringResult = types.NewSignature(nil, nil, nil,
+       types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])),
+       false)
+
+var unusedFuncs = make(map[string]bool)
+var unusedStringMethods = make(map[string]bool)
+
+func initUnusedFlags() {
+       commaSplit := func(s string, m map[string]bool) {
+               if s != "" {
+                       for _, name := range strings.Split(s, ",") {
+                               if len(name) == 0 {
+                                       flag.Usage()
+                               }
+                               m[name] = true
+                       }
+               }
+       }
+       commaSplit(*unusedFuncsFlag, unusedFuncs)
+       commaSplit(*unusedStringMethodsFlag, unusedStringMethods)
+}
+
+func checkUnusedResult(f *File, n ast.Node) {
+       call, ok := unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr)
+       if !ok {
+               return // not a call statement
+       }
+       fun := unparen(call.Fun)
+
+       if f.pkg.types[fun].IsType() {
+               return // a conversion, not a call
+       }
+
+       selector, ok := fun.(*ast.SelectorExpr)
+       if !ok {
+               return // neither a method call nor a qualified ident
+       }
+
+       sel, ok := f.pkg.selectors[selector]
+       if ok && sel.Kind() == types.MethodVal {
+               // method (e.g. foo.String())
+               obj := sel.Obj().(*types.Func)
+               sig := sel.Type().(*types.Signature)
+               if types.Identical(sig, sigNoArgsStringResult) {
+                       if unusedStringMethods[obj.Name()] {
+                               f.Badf(call.Lparen, "result of (%s).%s call not used",
+                                       sig.Recv().Type(), obj.Name())
+                       }
+               }
+       } else if !ok {
+               // package-qualified function (e.g. fmt.Errorf)
+               obj, _ := f.pkg.uses[selector.Sel]
+               if obj, ok := obj.(*types.Func); ok {
+                       qname := obj.Pkg().Path() + "." + obj.Name()
+                       if unusedFuncs[qname] {
+                               f.Badf(call.Lparen, "result of %v call not used", qname)
+                       }
+               }
+       }
+}
diff --git a/src/cmd/vet/vet_test.go b/src/cmd/vet/vet_test.go
new file mode 100644 (file)
index 0000000..33e54ae
--- /dev/null
@@ -0,0 +1,102 @@
+// Copyright 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.
+
+package main_test
+
+import (
+       "bytes"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "runtime"
+       "testing"
+)
+
+const (
+       dataDir = "testdata"
+       binary  = "testvet"
+)
+
+// Run this shell script, but do it in Go so it can be run by "go test".
+//     go build -o testvet
+//     $(GOROOT)/test/errchk ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s
+//     rm testvet
+//
+func TestVet(t *testing.T) {
+       // Plan 9 and Windows systems can't be guaranteed to have Perl and so can't run errchk.
+       switch runtime.GOOS {
+       case "plan9", "windows":
+               t.Skip("skipping test; no Perl on %q", runtime.GOOS)
+       }
+
+       // go build
+       cmd := exec.Command("go", "build", "-o", binary)
+       run(cmd, t)
+
+       // defer removal of vet
+       defer os.Remove(binary)
+
+       // errchk ./testvet
+       gos, err := filepath.Glob(filepath.Join(dataDir, "*.go"))
+       if err != nil {
+               t.Fatal(err)
+       }
+       asms, err := filepath.Glob(filepath.Join(dataDir, "*.s"))
+       if err != nil {
+               t.Fatal(err)
+       }
+       files := append(gos, asms...)
+       errchk := filepath.Join(runtime.GOROOT(), "test", "errchk")
+       flags := []string{
+               "./" + binary,
+               "-printfuncs=Warn:1,Warnf:1",
+               "-test", // TODO: Delete once -shadow is part of -all.
+       }
+       cmd = exec.Command(errchk, append(flags, files...)...)
+       if !run(cmd, t) {
+               t.Fatal("vet command failed")
+       }
+}
+
+func run(c *exec.Cmd, t *testing.T) bool {
+       output, err := c.CombinedOutput()
+       os.Stderr.Write(output)
+       if err != nil {
+               t.Fatal(err)
+       }
+       // Errchk delights by not returning non-zero status if it finds errors, so we look at the output.
+       // It prints "BUG" if there is a failure.
+       if !c.ProcessState.Success() {
+               return false
+       }
+       return !bytes.Contains(output, []byte("BUG"))
+}
+
+// TestTags verifies that the -tags argument controls which files to check.
+func TestTags(t *testing.T) {
+       // go build
+       cmd := exec.Command("go", "build", "-o", binary)
+       run(cmd, t)
+
+       // defer removal of vet
+       defer os.Remove(binary)
+
+       args := []string{
+               "-tags=testtag",
+               "-v", // We're going to look at the files it examines.
+               "testdata/tagtest",
+       }
+       cmd = exec.Command(filepath.Join(".", binary), args...)
+       output, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Fatal(err)
+       }
+       // file1 has testtag and file2 has !testtag.
+       if !bytes.Contains(output, []byte("tagtest/file1.go")) {
+               t.Error("file1 was excluded, should be included")
+       }
+       if bytes.Contains(output, []byte("tagtest/file2.go")) {
+               t.Error("file2 was included, should be excluded")
+       }
+}
diff --git a/src/cmd/vet/whitelist/whitelist.go b/src/cmd/vet/whitelist/whitelist.go
new file mode 100644 (file)
index 0000000..d6f0dce
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 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.
+
+// Package whitelist defines exceptions for the vet tool.
+package whitelist // import "golang.org/x/tools/cmd/vet/whitelist"
+
+// UnkeyedLiteral are types that are actually slices, but
+// syntactically, we cannot tell whether the Typ in pkg.Typ{1, 2, 3}
+// is a slice or a struct, so we whitelist all the standard package
+// library's exported slice types.
+var UnkeyedLiteral = map[string]bool{
+       /*
+               find $GOROOT/src -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \
+                       grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/,,' | \
+                       sed 's, ,.,' |  sed 's, .*,,' | grep -v '\.[a-z]' | \
+                       sort | awk '{ print "\"" $0 "\": true," }'
+       */
+       "crypto/x509/pkix.RDNSequence":                  true,
+       "crypto/x509/pkix.RelativeDistinguishedNameSET": true,
+       "database/sql.RawBytes":                         true,
+       "debug/macho.LoadBytes":                         true,
+       "encoding/asn1.ObjectIdentifier":                true,
+       "encoding/asn1.RawContent":                      true,
+       "encoding/json.RawMessage":                      true,
+       "encoding/xml.CharData":                         true,
+       "encoding/xml.Comment":                          true,
+       "encoding/xml.Directive":                        true,
+       "go/scanner.ErrorList":                          true,
+       "image/color.Palette":                           true,
+       "net.HardwareAddr":                              true,
+       "net.IP":                                        true,
+       "net.IPMask":                                    true,
+       "sort.Float64Slice":                             true,
+       "sort.IntSlice":                                 true,
+       "sort.StringSlice":                              true,
+       "unicode.SpecialCase":                           true,
+
+       // These image and image/color struct types are frozen. We will never add fields to them.
+       "image/color.Alpha16": true,
+       "image/color.Alpha":   true,
+       "image/color.CMYK":    true,
+       "image/color.Gray16":  true,
+       "image/color.Gray":    true,
+       "image/color.NRGBA64": true,
+       "image/color.NRGBA":   true,
+       "image/color.RGBA64":  true,
+       "image/color.RGBA":    true,
+       "image/color.YCbCr":   true,
+       "image.Point":         true,
+       "image.Rectangle":     true,
+       "image.Uniform":       true,
+}