From 1b8b2c1551a1f375e5141eec1645e69906d337b3 Mon Sep 17 00:00:00 2001
From: Robert Griesemer <gri@golang.org>
Date: Thu, 4 Jun 2015 12:54:58 -0700
Subject: [PATCH] cmd/vet: move cmd/vet into std repo

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>
---
 src/cmd/vet/asmdecl.go                 |  662 ++++++++
 src/cmd/vet/assign.go                  |   49 +
 src/cmd/vet/atomic.go                  |   66 +
 src/cmd/vet/bool.go                    |  186 +++
 src/cmd/vet/buildtag.go                |   91 +
 src/cmd/vet/composite.go               |  125 ++
 src/cmd/vet/copylock.go                |  155 ++
 src/cmd/vet/deadcode.go                |  296 ++++
 src/cmd/vet/doc.go                     |  190 +++
 src/cmd/vet/main.go                    |  488 ++++++
 src/cmd/vet/method.go                  |  182 ++
 src/cmd/vet/nilfunc.go                 |   68 +
 src/cmd/vet/print.go                   |  587 +++++++
 src/cmd/vet/rangeloop.go               |   70 +
 src/cmd/vet/shadow.go                  |  245 +++
 src/cmd/vet/shift.go                   |   83 +
 src/cmd/vet/structtag.go               |  122 ++
 src/cmd/vet/testdata/asm.go            |   33 +
 src/cmd/vet/testdata/asm1.s            |  254 +++
 src/cmd/vet/testdata/asm2.s            |  257 +++
 src/cmd/vet/testdata/asm3.s            |  178 ++
 src/cmd/vet/testdata/asm4.s            |   26 +
 src/cmd/vet/testdata/assign.go         |   18 +
 src/cmd/vet/testdata/atomic.go         |   43 +
 src/cmd/vet/testdata/bool.go           |  113 ++
 src/cmd/vet/testdata/buildtag.go       |   14 +
 src/cmd/vet/testdata/buildtag_bad.go   |   15 +
 src/cmd/vet/testdata/composite.go      |   63 +
 src/cmd/vet/testdata/copylock_func.go  |   90 +
 src/cmd/vet/testdata/copylock_range.go |   67 +
 src/cmd/vet/testdata/deadcode.go       | 2125 ++++++++++++++++++++++++
 src/cmd/vet/testdata/method.go         |   22 +
 src/cmd/vet/testdata/nilfunc.go        |   35 +
 src/cmd/vet/testdata/print.go          |  342 ++++
 src/cmd/vet/testdata/rangeloop.go      |   59 +
 src/cmd/vet/testdata/shadow.go         |   54 +
 src/cmd/vet/testdata/shift.go          |   78 +
 src/cmd/vet/testdata/structtag.go      |   36 +
 src/cmd/vet/testdata/tagtest/file1.go  |   10 +
 src/cmd/vet/testdata/tagtest/file2.go  |   10 +
 src/cmd/vet/testdata/unsafeptr.go      |   61 +
 src/cmd/vet/testdata/unused.go         |   29 +
 src/cmd/vet/types.go                   |  366 ++++
 src/cmd/vet/unsafeptr.go               |   98 ++
 src/cmd/vet/unused.go                  |   94 ++
 src/cmd/vet/vet_test.go                |  102 ++
 src/cmd/vet/whitelist/whitelist.go     |   53 +
 47 files changed, 8410 insertions(+)
 create mode 100644 src/cmd/vet/asmdecl.go
 create mode 100644 src/cmd/vet/assign.go
 create mode 100644 src/cmd/vet/atomic.go
 create mode 100644 src/cmd/vet/bool.go
 create mode 100644 src/cmd/vet/buildtag.go
 create mode 100644 src/cmd/vet/composite.go
 create mode 100644 src/cmd/vet/copylock.go
 create mode 100644 src/cmd/vet/deadcode.go
 create mode 100644 src/cmd/vet/doc.go
 create mode 100644 src/cmd/vet/main.go
 create mode 100644 src/cmd/vet/method.go
 create mode 100644 src/cmd/vet/nilfunc.go
 create mode 100644 src/cmd/vet/print.go
 create mode 100644 src/cmd/vet/rangeloop.go
 create mode 100644 src/cmd/vet/shadow.go
 create mode 100644 src/cmd/vet/shift.go
 create mode 100644 src/cmd/vet/structtag.go
 create mode 100644 src/cmd/vet/testdata/asm.go
 create mode 100644 src/cmd/vet/testdata/asm1.s
 create mode 100644 src/cmd/vet/testdata/asm2.s
 create mode 100644 src/cmd/vet/testdata/asm3.s
 create mode 100644 src/cmd/vet/testdata/asm4.s
 create mode 100644 src/cmd/vet/testdata/assign.go
 create mode 100644 src/cmd/vet/testdata/atomic.go
 create mode 100644 src/cmd/vet/testdata/bool.go
 create mode 100644 src/cmd/vet/testdata/buildtag.go
 create mode 100644 src/cmd/vet/testdata/buildtag_bad.go
 create mode 100644 src/cmd/vet/testdata/composite.go
 create mode 100644 src/cmd/vet/testdata/copylock_func.go
 create mode 100644 src/cmd/vet/testdata/copylock_range.go
 create mode 100644 src/cmd/vet/testdata/deadcode.go
 create mode 100644 src/cmd/vet/testdata/method.go
 create mode 100644 src/cmd/vet/testdata/nilfunc.go
 create mode 100644 src/cmd/vet/testdata/print.go
 create mode 100644 src/cmd/vet/testdata/rangeloop.go
 create mode 100644 src/cmd/vet/testdata/shadow.go
 create mode 100644 src/cmd/vet/testdata/shift.go
 create mode 100644 src/cmd/vet/testdata/structtag.go
 create mode 100644 src/cmd/vet/testdata/tagtest/file1.go
 create mode 100644 src/cmd/vet/testdata/tagtest/file2.go
 create mode 100644 src/cmd/vet/testdata/unsafeptr.go
 create mode 100644 src/cmd/vet/testdata/unused.go
 create mode 100644 src/cmd/vet/types.go
 create mode 100644 src/cmd/vet/unsafeptr.go
 create mode 100644 src/cmd/vet/unused.go
 create mode 100644 src/cmd/vet/vet_test.go
 create mode 100644 src/cmd/vet/whitelist/whitelist.go

diff --git a/src/cmd/vet/asmdecl.go b/src/cmd/vet/asmdecl.go
new file mode 100644
index 0000000000..6bdfdbf3be
--- /dev/null
+++ b/src/cmd/vet/asmdecl.go
@@ -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
index 0000000000..54c1ae1fdc
--- /dev/null
+++ b/src/cmd/vet/assign.go
@@ -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
index 0000000000..c084f13ab3
--- /dev/null
+++ b/src/cmd/vet/atomic.go
@@ -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
index 0000000000..07c2a93dff
--- /dev/null
+++ b/src/cmd/vet/bool.go
@@ -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
index 0000000000..2d86edf734
--- /dev/null
+++ b/src/cmd/vet/buildtag.go
@@ -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
index 0000000000..0c3f916558
--- /dev/null
+++ b/src/cmd/vet/composite.go
@@ -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
index 0000000000..e8a6820fce
--- /dev/null
+++ b/src/cmd/vet/copylock.go
@@ -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
index 0000000000..3b306c2104
--- /dev/null
+++ b/src/cmd/vet/deadcode.go
@@ -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
index 0000000000..a19de3fac3
--- /dev/null
+++ b/src/cmd/vet/doc.go
@@ -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
index 0000000000..e4b68770b0
--- /dev/null
+++ b/src/cmd/vet/main.go
@@ -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
index 0000000000..00949df437
--- /dev/null
+++ b/src/cmd/vet/method.go
@@ -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
index 0000000000..fa1bac7e64
--- /dev/null
+++ b/src/cmd/vet/nilfunc.go
@@ -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
index 0000000000..b20d935ef4
--- /dev/null
+++ b/src/cmd/vet/print.go
@@ -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
index 0000000000..96e2ca8062
--- /dev/null
+++ b/src/cmd/vet/rangeloop.go
@@ -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
index 0000000000..34e5db9091
--- /dev/null
+++ b/src/cmd/vet/shadow.go
@@ -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
index 0000000000..2385c23fdb
--- /dev/null
+++ b/src/cmd/vet/shift.go
@@ -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
index 0000000000..e8164a46f9
--- /dev/null
+++ b/src/cmd/vet/structtag.go
@@ -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
index 0000000000..9a3d5315ad
--- /dev/null
+++ b/src/cmd/vet/testdata/asm.go
@@ -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
index 0000000000..62f423cd8b
--- /dev/null
+++ b/src/cmd/vet/testdata/asm1.s
@@ -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
index 0000000000..c33c02a70b
--- /dev/null
+++ b/src/cmd/vet/testdata/asm2.s
@@ -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
index 0000000000..3d69356a0f
--- /dev/null
+++ b/src/cmd/vet/testdata/asm3.s
@@ -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
index 0000000000..044b050b6b
--- /dev/null
+++ b/src/cmd/vet/testdata/asm4.s
@@ -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
index 0000000000..32ba8683c1
--- /dev/null
+++ b/src/cmd/vet/testdata/assign.go
@@ -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
index 0000000000..1ba261d941
--- /dev/null
+++ b/src/cmd/vet/testdata/atomic.go
@@ -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
index 0000000000..af6cc011dd
--- /dev/null
+++ b/src/cmd/vet/testdata/bool.go
@@ -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
index 0000000000..eb36fd3259
--- /dev/null
+++ b/src/cmd/vet/testdata/buildtag.go
@@ -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
index 0000000000..fbe10cf748
--- /dev/null
+++ b/src/cmd/vet/testdata/buildtag_bad.go
@@ -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
index 0000000000..69e7d7ccb0
--- /dev/null
+++ b/src/cmd/vet/testdata/composite.go
@@ -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
index 0000000000..108c044209
--- /dev/null
+++ b/src/cmd/vet/testdata/copylock_func.go
@@ -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
index 0000000000..f95b0252b6
--- /dev/null
+++ b/src/cmd/vet/testdata/copylock_range.go
@@ -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
index 0000000000..5370bc32f6
--- /dev/null
+++ b/src/cmd/vet/testdata/deadcode.go
@@ -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
index 0000000000..52b500df27
--- /dev/null
+++ b/src/cmd/vet/testdata/method.go
@@ -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
index 0000000000..2ce7bc8ca8
--- /dev/null
+++ b/src/cmd/vet/testdata/nilfunc.go
@@ -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
index 0000000000..3390a31f2c
--- /dev/null
+++ b/src/cmd/vet/testdata/print.go
@@ -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
index 0000000000..37b5940ddd
--- /dev/null
+++ b/src/cmd/vet/testdata/rangeloop.go
@@ -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
index 0000000000..34a680681b
--- /dev/null
+++ b/src/cmd/vet/testdata/shadow.go
@@ -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
index 0000000000..6624f09cc1
--- /dev/null
+++ b/src/cmd/vet/testdata/shift.go
@@ -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
index 0000000000..6878f5642d
--- /dev/null
+++ b/src/cmd/vet/testdata/structtag.go
@@ -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
index 0000000000..22a1509acc
--- /dev/null
+++ b/src/cmd/vet/testdata/tagtest/file1.go
@@ -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
index 0000000000..ba7dd91bbd
--- /dev/null
+++ b/src/cmd/vet/testdata/tagtest/file2.go
@@ -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
index 0000000000..8f64030b85
--- /dev/null
+++ b/src/cmd/vet/testdata/unsafeptr.go
@@ -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
index 0000000000..d50f6594d9
--- /dev/null
+++ b/src/cmd/vet/testdata/unused.go
@@ -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
index 0000000000..89e9989d9a
--- /dev/null
+++ b/src/cmd/vet/types.go
@@ -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
index 0000000000..ca15f72578
--- /dev/null
+++ b/src/cmd/vet/unsafeptr.go
@@ -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
index 0000000000..db988fe1de
--- /dev/null
+++ b/src/cmd/vet/unused.go
@@ -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
index 0000000000..33e54ae900
--- /dev/null
+++ b/src/cmd/vet/vet_test.go
@@ -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
index 0000000000..d6f0dce821
--- /dev/null
+++ b/src/cmd/vet/whitelist/whitelist.go
@@ -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,
+}
-- 
2.30.9