Commit 1b8b2c15 authored by Robert Griesemer's avatar Robert Griesemer

cmd/vet: move cmd/vet into std repo

cp -r x/tools/cmd/vet cmd/vet without any changes.
The next change will adjust the source to use std
repo go/types and friends.

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

Change-Id: Ic45dab7066f13923e21f8c61200c8c3fd447b171
Reviewed-on: https://go-review.googlesource.com/10694Reviewed-by: default avatarRob Pike <r@golang.org>
parent ebcc7ec1
// 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())
}
}
// 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)
}
}
}
// 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")
}
}
// 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
}
}
// 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
}
}
}
// 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 ""
}
// 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
}
// 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
}
}
// 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"
// 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()
}
// 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
}
// 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]
}
// 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))
}
}
}
// 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
})
}
// 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()))
}
}
// 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)
}
}
// 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
}
// 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)
// 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\)"
// 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\)"
// 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
// 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
// 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"
}
// 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.
}
// 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"
}
// 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
// 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.
// 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}
// 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 :(
// 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"
}
}
// 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
}
// 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"
}
// 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")
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains tests for the printf checker.
package testdata
import (
"fmt"
"math"
"os"
"unsafe" // just for test case printing unsafe.Pointer
)
func UnsafePointerPrintfTest() {
var up unsafe.Pointer
fmt.Printf("%p, %x %X", up, up, up)
}
// Error methods that do not satisfy the Error interface and should be checked.
type errorTest1 int
func (errorTest1) Error(...interface{}) string {
return "hi"
}
type errorTest2 int // Analogous to testing's *T type.
func (errorTest2) Error(...interface{}) {
}
type errorTest3 int
func (errorTest3) Error() { // No return value.
}
type errorTest4 int
func (errorTest4) Error() int { // Different return type.
return 3
}
type errorTest5 int
func (errorTest5) error() { // niladic; don't complain if no args (was bug)
}
// This function never executes, but it serves as a simple test for the program.
// Test with make test.
func PrintfTests() {
var b bool
var i int
var r rune
var s string
var x float64
var p *int
var imap map[int]int
var fslice []float64
var c complex64
// Some good format/argtypes
fmt.Printf("")
fmt.Printf("%b %b %b", 3, i, x)
fmt.Printf("%c %c %c %c", 3, i, 'x', r)
fmt.Printf("%d %d %d", 3, i, imap)
fmt.Printf("%e %e %e %e", 3e9, x, fslice, c)
fmt.Printf("%E %E %E %E", 3e9, x, fslice, c)
fmt.Printf("%f %f %f %f", 3e9, x, fslice, c)
fmt.Printf("%F %F %F %F", 3e9, x, fslice, c)
fmt.Printf("%g %g %g %g", 3e9, x, fslice, c)
fmt.Printf("%G %G %G %G", 3e9, x, fslice, c)
fmt.Printf("%b %b %b %b", 3e9, x, fslice, c)
fmt.Printf("%o %o", 3, i)
fmt.Printf("%p %p", p, nil)
fmt.Printf("%q %q %q %q", 3, i, 'x', r)
fmt.Printf("%s %s %s", "hi", s, []byte{65})
fmt.Printf("%t %t", true, b)
fmt.Printf("%T %T", 3, i)
fmt.Printf("%U %U", 3, i)
fmt.Printf("%v %v", 3, i)
fmt.Printf("%x %x %x %x", 3, i, "hi", s)
fmt.Printf("%X %X %X %X", 3, i, "hi", s)
fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
fmt.Printf("%s", &stringerv)
fmt.Printf("%v", &stringerv)
fmt.Printf("%T", &stringerv)
fmt.Printf("%v", notstringerv)
fmt.Printf("%T", notstringerv)
fmt.Printf("%q", stringerarrayv)
fmt.Printf("%v", stringerarrayv)
fmt.Printf("%s", stringerarrayv)
fmt.Printf("%v", notstringerarrayv)
fmt.Printf("%T", notstringerarrayv)
fmt.Printf("%d", new(Formatter))
fmt.Printf("%*%", 2) // Ridiculous but allowed.
fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say.
fmt.Printf("%g", 1+2i)
// Some bad format/argTypes
fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type"
fmt.Printf("%t", c) // ERROR "arg c for printf verb %t of wrong type"
fmt.Printf("%t", 1+2i) // ERROR "arg 1 \+ 2i for printf verb %t of wrong type"
fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type"
fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type"
fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type"
fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type"
fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type"
fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type"
fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type"
fmt.Printf("%g", imap) // ERROR "arg imap for printf verb %g of wrong type"
fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type"
fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type"
fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type"
fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type"
fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type"
fmt.Printf("%s", byte(65)) // ERROR "arg byte\(65\) for printf verb %s of wrong type"
fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type"
fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type"
fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type"
fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type"
fmt.Printf("%s", stringerv) // ERROR "arg stringerv for printf verb %s of wrong type"
fmt.Printf("%t", stringerv) // ERROR "arg stringerv for printf verb %t of wrong type"
fmt.Printf("%q", notstringerv) // ERROR "arg notstringerv for printf verb %q of wrong type"
fmt.Printf("%t", notstringerv) // ERROR "arg notstringerv for printf verb %t of wrong type"
fmt.Printf("%t", stringerarrayv) // ERROR "arg stringerarrayv for printf verb %t of wrong type"
fmt.Printf("%t", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %t of wrong type"
fmt.Printf("%q", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %q of wrong type"
fmt.Printf("%d", Formatter(true)) // correct (the type is responsible for formatting)
fmt.Printf("%s", nonemptyinterface) // correct (the dynamic type of nonemptyinterface may be a stringer)
fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
fmt.Println() // not an error
fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call"
fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call"
_ = fmt.Sprintf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Sprintf call"
fmt.Printf("%s%%%d", "hi", 3) // correct
fmt.Printf("%08s", "woo") // correct
fmt.Printf("% 8s", "woo") // correct
fmt.Printf("%.*d", 3, 3) // correct
fmt.Printf("%.*d", 3, 3, 3, 3) // ERROR "wrong number of args for format in Printf call.*4 args"
fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int"
fmt.Printf("%.*d", i, 3) // correct
fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int"
fmt.Printf("%*%", 0.22) // ERROR "arg 0.22 for \* in printf format not of type int"
fmt.Printf("%q %q", multi()...) // ok
fmt.Printf("%#q", `blah`) // ok
printf("now is the time", "buddy") // ERROR "no formatting directive"
Printf("now is the time", "buddy") // ERROR "no formatting directive"
Printf("hi") // ok
const format = "%s %s\n"
Printf(format, "hi", "there")
Printf(format, "hi") // ERROR "missing argument for Printf..%s..: format reads arg 2, have only 1"
Printf("%s %d %.3v %q", "str", 4) // ERROR "missing argument for Printf..%.3v..: format reads arg 3, have only 2"
f := new(stringer)
f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call"
f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call"
f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb"
f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag"
Printf("d%", 2) // ERROR "missing verb at end of format string in Printf call"
Printf("%d", percentDV)
Printf("%d", &percentDV)
Printf("%d", notPercentDV) // ERROR "arg notPercentDV for printf verb %d of wrong type"
Printf("%d", &notPercentDV) // ERROR "arg &notPercentDV for printf verb %d of wrong type"
Printf("%p", &notPercentDV) // Works regardless: we print it as a pointer.
Printf("%s", percentSV)
Printf("%s", &percentSV)
// Good argument reorderings.
Printf("%[1]d", 3)
Printf("%[1]*d", 3, 1)
Printf("%[2]*[1]d", 1, 3)
Printf("%[2]*.[1]*[3]d", 2, 3, 4)
fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.
// Bad argument reorderings.
Printf("%[xd", 3) // ERROR "illegal syntax for printf argument index"
Printf("%[x]d", 3) // ERROR "illegal syntax for printf argument index"
Printf("%[3]*s", "hi", 2) // ERROR "missing argument for Printf.* reads arg 3, have only 2"
_ = fmt.Sprintf("%[3]d", 2) // ERROR "missing argument for Sprintf.* reads arg 3, have only 1"
Printf("%[2]*.[1]*[3]d", 2, "hi", 4) // ERROR "arg .hi. for \* in printf format not of type int"
Printf("%[0]s", "arg1") // ERROR "index value \[0\] for Printf.*; indexes start at 1"
Printf("%[0]d", 1) // ERROR "index value \[0\] for Printf.*; indexes start at 1"
// Something that satisfies the error interface.
var e error
fmt.Println(e.Error()) // ok
// Something that looks like an error interface but isn't, such as the (*T).Error method
// in the testing package.
var et1 errorTest1
fmt.Println(et1.Error()) // ERROR "no args in Error call"
fmt.Println(et1.Error("hi")) // ok
fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call"
var et2 errorTest2
et2.Error() // ERROR "no args in Error call"
et2.Error("hi") // ok, not an error method.
et2.Error("%d", 3) // ERROR "possible formatting directive in Error call"
var et3 errorTest3
et3.Error() // ok, not an error method.
var et4 errorTest4
et4.Error() // ok, not an error method.
var et5 errorTest5
et5.error() // ok, not an error method.
// Bug: used to recur forever.
Printf("%p %x", recursiveStructV, recursiveStructV.next)
Printf("%p %x", recursiveStruct1V, recursiveStruct1V.next)
Printf("%p %x", recursiveSliceV, recursiveSliceV)
Printf("%p %x", recursiveMapV, recursiveMapV)
// Special handling for Log.
math.Log(3) // OK
Log(3) // OK
Log("%d", 3) // ERROR "possible formatting directive in Log call"
Logf("%d", 3)
Logf("%d", "hi") // ERROR "arg .hi. for printf verb %d of wrong type: untyped string"
}
// Printf is used by the test so we must declare it.
func Printf(format string, args ...interface{}) {
panic("don't call - testing only")
}
// printf is used by the test so we must declare it.
func printf(format string, args ...interface{}) {
panic("don't call - testing only")
}
// multi is used by the test.
func multi() []interface{} {
panic("don't call - testing only")
}
type stringer float64
var stringerv stringer
func (*stringer) String() string {
return "string"
}
func (*stringer) Warn(int, ...interface{}) string {
return "warn"
}
func (*stringer) Warnf(int, string, ...interface{}) string {
return "warnf"
}
type notstringer struct {
f float64
}
var notstringerv notstringer
type stringerarray [4]float64
func (stringerarray) String() string {
return "string"
}
var stringerarrayv stringerarray
type notstringerarray [4]float64
var notstringerarrayv notstringerarray
var nonemptyinterface = interface {
f()
}(nil)
// A data type we can print with "%d".
type percentDStruct struct {
a int
b []byte
c *float64
}
var percentDV percentDStruct
// A data type we cannot print correctly with "%d".
type notPercentDStruct struct {
a int
b []byte
c bool
}
var notPercentDV notPercentDStruct
// A data type we can print with "%s".
type percentSStruct struct {
a string
b []byte
c stringerarray
}
var percentSV percentSStruct
type recursiveStringer int
func (s recursiveStringer) String() string {
_ = fmt.Sprintf("%d", s)
_ = fmt.Sprintf("%#v", s)
_ = fmt.Sprintf("%v", s) // ERROR "arg s for printf causes recursive call to String method"
_ = fmt.Sprintf("%v", &s) // ERROR "arg &s for printf causes recursive call to String method"
_ = fmt.Sprintf("%T", s) // ok; does not recursively call String
return fmt.Sprintln(s) // ERROR "arg s for print causes recursive call to String method"
}
type recursivePtrStringer int
func (p *recursivePtrStringer) String() string {
_ = fmt.Sprintf("%v", *p)
return fmt.Sprintln(p) // ERROR "arg p for print causes recursive call to String method"
}
type Formatter bool
func (*Formatter) Format(fmt.State, rune) {
}
type RecursiveSlice []RecursiveSlice
var recursiveSliceV = &RecursiveSlice{}
type RecursiveMap map[int]RecursiveMap
var recursiveMapV = make(RecursiveMap)
type RecursiveStruct struct {
next *RecursiveStruct
}
var recursiveStructV = &RecursiveStruct{}
type RecursiveStruct1 struct {
next *Recursive2Struct
}
type RecursiveStruct2 struct {
next *Recursive1Struct
}
var recursiveStruct1V = &RecursiveStruct1{}
// Fix for issue 7149: Missing return type on String method caused fault.
func (int) String() {
return ""
}
// 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"
}()
}
}
// 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
}
// 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"
}
// 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
}
// 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() {
}
// 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() {
}
// 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
}
// 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"
}
// 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
}
// 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
}
// 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)
}
}
}
}
// 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")
}
}
// 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,
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment