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>
--- /dev/null
+// 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())
+ }
+}
--- /dev/null
+// 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)
+ }
+ }
+}
--- /dev/null
+// 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")
+ }
+}
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+// 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
+ }
+ }
+}
--- /dev/null
+// 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 ""
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+// 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"
--- /dev/null
+// 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()
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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]
+}
--- /dev/null
+// 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))
+ }
+ }
+}
--- /dev/null
+// 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
+ })
+}
--- /dev/null
+// 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()))
+ }
+}
--- /dev/null
+// 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)
+ }
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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)
--- /dev/null
+// 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\)"
--- /dev/null
+// 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\)"
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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"
+}
--- /dev/null
+// 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.
+}
--- /dev/null
+// 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"
+}
--- /dev/null
+// 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
--- /dev/null
+// 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.
--- /dev/null
+// 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}
--- /dev/null
+// 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 :(
--- /dev/null
+// 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"
+ }
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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"
+}
--- /dev/null
+// 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")
+}
--- /dev/null
+// 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", ¬PercentDV) // ERROR "arg ¬PercentDV for printf verb %d of wrong type"
+ Printf("%p", ¬PercentDV) // 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 ""
+}
--- /dev/null
+// 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"
+ }()
+ }
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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"
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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() {
+}
--- /dev/null
+// 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() {
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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"
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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)
+ }
+ }
+ }
+}
--- /dev/null
+// 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")
+ }
+}
--- /dev/null
+// 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,
+}