Commit cb413099 authored by Alan Donovan's avatar Alan Donovan

cmd/vet: switch to x/tools/go/analysis implementation

This change deletes the legacy implementation of vet, replacing it
with a short main.go that merely selects the desired analyzers and
calls into the "unitchecker" implementation vendored from
golang.org/x/tools/go/analysis.

Unlike the full vet checker (x/tools/go/analysis/cmd/vet), the 'lite'
unitchecker cannot also be run standalone (as 'go tool vet' or
cmd/vet); it must be invoked by 'go vet'.
This design was chosen to avoid vendoring many
additional dependencies into GOROOT, in particular go/packages. If
go/packages should someday become part of the standard library, there
will be considerable opportunity for simplification.

This change also patches the vendored analysisflag package
(by adding patch.go) so that it fully supports the build
system's -V flag protocol.

Also:
- remove stale internal/unitchecker/ tree
  (belonged in https://go-review.googlesource.com/c/149778).
- move vet legacy flags (-all, -v, -source, -tags) into analysisflags
  as all drivers will need them, not just unitchecker.
  I will upstream this change.

A sampling of tests from the cmd/vet testsuite have been preserved as
a smoke test, to ensure that each analyzer is being run, and for
convenience when evaluating changes. Comprehensive tests for each
analyzer live upstream in x/tools. The tests have been heavily reduced
and reorganized so that they conform to the structure required by 'go
vet'.

Change-Id: I84b38caeef733e65deb95234b3b87b5f61046def
Reviewed-on: https://go-review.googlesource.com/c/149609Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent b00a6d8b
......@@ -56,10 +56,7 @@ func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer {
name := prefix + f.Name
flag.Var(f.Value, name, f.Usage)
var isBool bool
if b, ok := f.Value.(interface{ IsBoolFlag() bool }); ok {
isBool = b.IsBoolFlag()
}
isBool := isBoolFlag(f.Value)
analysisFlags = append(analysisFlags, analysisFlag{name, isBool, f.Usage})
})
}
......@@ -68,11 +65,23 @@ func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer {
printflags := flag.Bool("flags", false, "print analyzer flags in JSON")
addVersionFlag()
// Add shims for legacy vet flags.
// Add shims for legacy vet flags to enable existing
// scripts that run vet to continue to work.
_ = flag.Bool("source", false, "no effect (deprecated)")
_ = flag.Bool("v", false, "no effect (deprecated)")
_ = flag.Bool("all", false, "no effect (deprecated)")
_ = flag.String("tags", "", "no effect (deprecated)")
for _, name := range []string{"source", "v", "all", "tags"} {
f := flag.Lookup(name)
isBool := isBoolFlag(f.Value)
analysisFlags = append(analysisFlags, analysisFlag{name, isBool, f.Usage})
}
for old, new := range vetLegacyFlags {
newFlag := flag.Lookup(new)
if newFlag != nil && flag.Lookup(old) == nil {
flag.Var(newFlag.Value, old, "deprecated alias for -"+new)
isBool := isBoolFlag(newFlag.Value)
analysisFlags = append(analysisFlags, analysisFlag{old, isBool, newFlag.Usage})
}
}
......@@ -229,6 +238,11 @@ func (ts triState) IsBoolFlag() bool {
return true
}
func isBoolFlag(v flag.Value) bool {
b, ok := v.(interface{ IsBoolFlag() bool })
return ok && b.IsBoolFlag()
}
// Legacy flag support
// vetLegacyFlags maps flags used by legacy vet to their corresponding
......
package analysisflags
import "cmd/internal/objabi"
// This additional file changes the behavior of the vendored code.
func init() { addVersionFlag = objabi.AddVersionFlag }
// Copyright 2018 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.
// The unitchecker package defines the main function for an analysis
// driver that analyzes a single compilation unit during a build.
// It is invoked by a build system such as "go vet":
//
// $ go vet -vettool=$(which vet)
//
// It supports the following command-line protocol:
//
// -V=full describe executable (to the build tool)
// -flags describe flags (to the build tool)
// foo.cfg description of compilation unit (from the build tool)
//
// This package does not depend on go/packages.
// If you need a standalone tool, use multichecker,
// which supports this mode but can also load packages
// from source using go/packages.
package unitchecker
// TODO(adonovan):
// - with gccgo, go build does not build standard library,
// so we will not get to analyze it. Yet we must in order
// to create base facts for, say, the fmt package for the
// printf checker.
// - support JSON output, factored with multichecker.
import (
"encoding/gob"
"encoding/json"
"fmt"
"go/ast"
"go/build"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"io/ioutil"
"log"
"os"
"sort"
"strings"
"sync"
"time"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/facts"
)
// A Config describes a compilation unit to be analyzed.
// It is provided to the tool in a JSON-encoded file
// whose name ends with ".cfg".
type Config struct {
Compiler string
Dir string
ImportPath string
GoFiles []string
OtherFiles []string // TODO(adonovan): make go vet populate this (github.com/golang/go/issues/27665)
ImportMap map[string]string
PackageFile map[string]string
Standard map[string]bool
PackageVetx map[string]string
VetxOnly bool
VetxOutput string
SucceedOnTypecheckFailure bool
}
// Main reads the *.cfg file, runs the analysis,
// and calls os.Exit with an appropriate error code.
func Main(configFile string, analyzers []*analysis.Analyzer) {
cfg, err := readConfig(configFile)
if err != nil {
log.Fatal(err)
}
fset := token.NewFileSet()
diags, err := run(fset, cfg, analyzers)
if err != nil {
log.Fatal(err)
}
if len(diags) > 0 {
for _, diag := range diags {
fmt.Fprintf(os.Stderr, "%s: %s\n", fset.Position(diag.Pos), diag.Message)
}
os.Exit(1)
}
os.Exit(0)
}
func readConfig(filename string) (*Config, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
cfg := new(Config)
if err := json.Unmarshal(data, cfg); err != nil {
return nil, fmt.Errorf("cannot decode JSON config file %s: %v", filename, err)
}
if len(cfg.GoFiles) == 0 {
// The go command disallows packages with no files.
// The only exception is unsafe, but the go command
// doesn't call vet on it.
return nil, fmt.Errorf("package has no files: %s", cfg.ImportPath)
}
return cfg, nil
}
func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]analysis.Diagnostic, error) {
// Load, parse, typecheck.
var files []*ast.File
for _, name := range cfg.GoFiles {
f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
if err != nil {
if cfg.SucceedOnTypecheckFailure {
// Silently succeed; let the compiler
// report parse errors.
err = nil
}
return nil, err
}
files = append(files, f)
}
compilerImporter := importer.For(cfg.Compiler, func(path string) (io.ReadCloser, error) {
// path is a resolved package path, not an import path.
file, ok := cfg.PackageFile[path]
if !ok {
if cfg.Compiler == "gccgo" && cfg.Standard[path] {
return nil, nil // fall back to default gccgo lookup
}
return nil, fmt.Errorf("no package file for %q", path)
}
return os.Open(file)
})
importer := importerFunc(func(importPath string) (*types.Package, error) {
path, ok := cfg.ImportMap[importPath] // resolve vendoring, etc
if !ok {
return nil, fmt.Errorf("can't resolve import %q", path)
}
return compilerImporter.Import(path)
})
tc := &types.Config{
Importer: importer,
Sizes: types.SizesFor("gc", build.Default.GOARCH), // assume gccgo ≡ gc?
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
pkg, err := tc.Check(cfg.ImportPath, fset, files, info)
if err != nil {
if cfg.SucceedOnTypecheckFailure {
// Silently succeed; let the compiler
// report type errors.
err = nil
}
return nil, err
}
// Register fact types with gob.
// In VetxOnly mode, analyzers are only for their facts,
// so we can skip any analysis that neither produces facts
// nor depends on any analysis that produces facts.
// Also build a map to hold working state and result.
type action struct {
once sync.Once
result interface{}
err error
usesFacts bool // (transitively uses)
diagnostics []analysis.Diagnostic
}
actions := make(map[*analysis.Analyzer]*action)
var registerFacts func(a *analysis.Analyzer) bool
registerFacts = func(a *analysis.Analyzer) bool {
act, ok := actions[a]
if !ok {
act = new(action)
var usesFacts bool
for _, f := range a.FactTypes {
usesFacts = true
gob.Register(f)
}
for _, req := range a.Requires {
if registerFacts(req) {
usesFacts = true
}
}
act.usesFacts = usesFacts
actions[a] = act
}
return act.usesFacts
}
var filtered []*analysis.Analyzer
for _, a := range analyzers {
if registerFacts(a) || !cfg.VetxOnly {
filtered = append(filtered, a)
}
}
analyzers = filtered
// Read facts from imported packages.
read := func(path string) ([]byte, error) {
if vetx, ok := cfg.PackageVetx[path]; ok {
return ioutil.ReadFile(vetx)
}
return nil, nil // no .vetx file, no facts
}
facts, err := facts.Decode(pkg, read)
if err != nil {
return nil, err
}
// In parallel, execute the DAG of analyzers.
var exec func(a *analysis.Analyzer) *action
var execAll func(analyzers []*analysis.Analyzer)
exec = func(a *analysis.Analyzer) *action {
act := actions[a]
act.once.Do(func() {
execAll(a.Requires) // prefetch dependencies in parallel
// The inputs to this analysis are the
// results of its prerequisites.
inputs := make(map[*analysis.Analyzer]interface{})
var failed []string
for _, req := range a.Requires {
reqact := exec(req)
if reqact.err != nil {
failed = append(failed, req.String())
continue
}
inputs[req] = reqact.result
}
// Report an error if any dependency failed.
if failed != nil {
sort.Strings(failed)
act.err = fmt.Errorf("failed prerequisites: %s", strings.Join(failed, ", "))
return
}
pass := &analysis.Pass{
Analyzer: a,
Fset: fset,
Files: files,
OtherFiles: cfg.OtherFiles,
Pkg: pkg,
TypesInfo: info,
ResultOf: inputs,
Report: func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) },
ImportObjectFact: facts.ImportObjectFact,
ExportObjectFact: facts.ExportObjectFact,
ImportPackageFact: facts.ImportPackageFact,
ExportPackageFact: facts.ExportPackageFact,
}
t0 := time.Now()
act.result, act.err = a.Run(pass)
if false {
log.Printf("analysis %s = %s", pass, time.Since(t0))
}
})
return act
}
execAll = func(analyzers []*analysis.Analyzer) {
var wg sync.WaitGroup
for _, a := range analyzers {
wg.Add(1)
go func(a *analysis.Analyzer) {
_ = exec(a)
wg.Done()
}(a)
}
wg.Wait()
}
execAll(analyzers)
// Return diagnostics from root analyzers.
var diags []analysis.Diagnostic
for _, a := range analyzers {
act := actions[a]
if act.err != nil {
return nil, act.err // some analysis failed
}
diags = append(diags, act.diagnostics...)
}
data := facts.Encode()
if err := ioutil.WriteFile(cfg.VetxOutput, data, 0666); err != nil {
return nil, fmt.Errorf("failed to write analysis facts: %v", err)
}
return diags, nil
}
type importerFunc func(path string) (*types.Package, error)
func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
// 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/build"
"go/token"
"go/types"
"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
asmArray
asmInterface
asmEmptyInterface
asmStruct
asmComplex
)
// An asmArch describes assembly parameters for an architecture
type asmArch struct {
name string
bigEndian bool
stack string
lr bool
// calculated during initialization
sizes types.Sizes
intSize int
ptrSize int
maxAlign int
}
// 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{name: "386", bigEndian: false, stack: "SP", lr: false}
asmArchArm = asmArch{name: "arm", bigEndian: false, stack: "R13", lr: true}
asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true}
asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false}
asmArchAmd64p32 = asmArch{name: "amd64p32", bigEndian: false, stack: "SP", lr: false}
asmArchMips = asmArch{name: "mips", bigEndian: true, stack: "R29", lr: true}
asmArchMipsLE = asmArch{name: "mipsle", bigEndian: false, stack: "R29", lr: true}
asmArchMips64 = asmArch{name: "mips64", bigEndian: true, stack: "R29", lr: true}
asmArchMips64LE = asmArch{name: "mips64le", bigEndian: false, stack: "R29", lr: true}
asmArchPpc64 = asmArch{name: "ppc64", bigEndian: true, stack: "R1", lr: true}
asmArchPpc64LE = asmArch{name: "ppc64le", bigEndian: false, stack: "R1", lr: true}
asmArchS390X = asmArch{name: "s390x", bigEndian: true, stack: "R15", lr: true}
asmArchWasm = asmArch{name: "wasm", bigEndian: false, stack: "SP", lr: false}
arches = []*asmArch{
&asmArch386,
&asmArchArm,
&asmArchArm64,
&asmArchAmd64,
&asmArchAmd64p32,
&asmArchMips,
&asmArchMipsLE,
&asmArchMips64,
&asmArchMips64LE,
&asmArchPpc64,
&asmArchPpc64LE,
&asmArchS390X,
&asmArchWasm,
}
)
func init() {
for _, arch := range arches {
arch.sizes = types.SizesFor("gc", arch.name)
if arch.sizes == nil {
panic("missing SizesFor for gc/" + arch.name)
}
arch.intSize = int(arch.sizes.Sizeof(types.Typ[types.Int]))
arch.ptrSize = int(arch.sizes.Sizeof(types.Typ[types.UnsafePointer]))
arch.maxAlign = int(arch.sizes.Alignof(types.Typ[types.Int64]))
}
registerPkgCheck("asmdecl", asmCheck)
}
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*(.*))?`)
ppc64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`)
)
func asmCheck(pkg *Package) {
if vcfg.VetxOnly {
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 {
// There can be multiple architectures in a single +build line,
// so accumulate them all and then prefer the one that
// matches build.Default.GOARCH.
var archCandidates []*asmArch
for _, fld := range strings.Fields(m[1]) {
for _, a := range arches {
if a.name == fld {
archCandidates = append(archCandidates, a)
}
}
}
for _, a := range archCandidates {
if a.name == build.Default.GOARCH {
archCandidates = []*asmArch{a}
break
}
}
if len(archCandidates) > 0 {
arch = archCandidates[0].name
archDef = archCandidates[0]
}
}
}
if m := asmTEXT.FindStringSubmatch(line); m != nil {
flushRet()
if arch == "" {
// Arch not specified by filename or build tags.
// Fall back to build.Default.GOARCH.
for _, a := range arches {
if a.name == build.Default.GOARCH {
arch = a.name
archDef = a
break
}
}
if arch == "" {
f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
continue Files
}
}
fnName = m[2]
if pkgName := strings.TrimSpace(m[1]); pkgName != "" {
pathParts := strings.Split(pkgName, "∕")
pkgName = pathParts[len(pathParts)-1]
if pkgName != f.pkg.path {
f.Warnf(token.NoPos, "%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", f.name, lineno, arch, fnName, pkgName)
fn = nil
fnName = ""
continue
}
}
flag := m[3]
fn = knownFunc[fnName][arch]
if fn != nil {
size, _ := strconv.Atoi(m[5])
if size != fn.size && (flag != "7" && !strings.Contains(flag, "NOSPLIT") || size != 0) {
badf("wrong argument size %d; expected $...-%d", size, fn.size)
}
}
localSize, _ = strconv.Atoi(m[4])
localSize += archDef.intSize
if archDef.lr && !strings.Contains(flag, "NOFRAME") {
// Account for caller's saved LR
localSize += archDef.intSize
}
argSize, _ = strconv.Atoi(m[5])
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()
}
}
func asmKindForType(t types.Type, size int) asmKind {
switch t := t.Underlying().(type) {
case *types.Basic:
switch t.Kind() {
case types.String:
return asmString
case types.Complex64, types.Complex128:
return asmComplex
}
return asmKind(size)
case *types.Pointer, *types.Chan, *types.Map, *types.Signature:
return asmKind(size)
case *types.Struct:
return asmStruct
case *types.Interface:
if t.Empty() {
return asmEmptyInterface
}
return asmInterface
case *types.Array:
return asmArray
case *types.Slice:
return asmSlice
}
panic("unreachable")
}
// A component is an assembly-addressable component of a composite type,
// or a composite type itself.
type component struct {
size int
offset int
kind asmKind
typ string
suffix string // Such as _base for string base, _0_lo for lo half of first element of [1]uint64 on 32 bit machine.
outer string // The suffix for immediately containing composite type.
}
func newComponent(suffix string, kind asmKind, typ string, offset, size int, outer string) component {
return component{suffix: suffix, kind: kind, typ: typ, offset: offset, size: size, outer: outer}
}
// componentsOfType generates a list of components of type t.
// For example, given string, the components are the string itself, the base, and the length.
func componentsOfType(arch *asmArch, t types.Type) []component {
return appendComponentsRecursive(arch, t, nil, "", 0)
}
// appendComponentsRecursive implements componentsOfType.
// Recursion is required to correct handle structs and arrays,
// which can contain arbitrary other types.
func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suffix string, off int) []component {
s := t.String()
size := int(arch.sizes.Sizeof(t))
kind := asmKindForType(t, size)
cc = append(cc, newComponent(suffix, kind, s, off, size, suffix))
switch kind {
case 8:
if arch.ptrSize == 4 {
w1, w2 := "lo", "hi"
if arch.bigEndian {
w1, w2 = w2, w1
}
cc = append(cc, newComponent(suffix+"_"+w1, 4, "half "+s, off, 4, suffix))
cc = append(cc, newComponent(suffix+"_"+w2, 4, "half "+s, off+4, 4, suffix))
}
case asmEmptyInterface:
cc = append(cc, newComponent(suffix+"_type", asmKind(arch.ptrSize), "interface type", off, arch.ptrSize, suffix))
cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
case asmInterface:
cc = append(cc, newComponent(suffix+"_itable", asmKind(arch.ptrSize), "interface itable", off, arch.ptrSize, suffix))
cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
case asmSlice:
cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "slice base", off, arch.ptrSize, suffix))
cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "slice len", off+arch.ptrSize, arch.intSize, suffix))
cc = append(cc, newComponent(suffix+"_cap", asmKind(arch.intSize), "slice cap", off+arch.ptrSize+arch.intSize, arch.intSize, suffix))
case asmString:
cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "string base", off, arch.ptrSize, suffix))
cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "string len", off+arch.ptrSize, arch.intSize, suffix))
case asmComplex:
fsize := size / 2
cc = append(cc, newComponent(suffix+"_real", asmKind(fsize), fmt.Sprintf("real(complex%d)", size*8), off, fsize, suffix))
cc = append(cc, newComponent(suffix+"_imag", asmKind(fsize), fmt.Sprintf("imag(complex%d)", size*8), off+fsize, fsize, suffix))
case asmStruct:
tu := t.Underlying().(*types.Struct)
fields := make([]*types.Var, tu.NumFields())
for i := 0; i < tu.NumFields(); i++ {
fields[i] = tu.Field(i)
}
offsets := arch.sizes.Offsetsof(fields)
for i, f := range fields {
cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
}
case asmArray:
tu := t.Underlying().(*types.Array)
elem := tu.Elem()
// Calculate offset of each element array.
fields := []*types.Var{
types.NewVar(token.NoPos, nil, "fake0", elem),
types.NewVar(token.NoPos, nil, "fake1", elem),
}
offsets := arch.sizes.Offsetsof(fields)
elemoff := int(offsets[1])
for i := 0; i < int(tu.Len()); i++ {
cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
}
}
return cc
}
// 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
)
// addParams adds asmVars for each of the parameters in list.
// isret indicates whether the list are the arguments or the return values.
addParams := func(list []*ast.Field, isret bool) {
argnum := 0
for _, fld := range list {
t := f.pkg.types[fld.Type].Type
align := int(arch.sizes.Alignof(t))
size := int(arch.sizes.Sizeof(t))
offset += -offset & (align - 1)
cc := componentsOfType(arch, t)
// names is the list of names with this type.
names := fld.Names
if len(names) == 0 {
// Anonymous args will be called arg, arg1, arg2, ...
// Similarly so for return values: ret, ret1, ret2, ...
name := "arg"
if isret {
name = "ret"
}
if argnum > 0 {
name += strconv.Itoa(argnum)
}
names = []*ast.Ident{ast.NewIdent(name)}
}
argnum += len(names)
// Create variable for each name.
for _, id := range names {
name := id.Name
for _, c := range cc {
outer := name + c.outer
v := asmVar{
name: name + c.suffix,
kind: c.kind,
typ: c.typ,
off: offset + c.offset,
size: c.size,
}
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
}
}
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, false)
if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
offset += -offset & (arch.maxAlign - 1)
addParams(decl.Type.Results.List, true)
}
fn.size = offset
m[arch.name] = fn
}
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, "P") && strings.HasSuffix(op, "RD") {
// PINSRD, PEXTRD, etc
src = 4
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 "ppc64", "ppc64le":
// Strip standard suffixes to reveal size letter.
m := ppc64Suff.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
}
}
case "mips", "mipsle", "mips64", "mips64le":
switch op {
case "MOVB", "MOVBU":
src = 1
case "MOVH", "MOVHU":
src = 2
case "MOVW", "MOVWU", "MOVF":
src = 4
case "MOVV", "MOVD":
src = 8
}
case "s390x":
switch op {
case "MOVB", "MOVBZ":
src = 1
case "MOVH", "MOVHZ":
src = 2
case "MOVW", "MOVWZ", "FMOVS":
src = 4
case "MOVD", "FMOVD":
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
vs := v.size
vt := v.typ
switch vk {
case asmInterface, asmEmptyInterface, asmString, asmSlice:
// allow reference to first word (pointer)
vk = v.inner[0].kind
vs = v.inner[0].size
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, vs, 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 hasSideEffects(f, lhs) || hasSideEffects(f, rhs) {
continue // expressions may not be equal
}
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"
"go/types"
)
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
}
if len(n.Lhs) == 1 && n.Tok == token.DEFINE {
return
}
for i, right := range n.Rhs {
call, ok := right.(*ast.CallExpr)
if !ok {
continue
}
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
continue
}
pkgIdent, _ := sel.X.(*ast.Ident)
pkgName, ok := f.pkg.uses[pkgIdent].(*types.PkgName)
if !ok || pkgName.Imported().Path() != "sync/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(f, 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(f *File, 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(f, 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(f *File, e ast.Expr) bool {
safe := true
ast.Inspect(e, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.CallExpr:
typVal := f.pkg.types[n.Fun]
switch {
case typVal.IsType():
// Type conversion, which is safe.
case typVal.IsBuiltin():
// Builtin func, conservatively assumed to not
// be safe for now.
safe = false
return false
default:
// A non-builtin func or method call.
// Conservatively assume that all of them have
// side effects for now.
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")
)
func badfLine(f *File, line int, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
fmt.Fprintf(os.Stderr, "%s:%d: %s\n", f.name, line, msg)
setExit(1)
}
// checkBuildTag checks that build tags are in the correct location and well-formed.
func checkBuildTag(f *File) {
if !vet("buildtags") {
return
}
// we must look at the raw lines, as build tags may appear in non-Go
// files such as assembly files.
lines := bytes.SplitAfter(f.content, nl)
// lineWithComment reports whether a line corresponds to a comment in
// the source file. If the source file wasn't Go, the function always
// returns true.
lineWithComment := func(line int) bool {
if f.file == nil {
// Current source file is not Go, so be conservative.
return true
}
for _, group := range f.file.Comments {
startLine := f.fset.Position(group.Pos()).Line
endLine := f.fset.Position(group.End()).Line
if startLine <= line && line <= endLine {
return true
}
}
return false
}
// 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
}
if !bytes.Contains(line, plusBuild) {
// Check that the comment contains "+build" early, to
// avoid unnecessary lineWithComment calls that may
// incur linear searches.
continue
}
if !lineWithComment(i + 1) {
// This is a line in a Go source file that looks like a
// comment, but actually isn't - such as part of a raw
// string.
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.
badfLine(f, i+1, "possible malformed +build comment")
continue
}
if i >= cutoff {
badfLine(f, i+1, "+build comment must appear before package clause and be followed by a blank line")
continue
}
// Check arguments.
Args:
for _, arg := range fields[1:] {
for _, elem := range strings.Split(string(arg), ",") {
if strings.HasPrefix(elem, "!!") {
badfLine(f, i+1, "invalid double negative in build constraint: %s", arg)
break Args
}
elem = strings.TrimPrefix(elem, "!")
for _, c := range elem {
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
badfLine(f, i+1, "invalid non-alphanumeric build constraint: %s", arg)
break Args
}
}
}
}
continue
}
// Comment with +build but not at beginning.
if i < cutoff {
badfLine(f, i+1, "possible malformed +build comment")
continue
}
}
}
// 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.
// Check for invalid cgo pointer passing.
// This looks for code that uses cgo to call C code passing values
// whose types are almost always invalid according to the cgo pointer
// sharing rules.
// Specifically, it warns about attempts to pass a Go chan, map, func,
// or slice to C, either directly, or via a pointer, array, or struct.
package main
import (
"go/ast"
"go/token"
"go/types"
)
func init() {
register("cgocall",
"check for types that may not be passed to cgo calls",
checkCgoCall,
callExpr)
}
func checkCgoCall(f *File, node ast.Node) {
x := node.(*ast.CallExpr)
// We are only looking for calls to functions imported from
// the "C" package.
sel, ok := x.Fun.(*ast.SelectorExpr)
if !ok {
return
}
id, ok := sel.X.(*ast.Ident)
if !ok {
return
}
pkgname, ok := f.pkg.uses[id].(*types.PkgName)
if !ok || pkgname.Imported().Path() != "C" {
return
}
// A call to C.CBytes passes a pointer but is always safe.
if sel.Sel.Name == "CBytes" {
return
}
for _, arg := range x.Args {
if !typeOKForCgoCall(cgoBaseType(f, arg), make(map[types.Type]bool)) {
f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
}
// Check for passing the address of a bad type.
if conv, ok := arg.(*ast.CallExpr); ok && len(conv.Args) == 1 && f.hasBasicType(conv.Fun, types.UnsafePointer) {
arg = conv.Args[0]
}
if u, ok := arg.(*ast.UnaryExpr); ok && u.Op == token.AND {
if !typeOKForCgoCall(cgoBaseType(f, u.X), make(map[types.Type]bool)) {
f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
}
}
}
}
// cgoBaseType tries to look through type conversions involving
// unsafe.Pointer to find the real type. It converts:
// unsafe.Pointer(x) => x
// *(*unsafe.Pointer)(unsafe.Pointer(&x)) => x
func cgoBaseType(f *File, arg ast.Expr) types.Type {
switch arg := arg.(type) {
case *ast.CallExpr:
if len(arg.Args) == 1 && f.hasBasicType(arg.Fun, types.UnsafePointer) {
return cgoBaseType(f, arg.Args[0])
}
case *ast.StarExpr:
call, ok := arg.X.(*ast.CallExpr)
if !ok || len(call.Args) != 1 {
break
}
// Here arg is *f(v).
t := f.pkg.types[call.Fun].Type
if t == nil {
break
}
ptr, ok := t.Underlying().(*types.Pointer)
if !ok {
break
}
// Here arg is *(*p)(v)
elem, ok := ptr.Elem().Underlying().(*types.Basic)
if !ok || elem.Kind() != types.UnsafePointer {
break
}
// Here arg is *(*unsafe.Pointer)(v)
call, ok = call.Args[0].(*ast.CallExpr)
if !ok || len(call.Args) != 1 {
break
}
// Here arg is *(*unsafe.Pointer)(f(v))
if !f.hasBasicType(call.Fun, types.UnsafePointer) {
break
}
// Here arg is *(*unsafe.Pointer)(unsafe.Pointer(v))
u, ok := call.Args[0].(*ast.UnaryExpr)
if !ok || u.Op != token.AND {
break
}
// Here arg is *(*unsafe.Pointer)(unsafe.Pointer(&v))
return cgoBaseType(f, u.X)
}
return f.pkg.types[arg].Type
}
// typeOKForCgoCall reports whether the type of arg is OK to pass to a
// C function using cgo. This is not true for Go types with embedded
// pointers. m is used to avoid infinite recursion on recursive types.
func typeOKForCgoCall(t types.Type, m map[types.Type]bool) bool {
if t == nil || m[t] {
return true
}
m[t] = true
switch t := t.Underlying().(type) {
case *types.Chan, *types.Map, *types.Signature, *types.Slice:
return false
case *types.Pointer:
return typeOKForCgoCall(t.Elem(), m)
case *types.Array:
return typeOKForCgoCall(t.Elem(), m)
case *types.Struct:
for i := 0; i < t.NumFields(); i++ {
if !typeOKForCgoCall(t.Field(i).Type(), m) {
return false
}
}
}
return true
}
// 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 (
"cmd/vet/internal/whitelist"
"flag"
"go/ast"
"go/types"
"strings"
)
var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only")
func init() {
register("composites",
"check that composite literals of types from imported packages use 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) {
cl := node.(*ast.CompositeLit)
typ := f.pkg.types[cl].Type
if typ == nil {
// cannot determine composite literals' type, skip it
return
}
typeName := typ.String()
if *compositeWhiteList && whitelist.UnkeyedLiteral[typeName] {
// skip whitelisted types
return
}
under := typ.Underlying()
for {
ptr, ok := under.(*types.Pointer)
if !ok {
break
}
under = ptr.Elem().Underlying()
}
if _, ok := under.(*types.Struct); !ok {
// skip non-struct composite literals
return
}
if isLocalType(f, typ) {
// allow unkeyed locally defined composite literal
return
}
// check if the CompositeLit contains an unkeyed field
allKeyValue := true
for _, e := range cl.Elts {
if _, ok := e.(*ast.KeyValueExpr); !ok {
allKeyValue = false
break
}
}
if allKeyValue {
// all the composite literal fields are keyed
return
}
f.Badf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName)
}
func isLocalType(f *File, typ types.Type) bool {
switch x := typ.(type) {
case *types.Struct:
// struct literals are local types
return true
case *types.Pointer:
return isLocalType(f, x.Elem())
case *types.Named:
// names in package foo are local to foo_test too
return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(f.pkg.path, "_test")
}
return false
}
// 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"
"go/types"
)
func init() {
register("copylocks",
"check that locks are not passed by value",
checkCopyLocks,
funcDecl, rangeStmt, funcLit, callExpr, assignStmt, genDecl, compositeLit, returnStmt)
}
// 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.Name.Name, node.Recv, node.Type)
case *ast.FuncLit:
checkCopyLocksFunc(f, "func", nil, node.Type)
case *ast.CallExpr:
checkCopyLocksCallExpr(f, node)
case *ast.AssignStmt:
checkCopyLocksAssign(f, node)
case *ast.GenDecl:
checkCopyLocksGenDecl(f, node)
case *ast.CompositeLit:
checkCopyLocksCompositeLit(f, node)
case *ast.ReturnStmt:
checkCopyLocksReturnStmt(f, node)
}
}
// checkCopyLocksAssign checks whether an assignment
// copies a lock.
func checkCopyLocksAssign(f *File, as *ast.AssignStmt) {
for i, x := range as.Rhs {
if path := lockPathRhs(f, x); path != nil {
f.Badf(x.Pos(), "assignment copies lock value to %v: %v", f.gofmt(as.Lhs[i]), path)
}
}
}
// checkCopyLocksGenDecl checks whether lock is copied
// in variable declaration.
func checkCopyLocksGenDecl(f *File, gd *ast.GenDecl) {
if gd.Tok != token.VAR {
return
}
for _, spec := range gd.Specs {
valueSpec := spec.(*ast.ValueSpec)
for i, x := range valueSpec.Values {
if path := lockPathRhs(f, x); path != nil {
f.Badf(x.Pos(), "variable declaration copies lock value to %v: %v", valueSpec.Names[i].Name, path)
}
}
}
}
// checkCopyLocksCompositeLit detects lock copy inside a composite literal
func checkCopyLocksCompositeLit(f *File, cl *ast.CompositeLit) {
for _, x := range cl.Elts {
if node, ok := x.(*ast.KeyValueExpr); ok {
x = node.Value
}
if path := lockPathRhs(f, x); path != nil {
f.Badf(x.Pos(), "literal copies lock value from %v: %v", f.gofmt(x), path)
}
}
}
// checkCopyLocksReturnStmt detects lock copy in return statement
func checkCopyLocksReturnStmt(f *File, rs *ast.ReturnStmt) {
for _, x := range rs.Results {
if path := lockPathRhs(f, x); path != nil {
f.Badf(x.Pos(), "return copies lock value: %v", path)
}
}
}
// checkCopyLocksCallExpr detects lock copy in the arguments to a function call
func checkCopyLocksCallExpr(f *File, ce *ast.CallExpr) {
var id *ast.Ident
switch fun := ce.Fun.(type) {
case *ast.Ident:
id = fun
case *ast.SelectorExpr:
id = fun.Sel
}
if fun, ok := f.pkg.uses[id].(*types.Builtin); ok {
switch fun.Name() {
case "new", "len", "cap", "Sizeof":
return
}
}
for _, x := range ce.Args {
if path := lockPathRhs(f, x); path != nil {
f.Badf(x.Pos(), "call of %s copies lock value: %v", f.gofmt(ce.Fun), path)
}
}
}
// 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, name string, recv *ast.FieldList, typ *ast.FuncType) {
if recv != nil && len(recv.List) > 0 {
expr := 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", name, path)
}
}
if typ.Params != nil {
for _, field := range typ.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", name, path)
}
}
}
// Don't check typ.Results. If T has a Lock field it's OK to write
// return T{}
// because that is returning the zero value. Leave result checking
// to the return statement.
}
// 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()
}
func lockPathRhs(f *File, x ast.Expr) typePath {
if _, ok := x.(*ast.CompositeLit); ok {
return nil
}
if _, ok := x.(*ast.CallExpr); ok {
// A call may return a zero value.
return nil
}
if star, ok := x.(*ast.StarExpr); ok {
if _, ok := star.X.(*ast.CallExpr); ok {
// A call may return a pointer to a zero value.
return nil
}
}
return lockPath(f.pkg.typesPkg, f.pkg.types[x].Type)
}
// 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
}
for {
atyp, ok := typ.Underlying().(*types.Array)
if !ok {
break
}
typ = atyp.Elem()
}
// 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 pointer to this type
// is a sync.Locker, but a value is not. This differentiates
// embedded interfaces from embedded values.
if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) {
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
}
var lockerType *types.Interface
// Construct a sync.Locker interface type.
func init() {
nullary := types.NewSignature(nil, nil, nil, false) // func()
methods := []*types.Func{
types.NewFunc(token.NoPos, nil, "Lock", nullary),
types.NewFunc(token.NoPos, nil, "Unlock", nullary),
}
lockerType = types.NewInterface(methods, nil).Complete()
}
// Copyright 2017 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.
//
// Simplified dead code detector. Used for skipping certain checks
// on unreachable code (for instance, shift checks on arch-specific code).
package main
import (
"go/ast"
"go/constant"
)
// updateDead puts unreachable "if" and "case" nodes into f.dead.
func (f *File) updateDead(node ast.Node) {
if f.dead[node] {
// The node is already marked as dead.
return
}
switch stmt := node.(type) {
case *ast.IfStmt:
// "if" branch is dead if its condition evaluates
// to constant false.
v := f.pkg.types[stmt.Cond].Value
if v == nil {
return
}
if !constant.BoolVal(v) {
f.setDead(stmt.Body)
return
}
f.setDead(stmt.Else)
case *ast.SwitchStmt:
// Case clause with empty switch tag is dead if it evaluates
// to constant false.
if stmt.Tag == nil {
BodyLoopBool:
for _, stmt := range stmt.Body.List {
cc := stmt.(*ast.CaseClause)
if cc.List == nil {
// Skip default case.
continue
}
for _, expr := range cc.List {
v := f.pkg.types[expr].Value
if v == nil || v.Kind() != constant.Bool || constant.BoolVal(v) {
continue BodyLoopBool
}
}
f.setDead(cc)
}
return
}
// Case clause is dead if its constant value doesn't match
// the constant value from the switch tag.
// TODO: This handles integer comparisons only.
v := f.pkg.types[stmt.Tag].Value
if v == nil || v.Kind() != constant.Int {
return
}
tagN, ok := constant.Uint64Val(v)
if !ok {
return
}
BodyLoopInt:
for _, x := range stmt.Body.List {
cc := x.(*ast.CaseClause)
if cc.List == nil {
// Skip default case.
continue
}
for _, expr := range cc.List {
v := f.pkg.types[expr].Value
if v == nil {
continue BodyLoopInt
}
n, ok := constant.Uint64Val(v)
if !ok || tagN == n {
continue BodyLoopInt
}
}
f.setDead(cc)
}
}
}
// setDead marks the node and all the children as dead.
func (f *File) setDead(node ast.Node) {
dv := deadVisitor{
f: f,
}
ast.Walk(dv, node)
}
type deadVisitor struct {
f *File
}
func (dv deadVisitor) Visit(node ast.Node) ast.Visitor {
if node == nil {
return nil
}
dv.f.dead[node] = true
return dv
}
// 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.
//
// TODO(adonovan): use the new cfg package, which is more precise.
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.
Vet is normally invoked using the go command by running "go vet":
go vet
vets the package in the current directory.
go vet package/path/name
vets the package whose path is provided.
Use "go help packages" to see other ways of specifying which packages to vet.
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 the -all flag is set so 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.
By default vet uses the object files generated by 'go install some/pkg' to typecheck the code.
If the -source flag is provided, vet uses only source code.
Available checks:
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.
Invalid uses of cgo
Flag: -cgocall
Detect some violations of the cgo pointer passing rules.
Unkeyed composite literals
Flag: -composites
Composite struct literals that do not use the field-keyed syntax.
Copying locks
Flag: -copylocks
Locks that are erroneously passed by value.
HTTP responses used incorrectly
Flag: -httpresponse
Mistakes deferring a function call on an HTTP response before
checking whether the error returned with the response was nil.
Failure to call the cancelation function returned by WithCancel
Flag: -lostcancel
The cancelation function returned by context.WithCancel, WithTimeout,
and WithDeadline must be called or the new context will remain live
until its parent context is cancelled.
(The background context is never cancelled.)
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
Nil function comparison
Flag: -nilfunc
Comparisons between functions and nil.
Printf family
Flag: -printf
Suspicious calls to fmt.Print, fmt.Printf, and related functions.
The check applies to known functions (for example, those in package fmt)
as well as any detected wrappers of known functions.
The -printfuncs flag specifies a comma-separated list of names of
additional known formatting functions. Each name can be of the form
pkg.Name or pkg.Type.Name, where pkg is a complete import path,
or else can be a case-insensitive unqualified identifier like "errorf".
If a listed name ends in f, the function is assumed to be Printf-like,
taking a format string before the argument list. Otherwise it is
assumed to be Print-like, taking a list of arguments with no format string.
Range loop variables
Flag: -rangeloops
Incorrect uses of range loop variables in closures.
Shadowed variables
Flag: -shadow=false (experimental; must be set explicitly)
Variables that may have been unintentionally shadowed.
Shifts
Flag: -shift
Shifts equal to or longer than the variable's length.
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.
Tests and documentation examples
Flag: -tests
Mistakes involving tests including functions with incorrect names or signatures
and example tests that document identifiers not in the package.
Unreachable code
Flag: -unreachable
Unreachable code.
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.
Other flags
These flags configure the behavior of vet:
-all (default true)
Enable all non-experimental checks.
-v
Verbose mode
-printfuncs
A comma-separated list of print-like function names
to supplement the standard list.
For more information, see the discussion of the -printf flag.
-shadowstrict
Whether to be strict about shadowing; can be noisy.
Using vet directly
For testing and debugging vet can be run directly by invoking
"go tool vet" or just running the binary. Run this way, vet might not
have up to date information for imported packages.
go tool vet source/directory/*.go
vets the files named, all of which must be in the same package.
go tool vet source/directory
recursively descends the directory, vetting each package it finds.
*/
package main
// Copyright 2016 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 check for http.Response values being used before
// checking for errors.
package main
import (
"go/ast"
"go/types"
)
func init() {
register("httpresponse",
"check errors are checked before using an http Response",
checkHTTPResponse, callExpr)
}
func checkHTTPResponse(f *File, node ast.Node) {
call := node.(*ast.CallExpr)
if !isHTTPFuncOrMethodOnClient(f, call) {
return // the function call is not related to this check.
}
finder := &blockStmtFinder{node: call}
ast.Walk(finder, f.file)
stmts := finder.stmts()
if len(stmts) < 2 {
return // the call to the http function is the last statement of the block.
}
asg, ok := stmts[0].(*ast.AssignStmt)
if !ok {
return // the first statement is not assignment.
}
resp := rootIdent(asg.Lhs[0])
if resp == nil {
return // could not find the http.Response in the assignment.
}
def, ok := stmts[1].(*ast.DeferStmt)
if !ok {
return // the following statement is not a defer.
}
root := rootIdent(def.Call.Fun)
if root == nil {
return // could not find the receiver of the defer call.
}
if resp.Obj == root.Obj {
f.Badf(root.Pos(), "using %s before checking for errors", resp.Name)
}
}
// isHTTPFuncOrMethodOnClient checks whether the given call expression is on
// either a function of the net/http package or a method of http.Client that
// returns (*http.Response, error).
func isHTTPFuncOrMethodOnClient(f *File, expr *ast.CallExpr) bool {
fun, _ := expr.Fun.(*ast.SelectorExpr)
sig, _ := f.pkg.types[fun].Type.(*types.Signature)
if sig == nil {
return false // the call is not on of the form x.f()
}
res := sig.Results()
if res.Len() != 2 {
return false // the function called does not return two values.
}
if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !isNamedType(ptr.Elem(), "net/http", "Response") {
return false // the first return type is not *http.Response.
}
if !types.Identical(res.At(1).Type().Underlying(), errorType) {
return false // the second return type is not error
}
typ := f.pkg.types[fun.X].Type
if typ == nil {
id, ok := fun.X.(*ast.Ident)
return ok && id.Name == "http" // function in net/http package.
}
if isNamedType(typ, "net/http", "Client") {
return true // method on http.Client.
}
ptr, ok := typ.(*types.Pointer)
return ok && isNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client.
}
// blockStmtFinder is an ast.Visitor that given any ast node can find the
// statement containing it and its succeeding statements in the same block.
type blockStmtFinder struct {
node ast.Node // target of search
stmt ast.Stmt // innermost statement enclosing argument to Visit
block *ast.BlockStmt // innermost block enclosing argument to Visit.
}
// Visit finds f.node performing a search down the ast tree.
// It keeps the last block statement and statement seen for later use.
func (f *blockStmtFinder) Visit(node ast.Node) ast.Visitor {
if node == nil || f.node.Pos() < node.Pos() || f.node.End() > node.End() {
return nil // not here
}
switch n := node.(type) {
case *ast.BlockStmt:
f.block = n
case ast.Stmt:
f.stmt = n
}
if f.node.Pos() == node.Pos() && f.node.End() == node.End() {
return nil // found
}
return f // keep looking
}
// stmts returns the statements of f.block starting from the one including f.node.
func (f *blockStmtFinder) stmts() []ast.Stmt {
for i, v := range f.block.List {
if f.stmt == v {
return f.block.List[i:]
}
}
return nil
}
// rootIdent finds the root identifier x in a chain of selections x.y.z, or nil if not found.
func rootIdent(n ast.Node) *ast.Ident {
switch n := n.(type) {
case *ast.SelectorExpr:
return rootIdent(n.X)
case *ast.Ident:
return n
default:
return nil
}
}
// Copyright 2016 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 cfg
// This file implements the CFG construction pass.
import (
"fmt"
"go/ast"
"go/token"
)
type builder struct {
cfg *CFG
mayReturn func(*ast.CallExpr) bool
current *Block
lblocks map[*ast.Object]*lblock // labeled blocks
targets *targets // linked stack of branch targets
}
func (b *builder) stmt(_s ast.Stmt) {
// The label of the current statement. If non-nil, its _goto
// target is always set; its _break and _continue are set only
// within the body of switch/typeswitch/select/for/range.
// It is effectively an additional default-nil parameter of stmt().
var label *lblock
start:
switch s := _s.(type) {
case *ast.BadStmt,
*ast.SendStmt,
*ast.IncDecStmt,
*ast.GoStmt,
*ast.DeferStmt,
*ast.EmptyStmt,
*ast.AssignStmt:
// No effect on control flow.
b.add(s)
case *ast.ExprStmt:
b.add(s)
if call, ok := s.X.(*ast.CallExpr); ok && !b.mayReturn(call) {
// Calls to panic, os.Exit, etc, never return.
b.current = b.newUnreachableBlock("unreachable.call")
}
case *ast.DeclStmt:
// Treat each var ValueSpec as a separate statement.
d := s.Decl.(*ast.GenDecl)
if d.Tok == token.VAR {
for _, spec := range d.Specs {
if spec, ok := spec.(*ast.ValueSpec); ok {
b.add(spec)
}
}
}
case *ast.LabeledStmt:
label = b.labeledBlock(s.Label)
b.jump(label._goto)
b.current = label._goto
_s = s.Stmt
goto start // effectively: tailcall stmt(g, s.Stmt, label)
case *ast.ReturnStmt:
b.add(s)
b.current = b.newUnreachableBlock("unreachable.return")
case *ast.BranchStmt:
var block *Block
switch s.Tok {
case token.BREAK:
if s.Label != nil {
if lb := b.labeledBlock(s.Label); lb != nil {
block = lb._break
}
} else {
for t := b.targets; t != nil && block == nil; t = t.tail {
block = t._break
}
}
case token.CONTINUE:
if s.Label != nil {
if lb := b.labeledBlock(s.Label); lb != nil {
block = lb._continue
}
} else {
for t := b.targets; t != nil && block == nil; t = t.tail {
block = t._continue
}
}
case token.FALLTHROUGH:
for t := b.targets; t != nil; t = t.tail {
block = t._fallthrough
}
case token.GOTO:
if s.Label != nil {
block = b.labeledBlock(s.Label)._goto
}
}
if block == nil {
block = b.newBlock("undefined.branch")
}
b.jump(block)
b.current = b.newUnreachableBlock("unreachable.branch")
case *ast.BlockStmt:
b.stmtList(s.List)
case *ast.IfStmt:
if s.Init != nil {
b.stmt(s.Init)
}
then := b.newBlock("if.then")
done := b.newBlock("if.done")
_else := done
if s.Else != nil {
_else = b.newBlock("if.else")
}
b.add(s.Cond)
b.ifelse(then, _else)
b.current = then
b.stmt(s.Body)
b.jump(done)
if s.Else != nil {
b.current = _else
b.stmt(s.Else)
b.jump(done)
}
b.current = done
case *ast.SwitchStmt:
b.switchStmt(s, label)
case *ast.TypeSwitchStmt:
b.typeSwitchStmt(s, label)
case *ast.SelectStmt:
b.selectStmt(s, label)
case *ast.ForStmt:
b.forStmt(s, label)
case *ast.RangeStmt:
b.rangeStmt(s, label)
default:
panic(fmt.Sprintf("unexpected statement kind: %T", s))
}
}
func (b *builder) stmtList(list []ast.Stmt) {
for _, s := range list {
b.stmt(s)
}
}
func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
if s.Init != nil {
b.stmt(s.Init)
}
if s.Tag != nil {
b.add(s.Tag)
}
done := b.newBlock("switch.done")
if label != nil {
label._break = done
}
// We pull the default case (if present) down to the end.
// But each fallthrough label must point to the next
// body block in source order, so we preallocate a
// body block (fallthru) for the next case.
// Unfortunately this makes for a confusing block order.
var defaultBody *[]ast.Stmt
var defaultFallthrough *Block
var fallthru, defaultBlock *Block
ncases := len(s.Body.List)
for i, clause := range s.Body.List {
body := fallthru
if body == nil {
body = b.newBlock("switch.body") // first case only
}
// Preallocate body block for the next case.
fallthru = done
if i+1 < ncases {
fallthru = b.newBlock("switch.body")
}
cc := clause.(*ast.CaseClause)
if cc.List == nil {
// Default case.
defaultBody = &cc.Body
defaultFallthrough = fallthru
defaultBlock = body
continue
}
var nextCond *Block
for _, cond := range cc.List {
nextCond = b.newBlock("switch.next")
b.add(cond) // one half of the tag==cond condition
b.ifelse(body, nextCond)
b.current = nextCond
}
b.current = body
b.targets = &targets{
tail: b.targets,
_break: done,
_fallthrough: fallthru,
}
b.stmtList(cc.Body)
b.targets = b.targets.tail
b.jump(done)
b.current = nextCond
}
if defaultBlock != nil {
b.jump(defaultBlock)
b.current = defaultBlock
b.targets = &targets{
tail: b.targets,
_break: done,
_fallthrough: defaultFallthrough,
}
b.stmtList(*defaultBody)
b.targets = b.targets.tail
}
b.jump(done)
b.current = done
}
func (b *builder) typeSwitchStmt(s *ast.TypeSwitchStmt, label *lblock) {
if s.Init != nil {
b.stmt(s.Init)
}
if s.Assign != nil {
b.add(s.Assign)
}
done := b.newBlock("typeswitch.done")
if label != nil {
label._break = done
}
var default_ *ast.CaseClause
for _, clause := range s.Body.List {
cc := clause.(*ast.CaseClause)
if cc.List == nil {
default_ = cc
continue
}
body := b.newBlock("typeswitch.body")
var next *Block
for _, casetype := range cc.List {
next = b.newBlock("typeswitch.next")
// casetype is a type, so don't call b.add(casetype).
// This block logically contains a type assertion,
// x.(casetype), but it's unclear how to represent x.
_ = casetype
b.ifelse(body, next)
b.current = next
}
b.current = body
b.typeCaseBody(cc, done)
b.current = next
}
if default_ != nil {
b.typeCaseBody(default_, done)
} else {
b.jump(done)
}
b.current = done
}
func (b *builder) typeCaseBody(cc *ast.CaseClause, done *Block) {
b.targets = &targets{
tail: b.targets,
_break: done,
}
b.stmtList(cc.Body)
b.targets = b.targets.tail
b.jump(done)
}
func (b *builder) selectStmt(s *ast.SelectStmt, label *lblock) {
// First evaluate channel expressions.
// TODO(adonovan): fix: evaluate only channel exprs here.
for _, clause := range s.Body.List {
if comm := clause.(*ast.CommClause).Comm; comm != nil {
b.stmt(comm)
}
}
done := b.newBlock("select.done")
if label != nil {
label._break = done
}
var defaultBody *[]ast.Stmt
for _, cc := range s.Body.List {
clause := cc.(*ast.CommClause)
if clause.Comm == nil {
defaultBody = &clause.Body
continue
}
body := b.newBlock("select.body")
next := b.newBlock("select.next")
b.ifelse(body, next)
b.current = body
b.targets = &targets{
tail: b.targets,
_break: done,
}
switch comm := clause.Comm.(type) {
case *ast.ExprStmt: // <-ch
// nop
case *ast.AssignStmt: // x := <-states[state].Chan
b.add(comm.Lhs[0])
}
b.stmtList(clause.Body)
b.targets = b.targets.tail
b.jump(done)
b.current = next
}
if defaultBody != nil {
b.targets = &targets{
tail: b.targets,
_break: done,
}
b.stmtList(*defaultBody)
b.targets = b.targets.tail
b.jump(done)
}
b.current = done
}
func (b *builder) forStmt(s *ast.ForStmt, label *lblock) {
// ...init...
// jump loop
// loop:
// if cond goto body else done
// body:
// ...body...
// jump post
// post: (target of continue)
// ...post...
// jump loop
// done: (target of break)
if s.Init != nil {
b.stmt(s.Init)
}
body := b.newBlock("for.body")
done := b.newBlock("for.done") // target of 'break'
loop := body // target of back-edge
if s.Cond != nil {
loop = b.newBlock("for.loop")
}
cont := loop // target of 'continue'
if s.Post != nil {
cont = b.newBlock("for.post")
}
if label != nil {
label._break = done
label._continue = cont
}
b.jump(loop)
b.current = loop
if loop != body {
b.add(s.Cond)
b.ifelse(body, done)
b.current = body
}
b.targets = &targets{
tail: b.targets,
_break: done,
_continue: cont,
}
b.stmt(s.Body)
b.targets = b.targets.tail
b.jump(cont)
if s.Post != nil {
b.current = cont
b.stmt(s.Post)
b.jump(loop) // back-edge
}
b.current = done
}
func (b *builder) rangeStmt(s *ast.RangeStmt, label *lblock) {
b.add(s.X)
if s.Key != nil {
b.add(s.Key)
}
if s.Value != nil {
b.add(s.Value)
}
// ...
// loop: (target of continue)
// if ... goto body else done
// body:
// ...
// jump loop
// done: (target of break)
loop := b.newBlock("range.loop")
b.jump(loop)
b.current = loop
body := b.newBlock("range.body")
done := b.newBlock("range.done")
b.ifelse(body, done)
b.current = body
if label != nil {
label._break = done
label._continue = loop
}
b.targets = &targets{
tail: b.targets,
_break: done,
_continue: loop,
}
b.stmt(s.Body)
b.targets = b.targets.tail
b.jump(loop) // back-edge
b.current = done
}
// -------- helpers --------
// Destinations associated with unlabeled for/switch/select stmts.
// We push/pop one of these as we enter/leave each construct and for
// each BranchStmt we scan for the innermost target of the right type.
//
type targets struct {
tail *targets // rest of stack
_break *Block
_continue *Block
_fallthrough *Block
}
// Destinations associated with a labeled block.
// We populate these as labels are encountered in forward gotos or
// labeled statements.
//
type lblock struct {
_goto *Block
_break *Block
_continue *Block
}
// labeledBlock returns the branch target associated with the
// specified label, creating it if needed.
//
func (b *builder) labeledBlock(label *ast.Ident) *lblock {
lb := b.lblocks[label.Obj]
if lb == nil {
lb = &lblock{_goto: b.newBlock(label.Name)}
if b.lblocks == nil {
b.lblocks = make(map[*ast.Object]*lblock)
}
b.lblocks[label.Obj] = lb
}
return lb
}
// newBlock appends a new unconnected basic block to b.cfg's block
// slice and returns it.
// It does not automatically become the current block.
// comment is an optional string for more readable debugging output.
func (b *builder) newBlock(comment string) *Block {
g := b.cfg
block := &Block{
index: int32(len(g.Blocks)),
comment: comment,
}
block.Succs = block.succs2[:0]
g.Blocks = append(g.Blocks, block)
return block
}
func (b *builder) newUnreachableBlock(comment string) *Block {
block := b.newBlock(comment)
block.unreachable = true
return block
}
func (b *builder) add(n ast.Node) {
b.current.Nodes = append(b.current.Nodes, n)
}
// jump adds an edge from the current block to the target block,
// and sets b.current to nil.
func (b *builder) jump(target *Block) {
b.current.Succs = append(b.current.Succs, target)
b.current = nil
}
// ifelse emits edges from the current block to the t and f blocks,
// and sets b.current to nil.
func (b *builder) ifelse(t, f *Block) {
b.current.Succs = append(b.current.Succs, t, f)
b.current = nil
}
// Copyright 2016 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 package constructs a simple control-flow graph (CFG) of the
// statements and expressions within a single function.
//
// Use cfg.New to construct the CFG for a function body.
//
// The blocks of the CFG contain all the function's non-control
// statements. The CFG does not contain control statements such as If,
// Switch, Select, and Branch, but does contain their subexpressions.
// For example, this source code:
//
// if x := f(); x != nil {
// T()
// } else {
// F()
// }
//
// produces this CFG:
//
// 1: x := f()
// x != nil
// succs: 2, 3
// 2: T()
// succs: 4
// 3: F()
// succs: 4
// 4:
//
// The CFG does contain Return statements; even implicit returns are
// materialized (at the position of the function's closing brace).
//
// The CFG does not record conditions associated with conditional branch
// edges, nor the short-circuit semantics of the && and || operators,
// nor abnormal control flow caused by panic. If you need this
// information, use golang.org/x/tools/go/ssa instead.
//
package cfg
// Although the vet tool has type information, it is often extremely
// fragmentary, so for simplicity this package does not depend on
// go/types. Consequently control-flow conditions are ignored even
// when constant, and "mayReturn" information must be provided by the
// client.
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/token"
)
// A CFG represents the control-flow graph of a single function.
//
// The entry point is Blocks[0]; there may be multiple return blocks.
type CFG struct {
Blocks []*Block // block[0] is entry; order otherwise undefined
}
// A Block represents a basic block: a list of statements and
// expressions that are always evaluated sequentially.
//
// A block may have 0-2 successors: zero for a return block or a block
// that calls a function such as panic that never returns; one for a
// normal (jump) block; and two for a conditional (if) block.
type Block struct {
Nodes []ast.Node // statements, expressions, and ValueSpecs
Succs []*Block // successor nodes in the graph
comment string // for debugging
index int32 // index within CFG.Blocks
unreachable bool // is block of stmts following return/panic/for{}
succs2 [2]*Block // underlying array for Succs
}
// New returns a new control-flow graph for the specified function body,
// which must be non-nil.
//
// The CFG builder calls mayReturn to determine whether a given function
// call may return. For example, calls to panic, os.Exit, and log.Fatal
// do not return, so the builder can remove infeasible graph edges
// following such calls. The builder calls mayReturn only for a
// CallExpr beneath an ExprStmt.
func New(body *ast.BlockStmt, mayReturn func(*ast.CallExpr) bool) *CFG {
b := builder{
mayReturn: mayReturn,
cfg: new(CFG),
}
b.current = b.newBlock("entry")
b.stmt(body)
// Does control fall off the end of the function's body?
// Make implicit return explicit.
if b.current != nil && !b.current.unreachable {
b.add(&ast.ReturnStmt{
Return: body.End() - 1,
})
}
return b.cfg
}
func (b *Block) String() string {
return fmt.Sprintf("block %d (%s)", b.index, b.comment)
}
// Return returns the return statement at the end of this block if present, nil otherwise.
func (b *Block) Return() (ret *ast.ReturnStmt) {
if len(b.Nodes) > 0 {
ret, _ = b.Nodes[len(b.Nodes)-1].(*ast.ReturnStmt)
}
return
}
// Format formats the control-flow graph for ease of debugging.
func (g *CFG) Format(fset *token.FileSet) string {
var buf bytes.Buffer
for _, b := range g.Blocks {
fmt.Fprintf(&buf, ".%d: # %s\n", b.index, b.comment)
for _, n := range b.Nodes {
fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n))
}
if len(b.Succs) > 0 {
fmt.Fprintf(&buf, "\tsuccs:")
for _, succ := range b.Succs {
fmt.Fprintf(&buf, " %d", succ.index)
}
buf.WriteByte('\n')
}
buf.WriteByte('\n')
}
return buf.String()
}
func formatNode(fset *token.FileSet, n ast.Node) string {
var buf bytes.Buffer
format.Node(&buf, fset, n)
// Indent secondary lines by a tab.
return string(bytes.Replace(buf.Bytes(), []byte("\n"), []byte("\n\t"), -1))
}
// Copyright 2016 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 cfg
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/token"
"testing"
)
const src = `package main
import "log"
func f1() {
live()
return
dead()
}
func f2() {
for {
live()
}
dead()
}
func f3() {
if true { // even known values are ignored
return
}
for true { // even known values are ignored
live()
}
for {
live()
}
dead()
}
func f4(x int) {
switch x {
case 1:
live()
fallthrough
case 2:
live()
log.Fatal()
default:
panic("oops")
}
dead()
}
func f4(ch chan int) {
select {
case <-ch:
live()
return
default:
live()
panic("oops")
}
dead()
}
func f5(unknown bool) {
for {
if unknown {
break
}
continue
dead()
}
live()
}
func f6(unknown bool) {
outer:
for {
for {
break outer
dead()
}
dead()
}
live()
}
func f7() {
for {
break nosuchlabel
dead()
}
dead()
}
func f8() {
select{}
dead()
}
func f9(ch chan int) {
select {
case <-ch:
return
}
dead()
}
func f10(ch chan int) {
select {
case <-ch:
return
dead()
default:
}
live()
}
func f11() {
goto; // mustn't crash
dead()
}
`
func TestDeadCode(t *testing.T) {
// We'll use dead code detection to verify the CFG.
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "dummy.go", src, parser.Mode(0))
if err != nil {
t.Fatal(err)
}
for _, decl := range f.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok {
g := New(decl.Body, mayReturn)
// Mark blocks reachable from entry.
live := make(map[*Block]bool)
var visit func(*Block)
visit = func(b *Block) {
if !live[b] {
live[b] = true
for _, succ := range b.Succs {
visit(succ)
}
}
}
visit(g.Blocks[0])
// Print statements in unreachable blocks
// (in order determined by builder).
var buf bytes.Buffer
for _, b := range g.Blocks {
if !live[b] {
for _, n := range b.Nodes {
fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n))
}
}
}
// Check that the result contains "dead" at least once but not "live".
if !bytes.Contains(buf.Bytes(), []byte("dead")) ||
bytes.Contains(buf.Bytes(), []byte("live")) {
t.Errorf("unexpected dead statements in function %s:\n%s",
decl.Name.Name,
&buf)
t.Logf("control flow graph:\n%s", g.Format(fset))
}
}
}
}
// A trivial mayReturn predicate that looks only at syntax, not types.
func mayReturn(call *ast.CallExpr) bool {
switch fun := call.Fun.(type) {
case *ast.Ident:
return fun.Name != "panic"
case *ast.SelectorExpr:
return fun.Sel.Name != "Fatal"
}
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.
// Package whitelist defines exceptions for the vet tool.
package whitelist
// UnkeyedLiteral is a white list of types in the standard packages
// that are used with unkeyed literals we deem to be acceptable.
var UnkeyedLiteral = map[string]bool{
// 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.NYCbCrA": true,
"image/color.RGBA64": true,
"image/color.RGBA": true,
"image/color.YCbCr": true,
"image.Point": true,
"image.Rectangle": true,
"image.Uniform": true,
"unicode.Range16": true,
}
// Copyright 2016 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 (
"cmd/vet/internal/cfg"
"fmt"
"go/ast"
"go/types"
"strconv"
)
func init() {
register("lostcancel",
"check for failure to call cancelation function returned by context.WithCancel",
checkLostCancel,
funcDecl, funcLit)
}
const debugLostCancel = false
var contextPackage = "context"
// checkLostCancel reports a failure to the call the cancel function
// returned by context.WithCancel, either because the variable was
// assigned to the blank identifier, or because there exists a
// control-flow path from the call to a return statement and that path
// does not "use" the cancel function. Any reference to the variable
// counts as a use, even within a nested function literal.
//
// checkLostCancel analyzes a single named or literal function.
func checkLostCancel(f *File, node ast.Node) {
// Fast path: bypass check if file doesn't use context.WithCancel.
if !hasImport(f.file, contextPackage) {
return
}
// Maps each cancel variable to its defining ValueSpec/AssignStmt.
cancelvars := make(map[*types.Var]ast.Node)
// Find the set of cancel vars to analyze.
stack := make([]ast.Node, 0, 32)
ast.Inspect(node, func(n ast.Node) bool {
switch n.(type) {
case *ast.FuncLit:
if len(stack) > 0 {
return false // don't stray into nested functions
}
case nil:
stack = stack[:len(stack)-1] // pop
return true
}
stack = append(stack, n) // push
// Look for [{AssignStmt,ValueSpec} CallExpr SelectorExpr]:
//
// ctx, cancel := context.WithCancel(...)
// ctx, cancel = context.WithCancel(...)
// var ctx, cancel = context.WithCancel(...)
//
if isContextWithCancel(f, n) && isCall(stack[len(stack)-2]) {
var id *ast.Ident // id of cancel var
stmt := stack[len(stack)-3]
switch stmt := stmt.(type) {
case *ast.ValueSpec:
if len(stmt.Names) > 1 {
id = stmt.Names[1]
}
case *ast.AssignStmt:
if len(stmt.Lhs) > 1 {
id, _ = stmt.Lhs[1].(*ast.Ident)
}
}
if id != nil {
if id.Name == "_" {
f.Badf(id.Pos(), "the cancel function returned by context.%s should be called, not discarded, to avoid a context leak",
n.(*ast.SelectorExpr).Sel.Name)
} else if v, ok := f.pkg.uses[id].(*types.Var); ok {
cancelvars[v] = stmt
} else if v, ok := f.pkg.defs[id].(*types.Var); ok {
cancelvars[v] = stmt
}
}
}
return true
})
if len(cancelvars) == 0 {
return // no need to build CFG
}
// Tell the CFG builder which functions never return.
info := &types.Info{Uses: f.pkg.uses, Selections: f.pkg.selectors}
mayReturn := func(call *ast.CallExpr) bool {
name := callName(info, call)
return !noReturnFuncs[name]
}
// Build the CFG.
var g *cfg.CFG
var sig *types.Signature
switch node := node.(type) {
case *ast.FuncDecl:
obj := f.pkg.defs[node.Name]
if obj == nil {
return // type error (e.g. duplicate function declaration)
}
sig, _ = obj.Type().(*types.Signature)
g = cfg.New(node.Body, mayReturn)
case *ast.FuncLit:
sig, _ = f.pkg.types[node.Type].Type.(*types.Signature)
g = cfg.New(node.Body, mayReturn)
}
// Print CFG.
if debugLostCancel {
fmt.Println(g.Format(f.fset))
}
// Examine the CFG for each variable in turn.
// (It would be more efficient to analyze all cancelvars in a
// single pass over the AST, but seldom is there more than one.)
for v, stmt := range cancelvars {
if ret := lostCancelPath(f, g, v, stmt, sig); ret != nil {
lineno := f.fset.Position(stmt.Pos()).Line
f.Badf(stmt.Pos(), "the %s function is not used on all paths (possible context leak)", v.Name())
f.Badf(ret.Pos(), "this return statement may be reached without using the %s var defined on line %d", v.Name(), lineno)
}
}
}
func isCall(n ast.Node) bool { _, ok := n.(*ast.CallExpr); return ok }
func hasImport(f *ast.File, path string) bool {
for _, imp := range f.Imports {
v, _ := strconv.Unquote(imp.Path.Value)
if v == path {
return true
}
}
return false
}
// isContextWithCancel reports whether n is one of the qualified identifiers
// context.With{Cancel,Timeout,Deadline}.
func isContextWithCancel(f *File, n ast.Node) bool {
if sel, ok := n.(*ast.SelectorExpr); ok {
switch sel.Sel.Name {
case "WithCancel", "WithTimeout", "WithDeadline":
if x, ok := sel.X.(*ast.Ident); ok {
if pkgname, ok := f.pkg.uses[x].(*types.PkgName); ok {
return pkgname.Imported().Path() == contextPackage
}
// Import failed, so we can't check package path.
// Just check the local package name (heuristic).
return x.Name == "context"
}
}
}
return false
}
// lostCancelPath finds a path through the CFG, from stmt (which defines
// the 'cancel' variable v) to a return statement, that doesn't "use" v.
// If it finds one, it returns the return statement (which may be synthetic).
// sig is the function's type, if known.
func lostCancelPath(f *File, g *cfg.CFG, v *types.Var, stmt ast.Node, sig *types.Signature) *ast.ReturnStmt {
vIsNamedResult := sig != nil && tupleContains(sig.Results(), v)
// uses reports whether stmts contain a "use" of variable v.
uses := func(f *File, v *types.Var, stmts []ast.Node) bool {
found := false
for _, stmt := range stmts {
ast.Inspect(stmt, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.Ident:
if f.pkg.uses[n] == v {
found = true
}
case *ast.ReturnStmt:
// A naked return statement counts as a use
// of the named result variables.
if n.Results == nil && vIsNamedResult {
found = true
}
}
return !found
})
}
return found
}
// blockUses computes "uses" for each block, caching the result.
memo := make(map[*cfg.Block]bool)
blockUses := func(f *File, v *types.Var, b *cfg.Block) bool {
res, ok := memo[b]
if !ok {
res = uses(f, v, b.Nodes)
memo[b] = res
}
return res
}
// Find the var's defining block in the CFG,
// plus the rest of the statements of that block.
var defblock *cfg.Block
var rest []ast.Node
outer:
for _, b := range g.Blocks {
for i, n := range b.Nodes {
if n == stmt {
defblock = b
rest = b.Nodes[i+1:]
break outer
}
}
}
if defblock == nil {
panic("internal error: can't find defining block for cancel var")
}
// Is v "used" in the remainder of its defining block?
if uses(f, v, rest) {
return nil
}
// Does the defining block return without using v?
if ret := defblock.Return(); ret != nil {
return ret
}
// Search the CFG depth-first for a path, from defblock to a
// return block, in which v is never "used".
seen := make(map[*cfg.Block]bool)
var search func(blocks []*cfg.Block) *ast.ReturnStmt
search = func(blocks []*cfg.Block) *ast.ReturnStmt {
for _, b := range blocks {
if !seen[b] {
seen[b] = true
// Prune the search if the block uses v.
if blockUses(f, v, b) {
continue
}
// Found path to return statement?
if ret := b.Return(); ret != nil {
if debugLostCancel {
fmt.Printf("found path to return in block %s\n", b)
}
return ret // found
}
// Recur
if ret := search(b.Succs); ret != nil {
if debugLostCancel {
fmt.Printf(" from block %s\n", b)
}
return ret
}
}
}
return nil
}
return search(defblock.Succs)
}
func tupleContains(tuple *types.Tuple, v *types.Var) bool {
for i := 0; i < tuple.Len(); i++ {
if tuple.At(i) == v {
return true
}
}
return false
}
var noReturnFuncs = map[string]bool{
"(*testing.common).FailNow": true,
"(*testing.common).Fatal": true,
"(*testing.common).Fatalf": true,
"(*testing.common).Skip": true,
"(*testing.common).SkipNow": true,
"(*testing.common).Skipf": true,
"log.Fatal": true,
"log.Fatalf": true,
"log.Fatalln": true,
"os.Exit": true,
"panic": true,
"runtime.Goexit": true,
}
// callName returns the canonical name of the builtin, method, or
// function called by call, if known.
func callName(info *types.Info, call *ast.CallExpr) string {
switch fun := call.Fun.(type) {
case *ast.Ident:
// builtin, e.g. "panic"
if obj, ok := info.Uses[fun].(*types.Builtin); ok {
return obj.Name()
}
case *ast.SelectorExpr:
if sel, ok := info.Selections[fun]; ok && sel.Kind() == types.MethodVal {
// method call, e.g. "(*testing.common).Fatal"
meth := sel.Obj()
return fmt.Sprintf("(%s).%s",
meth.Type().(*types.Signature).Recv().Type(),
meth.Name())
}
if obj, ok := info.Uses[fun.Sel]; ok {
// qualified identifier, e.g. "os.Exit"
return fmt.Sprintf("%s.%s",
obj.Pkg().Path(),
obj.Name())
}
}
// function with no name, or defined in missing imported package
return ""
}
// 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.
// The vet command is a driver for static checkers conforming to
// the golang.org/x/tools/go/analysis API. Run it using 'go vet'.
//
// For a tool capable of running standalone, use a multichecker-based
// tool such as golang.org/x/tools/go/analysis/cmd/vet.
package main
import (
"bytes"
"encoding/gob"
"encoding/json"
"flag"
"fmt"
"go/ast"
"go/build"
"go/importer"
"go/parser"
"go/printer"
"go/token"
"go/types"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"cmd/internal/objabi"
)
var (
verbose = flag.Bool("v", false, "verbose")
flags = flag.Bool("flags", false, "print flags in JSON")
source = flag.Bool("source", false, "import from source instead of compiled object files")
tags = flag.String("tags", "", "space-separated list of build tags to apply when parsing")
tagList = []string{} // exploded version of tags flag; set in main
vcfg vetConfig
mustTypecheck bool
)
var exitCode = 0
// "-all" flag enables all non-experimental checks
var all = triStateFlag("all", unset, "enable all non-experimental checks")
// 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
// dirsRun and filesRun indicate whether the vet is applied to directory or
// file targets. The distinction affects which checks are run.
var dirsRun, filesRun bool
// includesNonTest indicates whether the vet is applied to non-test targets.
// Certain checks are relevant only if they touch both test and non-test files.
var includesNonTest bool
// 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
"golang.org/x/tools/go/analysis/unitchecker"
"golang.org/x/tools/go/analysis/passes/asmdecl"
"golang.org/x/tools/go/analysis/passes/assign"
"golang.org/x/tools/go/analysis/passes/atomic"
"golang.org/x/tools/go/analysis/passes/bools"
"golang.org/x/tools/go/analysis/passes/buildtag"
"golang.org/x/tools/go/analysis/passes/cgocall"
"golang.org/x/tools/go/analysis/passes/composite"
"golang.org/x/tools/go/analysis/passes/copylock"
"golang.org/x/tools/go/analysis/passes/httpresponse"
"golang.org/x/tools/go/analysis/passes/loopclosure"
"golang.org/x/tools/go/analysis/passes/lostcancel"
"golang.org/x/tools/go/analysis/passes/nilfunc"
"golang.org/x/tools/go/analysis/passes/pkgfact"
"golang.org/x/tools/go/analysis/passes/printf"
"golang.org/x/tools/go/analysis/passes/shift"
"golang.org/x/tools/go/analysis/passes/stdmethods"
"golang.org/x/tools/go/analysis/passes/structtag"
"golang.org/x/tools/go/analysis/passes/tests"
"golang.org/x/tools/go/analysis/passes/unmarshal"
"golang.org/x/tools/go/analysis/passes/unreachable"
"golang.org/x/tools/go/analysis/passes/unsafeptr"
"golang.org/x/tools/go/analysis/passes/unusedresult"
)
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 "true" // An unset flag will be set by -all, so defaults to true.
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 {
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
forStmt *ast.ForStmt
funcDecl *ast.FuncDecl
funcLit *ast.FuncLit
genDecl *ast.GenDecl
interfaceType *ast.InterfaceType
rangeStmt *ast.RangeStmt
returnStmt *ast.ReturnStmt
structType *ast.StructType
// 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))
pkgCheckers = make(map[string]func(*Package))
exporters = make(map[string]func() interface{})
)
// The exporters data as written to the vetx output file.
type vetxExport struct {
Name string
Data interface{}
}
// Vet can provide its own "export information"
// about package A to future invocations of vet
// on packages importing A. If B imports A,
// then running "go vet B" actually invokes vet twice:
// first, it runs vet on A, in "vetx-only" mode, which
// skips most checks and only computes export data
// describing A. Then it runs vet on B, making A's vetx
// data available for consultation. The vet of B
// computes vetx data for B in addition to its
// usual vet checks.
// register registers the named check function,
// to be called with AST nodes of the given types.
// The registered functions are not called in vetx-only mode.
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
}
}
// registerPkgCheck registers a package-level checking function,
// to be invoked with the whole package being vetted
// before any of the per-node handlers.
// The registered function fn is called even in vetx-only mode
// (see comment above), so fn must take care not to report
// errors when vcfg.VetxOnly is true.
func registerPkgCheck(name string, fn func(*Package)) {
pkgCheckers[name] = fn
}
// registerExport registers a function to return vetx export data
// that should be saved and provided to future invocations of vet
// when checking packages importing this one.
// The value returned by fn should be nil or else valid to encode using gob.
// Typically a registerExport call is paired with a call to gob.Register.
func registerExport(name string, fn func() interface{}) {
exporters[name] = fn
}
// Usage is a replacement usage function for the flags package.
func Usage() {
fmt.Fprintf(os.Stderr, "Usage of vet:\n")
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, "By default, -all is set and all non-experimental checks are run.\n")
fmt.Fprintf(os.Stderr, "For more information run\n")
fmt.Fprintf(os.Stderr, "\tgo doc 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
// Parsed package "foo" when checking package "foo_test"
basePkg *Package
// The keys are the objects that are receivers of a "String()
// string" method. The value reports whether the method has a
// pointer receiver.
// This is used by the recursiveStringer method in print.go.
stringerPtrs map[*ast.Object]bool
// Registered checkers to run.
checkers map[ast.Node][]func(*File, ast.Node)
// Unreachable nodes; can be ignored in shift check.
dead map[ast.Node]bool
}
// Legacy vet had the concept of "experimental" checkers.
// There was exactly one, shadow, and it had to be explicitly
// enabled by the -shadow flag, which would of course disable
// all the other tristate flags, requiring the -all flag (which
// is now a no-op) to reenable them.
//
// The shadow analyzer has been removed from the suite,
// but can be run using these additional commands:
// $ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
// $ go vet -vettool=$(which shadow)
// Alternatively, one could build a multichecker containing all
// the desired checks (vet's suite + shadow) and run it in a
// single "go vet" command.
func main() {
objabi.AddVersionFlag()
flag.Usage = Usage
flag.Parse()
// -flags: print flags as JSON. Used by go vet.
if *flags {
type jsonFlag struct {
Name string
Bool bool
Usage string
}
var jsonFlags []jsonFlag
flag.VisitAll(func(f *flag.Flag) {
isBool := false
switch v := f.Value.(type) {
case interface{ BoolFlag() bool }:
isBool = v.BoolFlag()
case *triState:
isBool = true // go vet should treat it as boolean
}
jsonFlags = append(jsonFlags, jsonFlag{f.Name, isBool, f.Usage})
})
data, err := json.MarshalIndent(jsonFlags, "", "\t")
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(data)
os.Exit(0)
}
// If any flag is set, we run only those checks requested.
// If all flag is set true or if no flags are set true, set all the non-experimental ones
// not explicitly set (in effect, set the "-all" flag).
if setTrueCount == 0 || *all == setTrue {
for name, setting := range report {
if *setting == unset && !experimental[name] {
*setting = setTrue
}
}
}
// Accept space-separated tags because that matches
// the go command's other subcommands.
// Accept commas because go tool vet traditionally has.
tagList = strings.Fields(strings.ReplaceAll(*tags, ",", " "))
initPrintFlags()
initUnusedFlags()
if flag.NArg() == 0 {
Usage()
}
// Special case for "go vet" passing an explicit configuration:
// single argument ending in vet.cfg.
// Once we have a more general mechanism for obtaining this
// information from build tools like the go command,
// vet should be changed to use it. This vet.cfg hack is an
// experiment to learn about what form that information should take.
if flag.NArg() == 1 && strings.HasSuffix(flag.Arg(0), "vet.cfg") {
doPackageCfg(flag.Arg(0))
os.Exit(exitCode)
}
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() {
dirsRun = true
} else {
filesRun = true
if !strings.HasSuffix(name, "_test.go") {
includesNonTest = true
}
}
}
if dirsRun && filesRun {
Usage()
}
if dirsRun {
for _, name := range flag.Args() {
walkDir(name)
}
os.Exit(exitCode)
}
if doPackage(flag.Args(), nil) == nil {
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)
}
}
}
// vetConfig is the JSON config struct prepared by the Go command.
type vetConfig struct {
Compiler string
Dir string
ImportPath string
GoFiles []string
NonGoFiles []string
ImportMap map[string]string
PackageFile map[string]string
Standard map[string]bool
PackageVetx map[string]string // map from import path to vetx data file
VetxOnly bool // only compute vetx output; don't run ordinary checks
VetxOutput string // file where vetx output should be written
SucceedOnTypecheckFailure bool
imp types.Importer
}
func (v *vetConfig) Import(path string) (*types.Package, error) {
if v.imp == nil {
v.imp = importer.For(v.Compiler, v.openPackageFile)
}
if path == "unsafe" {
return v.imp.Import("unsafe")
}
p := v.ImportMap[path]
if p == "" {
return nil, fmt.Errorf("unknown import path %q", path)
}
if v.PackageFile[p] == "" {
if v.Compiler == "gccgo" && v.Standard[path] {
// gccgo doesn't have sources for standard library packages,
// but the importer will do the right thing.
return v.imp.Import(path)
}
return nil, fmt.Errorf("unknown package file for import %q", path)
}
return v.imp.Import(p)
}
func (v *vetConfig) openPackageFile(path string) (io.ReadCloser, error) {
file := v.PackageFile[path]
if file == "" {
if v.Compiler == "gccgo" && v.Standard[path] {
// The importer knows how to handle this.
return nil, nil
}
// Note that path here has been translated via v.ImportMap,
// unlike in the error in Import above. We prefer the error in
// Import, but it's worth diagnosing this one too, just in case.
return nil, fmt.Errorf("unknown package file for %q", path)
}
f, err := os.Open(file)
if err != nil {
return nil, err
}
return f, nil
}
// doPackageCfg analyzes a single package described in a config file.
func doPackageCfg(cfgFile string) {
js, err := ioutil.ReadFile(cfgFile)
if err != nil {
errorf("%v", err)
}
if err := json.Unmarshal(js, &vcfg); err != nil {
errorf("parsing vet config %s: %v", cfgFile, err)
}
stdImporter = &vcfg
inittypes()
mustTypecheck = true
var allFiles []string
allFiles = append(allFiles, vcfg.GoFiles...)
allFiles = append(allFiles, vcfg.NonGoFiles...)
doPackage(allFiles, nil)
if vcfg.VetxOutput != "" {
out := make([]vetxExport, 0, len(exporters))
for name, fn := range exporters {
out = append(out, vetxExport{
Name: name,
Data: fn(),
})
}
// Sort the data so that it is consistent across builds.
sort.Slice(out, func(i, j int) bool {
return out[i].Name < out[j].Name
})
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(out); err != nil {
errorf("encoding vet output: %v", err)
return
}
if err := ioutil.WriteFile(vcfg.VetxOutput, buf.Bytes(), 0666); err != nil {
errorf("saving vet output: %v", err)
return
}
}
}
// 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)
basePkg := doPackage(names, nil)
// 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(names, basePkg)
}
}
type Package struct {
path string
defs map[*ast.Ident]types.Object
uses map[*ast.Ident]types.Object
implicits map[ast.Node]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 the parsed Package or nil if none of the files have been checked.
func doPackage(names []string, basePkg *Package) *Package {
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 nil
}
var parsedFile *ast.File
if strings.HasSuffix(name, ".go") {
parsedFile, err = parser.ParseFile(fs, name, data, parser.ParseComments)
if err != nil {
warnf("%s: %s", name, err)
return nil
}
astFiles = append(astFiles, parsedFile)
}
file := &File{
fset: fs,
content: data,
name: name,
file: parsedFile,
dead: make(map[ast.Node]bool),
}
files = append(files, file)
}
if len(astFiles) == 0 {
return nil
}
pkg := new(Package)
pkg.path = astFiles[0].Name.Name
pkg.files = files
// Type check the package.
errs := pkg.check(fs, astFiles)
if errs != nil {
if vcfg.SucceedOnTypecheckFailure {
os.Exit(0)
}
if *verbose || mustTypecheck {
for _, err := range errs {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
if mustTypecheck {
// This message could be silenced, and we could just exit,
// but it might be helpful at least at first to make clear that the
// above errors are coming from vet and not the compiler
// (they often look like compiler errors, such as "declared but not used").
errorf("typecheck failures")
}
}
}
// Check.
for _, file := range files {
file.pkg = pkg
file.basePkg = basePkg
}
for name, fn := range pkgCheckers {
if vet(name) {
fn(pkg)
}
}
if vcfg.VetxOnly {
return pkg
}
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 {
checkBuildTag(file)
file.checkers = chk
if file.file != nil {
file.walkFile(file.name, file.file)
}
}
return pkg
}
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)
}
// locPrefix returns a formatted representation of the position for use as a line prefix.
func (f *File) locPrefix(pos token.Pos) string {
if pos == token.NoPos {
return ""
}
return fmt.Sprintf("%s: ", f.loc(pos))
}
// Warn reports an error but does not set the exit code.
func (f *File) Warn(pos token.Pos, args ...interface{}) {
fmt.Fprintf(os.Stderr, "%s%s", f.locPrefix(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, "%s%s\n", f.locPrefix(pos), fmt.Sprintf(format, 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 {
f.updateDead(node)
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.ForStmt:
key = forStmt
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
case *ast.ReturnStmt:
key = returnStmt
case *ast.StructType:
key = structType
}
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()
}
// imported[path][key] is previously written export data.
var imported = make(map[string]map[string]interface{})
// readVetx reads export data written by a previous
// invocation of vet on an imported package (path).
// The key is the name passed to registerExport
// when the data was originally generated.
// readVetx returns nil if the data is unavailable.
func readVetx(path, key string) interface{} {
if path == "unsafe" || vcfg.ImportPath == "" {
return nil
}
m := imported[path]
if m == nil {
file := vcfg.PackageVetx[path]
if file == "" {
return nil
}
data, err := ioutil.ReadFile(file)
if err != nil {
return nil
}
var out []vetxExport
err = gob.NewDecoder(bytes.NewReader(data)).Decode(&out)
if err != nil {
return nil
}
m = make(map[string]interface{})
for _, x := range out {
m[x.Name] = x.Data
}
imported[path] = m
}
return m[key]
unitchecker.Main(
asmdecl.Analyzer,
assign.Analyzer,
atomic.Analyzer,
bools.Analyzer,
buildtag.Analyzer,
cgocall.Analyzer,
composite.Analyzer,
copylock.Analyzer,
httpresponse.Analyzer,
loopclosure.Analyzer,
lostcancel.Analyzer,
nilfunc.Analyzer,
pkgfact.Analyzer,
printf.Analyzer,
shift.Analyzer,
stdmethods.Analyzer,
structtag.Analyzer,
tests.Analyzer,
unmarshal.Analyzer,
unreachable.Analyzer,
unsafeptr.Analyzer,
unusedresult.Analyzer,
)
}
// 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 (
"go/ast"
"go/types"
"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
"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)
}
case *ast.InterfaceType:
for _, field := range n.Methods.List {
for _, id := range field.Names {
canonicalMethod(f, id)
}
}
}
}
func canonicalMethod(f *File, id *ast.Ident) {
// Expected input/output.
expect, ok := canonicalMethods[id.Name]
if !ok {
return
}
sign := f.pkg.defs[id].Type().(*types.Signature)
args := sign.Params()
results := sign.Results()
// 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) + ")"
}
actual := sign.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, ", ")
}
// Does each type in expect with the given prefix match the corresponding type in actual?
func (f *File) matchParams(expect []string, actual *types.Tuple, prefix string) bool {
for i, x := range expect {
if !strings.HasPrefix(x, prefix) {
continue
}
if i >= actual.Len() {
return false
}
if !f.matchParamType(x, actual.At(i).Type()) {
return false
}
}
if prefix == "" && actual.Len() > len(expect) {
return false
}
return true
}
// Does this one type match?
func (f *File) matchParamType(expect string, actual types.Type) bool {
expect = strings.TrimPrefix(expect, "=")
// 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.
return actual.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"
"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"
"encoding/gob"
"flag"
"fmt"
"go/ast"
"go/constant"
"go/token"
"go/types"
"regexp"
"sort"
"strconv"
"strings"
"unicode/utf8"
)
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)
registerPkgCheck("printf", findPrintfLike)
registerExport("printf", exportPrintfLike)
gob.Register([]printfExport(nil))
}
func initPrintFlags() {
if *printfuncs == "" {
return
}
for _, name := range strings.Split(*printfuncs, ",") {
if len(name) == 0 {
flag.Usage()
}
// Backwards compatibility: skip optional first argument
// index after the colon.
if colon := strings.LastIndex(name, ":"); colon > 0 {
name = name[:colon]
}
if !strings.Contains(name, ".") {
name = strings.ToLower(name)
}
isPrint[name] = true
}
}
var localPrintfLike = make(map[string]int)
type printfExport struct {
Name string
Kind int
}
// printfImported maps from package name to the printf vet data
// exported by that package.
var printfImported = make(map[string]map[string]int)
type printfWrapper struct {
name string
fn *ast.FuncDecl
format *ast.Field
args *ast.Field
callers []printfCaller
failed bool // if true, not a printf wrapper
}
type printfCaller struct {
w *printfWrapper
call *ast.CallExpr
}
// maybePrintfWrapper decides whether decl (a declared function) may be a wrapper
// around a fmt.Printf or fmt.Print function. If so it returns a printfWrapper
// function describing the declaration. Later processing will analyze the
// graph of potential printf wrappers to pick out the ones that are true wrappers.
// A function may be a Printf or Print wrapper if its last argument is ...interface{}.
// If the next-to-last argument is a string, then this may be a Printf wrapper.
// Otherwise it may be a Print wrapper.
func maybePrintfWrapper(decl ast.Decl) *printfWrapper {
// Look for functions with final argument type ...interface{}.
fn, ok := decl.(*ast.FuncDecl)
if !ok || fn.Body == nil {
return nil
}
name := fn.Name.Name
if fn.Recv != nil {
// For (*T).Name or T.name, use "T.name".
rcvr := fn.Recv.List[0].Type
if ptr, ok := rcvr.(*ast.StarExpr); ok {
rcvr = ptr.X
}
id, ok := rcvr.(*ast.Ident)
if !ok {
return nil
}
name = id.Name + "." + name
}
params := fn.Type.Params.List
if len(params) == 0 {
return nil
}
args := params[len(params)-1]
if len(args.Names) != 1 {
return nil
}
ddd, ok := args.Type.(*ast.Ellipsis)
if !ok {
return nil
}
iface, ok := ddd.Elt.(*ast.InterfaceType)
if !ok || len(iface.Methods.List) > 0 {
return nil
}
var format *ast.Field
if len(params) >= 2 {
p := params[len(params)-2]
if len(p.Names) == 1 {
if id, ok := p.Type.(*ast.Ident); ok && id.Name == "string" {
format = p
}
}
}
return &printfWrapper{
name: name,
fn: fn,
format: format,
args: args,
}
}
// findPrintfLike scans the entire package to find printf-like functions.
func findPrintfLike(pkg *Package) {
if vcfg.ImportPath == "" { // no type or vetx information; don't bother
return
}
// Gather potential wrappesr and call graph between them.
byName := make(map[string]*printfWrapper)
var wrappers []*printfWrapper
for _, file := range pkg.files {
if file.file == nil {
continue
}
for _, decl := range file.file.Decls {
w := maybePrintfWrapper(decl)
if w == nil {
continue
}
byName[w.name] = w
wrappers = append(wrappers, w)
}
}
// Walk the graph to figure out which are really printf wrappers.
for _, w := range wrappers {
// Scan function for calls that could be to other printf-like functions.
ast.Inspect(w.fn.Body, func(n ast.Node) bool {
if w.failed {
return false
}
// TODO: Relax these checks; issue 26555.
if assign, ok := n.(*ast.AssignStmt); ok {
for _, lhs := range assign.Lhs {
if match(lhs, w.format) || match(lhs, w.args) {
// Modifies the format
// string or args in
// some way, so not a
// simple wrapper.
w.failed = true
return false
}
}
}
if un, ok := n.(*ast.UnaryExpr); ok && un.Op == token.AND {
if match(un.X, w.format) || match(un.X, w.args) {
// Taking the address of the
// format string or args,
// so not a simple wrapper.
w.failed = true
return false
}
}
call, ok := n.(*ast.CallExpr)
if !ok || len(call.Args) == 0 || !match(call.Args[len(call.Args)-1], w.args) {
return true
}
pkgpath, name, kind := printfNameAndKind(pkg, call.Fun)
if kind != 0 {
checkPrintfFwd(pkg, w, call, kind)
return true
}
// If the call is to another function in this package,
// maybe we will find out it is printf-like later.
// Remember this call for later checking.
if pkgpath == "" && byName[name] != nil {
callee := byName[name]
callee.callers = append(callee.callers, printfCaller{w, call})
}
return true
})
}
}
func match(arg ast.Expr, param *ast.Field) bool {
id, ok := arg.(*ast.Ident)
return ok && id.Obj != nil && id.Obj.Decl == param
}
const (
kindPrintf = 1
kindPrint = 2
)
// printfLike reports whether a call to fn should be considered a call to a printf-like function.
// It returns 0 (indicating not a printf-like function), kindPrintf, or kindPrint.
func printfLike(pkg *Package, fn ast.Expr, byName map[string]*printfWrapper) int {
if id, ok := fn.(*ast.Ident); ok && id.Obj != nil {
if w := byName[id.Name]; w != nil && id.Obj.Decl == w.fn {
// Found call to function in same package.
return localPrintfLike[id.Name]
}
}
if sel, ok := fn.(*ast.SelectorExpr); ok {
if id, ok := sel.X.(*ast.Ident); ok && id.Name == "fmt" && strings.Contains(sel.Sel.Name, "rint") {
if strings.HasSuffix(sel.Sel.Name, "f") {
return kindPrintf
}
return kindPrint
}
}
return 0
}
// checkPrintfFwd checks that a printf-forwarding wrapper is forwarding correctly.
// It diagnoses writing fmt.Printf(format, args) instead of fmt.Printf(format, args...).
func checkPrintfFwd(pkg *Package, w *printfWrapper, call *ast.CallExpr, kind int) {
matched := kind == kindPrint ||
kind == kindPrintf && len(call.Args) >= 2 && match(call.Args[len(call.Args)-2], w.format)
if !matched {
return
}
if !call.Ellipsis.IsValid() {
typ, ok := pkg.types[call.Fun].Type.(*types.Signature)
if !ok {
return
}
if len(call.Args) > typ.Params().Len() {
// If we're passing more arguments than what the
// print/printf function can take, adding an ellipsis
// would break the program. For example:
//
// func foo(arg1 string, arg2 ...interface{} {
// fmt.Printf("%s %v", arg1, arg2)
// }
return
}
if !vcfg.VetxOnly {
desc := "printf"
if kind == kindPrint {
desc = "print"
}
pkg.files[0].Badf(call.Pos(), "missing ... in args forwarded to %s-like function", desc)
}
return
}
name := w.name
if localPrintfLike[name] == 0 {
localPrintfLike[name] = kind
for _, caller := range w.callers {
checkPrintfFwd(pkg, caller.w, caller.call, kind)
}
}
}
func exportPrintfLike() interface{} {
out := make([]printfExport, 0, len(localPrintfLike))
for name, kind := range localPrintfLike {
out = append(out, printfExport{
Name: name,
Kind: kind,
})
}
sort.Slice(out, func(i, j int) bool {
return out[i].Name < out[j].Name
})
return out
}
// isPrint records the print functions.
// If a key ends in 'f' then it is assumed to be a formatted print.
var isPrint = map[string]bool{
"fmt.Errorf": true,
"fmt.Fprint": true,
"fmt.Fprintf": true,
"fmt.Fprintln": true,
"fmt.Print": true,
"fmt.Printf": true,
"fmt.Println": true,
"fmt.Sprint": true,
"fmt.Sprintf": true,
"fmt.Sprintln": true,
// testing.B, testing.T not auto-detected
// because the methods are picked up by embedding.
"testing.B.Error": true,
"testing.B.Errorf": true,
"testing.B.Fatal": true,
"testing.B.Fatalf": true,
"testing.B.Log": true,
"testing.B.Logf": true,
"testing.B.Skip": true,
"testing.B.Skipf": true,
"testing.T.Error": true,
"testing.T.Errorf": true,
"testing.T.Fatal": true,
"testing.T.Fatalf": true,
"testing.T.Log": true,
"testing.T.Logf": true,
"testing.T.Skip": true,
"testing.T.Skipf": true,
// testing.TB is an interface, so can't detect wrapping.
"testing.TB.Error": true,
"testing.TB.Errorf": true,
"testing.TB.Fatal": true,
"testing.TB.Fatalf": true,
"testing.TB.Log": true,
"testing.TB.Logf": true,
"testing.TB.Skip": true,
"testing.TB.Skipf": true,
}
// formatString returns the format string argument and its index within
// the given printf-like call expression.
//
// The last parameter before variadic arguments is assumed to be
// a format string.
//
// The first string literal or string constant is assumed to be a format string
// if the call's signature cannot be determined.
//
// If it cannot find any format string parameter, it returns ("", -1).
func formatString(f *File, call *ast.CallExpr) (format string, idx int) {
typ := f.pkg.types[call.Fun].Type
if typ != nil {
if sig, ok := typ.(*types.Signature); ok {
if !sig.Variadic() {
// Skip checking non-variadic functions.
return "", -1
}
idx := sig.Params().Len() - 2
if idx < 0 {
// Skip checking variadic functions without
// fixed arguments.
return "", -1
}
s, ok := stringConstantArg(f, call, idx)
if !ok {
// The last argument before variadic args isn't a string.
return "", -1
}
return s, idx
}
}
// Cannot determine call's signature. Fall back to scanning for the first
// string constant in the call.
for idx := range call.Args {
if s, ok := stringConstantArg(f, call, idx); ok {
return s, idx
}
if f.pkg.types[call.Args[idx]].Type == types.Typ[types.String] {
// Skip checking a call with a non-constant format
// string argument, since its contents are unavailable
// for validation.
return "", -1
}
}
return "", -1
}
// stringConstantArg returns call's string constant argument at the index idx.
//
// ("", false) is returned if call's argument at the index idx isn't a string
// constant.
func stringConstantArg(f *File, call *ast.CallExpr, idx int) (string, bool) {
if idx >= len(call.Args) {
return "", false
}
arg := call.Args[idx]
lit := f.pkg.types[arg].Value
if lit != nil && lit.Kind() == constant.String {
return constant.StringVal(lit), true
}
return "", false
}
// checkCall triggers the print-specific checks if the call invokes a print function.
func checkFmtPrintfCall(f *File, node ast.Node) {
if f.pkg.typesPkg == nil {
// This check now requires type information.
return
}
if d, ok := node.(*ast.FuncDecl); ok && isStringer(f, d) {
// Remember we saw this.
if f.stringerPtrs == nil {
f.stringerPtrs = make(map[*ast.Object]bool)
}
if l := d.Recv.List; len(l) == 1 {
if n := l[0].Names; len(n) == 1 {
typ := f.pkg.types[l[0].Type]
_, ptrRecv := typ.Type.(*types.Pointer)
f.stringerPtrs[n[0].Obj] = ptrRecv
}
}
return
}
call, ok := node.(*ast.CallExpr)
if !ok {
return
}
// Construct name like pkg.Printf or pkg.Type.Printf for lookup.
_, name, kind := printfNameAndKind(f.pkg, call.Fun)
if kind == kindPrintf {
f.checkPrintf(call, name)
}
if kind == kindPrint {
f.checkPrint(call, name)
}
}
func printfName(pkg *Package, called ast.Expr) (pkgpath, name string) {
switch x := called.(type) {
case *ast.Ident:
if fn, ok := pkg.uses[x].(*types.Func); ok {
if fn.Pkg() == nil || fn.Pkg() == pkg.typesPkg {
pkgpath = ""
} else {
pkgpath = fn.Pkg().Path()
}
return pkgpath, x.Name
}
case *ast.SelectorExpr:
// Check for "fmt.Printf".
if id, ok := x.X.(*ast.Ident); ok {
if pkgName, ok := pkg.uses[id].(*types.PkgName); ok {
return pkgName.Imported().Path(), x.Sel.Name
}
}
// Check for t.Logf where t is a *testing.T.
if sel := pkg.selectors[x]; sel != nil {
recv := sel.Recv()
if p, ok := recv.(*types.Pointer); ok {
recv = p.Elem()
}
if named, ok := recv.(*types.Named); ok {
obj := named.Obj()
if obj.Pkg() == nil || obj.Pkg() == pkg.typesPkg {
pkgpath = ""
} else {
pkgpath = obj.Pkg().Path()
}
return pkgpath, obj.Name() + "." + x.Sel.Name
}
}
}
return "", ""
}
func printfNameAndKind(pkg *Package, called ast.Expr) (pkgpath, name string, kind int) {
pkgpath, name = printfName(pkg, called)
if name == "" {
return pkgpath, name, 0
}
if pkgpath == "" {
kind = localPrintfLike[name]
} else if m, ok := printfImported[pkgpath]; ok {
kind = m[name]
} else {
var m map[string]int
if out, ok := readVetx(pkgpath, "printf").([]printfExport); ok {
m = make(map[string]int)
for _, x := range out {
m[x.Name] = x.Kind
}
}
printfImported[pkgpath] = m
kind = m[name]
}
if kind == 0 {
_, ok := isPrint[pkgpath+"."+name]
if !ok {
// Next look up just "printf", for use with -printfuncs.
short := name[strings.LastIndex(name, ".")+1:]
_, ok = isPrint[strings.ToLower(short)]
}
if ok {
if strings.HasSuffix(name, "f") {
kind = kindPrintf
} else {
kind = kindPrint
}
}
}
return pkgpath, name, kind
}
// isStringer reports whether 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]
}
// isFormatter reports whether t satisfies fmt.Formatter.
// Unlike fmt.Stringer, it's impossible to satisfy fmt.Formatter without importing fmt.
func (f *File) isFormatter(t types.Type) bool {
return formatterType != nil && types.Implements(t, formatterType)
}
// 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
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.
hasIndex bool // Whether the argument is indexed.
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.
func (f *File) checkPrintf(call *ast.CallExpr, name string) {
format, idx := formatString(f, call)
if idx < 0 {
if *verbose {
f.Warn(call.Pos(), "can't check non-constant format in call to", name)
}
return
}
firstArg := idx + 1 // Arguments are immediately after format string.
if !strings.Contains(format, "%") {
if len(call.Args) > firstArg {
f.Badf(call.Pos(), "%s call has arguments but no formatting directives", name)
}
return
}
// Hard part: check formats against args.
argNum := firstArg
maxArgNum := firstArg
anyIndex := false
for i, w := 0, 0; i < len(format); i += w {
w = 1
if format[i] != '%' {
continue
}
state := f.parsePrintfVerb(call, name, format[i:], firstArg, argNum)
if state == nil {
return
}
w = len(state.format)
if !f.okPrintfArg(call, state) { // One error per format is enough.
return
}
if state.hasIndex {
anyIndex = true
}
if len(state.argNums) > 0 {
// Continue with the next sequential argument.
argNum = state.argNums[len(state.argNums)-1] + 1
}
for _, n := range state.argNums {
if n >= maxArgNum {
maxArgNum = n + 1
}
}
}
// Dotdotdot is hard.
if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 {
return
}
// If any formats are indexed, extra arguments are ignored.
if anyIndex {
return
}
// There should be no leftover arguments.
if maxArgNum != len(call.Args) {
expect := maxArgNum - firstArg
numArgs := len(call.Args) - firstArg
f.Badf(call.Pos(), "%s call needs %v but has %v", name, count(expect, "arg"), count(numArgs, "arg"))
}
}
// 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.nbytes++ // skip '['
start := s.nbytes
s.scanNum()
ok := true
if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' {
ok = false
s.nbytes = strings.Index(s.format, "]")
if s.nbytes < 0 {
s.file.Badf(s.call.Pos(), "%s format %s is missing closing ]", s.name, s.format)
return false
}
}
arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) {
s.file.Badf(s.call.Pos(), "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes])
return false
}
s.nbytes++ // skip ']'
arg := int(arg32)
arg += s.firstArg - 1 // We want to zero-index the actual arguments.
s.argNum = arg
s.hasIndex = true
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.
firstArg: firstArg,
file: f,
call: call,
}
// There may be flags.
state.parseFlags()
// 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 !state.indexPending && !state.parseIndex() {
return nil
}
if state.nbytes == len(state.format) {
f.Badf(call.Pos(), "%s format %s is missing verb at end of string", name, state.format)
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.
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 | argPointer},
{'e', sharpNumFlag, argFloat | argComplex},
{'E', sharpNumFlag, argFloat | argComplex},
{'f', sharpNumFlag, argFloat | argComplex},
{'F', sharpNumFlag, argFloat | argComplex},
{'g', sharpNumFlag, argFloat | argComplex},
{'G', sharpNumFlag, 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 | argPointer},
{'X', sharpNumFlag, argRune | argInt | argString | argPointer},
}
// 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
}
}
// Does current arg implement fmt.Formatter?
formatter := false
if state.argNum < len(call.Args) {
if tv, ok := f.pkg.types[call.Args[state.argNum]]; ok {
formatter = f.isFormatter(tv.Type)
}
}
if !formatter {
if !found {
f.Badf(call.Pos(), "%s format %s has unknown verb %c", state.name, state.format, state.verb)
return false
}
for _, flag := range state.flags {
// TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11.
// See issues 23598 and 23605.
if flag == '0' {
continue
}
if !strings.ContainsRune(v.flags, rune(flag)) {
f.Badf(call.Pos(), "%s format %s has unrecognized flag %c", state.name, state.format, 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, state) {
return
}
arg := call.Args[argNum]
if !f.matchArgType(argInt, nil, arg) {
f.Badf(call.Pos(), "%s format %s uses non-int %s as argument of *", state.name, state.format, f.gofmt(arg))
return false
}
}
if state.verb == '%' || formatter {
return true
}
argNum := state.argNums[len(state.argNums)-1]
if !f.argCanBeChecked(call, len(state.argNums)-1, state) {
return false
}
arg := call.Args[argNum]
if f.isFunctionValue(arg) && state.verb != 'p' && state.verb != 'T' {
f.Badf(call.Pos(), "%s format %s arg %s is a func value, not called", state.name, state.format, f.gofmt(arg))
return false
}
if !f.matchArgType(v.typ, nil, arg) {
typeString := ""
if typ := f.pkg.types[arg].Type; typ != nil {
typeString = typ.String()
}
f.Badf(call.Pos(), "%s format %s has arg %s of wrong type %s", state.name, state.format, f.gofmt(arg), typeString)
return false
}
if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && f.recursiveStringer(arg) {
f.Badf(call.Pos(), "%s format %s with arg %s causes recursive String method call", state.name, state.format, 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.stringerPtrs) == 0 {
return false
}
ptr := 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
ptr = true
}
}
// It's unlikely to be a recursive stringer if it has a Format method.
if typ := f.pkg.types[e].Type; typ != nil {
if f.isFormatter(typ) {
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.
ptrRecv, exist := f.stringerPtrs[obj]
if !exist {
return false
}
// We also need to check that using &t when we declared String
// on (t *T) is ok; in such a case, the address is printed.
if ptr && ptrRecv {
return false
}
return true
}
// isFunctionValue reports whether the expression is a function as opposed to a function call.
// It is almost always a mistake to print a function value.
func (f *File) isFunctionValue(e ast.Expr) bool {
if typ := f.pkg.types[e].Type; typ != nil {
_, ok := typ.(*types.Signature)
return ok
}
return false
}
// 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, state *formatState) bool {
argNum := state.argNums[formatArg]
if argNum <= 0 {
// Shouldn't happen, so catch it with prejudice.
panic("negative arg num")
}
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(), "%s format %s reads arg #%d, but call has %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg"))
return false
}
// printFormatRE is the regexp we match and report as a possible format string
// in the first argument to unformatted prints like fmt.Print.
// We exclude the space flag, so that printing a string like "x % y" is not reported as a format.
var printFormatRE = regexp.MustCompile(`%` + flagsRE + numOptRE + `\.?` + numOptRE + indexOptRE + verbRE)
const (
flagsRE = `[+\-#]*`
indexOptRE = `(\[[0-9]+\])?`
numOptRE = `([0-9]+|` + indexOptRE + `\*)?`
verbRE = `[bcdefgopqstvxEFGTUX]`
)
// checkPrint checks a call to an unformatted print routine such as Println.
func (f *File) checkPrint(call *ast.CallExpr, name string) {
firstArg := 0
typ := f.pkg.types[call.Fun].Type
if typ == nil {
// Skip checking functions with unknown type.
return
}
if sig, ok := typ.(*types.Signature); ok {
if !sig.Variadic() {
// Skip checking non-variadic functions.
return
}
params := sig.Params()
firstArg = params.Len() - 1
typ := params.At(firstArg).Type()
typ = typ.(*types.Slice).Elem()
it, ok := typ.(*types.Interface)
if !ok || !it.Empty() {
// Skip variadic functions accepting non-interface{} args.
return
}
}
args := call.Args
if len(args) <= firstArg {
// Skip calls without variadic args.
return
}
args = args[firstArg:]
if firstArg == 0 {
if sel, ok := call.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(), "%s does not take io.Writer but has first arg %s", name, f.gofmt(call.Args[0]))
}
}
}
}
arg := args[0]
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
// Ignore trailing % character in lit.Value.
// The % in "abc 0.0%" couldn't be a formatting directive.
s := strings.TrimSuffix(lit.Value, `%"`)
if strings.Contains(s, "%") {
m := printFormatRE.FindStringSubmatch(s)
if m != nil {
f.Badf(call.Pos(), "%s call has possible formatting directive %s", name, m[0])
}
}
}
if strings.HasSuffix(name, "ln") {
// The last item, if a string, should not have a newline.
arg = args[len(args)-1]
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
str, _ := strconv.Unquote(lit.Value)
if strings.HasSuffix(str, "\n") {
f.Badf(call.Pos(), "%s arg list ends with redundant newline", name)
}
}
}
for _, arg := range args {
if f.isFunctionValue(arg) {
f.Badf(call.Pos(), "%s arg %s is a func value, not called", name, f.gofmt(arg))
}
if f.recursiveStringer(arg) {
f.Badf(call.Pos(), "%s arg %s causes recursive call to String method", name, f.gofmt(arg))
}
}
}
// count(n, what) returns "1 what" or "N whats"
// (assuming the plural of what is whats).
func count(n int, what string) string {
if n == 1 {
return "1 " + what
}
return fmt.Sprintf("%d %ss", n, what)
}
// 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: https://golang.org/doc/go_faq.html#closures_and_goroutines
*/
package main
import "go/ast"
func init() {
register("rangeloops",
"check that loop variables are used correctly",
checkLoop,
rangeStmt, forStmt)
}
// checkLoop walks the body of the provided loop statement, checking whether
// its index or value variables are used unsafely inside goroutines or deferred
// function literals.
func checkLoop(f *File, node ast.Node) {
// Find the variables updated by the loop statement.
var vars []*ast.Ident
addVar := func(expr ast.Expr) {
if id, ok := expr.(*ast.Ident); ok {
vars = append(vars, id)
}
}
var body *ast.BlockStmt
switch n := node.(type) {
case *ast.RangeStmt:
body = n.Body
addVar(n.Key)
addVar(n.Value)
case *ast.ForStmt:
body = n.Body
switch post := n.Post.(type) {
case *ast.AssignStmt:
// e.g. for p = head; p != nil; p = p.next
for _, lhs := range post.Lhs {
addVar(lhs)
}
case *ast.IncDecStmt:
// e.g. for i := 0; i < n; i++
addVar(post.X)
}
}
if vars == nil {
return
}
// Inspect a go or defer statement
// if it's the last one in the loop body.
// (We give up if there are following statements,
// because it's hard to prove go isn't followed by wait,
// or defer by return.)
if len(body.List) == 0 {
return
}
var last *ast.CallExpr
switch s := body.List[len(body.List)-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 f.pkg.types[id].Type == nil {
// Not referring to a variable (e.g. struct field name)
return true
}
for _, v := range vars {
if v.Obj == id.Obj {
f.Badf(id.Pos(), "loop variable %s captured by func literal",
id.Name)
}
}
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"
"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 source range [pos, end).
func (pkg *Package) growSpan(obj types.Object, pos, end token.Pos) {
if *strictShadowing {
return // No need
}
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
}
}
default:
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(), obj.Pos())
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(shadowed.Pos(), "internal error: no range for %q", shadowed.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 %q 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/constant"
"go/token"
"go/types"
)
func init() {
register("shift",
"check for useless shifts",
checkShift,
binaryExpr, assignStmt)
}
func checkShift(f *File, node ast.Node) {
if f.dead[node] {
// Skip shift checks on unreachable nodes.
return
}
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) {
if f.pkg.types[x].Value != nil {
// Ignore shifts of constants.
// These are frequently used for bit-twiddling tricks
// like ^uint(0) >> 63 for 32/64 bit detection and compatibility.
return
}
v := f.pkg.types[y].Value
if v == nil {
return
}
amt, ok := constant.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
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:
size = uintBitSize
case types.Uintptr:
size = uintptrBitSize
default:
return
}
if amt >= size {
ident := f.gofmt(x)
f.Badf(node.Pos(), "%s (%d bits) too small for shift of %d", ident, size, amt)
}
}
var (
uintBitSize = 8 * archSizes.Sizeof(types.Typ[types.Uint])
uintptrBitSize = 8 * archSizes.Sizeof(types.Typ[types.Uintptr])
)
// 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"
"go/token"
"go/types"
"reflect"
"strconv"
"strings"
)
func init() {
register("structtags",
"check that struct field tags have canonical format and apply to exported fields as needed",
checkStructFieldTags,
structType)
}
// checkStructFieldTags checks all the field tags of a struct, including checking for duplicates.
func checkStructFieldTags(f *File, node ast.Node) {
astType := node.(*ast.StructType)
typ := f.pkg.types[astType].Type.(*types.Struct)
var seen map[[2]string]token.Pos
for i := 0; i < typ.NumFields(); i++ {
field := typ.Field(i)
tag := typ.Tag(i)
checkCanonicalFieldTag(f, astType, field, tag, &seen)
}
}
var checkTagDups = []string{"json", "xml"}
var checkTagSpaces = map[string]bool{"json": true, "xml": true, "asn1": true}
// checkCanonicalFieldTag checks a single struct field tag.
// top is the top-level struct type that is currently being checked.
func checkCanonicalFieldTag(f *File, top *ast.StructType, field *types.Var, tag string, seen *map[[2]string]token.Pos) {
for _, key := range checkTagDups {
checkTagDuplicates(f, tag, key, field, field, seen)
}
if err := validateStructTag(tag); err != nil {
f.Badf(field.Pos(), "struct field tag %#q not compatible with reflect.StructTag.Get: %s", tag, 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 field.Anonymous() {
return
}
if field.Exported() {
return
}
for _, enc := range [...]string{"json", "xml"} {
if reflect.StructTag(tag).Get(enc) != "" {
f.Badf(field.Pos(), "struct field %s has %s tag but is not exported", field.Name(), enc)
return
}
}
}
// checkTagDuplicates checks a single struct field tag to see if any tags are
// duplicated. nearest is the field that's closest to the field being checked,
// while still being part of the top-level struct type.
func checkTagDuplicates(f *File, tag, key string, nearest, field *types.Var, seen *map[[2]string]token.Pos) {
val := reflect.StructTag(tag).Get(key)
if val == "-" {
// Ignored, even if the field is anonymous.
return
}
if val == "" || val[0] == ',' {
if field.Anonymous() {
typ, ok := field.Type().Underlying().(*types.Struct)
if !ok {
return
}
for i := 0; i < typ.NumFields(); i++ {
field := typ.Field(i)
if !field.Exported() {
continue
}
tag := typ.Tag(i)
checkTagDuplicates(f, tag, key, nearest, field, seen)
}
}
// Ignored if the field isn't anonymous.
return
}
if key == "xml" && field.Name() == "XMLName" {
// XMLName defines the XML element name of the struct being
// checked. That name cannot collide with element or attribute
// names defined on other fields of the struct. Vet does not have a
// check for untagged fields of type struct defining their own name
// by containing a field named XMLName; see issue 18256.
return
}
if i := strings.Index(val, ","); i >= 0 {
if key == "xml" {
// Use a separate namespace for XML attributes.
for _, opt := range strings.Split(val[i:], ",") {
if opt == "attr" {
key += " attribute" // Key is part of the error message.
break
}
}
}
val = val[:i]
}
if *seen == nil {
*seen = map[[2]string]token.Pos{}
}
if pos, ok := (*seen)[[2]string{key, val}]; ok {
f.Badf(nearest.Pos(), "struct field %s repeats %s tag %q also at %s", field.Name(), key, val, f.loc(pos))
} else {
(*seen)[[2]string{key, val}] = field.Pos()
}
}
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")
errTagValueSpace = errors.New("suspicious space in struct tag value")
errTagSpace = errors.New("key:\"value\" pairs not separated by spaces")
)
// 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.
n := 0
for ; tag != ""; n++ {
if n > 0 && tag != "" && tag[0] != ' ' {
// More restrictive than reflect, but catches likely mistakes
// like `x:"foo",y:"bar"`, which parses as `x:"foo" ,y:"bar"` with second key ",y".
return errTagSpace
}
// 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
}
key := tag[:i]
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 := tag[:i+1]
tag = tag[i+1:]
value, err := strconv.Unquote(qvalue)
if err != nil {
return errTagValueSyntax
}
if !checkTagSpaces[key] {
continue
}
switch key {
case "xml":
// If the first or last character in the XML tag is a space, it is
// suspicious.
if strings.Trim(value, " ") != value {
return errTagValueSpace
}
// If there are multiple spaces, they are suspicious.
if strings.Count(value, " ") > 1 {
return errTagValueSpace
}
// If there is no comma, skip the rest of the checks.
comma := strings.IndexRune(value, ',')
if comma < 0 {
continue
}
// If the character before a comma is a space, this is suspicious.
if comma > 0 && value[comma-1] == ' ' {
return errTagValueSpace
}
value = value[comma+1:]
case "json":
// JSON allows using spaces in the name, so skip it.
comma := strings.IndexRune(value, ',')
if comma < 0 {
continue
}
value = value[comma+1:]
}
if strings.IndexByte(value, ' ') >= 0 {
return errTagValueSpace
}
}
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
type S struct {
i int32
b bool
s string
}
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 argcomplex(x complex64, y complex128)
func argstruct(x S, y struct{})
func argarray(x [2]S)
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)
func f15271() (x uint32)
func f17584(x float32, y complex64)
func noframe1(x int32)
func noframe2(x int32)
// 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 ·argcomplex(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
MOVSS x+0(FP), X0 // ERROR "invalid MOVSS of x\+0\(FP\); complex64 is 8-byte value containing x_real\+0\(FP\) and x_imag\+4\(FP\)"
MOVSD x+0(FP), X0 // ERROR "invalid MOVSD of x\+0\(FP\); complex64 is 8-byte value containing x_real\+0\(FP\) and x_imag\+4\(FP\)"
MOVSS x_real+0(FP), X0
MOVSD x_real+0(FP), X0 // ERROR "invalid MOVSD of x_real\+0\(FP\); real\(complex64\) is 4-byte value"
MOVSS x_real+4(FP), X0 // ERROR "invalid offset x_real\+4\(FP\); expected x_real\+0\(FP\)"
MOVSS x_imag+4(FP), X0
MOVSD x_imag+4(FP), X0 // ERROR "invalid MOVSD of x_imag\+4\(FP\); imag\(complex64\) is 4-byte value"
MOVSS x_imag+8(FP), X0 // ERROR "invalid offset x_imag\+8\(FP\); expected x_imag\+4\(FP\)"
MOVSD y+8(FP), X0 // ERROR "invalid MOVSD of y\+8\(FP\); complex128 is 16-byte value containing y_real\+8\(FP\) and y_imag\+16\(FP\)"
MOVSS y_real+8(FP), X0 // ERROR "invalid MOVSS of y_real\+8\(FP\); real\(complex128\) is 8-byte value"
MOVSD y_real+8(FP), X0
MOVSS y_real+16(FP), X0 // ERROR "invalid offset y_real\+16\(FP\); expected y_real\+8\(FP\)"
MOVSS y_imag+16(FP), X0 // ERROR "invalid MOVSS of y_imag\+16\(FP\); imag\(complex128\) is 8-byte value"
MOVSD y_imag+16(FP), X0
MOVSS y_imag+24(FP), X0 // ERROR "invalid offset y_imag\+24\(FP\); expected y_imag\+16\(FP\)"
RET
TEXT ·argstruct(SB),0,$64 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); testdata.S is 24-byte value"
MOVQ x_i+0(FP), AX // ERROR "invalid MOVQ of x_i\+0\(FP\); int32 is 4-byte value"
MOVQ x_b+0(FP), AX // ERROR "invalid offset x_b\+0\(FP\); expected x_b\+4\(FP\)"
MOVQ x_s+8(FP), AX
MOVQ x_s_base+8(FP), AX
MOVQ x_s+16(FP), AX // ERROR "invalid offset x_s\+16\(FP\); expected x_s\+8\(FP\), x_s_base\+8\(FP\), or x_s_len\+16\(FP\)"
MOVQ x_s_len+16(FP), AX
RET
TEXT ·argarray(SB),0,$64 // ERROR "wrong argument size 0; expected \$\.\.\.-48"
MOVQ x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); \[2\]testdata.S is 48-byte value"
MOVQ x_0_i+0(FP), AX // ERROR "invalid MOVQ of x_0_i\+0\(FP\); int32 is 4-byte value"
MOVQ x_0_b+0(FP), AX // ERROR "invalid offset x_0_b\+0\(FP\); expected x_0_b\+4\(FP\)"
MOVQ x_0_s+8(FP), AX
MOVQ x_0_s_base+8(FP), AX
MOVQ x_0_s+16(FP), AX // ERROR "invalid offset x_0_s\+16\(FP\); expected x_0_s\+8\(FP\), x_0_s_base\+8\(FP\), or x_0_s_len\+16\(FP\)"
MOVQ x_0_s_len+16(FP), AX
MOVB foo+25(FP), AX // ERROR "unknown variable foo; offset 25 is x_1_i\+24\(FP\)"
MOVQ x_1_s+32(FP), AX
MOVQ x_1_s_base+32(FP), AX
MOVQ x_1_s+40(FP), AX // ERROR "invalid offset x_1_s\+40\(FP\); expected x_1_s\+32\(FP\), x_1_s_base\+32\(FP\), or x_1_s_len\+40\(FP\)"
MOVQ x_1_s_len+40(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\)"
// issue 15271
TEXT ·f15271(SB), NOSPLIT, $0-4
// Stick 123 into the low 32 bits of X0.
MOVQ $123, AX
PINSRD $0, AX, X0
// Return them.
PEXTRD $0, X0, x+0(FP)
RET
// issue 17584
TEXT ·f17584(SB), NOSPLIT, $12
MOVSS x+0(FP), X0
MOVSS y_real+4(FP), X0
MOVSS y_imag+8(FP), X0
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 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
TEXT ·noframe1(SB),0,$0-4
MOVW 0(R13), AX // Okay; our saved LR
MOVW 4(R13), AX // Okay; caller's saved LR
MOVW x+8(R13), AX // Okay; x argument
MOVW 12(R13), AX // ERROR "use of 12\(R13\) points beyond argument frame"
RET
TEXT ·noframe2(SB),NOFRAME,$0-4
MOVW 0(R13), AX // Okay; caller's saved LR
MOVW x+4(R13), AX // Okay; x argument
MOVW 8(R13), AX // ERROR "use of 8\(R13\) points beyond argument frame"
MOVW 12(R13), AX // ERROR "use of 12\(R13\) points beyond argument frame"
RET
// Copyright 2016 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 mips64
// +build vet_test
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), R1
MOVBU y+1(FP), R2
MOVH x+0(FP), R1 // ERROR "\[mips64\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
MOVHU y+1(FP), R1 // ERROR "invalid MOVHU of y\+1\(FP\); uint8 is 1-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
MOVWU y+1(FP), R1 // ERROR "invalid MOVWU of y\+1\(FP\); uint8 is 1-byte value"
MOVV x+0(FP), R1 // ERROR "invalid MOVV of x\+0\(FP\); int8 is 1-byte value"
MOVV y+1(FP), R1 // ERROR "invalid MOVV of y\+1\(FP\); uint8 is 1-byte value"
MOVB x+1(FP), R1 // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
MOVBU y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
MOVB 16(R29), R1 // ERROR "16\(R29\) should be x\+0\(FP\)"
MOVB 17(R29), R1 // ERROR "17\(R29\) should be y\+1\(FP\)"
MOVB 18(R29), R1 // ERROR "use of 18\(R29\) points beyond argument frame"
RET
TEXT ·arg2(SB),0,$0-4
MOVBU x+0(FP), R1 // ERROR "arg2: invalid MOVBU of x\+0\(FP\); int16 is 2-byte value"
MOVB y+2(FP), R1 // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
MOVHU x+0(FP), R1
MOVH y+2(FP), R2
MOVWU x+0(FP), R1 // ERROR "invalid MOVWU of x\+0\(FP\); int16 is 2-byte value"
MOVW y+2(FP), R1 // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
MOVV x+0(FP), R1 // ERROR "invalid MOVV of x\+0\(FP\); int16 is 2-byte value"
MOVV y+2(FP), R1 // ERROR "invalid MOVV of y\+2\(FP\); uint16 is 2-byte value"
MOVHU x+2(FP), R1 // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
MOVH y+0(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
MOVB y+4(FP), R2 // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
MOVH y+4(FP), R1 // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVV x+0(FP), R1 // ERROR "invalid MOVV of x\+0\(FP\); int32 is 4-byte value"
MOVV y+4(FP), R1 // ERROR "invalid MOVV of y\+4\(FP\); uint32 is 4-byte value"
MOVW x+4(FP), R1 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
MOVW y+2(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
MOVB y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
MOVH y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
MOVW y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
MOVV x+0(FP), R1
MOVV y+8(FP), R1
MOVV x+8(FP), R1 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVV y+2(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
MOVB y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int is 8-byte value"
MOVH y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); uint is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
MOVW y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
MOVV x+0(FP), R1
MOVV y+8(FP), R1
MOVV x+8(FP), R1 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVV y+2(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
MOVB y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 8-byte value"
MOVH y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); \*byte is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
MOVW y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
MOVV x+0(FP), R1
MOVV y+8(FP), R1
MOVV x+8(FP), R1 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVV y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
MOVW c+16(FP), R1 // ERROR "invalid MOVW of c\+16\(FP\); chan int is 8-byte value"
MOVW m+24(FP), R1 // ERROR "invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value"
MOVW f+32(FP), R1 // ERROR "invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value"
RET
TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); string base is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
MOVV x+0(FP), R1
MOVH x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 8-byte value"
MOVW x_base+0(FP), R1 // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
MOVV x_base+0(FP), R1
MOVH x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVW x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVV x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVH x_len+8(FP), R1 // ERROR "invalid MOVH of x_len\+8\(FP\); string len is 8-byte value"
MOVW x_len+8(FP), R1 // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
MOVV x_len+8(FP), R1
MOVV y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
MOVV y_len+8(FP), R1 // 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"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); slice base is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
MOVV x+0(FP), R1
MOVH x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value"
MOVW x_base+0(FP), R1 // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
MOVV x_base+0(FP), R1
MOVH x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVW x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVV x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVH x_len+8(FP), R1 // ERROR "invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value"
MOVW x_len+8(FP), R1 // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
MOVV x_len+8(FP), R1
MOVH x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
MOVW x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
MOVV x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
MOVH x_cap+16(FP), R1 // ERROR "invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value"
MOVW x_cap+16(FP), R1 // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
MOVV x_cap+16(FP), R1
MOVV y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
MOVV y_len+8(FP), R1 // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
MOVV y_cap+16(FP), R1 // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
RET
TEXT ·argiface(SB),0,$0-32
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); interface type is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
MOVV x+0(FP), R1
MOVH x_type+0(FP), R1 // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value"
MOVW x_type+0(FP), R1 // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
MOVV x_type+0(FP), R1
MOVV x_itable+0(FP), R1 // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
MOVV x_itable+1(FP), R1 // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
MOVH x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
MOVW x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
MOVV x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
MOVH x_data+8(FP), R1 // ERROR "invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value"
MOVW x_data+8(FP), R1 // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
MOVV x_data+8(FP), R1
MOVH y+16(FP), R1 // ERROR "invalid MOVH of y\+16\(FP\); interface itable is 8-byte value"
MOVW y+16(FP), R1 // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
MOVV y+16(FP), R1
MOVH y_itable+16(FP), R1 // ERROR "invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value"
MOVW y_itable+16(FP), R1 // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
MOVV y_itable+16(FP), R1
MOVV y_type+16(FP), R1 // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
MOVH y_data+16(FP), R1 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
MOVW y_data+16(FP), R1 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
MOVV y_data+16(FP), R1 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
MOVH y_data+24(FP), R1 // ERROR "invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value"
MOVW y_data+24(FP), R1 // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
MOVV y_data+24(FP), R1
RET
TEXT ·returnint(SB),0,$0-8
MOVB R1, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
MOVH R1, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 8-byte value"
MOVW R1, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
MOVV R1, ret+0(FP)
MOVV R1, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
MOVV R1, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
RET
TEXT ·returnbyte(SB),0,$0-9
MOVV x+0(FP), R1
MOVB R1, ret+8(FP)
MOVH R1, ret+8(FP) // ERROR "invalid MOVH of ret\+8\(FP\); byte is 1-byte value"
MOVW R1, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
MOVV R1, ret+8(FP) // ERROR "invalid MOVV of ret\+8\(FP\); byte is 1-byte value"
MOVB R1, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
RET
TEXT ·returnnamed(SB),0,$0-41
MOVB x+0(FP), R1
MOVV R1, r1+8(FP)
MOVH R1, r2+16(FP)
MOVV R1, r3+24(FP)
MOVV R1, r3_base+24(FP)
MOVV R1, r3_len+32(FP)
MOVB R1, r4+40(FP)
MOVW R1, r1+8(FP) // ERROR "invalid MOVW 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 2016 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 s390x
// +build vet_test
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), R1
MOVBZ y+1(FP), R2
MOVH x+0(FP), R1 // ERROR "\[s390x\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
MOVHZ y+1(FP), R1 // ERROR "invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
MOVWZ y+1(FP), R1 // ERROR "invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value"
MOVD x+0(FP), R1 // ERROR "invalid MOVD of x\+0\(FP\); int8 is 1-byte value"
MOVD y+1(FP), R1 // ERROR "invalid MOVD of y\+1\(FP\); uint8 is 1-byte value"
MOVB x+1(FP), R1 // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
MOVBZ y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
MOVB 16(R15), R1 // ERROR "16\(R15\) should be x\+0\(FP\)"
MOVB 17(R15), R1 // ERROR "17\(R15\) should be y\+1\(FP\)"
MOVB 18(R15), R1 // ERROR "use of 18\(R15\) points beyond argument frame"
RET
TEXT ·arg2(SB),0,$0-4
MOVBZ x+0(FP), R1 // ERROR "arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value"
MOVB y+2(FP), R1 // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
MOVHZ x+0(FP), R1
MOVH y+2(FP), R2
MOVWZ x+0(FP), R1 // ERROR "invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value"
MOVW y+2(FP), R1 // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
MOVD x+0(FP), R1 // ERROR "invalid MOVD of x\+0\(FP\); int16 is 2-byte value"
MOVD y+2(FP), R1 // ERROR "invalid MOVD of y\+2\(FP\); uint16 is 2-byte value"
MOVHZ x+2(FP), R1 // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
MOVH y+0(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
MOVB y+4(FP), R2 // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
MOVH y+4(FP), R1 // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVD x+0(FP), R1 // ERROR "invalid MOVD of x\+0\(FP\); int32 is 4-byte value"
MOVD y+4(FP), R1 // ERROR "invalid MOVD of y\+4\(FP\); uint32 is 4-byte value"
MOVW x+4(FP), R1 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
MOVW y+2(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
MOVB y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
MOVH y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
MOVW y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
MOVD x+0(FP), R1
MOVD y+8(FP), R1
MOVD x+8(FP), R1 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVD y+2(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
MOVB y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int is 8-byte value"
MOVH y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); uint is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
MOVW y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
MOVD x+0(FP), R1
MOVD y+8(FP), R1
MOVD x+8(FP), R1 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVD y+2(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
MOVB y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 8-byte value"
MOVH y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); \*byte is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
MOVW y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
MOVD x+0(FP), R1
MOVD y+8(FP), R1
MOVD x+8(FP), R1 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVD y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
MOVW c+16(FP), R1 // ERROR "invalid MOVW of c\+16\(FP\); chan int is 8-byte value"
MOVW m+24(FP), R1 // ERROR "invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value"
MOVW f+32(FP), R1 // ERROR "invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value"
RET
TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); string base is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
MOVD x+0(FP), R1
MOVH x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 8-byte value"
MOVW x_base+0(FP), R1 // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
MOVD x_base+0(FP), R1
MOVH x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVW x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVD x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVH x_len+8(FP), R1 // ERROR "invalid MOVH of x_len\+8\(FP\); string len is 8-byte value"
MOVW x_len+8(FP), R1 // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
MOVD x_len+8(FP), R1
MOVD y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
MOVD y_len+8(FP), R1 // 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"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); slice base is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
MOVD x+0(FP), R1
MOVH x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value"
MOVW x_base+0(FP), R1 // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
MOVD x_base+0(FP), R1
MOVH x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVW x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVD x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVH x_len+8(FP), R1 // ERROR "invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value"
MOVW x_len+8(FP), R1 // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
MOVD x_len+8(FP), R1
MOVH x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
MOVW x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
MOVD x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
MOVH x_cap+16(FP), R1 // ERROR "invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value"
MOVW x_cap+16(FP), R1 // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
MOVD x_cap+16(FP), R1
MOVD y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
MOVD y_len+8(FP), R1 // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
MOVD y_cap+16(FP), R1 // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
RET
TEXT ·argiface(SB),0,$0-32
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); interface type is 8-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
MOVD x+0(FP), R1
MOVH x_type+0(FP), R1 // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value"
MOVW x_type+0(FP), R1 // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
MOVD x_type+0(FP), R1
MOVD x_itable+0(FP), R1 // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
MOVD x_itable+1(FP), R1 // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
MOVH x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
MOVW x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
MOVD x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
MOVH x_data+8(FP), R1 // ERROR "invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value"
MOVW x_data+8(FP), R1 // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
MOVD x_data+8(FP), R1
MOVH y+16(FP), R1 // ERROR "invalid MOVH of y\+16\(FP\); interface itable is 8-byte value"
MOVW y+16(FP), R1 // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
MOVD y+16(FP), R1
MOVH y_itable+16(FP), R1 // ERROR "invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value"
MOVW y_itable+16(FP), R1 // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
MOVD y_itable+16(FP), R1
MOVD y_type+16(FP), R1 // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
MOVH y_data+16(FP), R1 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
MOVW y_data+16(FP), R1 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
MOVD y_data+16(FP), R1 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
MOVH y_data+24(FP), R1 // ERROR "invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value"
MOVW y_data+24(FP), R1 // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
MOVD y_data+24(FP), R1
RET
TEXT ·returnint(SB),0,$0-8
MOVB R1, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
MOVH R1, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 8-byte value"
MOVW R1, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
MOVD R1, ret+0(FP)
MOVD R1, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
MOVD R1, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
RET
TEXT ·returnbyte(SB),0,$0-9
MOVD x+0(FP), R1
MOVB R1, ret+8(FP)
MOVH R1, ret+8(FP) // ERROR "invalid MOVH of ret\+8\(FP\); byte is 1-byte value"
MOVW R1, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
MOVD R1, ret+8(FP) // ERROR "invalid MOVD of ret\+8\(FP\); byte is 1-byte value"
MOVB R1, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
RET
TEXT ·returnnamed(SB),0,$0-41
MOVB x+0(FP), R1
MOVD R1, r1+8(FP)
MOVH R1, r2+16(FP)
MOVD R1, r3+24(FP)
MOVD R1, r3_base+24(FP)
MOVD R1, r3_len+32(FP)
MOVB R1, r4+40(FP)
MOVW R1, r1+8(FP) // ERROR "invalid MOVW 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 2016 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 ppc64 ppc64le
// +build vet_test
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), R3
MOVBZ y+1(FP), R4
MOVH x+0(FP), R3 // ERROR "\[(ppc64|ppc64le)\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
MOVHZ y+1(FP), R3 // ERROR "invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value"
MOVW x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
MOVWZ y+1(FP), R3 // ERROR "invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value"
MOVD x+0(FP), R3 // ERROR "invalid MOVD of x\+0\(FP\); int8 is 1-byte value"
MOVD y+1(FP), R3 // ERROR "invalid MOVD of y\+1\(FP\); uint8 is 1-byte value"
MOVB x+1(FP), R3 // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
MOVBZ y+2(FP), R3 // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
MOVB 16(R1), R3 // ERROR "16\(R1\) should be x\+0\(FP\)"
MOVB 17(R1), R3 // ERROR "17\(R1\) should be y\+1\(FP\)"
MOVB 18(R1), R3 // ERROR "use of 18\(R1\) points beyond argument frame"
RET
TEXT ·arg2(SB),0,$0-4
MOVBZ x+0(FP), R3 // ERROR "arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value"
MOVB y+2(FP), R3 // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
MOVHZ x+0(FP), R3
MOVH y+2(FP), R4
MOVWZ x+0(FP), R3 // ERROR "invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value"
MOVW y+2(FP), R3 // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
MOVD x+0(FP), R3 // ERROR "invalid MOVD of x\+0\(FP\); int16 is 2-byte value"
MOVD y+2(FP), R3 // ERROR "invalid MOVD of y\+2\(FP\); uint16 is 2-byte value"
MOVHZ x+2(FP), R3 // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
MOVH y+0(FP), R3 // 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), R3 // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
MOVB y+4(FP), R4 // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
MOVH x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
MOVH y+4(FP), R3 // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
MOVW x+0(FP), R3
MOVW y+4(FP), R3
MOVD x+0(FP), R3 // ERROR "invalid MOVD of x\+0\(FP\); int32 is 4-byte value"
MOVD y+4(FP), R3 // ERROR "invalid MOVD of y\+4\(FP\); uint32 is 4-byte value"
MOVW x+4(FP), R3 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
MOVW y+2(FP), R3 // 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), R3 // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
MOVB y+8(FP), R4 // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
MOVH x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
MOVH y+8(FP), R3 // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
MOVW x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
MOVW y+8(FP), R3 // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
MOVD x+0(FP), R3
MOVD y+8(FP), R3
MOVD x+8(FP), R3 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVD y+2(FP), R3 // 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), R3 // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
MOVB y+8(FP), R4 // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
MOVH x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); int is 8-byte value"
MOVH y+8(FP), R3 // ERROR "invalid MOVH of y\+8\(FP\); uint is 8-byte value"
MOVW x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
MOVW y+8(FP), R3 // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
MOVD x+0(FP), R3
MOVD y+8(FP), R3
MOVD x+8(FP), R3 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVD y+2(FP), R3 // 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), R3 // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
MOVB y+8(FP), R4 // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
MOVH x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 8-byte value"
MOVH y+8(FP), R3 // ERROR "invalid MOVH of y\+8\(FP\); \*byte is 8-byte value"
MOVW x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
MOVW y+8(FP), R3 // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
MOVD x+0(FP), R3
MOVD y+8(FP), R3
MOVD x+8(FP), R3 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVD y+2(FP), R3 // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
MOVW c+16(FP), R3 // ERROR "invalid MOVW of c\+16\(FP\); chan int is 8-byte value"
MOVW m+24(FP), R3 // ERROR "invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value"
MOVW f+32(FP), R3 // ERROR "invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value"
RET
TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
MOVH x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); string base is 8-byte value"
MOVW x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
MOVD x+0(FP), R3
MOVH x_base+0(FP), R3 // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 8-byte value"
MOVW x_base+0(FP), R3 // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
MOVD x_base+0(FP), R3
MOVH x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVW x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVD x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVH x_len+8(FP), R3 // ERROR "invalid MOVH of x_len\+8\(FP\); string len is 8-byte value"
MOVW x_len+8(FP), R3 // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
MOVD x_len+8(FP), R3
MOVD y+0(FP), R3 // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
MOVD y_len+8(FP), R3 // 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"
MOVH x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); slice base is 8-byte value"
MOVW x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
MOVD x+0(FP), R3
MOVH x_base+0(FP), R3 // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value"
MOVW x_base+0(FP), R3 // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
MOVD x_base+0(FP), R3
MOVH x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVW x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVD x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
MOVH x_len+8(FP), R3 // ERROR "invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value"
MOVW x_len+8(FP), R3 // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
MOVD x_len+8(FP), R3
MOVH x_cap+0(FP), R3 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
MOVW x_cap+0(FP), R3 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
MOVD x_cap+0(FP), R3 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
MOVH x_cap+16(FP), R3 // ERROR "invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value"
MOVW x_cap+16(FP), R3 // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
MOVD x_cap+16(FP), R3
MOVD y+0(FP), R3 // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
MOVD y_len+8(FP), R3 // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
MOVD y_cap+16(FP), R3 // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
RET
TEXT ·argiface(SB),0,$0-32
MOVH x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); interface type is 8-byte value"
MOVW x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
MOVD x+0(FP), R3
MOVH x_type+0(FP), R3 // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value"
MOVW x_type+0(FP), R3 // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
MOVD x_type+0(FP), R3
MOVD x_itable+0(FP), R3 // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
MOVD x_itable+1(FP), R3 // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
MOVH x_data+0(FP), R3 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
MOVW x_data+0(FP), R3 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
MOVD x_data+0(FP), R3 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
MOVH x_data+8(FP), R3 // ERROR "invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value"
MOVW x_data+8(FP), R3 // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
MOVD x_data+8(FP), R3
MOVH y+16(FP), R3 // ERROR "invalid MOVH of y\+16\(FP\); interface itable is 8-byte value"
MOVW y+16(FP), R3 // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
MOVD y+16(FP), R3
MOVH y_itable+16(FP), R3 // ERROR "invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value"
MOVW y_itable+16(FP), R3 // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
MOVD y_itable+16(FP), R3
MOVD y_type+16(FP), R3 // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
MOVH y_data+16(FP), R3 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
MOVW y_data+16(FP), R3 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
MOVD y_data+16(FP), R3 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
MOVH y_data+24(FP), R3 // ERROR "invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value"
MOVW y_data+24(FP), R3 // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
MOVD y_data+24(FP), R3
RET
TEXT ·returnint(SB),0,$0-8
MOVB R3, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
MOVH R3, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 8-byte value"
MOVW R3, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
MOVD R3, ret+0(FP)
MOVD R3, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
MOVD R3, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
RET
TEXT ·returnbyte(SB),0,$0-9
MOVD x+0(FP), R3
MOVB R3, ret+8(FP)
MOVH R3, ret+8(FP) // ERROR "invalid MOVH of ret\+8\(FP\); byte is 1-byte value"
MOVW R3, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
MOVD R3, ret+8(FP) // ERROR "invalid MOVD of ret\+8\(FP\); byte is 1-byte value"
MOVB R3, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
RET
TEXT ·returnnamed(SB),0,$0-41
MOVB x+0(FP), R3
MOVD R3, r1+8(FP)
MOVH R3, r2+16(FP)
MOVD R3, r3+24(FP)
MOVD R3, r3_base+24(FP)
MOVD R3, r3_len+32(FP)
MOVB R3, r4+40(FP)
MOVW R3, r1+8(FP) // ERROR "invalid MOVW 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 2016 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 mipsle
// +build vet_test
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), R1
MOVBU y+1(FP), R2
MOVH x+0(FP), R1 // ERROR "\[mipsle\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
MOVHU y+1(FP), R1 // ERROR "invalid MOVHU of y\+1\(FP\); uint8 is 1-byte value"
MOVW x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
MOVWU y+1(FP), R1 // ERROR "invalid MOVWU of y\+1\(FP\); uint8 is 1-byte value"
MOVW y+1(FP), R1 // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
MOVB x+1(FP), R1 // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
MOVBU y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
MOVB 8(R29), R1 // ERROR "8\(R29\) should be x\+0\(FP\)"
MOVB 9(R29), R1 // ERROR "9\(R29\) should be y\+1\(FP\)"
MOVB 10(R29), R1 // ERROR "use of 10\(R29\) points beyond argument frame"
RET
TEXT ·arg2(SB),0,$0-4
MOVBU x+0(FP), R1 // ERROR "arg2: invalid MOVBU of x\+0\(FP\); int16 is 2-byte value"
MOVB y+2(FP), R1 // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
MOVHU x+0(FP), R1
MOVH y+2(FP), R2
MOVWU x+0(FP), R1 // ERROR "invalid MOVWU of x\+0\(FP\); int16 is 2-byte value"
MOVW y+2(FP), R1 // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
MOVHU x+2(FP), R1 // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
MOVH y+0(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
MOVB y+4(FP), R2 // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
MOVH y+4(FP), R1 // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVW x+4(FP), R1 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
MOVW y+2(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
MOVB y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
MOVH y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
MOVW x+0(FP), R1 // 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), R1
MOVW x_hi+4(FP), R1
MOVW y+8(FP), R1 // 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), R1
MOVW y_hi+12(FP), R1
RET
TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
MOVB x+0(FP), R1 // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value"
MOVB y+4(FP), R2 // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int is 4-byte value"
MOVH y+4(FP), R1 // ERROR "invalid MOVH of y\+4\(FP\); uint is 4-byte value"
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVW x+4(FP), R1 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
MOVW y+2(FP), R1 // 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), R1 // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value"
MOVB y+4(FP), R2 // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value"
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 4-byte value"
MOVH y+4(FP), R1 // ERROR "invalid MOVH of y\+4\(FP\); \*byte is 4-byte value"
MOVW x+0(FP), R1
MOVW y+4(FP), R1
MOVW x+4(FP), R1 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
MOVW y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
MOVH c+8(FP), R1 // ERROR "invalid MOVH of c\+8\(FP\); chan int is 4-byte value"
MOVH m+12(FP), R1 // ERROR "invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value"
MOVH f+16(FP), R1 // 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), R1 // ERROR "invalid MOVH of x\+0\(FP\); string base is 4-byte value"
MOVW x+0(FP), R1
MOVH x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 4-byte value"
MOVW x_base+0(FP), R1
MOVH x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVW x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVH x_len+4(FP), R1 // ERROR "invalid MOVH of x_len\+4\(FP\); string len is 4-byte value"
MOVW x_len+4(FP), R1
MOVW y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)"
MOVW y_len+4(FP), R1 // 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), R1 // ERROR "invalid MOVH of x\+0\(FP\); slice base is 4-byte value"
MOVW x+0(FP), R1
MOVH x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value"
MOVW x_base+0(FP), R1
MOVH x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVW x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVH x_len+4(FP), R1 // ERROR "invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value"
MOVW x_len+4(FP), R1
MOVH x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
MOVW x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
MOVH x_cap+8(FP), R1 // ERROR "invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value"
MOVW x_cap+8(FP), R1
MOVW y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)"
MOVW y_len+4(FP), R1 // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)"
MOVW y_cap+8(FP), R1 // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)"
RET
TEXT ·argiface(SB),0,$0-16
MOVH x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); interface type is 4-byte value"
MOVW x+0(FP), R1
MOVH x_type+0(FP), R1 // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value"
MOVW x_type+0(FP), R1
MOVQ x_itable+0(FP), R1 // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
MOVQ x_itable+1(FP), R1 // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
MOVH x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
MOVW x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
MOVQ x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
MOVH x_data+4(FP), R1 // ERROR "invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value"
MOVW x_data+4(FP), R1
MOVH y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); interface itable is 4-byte value"
MOVW y+8(FP), R1
MOVH y_itable+8(FP), R1 // ERROR "invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value"
MOVW y_itable+8(FP), R1
MOVW 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\)"
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 ·returnbyte(SB),0,$0-5
MOVW x+0(FP), R1
MOVB R1, ret+4(FP)
MOVH R1, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value"
MOVW R1, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
MOVB R1, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
RET
TEXT ·returnbyte(SB),0,$0-5
MOVW x+0(FP), R1
MOVB R1, ret+4(FP)
MOVH R1, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value"
MOVW R1, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
MOVB R1, 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 R1, r1+4(FP)
MOVH R1, r2+8(FP)
MOVW R1, r3+12(FP)
MOVW R1, r3_base+12(FP)
MOVW R1, r3_len+16(FP)
MOVB R1, r4+20(FP)
MOVB R1, 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\)"
// 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.
{
// A variable declaration creates a new variable in the current scope.
x := atomic.AddUint64(&x, 1) // ERROR "declaration of .x. shadows declaration at atomic.go:16"
// Re-declaration assigns a new value.
x, w := atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value"
_ = w
}
}
type T struct{}
func (T) AddUint64(addr *uint64, delta uint64) uint64 { return 0 }
func NonAtomic() {
x := uint64(1)
var atomic T
x = atomic.AddUint64(&x, 1) // ok; not the imported pkg
}
// 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"
type T int
func (t T) Foo() int { return int(t) }
type FT func() int
var S []int
func RatherStupidConditions() {
var f, g func() int
if f() == 0 || f() == 0 { // OK f might have side effects
}
var t T
_ = t.Foo() == 2 || t.Foo() == 2 // OK Foo 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) // ERROR "redundant or: i == byte(1) || i == byte(1)"
_ = i == T(2) || i == T(2) // ERROR "redundant or: i == T(2) || i == T(2)"
_ = FT(f) == nil || FT(f) == nil // ERROR "redundant or: FT(f) == nil || FT(f) == nil"
_ = (func() int)(f) == nil || (func() int)(f) == nil // ERROR "redundant or: (func() int)(f) == nil || (func() int)(f) == nil"
_ = append(S, 3) == nil || append(S, 3) == nil // OK append has side effects
var namedFuncVar FT
_ = namedFuncVar() == namedFuncVar() // OK still func calls
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"
}
// 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 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 cgo checker.
package testdata
// void f(void *);
import "C"
import "unsafe"
func CgoTests() {
var c chan bool
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&c))) // ERROR "embedded pointer"
C.f(unsafe.Pointer(&c)) // ERROR "embedded pointer"
var m map[string]string
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&m))) // ERROR "embedded pointer"
C.f(unsafe.Pointer(&m)) // ERROR "embedded pointer"
var f func()
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&f))) // ERROR "embedded pointer"
C.f(unsafe.Pointer(&f)) // ERROR "embedded pointer"
var s []int
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s))) // ERROR "embedded pointer"
C.f(unsafe.Pointer(&s)) // ERROR "embedded pointer"
var a [1][]int
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a))) // ERROR "embedded pointer"
C.f(unsafe.Pointer(&a)) // ERROR "embedded pointer"
var st struct{ f []int }
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st))) // ERROR "embedded pointer"
C.f(unsafe.Pointer(&st)) // ERROR "embedded pointer"
// The following cases are OK.
var i int
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&i)))
C.f(unsafe.Pointer(&i))
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s[0])))
C.f(unsafe.Pointer(&s[0]))
var a2 [1]int
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a2)))
C.f(unsafe.Pointer(&a2))
var st2 struct{ i int }
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st2)))
C.f(unsafe.Pointer(&st2))
type cgoStruct struct{ p *cgoStruct }
C.f(unsafe.Pointer(&cgoStruct{}))
C.CBytes([]byte("hello"))
}
// Copyright 2017 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.
// Test the cgo checker on a file that doesn't use cgo, but has an
// import named "C".
package testdata
import C "fmt"
var _ = C.Println(*p(**p))
// Passing a pointer (via a slice), but C is fmt, not cgo.
var _ = C.Println([]int{3})
// 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 untagged struct literals.
package testdata
import (
"flag"
"go/scanner"
"image"
"unicode"
"path/to/unknownpkg"
)
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",
}
var Okay4 = []struct {
A int
B int
}{
{1, 2},
{3, 4},
}
type MyStruct struct {
X string
Y string
Z string
}
var Okay5 = &MyStruct{
"Name",
"Usage",
"DefValue",
}
var Okay6 = []MyStruct{
{"foo", "bar", "baz"},
{"aa", "bb", "cc"},
}
var Okay7 = []*MyStruct{
{"foo", "bar", "baz"},
{"aa", "bb", "cc"},
}
// Testing is awkward because we need to reference things from a separate package
// to trigger the warnings.
var goodStructLiteral = flag.Flag{
Name: "Name",
Usage: "Usage",
}
var badStructLiteral = flag.Flag{ // ERROR "unkeyed fields"
"Name",
"Usage",
nil, // Value
"DefValue",
}
// SpecialCase is a named slice of CaseRange to test issue 9171.
var goodNamedSliceLiteral = unicode.SpecialCase{
{Lo: 1, Hi: 2},
unicode.CaseRange{Lo: 1, Hi: 2},
}
var badNamedSliceLiteral = unicode.SpecialCase{
{1, 2}, // ERROR "unkeyed fields"
unicode.CaseRange{1, 2}, // ERROR "unkeyed fields"
}
// ErrorList is a named slice, so no warnings should be emitted.
var goodScannerErrorList = scanner.ErrorList{
&scanner.Error{Msg: "foobar"},
}
var badScannerErrorList = scanner.ErrorList{
&scanner.Error{"foobar"}, // ERROR "unkeyed fields"
}
// Check whitelisted structs: if vet is run with --compositewhitelist=false,
// this line triggers an error.
var whitelistedPoint = image.Point{1, 2}
// Do not check type from unknown package.
// See issue 15408.
var unknownPkgVar = unknownpkg.Foobar{"foo", "bar"}
// A named pointer slice of CaseRange to test issue 23539. In
// particular, we're interested in how some slice elements omit their
// type.
var goodNamedPointerSliceLiteral = []*unicode.CaseRange{
{Lo: 1, Hi: 2},
&unicode.CaseRange{Lo: 1, Hi: 2},
}
var badNamedPointerSliceLiteral = []*unicode.CaseRange{
{1, 2}, // ERROR "unkeyed fields"
&unicode.CaseRange{1, 2}, // ERROR "unkeyed fields"
}
package testdata
import (
"sync"
"sync/atomic"
"unsafe"
. "unsafe"
unsafe1 "unsafe"
)
func OkFunc() {
var x *sync.Mutex
p := x
var y sync.Mutex
p = &y
var z = sync.Mutex{}
w := sync.Mutex{}
w = sync.Mutex{}
q := struct{ L sync.Mutex }{
L: sync.Mutex{},
}
yy := []Tlock{
Tlock{},
Tlock{
once: sync.Once{},
},
}
nl := new(sync.Mutex)
mx := make([]sync.Mutex, 10)
xx := struct{ L *sync.Mutex }{
L: new(sync.Mutex),
}
}
type Tlock struct {
once sync.Once
}
func BadFunc() {
var x *sync.Mutex
p := x
var y sync.Mutex
p = &y
*p = *x // ERROR "assignment copies lock value to \*p: sync.Mutex"
var t Tlock
var tp *Tlock
tp = &t
*tp = t // ERROR "assignment copies lock value to \*tp: testdata.Tlock contains sync.Once contains sync.Mutex"
t = *tp // ERROR "assignment copies lock value to t: testdata.Tlock contains sync.Once contains sync.Mutex"
y := *x // ERROR "assignment copies lock value to y: sync.Mutex"
var z = t // ERROR "variable declaration copies lock value to z: testdata.Tlock contains sync.Once contains sync.Mutex"
w := struct{ L sync.Mutex }{
L: *x, // ERROR "literal copies lock value from \*x: sync.Mutex"
}
var q = map[int]Tlock{
1: t, // ERROR "literal copies lock value from t: testdata.Tlock contains sync.Once contains sync.Mutex"
2: *tp, // ERROR "literal copies lock value from \*tp: testdata.Tlock contains sync.Once contains sync.Mutex"
}
yy := []Tlock{
t, // ERROR "literal copies lock value from t: testdata.Tlock contains sync.Once contains sync.Mutex"
*tp, // ERROR "literal copies lock value from \*tp: testdata.Tlock contains sync.Once contains sync.Mutex"
}
// override 'new' keyword
new := func(interface{}) {}
new(t) // ERROR "call of new copies lock value: testdata.Tlock contains sync.Once contains sync.Mutex"
// copy of array of locks
var muA [5]sync.Mutex
muB := muA // ERROR "assignment copies lock value to muB: sync.Mutex"
muA = muB // ERROR "assignment copies lock value to muA: sync.Mutex"
muSlice := muA[:] // OK
// multidimensional array
var mmuA [5][5]sync.Mutex
mmuB := mmuA // ERROR "assignment copies lock value to mmuB: sync.Mutex"
mmuA = mmuB // ERROR "assignment copies lock value to mmuA: sync.Mutex"
mmuSlice := mmuA[:] // OK
// slice copy is ok
var fmuA [5][][5]sync.Mutex
fmuB := fmuA // OK
fmuA = fmuB // OK
fmuSlice := fmuA[:] // OK
}
func LenAndCapOnLockArrays() {
var a [5]sync.Mutex
aLen := len(a) // OK
aCap := cap(a) // OK
// override 'len' and 'cap' keywords
len := func(interface{}) {}
len(a) // ERROR "call of len copies lock value: sync.Mutex"
cap := func(interface{}) {}
cap(a) // ERROR "call of cap copies lock value: sync.Mutex"
}
func SizeofMutex() {
var mu sync.Mutex
unsafe.Sizeof(mu) // OK
unsafe1.Sizeof(mu) // OK
Sizeof(mu) // OK
unsafe := struct{ Sizeof func(interface{}) }{}
unsafe.Sizeof(mu) // ERROR "call of unsafe.Sizeof copies lock value: sync.Mutex"
Sizeof := func(interface{}) {}
Sizeof(mu) // ERROR "call of Sizeof copies lock value: sync.Mutex"
}
// SyncTypesCheck checks copying of sync.* types except sync.Mutex
func SyncTypesCheck() {
// sync.RWMutex copying
var rwmuX sync.RWMutex
var rwmuXX = sync.RWMutex{}
rwmuX1 := new(sync.RWMutex)
rwmuY := rwmuX // ERROR "assignment copies lock value to rwmuY: sync.RWMutex"
rwmuY = rwmuX // ERROR "assignment copies lock value to rwmuY: sync.RWMutex"
var rwmuYY = rwmuX // ERROR "variable declaration copies lock value to rwmuYY: sync.RWMutex"
rwmuP := &rwmuX
rwmuZ := &sync.RWMutex{}
// sync.Cond copying
var condX sync.Cond
var condXX = sync.Cond{}
condX1 := new(sync.Cond)
condY := condX // ERROR "assignment copies lock value to condY: sync.Cond contains sync.noCopy"
condY = condX // ERROR "assignment copies lock value to condY: sync.Cond contains sync.noCopy"
var condYY = condX // ERROR "variable declaration copies lock value to condYY: sync.Cond contains sync.noCopy"
condP := &condX
condZ := &sync.Cond{
L: &sync.Mutex{},
}
condZ = sync.NewCond(&sync.Mutex{})
// sync.WaitGroup copying
var wgX sync.WaitGroup
var wgXX = sync.WaitGroup{}
wgX1 := new(sync.WaitGroup)
wgY := wgX // ERROR "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy"
wgY = wgX // ERROR "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy"
var wgYY = wgX // ERROR "variable declaration copies lock value to wgYY: sync.WaitGroup contains sync.noCopy"
wgP := &wgX
wgZ := &sync.WaitGroup{}
// sync.Pool copying
var poolX sync.Pool
var poolXX = sync.Pool{}
poolX1 := new(sync.Pool)
poolY := poolX // ERROR "assignment copies lock value to poolY: sync.Pool contains sync.noCopy"
poolY = poolX // ERROR "assignment copies lock value to poolY: sync.Pool contains sync.noCopy"
var poolYY = poolX // ERROR "variable declaration copies lock value to poolYY: sync.Pool contains sync.noCopy"
poolP := &poolX
poolZ := &sync.Pool{}
// sync.Once copying
var onceX sync.Once
var onceXX = sync.Once{}
onceX1 := new(sync.Once)
onceY := onceX // ERROR "assignment copies lock value to onceY: sync.Once contains sync.Mutex"
onceY = onceX // ERROR "assignment copies lock value to onceY: sync.Once contains sync.Mutex"
var onceYY = onceX // ERROR "variable declaration copies lock value to onceYY: sync.Once contains sync.Mutex"
onceP := &onceX
onceZ := &sync.Once{}
}
// AtomicTypesCheck checks copying of sync/atomic types
func AtomicTypesCheck() {
// atomic.Value copying
var vX atomic.Value
var vXX = atomic.Value{}
vX1 := new(atomic.Value)
// These are OK because the value has not been used yet.
// (And vet can't tell whether it has been used, so they're always OK.)
vY := vX
vY = vX
var vYY = vX
vP := &vX
vZ := &atomic.Value{}
}
// 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 BadFunc2(sync.Map) {} // ERROR "BadFunc2 passes lock by value: sync.Map contains sync.Mutex"
func OkRet() *sync.Mutex {}
func BadRet() sync.Mutex {} // Don't warn about results
var (
OkClosure = func(*sync.Mutex) {}
BadClosure = func(sync.Mutex) {} // ERROR "func passes lock by value: sync.Mutex"
BadClosure2 = func(sync.Map) {} // ERROR "func passes lock by value: sync.Map contains 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 {} // Don't warn about results
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"
// Passing lock values into interface function arguments
func FuncCallInterfaceArg(f func(a int, b interface{})) {
var m sync.Mutex
var t struct{ lock sync.Mutex }
f(1, "foo")
f(2, &t)
f(3, &sync.Mutex{})
f(4, m) // ERROR "call of f copies lock value: sync.Mutex"
f(5, t) // ERROR "call of f copies lock value: struct.lock sync.Mutex. contains sync.Mutex"
var fntab []func(t)
fntab[0](t) // ERROR "call of fntab.0. copies lock value: struct.lock sync.Mutex. contains sync.Mutex"
}
// Returning lock via interface value
func ReturnViaInterface(x int) (int, interface{}) {
var m sync.Mutex
var t struct{ lock sync.Mutex }
switch x % 4 {
case 0:
return 0, "qwe"
case 1:
return 1, &sync.Mutex{}
case 2:
return 2, m // ERROR "return copies lock value: sync.Mutex"
default:
return 3, t // ERROR "return copies lock value: struct.lock sync.Mutex. contains sync.Mutex"
}
}
// Some cases that we don't warn about.
func AcceptedCases() {
x := EmbeddedRwMutex{} // composite literal on RHS is OK (#16227)
x = BadRet() // function call on RHS is OK (#16227)
x = *OKRet() // indirection of function call on RHS is OK (#16227)
}
// 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
}
func _() int {
// Empty switch tag with non-bool case value used to panic.
switch {
case 1:
println()
}
println()
}
// Test of examples with divergent packages.
// Package buf ...
package buf
// Buf is a ...
type Buf []byte
// Append ...
func (*Buf) Append([]byte) {}
func (Buf) Reset() {}
func (Buf) Len() int { return 0 }
// DefaultBuf is a ...
var DefaultBuf Buf
// Test of examples with divergent packages.
package buf_test
func Example() {} // OK because is package-level.
func Example_suffix() {} // OK because refers to suffix annotation.
func Example_BadSuffix() {} // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix"
func ExampleBuf() {} // OK because refers to known top-level type.
func ExampleBuf_Append() {} // OK because refers to known method.
func ExampleBuf_Clear() {} // ERROR "ExampleBuf_Clear refers to unknown field or method: Buf.Clear"
func ExampleBuf_suffix() {} // OK because refers to suffix annotation.
func ExampleBuf_Append_Bad() {} // ERROR "ExampleBuf_Append_Bad has malformed example suffix: Bad"
func ExampleBuf_Append_suffix() {} // OK because refers to known method with valid suffix.
func ExampleDefaultBuf() {} // OK because refers to top-level identifier.
func ExampleBuf_Reset() bool { return true } // ERROR "ExampleBuf_Reset should return nothing"
func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic"
// "Puffer" is German for "Buffer".
func ExamplePuffer() {} // ERROR "ExamplePuffer refers to unknown identifier: Puffer"
func ExamplePuffer_Append() {} // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
func ExamplePuffer_suffix() {} // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer"
package testdata
import (
"log"
"net/http"
)
func goodHTTPGet() {
res, err := http.Get("http://foo.com")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
}
func badHTTPGet() {
res, err := http.Get("http://foo.com")
defer res.Body.Close() // ERROR "using res before checking for errors"
if err != nil {
log.Fatal(err)
}
}
func badHTTPHead() {
res, err := http.Head("http://foo.com")
defer res.Body.Close() // ERROR "using res before checking for errors"
if err != nil {
log.Fatal(err)
}
}
func goodClientGet() {
client := http.DefaultClient
res, err := client.Get("http://foo.com")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
}
func badClientPtrGet() {
client := http.DefaultClient
resp, err := client.Get("http://foo.com")
defer resp.Body.Close() // ERROR "using resp before checking for errors"
if err != nil {
log.Fatal(err)
}
}
func badClientGet() {
client := http.Client{}
resp, err := client.Get("http://foo.com")
defer resp.Body.Close() // ERROR "using resp before checking for errors"
if err != nil {
log.Fatal(err)
}
}
func badClientPtrDo() {
client := http.DefaultClient
req, err := http.NewRequest("GET", "http://foo.com", nil)
if err != nil {
log.Fatal(err)
}
resp, err := client.Do(req)
defer resp.Body.Close() // ERROR "using resp before checking for errors"
if err != nil {
log.Fatal(err)
}
}
func badClientDo() {
var client http.Client
req, err := http.NewRequest("GET", "http://foo.com", nil)
if err != nil {
log.Fatal(err)
}
resp, err := client.Do(req)
defer resp.Body.Close() // ERROR "using resp before checking for errors"
if err != nil {
log.Fatal(err)
}
}
// Test of examples.
package testdata
func Example() {} // OK because is package-level.
func Example_suffix() // OK because refers to suffix annotation.
func Example_BadSuffix() // OK because non-test package was excluded. No false positives wanted.
func ExampleBuf() // OK because non-test package was excluded. No false positives wanted.
func ExampleBuf_Append() {} // OK because non-test package was excluded. No false positives wanted.
func ExampleBuf_Clear() {} // OK because non-test package was excluded. No false positives wanted.
func ExampleBuf_suffix() {} // OK because refers to suffix annotation.
func ExampleBuf_Append_Bad() {} // OK because non-test package was excluded. No false positives wanted.
func ExampleBuf_Append_suffix() {} // OK because refers to known method with valid suffix.
func ExampleBuf_Reset() bool { return true } // ERROR "ExampleBuf_Reset should return nothing"
func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic"
// "Puffer" is German for "Buffer".
func ExamplePuffer() // OK because non-test package was excluded. No false positives wanted.
func ExamplePuffer_Append() // OK because non-test package was excluded. No false positives wanted.
func ExamplePuffer_suffix() // OK because non-test package was excluded. No false positives wanted.
// Copyright 2016 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 (
"context"
"log"
"os"
"testing"
)
// Check the three functions and assignment forms (var, :=, =) we look for.
// (Do these early: line numbers are fragile.)
func _() {
var ctx, cancel = context.WithCancel() // ERROR "the cancel function is not used on all paths \(possible context leak\)"
} // ERROR "this return statement may be reached without using the cancel var defined on line 17"
func _() {
ctx, cancel2 := context.WithDeadline() // ERROR "the cancel2 function is not used..."
} // ERROR "may be reached without using the cancel2 var defined on line 21"
func _() {
var ctx context.Context
var cancel3 func()
ctx, cancel3 = context.WithTimeout() // ERROR "function is not used..."
} // ERROR "this return statement may be reached without using the cancel3 var defined on line 27"
func _() {
ctx, _ := context.WithCancel() // ERROR "the cancel function returned by context.WithCancel should be called, not discarded, to avoid a context leak"
ctx, _ = context.WithTimeout() // ERROR "the cancel function returned by context.WithTimeout should be called, not discarded, to avoid a context leak"
ctx, _ = context.WithDeadline() // ERROR "the cancel function returned by context.WithDeadline should be called, not discarded, to avoid a context leak"
}
func _() {
ctx, cancel := context.WithCancel()
defer cancel() // ok
}
func _() {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
if condition {
cancel()
}
return // ERROR "this return statement may be reached without using the cancel var"
}
func _() {
ctx, cancel := context.WithCancel()
if condition {
cancel()
} else {
// ok: infinite loop
for {
print(0)
}
}
}
func _() {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
if condition {
cancel()
} else {
for i := 0; i < 10; i++ {
print(0)
}
}
} // ERROR "this return statement may be reached without using the cancel var"
func _() {
ctx, cancel := context.WithCancel()
// ok: used on all paths
switch someInt {
case 0:
new(testing.T).FailNow()
case 1:
log.Fatal()
case 2:
cancel()
case 3:
print("hi")
fallthrough
default:
os.Exit(1)
}
}
func _() {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
switch someInt {
case 0:
new(testing.T).FailNow()
case 1:
log.Fatal()
case 2:
cancel()
case 3:
print("hi") // falls through to implicit return
default:
os.Exit(1)
}
} // ERROR "this return statement may be reached without using the cancel var"
func _(ch chan int) int {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
select {
case <-ch:
new(testing.T).FailNow()
case y <- ch:
print("hi") // falls through to implicit return
case ch <- 1:
cancel()
default:
os.Exit(1)
}
} // ERROR "this return statement may be reached without using the cancel var"
func _(ch chan int) int {
ctx, cancel := context.WithCancel()
// A blocking select must execute one of its cases.
select {
case <-ch:
panic()
}
}
func _() {
go func() {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
print(ctx)
}() // ERROR "may be reached without using the cancel var"
}
var condition bool
var someInt int
// Regression test for Go issue 16143.
func _() {
var x struct{ f func() }
x.f()
}
// Regression test for Go issue 16230.
func _() (ctx context.Context, cancel func()) {
ctx, cancel = context.WithCancel()
return // a naked return counts as a load of the named result values
}
// Same as above, but for literal function.
var _ = func() (ctx context.Context, cancel func()) {
ctx, cancel = context.WithCancel()
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 "loop variable i captured by func literal"
println(v) // ERROR "loop variable v captured by func literal"
}()
}
for i, v := range s {
defer func() {
println(i) // ERROR "loop variable i captured by func literal"
println(v) // ERROR "loop variable v captured by func literal"
}()
}
for i := range s {
go func() {
println(i) // ERROR "loop variable i captured by func literal"
}()
}
for _, v := range s {
go func() {
println(v) // ERROR "loop 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 "loop variable f captured by func literal"
}()
}
type T struct {
v int
}
for _, v := range s {
go func() {
_ = T{v: 1}
_ = []int{v: 1} // ERROR "loop variable v captured by func literal"
}()
}
// ordinary for-loops
for i := 0; i < 10; i++ {
go func() {
print(i) // ERROR "loop variable i captured by func literal"
}()
}
for i, j := 0, 1; i < 100; i, j = j, i+j {
go func() {
print(j) // ERROR "loop variable j captured by func literal"
}()
}
type cons struct {
car int
cdr *cons
}
var head *cons
for p := head; p != nil; p = p.next {
go func() {
print(p.car) // ERROR "loop variable p 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 shadow.go:13"
if err != nil {
return err
}
i := 3 // OK
_ = i
}
if f != nil {
x := one() // ERROR "declaration of .x. shadows declaration at shadow.go:14"
var _, err = f.Read(buf) // ERROR "declaration of .err. shadows declaration at shadow.go:13"
if x == 1 && 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 shadow.go:14"
_, _, _ = x, f, shadowTemp
}
// Use a couple of variables to trigger shadowing errors.
_, _ = err, x
return
}
func one() int {
return 1
}
// Must not complain with an internal error for the
// implicitly declared type switch variable v.
func issue26725(x interface{}) int {
switch v := x.(type) {
case int, int32:
if v, ok := x.(int); ok {
return v
}
case int64:
return int(v)
}
return 0
}
// Verify that implicitly declared variables from
// type switches are considered in shadowing analysis.
func shadowTypeSwitch(a interface{}) {
switch t := a.(type) {
case int:
{
t := 0 // ERROR "declaration of .t. shadows declaration at shadow.go:78"
_ = t
}
_ = t
case uint:
{
t := uint(0) // OK because t is not mentioned later in this function
_ = t
}
}
}
// 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
import (
"fmt"
"unsafe"
)
func ShiftTest() {
var i8 int8
_ = i8 << 7
_ = (i8 + 1) << 8 // ERROR ".i8 . 1. .8 bits. too small for shift of 8"
_ = i8 << (7 + 1) // ERROR "i8 .8 bits. too small for shift of 8"
_ = i8 >> 8 // ERROR "i8 .8 bits. too small for shift of 8"
i8 <<= 8 // ERROR "i8 .8 bits. too small for shift of 8"
i8 >>= 8 // ERROR "i8 .8 bits. too small for shift of 8"
var i16 int16
_ = i16 << 15
_ = i16 << 16 // ERROR "i16 .16 bits. too small for shift of 16"
_ = i16 >> 16 // ERROR "i16 .16 bits. too small for shift of 16"
i16 <<= 16 // ERROR "i16 .16 bits. too small for shift of 16"
i16 >>= 16 // ERROR "i16 .16 bits. too small for shift of 16"
var i32 int32
_ = i32 << 31
_ = i32 << 32 // ERROR "i32 .32 bits. too small for shift of 32"
_ = i32 >> 32 // ERROR "i32 .32 bits. too small for shift of 32"
i32 <<= 32 // ERROR "i32 .32 bits. too small for shift of 32"
i32 >>= 32 // ERROR "i32 .32 bits. too small for shift of 32"
var i64 int64
_ = i64 << 63
_ = i64 << 64 // ERROR "i64 .64 bits. too small for shift of 64"
_ = i64 >> 64 // ERROR "i64 .64 bits. too small for shift of 64"
i64 <<= 64 // ERROR "i64 .64 bits. too small for shift of 64"
i64 >>= 64 // ERROR "i64 .64 bits. too small for shift of 64"
var u8 uint8
_ = u8 << 7
_ = u8 << 8 // ERROR "u8 .8 bits. too small for shift of 8"
_ = u8 >> 8 // ERROR "u8 .8 bits. too small for shift of 8"
u8 <<= 8 // ERROR "u8 .8 bits. too small for shift of 8"
u8 >>= 8 // ERROR "u8 .8 bits. too small for shift of 8"
var u16 uint16
_ = u16 << 15
_ = u16 << 16 // ERROR "u16 .16 bits. too small for shift of 16"
_ = u16 >> 16 // ERROR "u16 .16 bits. too small for shift of 16"
u16 <<= 16 // ERROR "u16 .16 bits. too small for shift of 16"
u16 >>= 16 // ERROR "u16 .16 bits. too small for shift of 16"
var u32 uint32
_ = u32 << 31
_ = u32 << 32 // ERROR "u32 .32 bits. too small for shift of 32"
_ = u32 >> 32 // ERROR "u32 .32 bits. too small for shift of 32"
u32 <<= 32 // ERROR "u32 .32 bits. too small for shift of 32"
u32 >>= 32 // ERROR "u32 .32 bits. too small for shift of 32"
var u64 uint64
_ = u64 << 63
_ = u64 << 64 // ERROR "u64 .64 bits. too small for shift of 64"
_ = u64 >> 64 // ERROR "u64 .64 bits. too small for shift of 64"
u64 <<= 64 // ERROR "u64 .64 bits. too small for shift of 64"
u64 >>= 64 // ERROR "u64 .64 bits. too small for shift of 64"
_ = u64 << u64 // Non-constant shifts should succeed.
var i int
_ = i << 31
const in = 8 * unsafe.Sizeof(i)
_ = i << in // ERROR "too small for shift"
_ = i >> in // ERROR "too small for shift"
i <<= in // ERROR "too small for shift"
i >>= in // ERROR "too small for shift"
const ix = 8*unsafe.Sizeof(i) - 1
_ = i << ix
_ = i >> ix
i <<= ix
i >>= ix
var u uint
_ = u << 31
const un = 8 * unsafe.Sizeof(u)
_ = u << un // ERROR "too small for shift"
_ = u >> un // ERROR "too small for shift"
u <<= un // ERROR "too small for shift"
u >>= un // ERROR "too small for shift"
const ux = 8*unsafe.Sizeof(u) - 1
_ = u << ux
_ = u >> ux
u <<= ux
u >>= ux
var p uintptr
_ = p << 31
const pn = 8 * unsafe.Sizeof(p)
_ = p << pn // ERROR "too small for shift"
_ = p >> pn // ERROR "too small for shift"
p <<= pn // ERROR "too small for shift"
p >>= pn // ERROR "too small for shift"
const px = 8*unsafe.Sizeof(p) - 1
_ = p << px
_ = p >> px
p <<= px
p >>= px
const oneIf64Bit = ^uint(0) >> 63 // allow large shifts of constants; they are used for 32/64 bit compatibility tricks
var h uintptr
h = h<<8 | (h >> (8 * (unsafe.Sizeof(h) - 1)))
h <<= 8 * unsafe.Sizeof(h) // ERROR "too small for shift"
h >>= 7 * unsafe.Alignof(h)
h >>= 8 * unsafe.Alignof(h) // ERROR "too small for shift"
}
func ShiftDeadCode() {
var i int
const iBits = 8 * unsafe.Sizeof(i)
if iBits <= 32 {
if iBits == 16 {
_ = i >> 8
} else {
_ = i >> 16
}
} else {
_ = i >> 32
}
if iBits >= 64 {
_ = i << 32
if iBits == 128 {
_ = i << 64
}
} else {
_ = i << 16
}
if iBits == 64 {
_ = i << 32
}
switch iBits {
case 128, 64:
_ = i << 32
default:
_ = i << 16
}
switch {
case iBits < 32:
_ = i << 16
case iBits > 64:
_ = i << 64
default:
_ = i << 64 // ERROR "too small for shift"
}
// Make sure other vet checks work in dead code.
if iBits == 1024 {
_ = i << 512 // OK
fmt.Printf("foo %s bar", 123) // ERROR "Printf"
}
}
// Copyright 2017 The Go Authors. All rights reserved.
// 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.
// Used by TestVetVerbose to test that vet -v doesn't fail because it
// can't find "C".
// This file contains declarations to test the assembly in asm1.s.
package testdata
import "C"
func F() {
}
func arg1(x int8, y uint8)
......@@ -3,24 +3,6 @@
// 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
TEXT ·arg1(SB),0,$0-2
MOVW x+0(FP), AX // ERROR "\[amd64\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
......@@ -4,7 +4,7 @@
// This file contains tests for the useless-assignment checker.
package testdata
package assign
import "math/rand"
......
// 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 atomic
import "sync/atomic"
func AtomicTests() {
x := uint64(1)
x = atomic.AddUint64(&x, 1) // ERROR "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 tests for the bool checker.
package bool
func _() {
var f, g func() int
if v, w := f(), g(); v == w || v == w { // ERROR "redundant or: v == w || v == w"
}
}
// 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 cgo checker.
package testdata
// void f(void *p) {}
import "C"
import "unsafe"
func CgoTests() {
var c chan bool
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&c))) // ERROR "embedded pointer"
C.f(unsafe.Pointer(&c)) // ERROR "embedded pointer"
}
// 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 untagged struct literals.
package composite
import "flag"
// Testing is awkward because we need to reference things from a separate package
// to trigger the warnings.
var goodStructLiteral = flag.Flag{
Name: "Name",
Usage: "Usage",
}
var badStructLiteral = flag.Flag{ // ERROR "unkeyed fields"
"Name",
"Usage",
nil, // Value
"DefValue",
}
package copylock
import "sync"
func BadFunc() {
var x *sync.Mutex
p := x
var y sync.Mutex
p = &y
*p = *x // ERROR "assignment copies lock value to \*p: 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.
// This file contains tests for the dead code checker.
package deadcode
func _() int {
print(1)
return 2
println() // ERROR "unreachable code"
return 3
}
package httpresponse
import (
"log"
"net/http"
)
func goodHTTPGet() {
res, err := http.Get("http://foo.com")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
}
func badHTTPGet() {
res, err := http.Get("http://foo.com")
defer res.Body.Close() // ERROR "using res before checking for errors"
if err != nil {
log.Fatal(err)
}
}
......@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Test the cgo checker on a file that doesn't use cgo.
package lostcancel
package testdata
import "context"
var _ = C.f(*p(**p))
// Passing a pointer (via the slice), but C isn't cgo.
var _ = C.f([]int{3})
func _() {
var _, cancel = context.WithCancel(context.Background()) // ERROR "the cancel function is not used on all paths \(possible context leak\)"
if false {
_ = cancel
}
} // ERROR "this return statement may be reached without using the cancel var defined on line 10"
......@@ -2,21 +2,13 @@
// 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
package method
import (
"fmt"
)
import "fmt"
type MethodTest int
func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan\(fmt\.ScanState, rune\) error"
}
type MethodTestInterface interface {
ReadByte() byte // ERROR "should have signature ReadByte\(\) \(byte, error\)"
}
......@@ -2,34 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package testdata
package nilfunc
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")
}
......@@ -4,7 +4,7 @@
// This file contains tests for the printf checker.
package testdata
package print
import (
"fmt"
......@@ -126,16 +126,16 @@ func PrintfTests() {
fmt.Printf("%U", x) // ERROR "Printf format %U has arg x of wrong type float64"
fmt.Printf("%x", nil) // ERROR "Printf format %x has arg nil of wrong type untyped nil"
fmt.Printf("%X", 2.3) // ERROR "Printf format %X has arg 2.3 of wrong type float64"
fmt.Printf("%s", stringerv) // ERROR "Printf format %s has arg stringerv of wrong type testdata.ptrStringer"
fmt.Printf("%t", stringerv) // ERROR "Printf format %t has arg stringerv of wrong type testdata.ptrStringer"
fmt.Printf("%s", embeddedStringerv) // ERROR "Printf format %s has arg embeddedStringerv of wrong type testdata.embeddedStringer"
fmt.Printf("%t", embeddedStringerv) // ERROR "Printf format %t has arg embeddedStringerv of wrong type testdata.embeddedStringer"
fmt.Printf("%q", notstringerv) // ERROR "Printf format %q has arg notstringerv of wrong type testdata.notstringer"
fmt.Printf("%t", notstringerv) // ERROR "Printf format %t has arg notstringerv of wrong type testdata.notstringer"
fmt.Printf("%t", stringerarrayv) // ERROR "Printf format %t has arg stringerarrayv of wrong type testdata.stringerarray"
fmt.Printf("%t", notstringerarrayv) // ERROR "Printf format %t has arg notstringerarrayv of wrong type testdata.notstringerarray"
fmt.Printf("%q", notstringerarrayv) // ERROR "Printf format %q has arg notstringerarrayv of wrong type testdata.notstringerarray"
fmt.Printf("%d", BoolFormatter(true)) // ERROR "Printf format %d has arg BoolFormatter\(true\) of wrong type testdata.BoolFormatter"
fmt.Printf("%s", stringerv) // ERROR "Printf format %s has arg stringerv of wrong type print.ptrStringer"
fmt.Printf("%t", stringerv) // ERROR "Printf format %t has arg stringerv of wrong type print.ptrStringer"
fmt.Printf("%s", embeddedStringerv) // ERROR "Printf format %s has arg embeddedStringerv of wrong type print.embeddedStringer"
fmt.Printf("%t", embeddedStringerv) // ERROR "Printf format %t has arg embeddedStringerv of wrong type print.embeddedStringer"
fmt.Printf("%q", notstringerv) // ERROR "Printf format %q has arg notstringerv of wrong type print.notstringer"
fmt.Printf("%t", notstringerv) // ERROR "Printf format %t has arg notstringerv of wrong type print.notstringer"
fmt.Printf("%t", stringerarrayv) // ERROR "Printf format %t has arg stringerarrayv of wrong type print.stringerarray"
fmt.Printf("%t", notstringerarrayv) // ERROR "Printf format %t has arg notstringerarrayv of wrong type print.notstringerarray"
fmt.Printf("%q", notstringerarrayv) // ERROR "Printf format %q has arg notstringerarrayv of wrong type print.notstringerarray"
fmt.Printf("%d", BoolFormatter(true)) // ERROR "Printf format %d has arg BoolFormatter\(true\) of wrong type print.BoolFormatter"
fmt.Printf("%z", FormatterVal(true)) // correct (the type is responsible for formatting)
fmt.Printf("%d", FormatterVal(true)) // correct (the type is responsible for formatting)
fmt.Printf("%s", nonemptyinterface) // correct (the type is responsible for formatting)
......@@ -186,10 +186,10 @@ func PrintfTests() {
Printf("d%", 2) // ERROR "Printf format % is missing verb at end of string"
Printf("%d", percentDV)
Printf("%d", &percentDV)
Printf("%d", notPercentDV) // ERROR "Printf format %d has arg notPercentDV of wrong type testdata.notPercentDStruct"
Printf("%d", &notPercentDV) // ERROR "Printf format %d has arg &notPercentDV of wrong type \*testdata.notPercentDStruct"
Printf("%d", notPercentDV) // ERROR "Printf format %d has arg notPercentDV of wrong type print.notPercentDStruct"
Printf("%d", &notPercentDV) // ERROR "Printf format %d has arg &notPercentDV of wrong type \*print.notPercentDStruct"
Printf("%p", &notPercentDV) // Works regardless: we print it as a pointer.
Printf("%q", &percentDV) // ERROR "Printf format %q has arg &percentDV of wrong type \*testdata.percentDStruct"
Printf("%q", &percentDV) // ERROR "Printf format %q has arg &percentDV of wrong type \*print.percentDStruct"
Printf("%s", percentSV)
Printf("%s", &percentSV)
// Good argument reorderings.
......@@ -234,7 +234,7 @@ func PrintfTests() {
Printf("%T", someFunction) // ok: maybe someone wants to see the type
// Bug: used to recur forever.
Printf("%p %x", recursiveStructV, recursiveStructV.next)
Printf("%p %x", recursiveStruct1V, recursiveStruct1V.next) // ERROR "Printf format %x has arg recursiveStruct1V\.next of wrong type \*testdata\.RecursiveStruct2"
Printf("%p %x", recursiveStruct1V, recursiveStruct1V.next) // ERROR "Printf format %x has arg recursiveStruct1V\.next of wrong type \*print\.RecursiveStruct2"
Printf("%p %x", recursiveSliceV, recursiveSliceV)
Printf("%p %x", recursiveMapV, recursiveMapV)
// Special handling for Log.
......@@ -250,7 +250,7 @@ func PrintfTests() {
// Multiple string arguments before variadic args
errorf("WARNING", "foobar") // OK
errorf("INFO", "s=%s, n=%d", "foo", 1) // OK
errorf("ERROR", "%d") // no error "errorf format %d reads arg #1, but call has 0 args"
errorf("ERROR", "%d") // ERROR "errorf format %d reads arg #1, but call has 0 args"
// Printf from external package
// externalprintf.Printf("%d", 42) // OK
......@@ -587,37 +587,37 @@ func UnexportedStringerOrError() {
fmt.Printf("%s", unexportedInterface{3}) // ok; we can't see the problem
us := unexportedStringer{}
fmt.Printf("%s", us) // ERROR "Printf format %s has arg us of wrong type testdata.unexportedStringer"
fmt.Printf("%s", &us) // ERROR "Printf format %s has arg &us of wrong type [*]testdata.unexportedStringer"
fmt.Printf("%s", us) // ERROR "Printf format %s has arg us of wrong type print.unexportedStringer"
fmt.Printf("%s", &us) // ERROR "Printf format %s has arg &us of wrong type [*]print.unexportedStringer"
usf := unexportedStringerOtherFields{
s: "foo",
S: "bar",
}
fmt.Printf("%s", usf) // ERROR "Printf format %s has arg usf of wrong type testdata.unexportedStringerOtherFields"
fmt.Printf("%s", &usf) // ERROR "Printf format %s has arg &usf of wrong type [*]testdata.unexportedStringerOtherFields"
fmt.Printf("%s", usf) // ERROR "Printf format %s has arg usf of wrong type print.unexportedStringerOtherFields"
fmt.Printf("%s", &usf) // ERROR "Printf format %s has arg &usf of wrong type [*]print.unexportedStringerOtherFields"
ue := unexportedError{
e: &errorer{},
}
fmt.Printf("%s", ue) // ERROR "Printf format %s has arg ue of wrong type testdata.unexportedError"
fmt.Printf("%s", &ue) // ERROR "Printf format %s has arg &ue of wrong type [*]testdata.unexportedError"
fmt.Printf("%s", ue) // ERROR "Printf format %s has arg ue of wrong type print.unexportedError"
fmt.Printf("%s", &ue) // ERROR "Printf format %s has arg &ue of wrong type [*]print.unexportedError"
uef := unexportedErrorOtherFields{
s: "foo",
e: &errorer{},
S: "bar",
}
fmt.Printf("%s", uef) // ERROR "Printf format %s has arg uef of wrong type testdata.unexportedErrorOtherFields"
fmt.Printf("%s", &uef) // ERROR "Printf format %s has arg &uef of wrong type [*]testdata.unexportedErrorOtherFields"
fmt.Printf("%s", uef) // ERROR "Printf format %s has arg uef of wrong type print.unexportedErrorOtherFields"
fmt.Printf("%s", &uef) // ERROR "Printf format %s has arg &uef of wrong type [*]print.unexportedErrorOtherFields"
uce := unexportedCustomError{
e: errorer{},
}
fmt.Printf("%s", uce) // ERROR "Printf format %s has arg uce of wrong type testdata.unexportedCustomError"
fmt.Printf("%s", uce) // ERROR "Printf format %s has arg uce of wrong type print.unexportedCustomError"
uei := unexportedErrorInterface{}
fmt.Printf("%s", uei) // ERROR "Printf format %s has arg uei of wrong type testdata.unexportedErrorInterface"
fmt.Printf("%s", uei) // ERROR "Printf format %s has arg uei of wrong type print.unexportedErrorInterface"
fmt.Println("foo\n", "bar") // not an error
fmt.Println("foo\n") // ERROR "Println arg list ends with redundant newline"
......@@ -627,7 +627,7 @@ func UnexportedStringerOrError() {
intSlice := []int{3, 4}
fmt.Printf("%s", intSlice) // ERROR "Printf format %s has arg intSlice of wrong type \[\]int"
nonStringerArray := [1]unexportedStringer{{}}
fmt.Printf("%s", nonStringerArray) // ERROR "Printf format %s has arg nonStringerArray of wrong type \[1\]testdata.unexportedStringer"
fmt.Printf("%s", nonStringerArray) // ERROR "Printf format %s has arg nonStringerArray of wrong type \[1\]print.unexportedStringer"
fmt.Printf("%s", []stringer{3, 4}) // not an error
fmt.Printf("%s", [2]stringer{3, 4}) // not an error
}
......@@ -677,5 +677,5 @@ func PointersToCompoundTypes() {
type T1 struct {
X *T2
}
fmt.Printf("%s\n", T1{&T2{"x"}}) // ERROR "Printf format %s has arg T1{&T2{.x.}} of wrong type testdata\.T1"
fmt.Printf("%s\n", T1{&T2{"x"}}) // ERROR "Printf format %s has arg T1{&T2{.x.}} of wrong type print\.T1"
}
// 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 rangeloop
func RangeLoopTests() {
var s []int
for i, v := range s {
go func() {
println(i) // ERROR "loop variable i captured by func literal"
println(v) // ERROR "loop variable v captured by func literal"
}()
}
}
// 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 shift
func ShiftTest() {
var i8 int8
_ = i8 << 7
_ = (i8 + 1) << 8 // ERROR ".i8 . 1. .8 bits. too small for shift of 8"
}
// 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 structtag
type StructTagTest struct {
A int "hello" // ERROR "`hello` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair"
}
......@@ -6,5 +6,8 @@
package main
import "fmt"
func main() {
fmt.Printf("%s", 0)
}
......@@ -6,5 +6,8 @@
package main
func ignore() {
import "fmt"
func main() {
fmt.Printf("%s", 0)
}
package testdata
func Example_BadSuffix() {} // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix"
// Copyright 2018 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 unmarshal checker.
package unmarshal
import "encoding/json"
func _() {
type t struct {
a int
}
var v t
json.Unmarshal([]byte{}, v) // ERROR "call of Unmarshal passes non-pointer as second argument"
}
// 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 unsafeptr
import "unsafe"
func _() {
var x unsafe.Pointer
var y uintptr
x = unsafe.Pointer(y) // ERROR "possible misuse of unsafe.Pointer"
_ = x
}
......@@ -4,26 +4,10 @@
// This file contains tests for the unusedresult checker.
package testdata
package unused
import (
"bytes"
"errors"
"fmt"
)
import "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 test for canonical struct tags.
package testdata
import "encoding/xml"
type StructTagTest struct {
A int "hello" // ERROR "`hello` 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"
I int `x:"foo",y:"bar"` // ERROR "not compatible with reflect.StructTag.Get: key:.value. pairs not separated by spaces"
J int `x:"foo"y:"bar"` // ERROR "not compatible with reflect.StructTag.Get: key:.value. pairs not separated by spaces"
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
}
type AnonymousJSON struct{}
type AnonymousXML struct{}
type AnonymousJSONField struct {
DuplicateAnonJSON int `json:"a"`
A int "hello" // ERROR "`hello` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair"
}
type DuplicateJSONFields struct {
JSON int `json:"a"`
DuplicateJSON int `json:"a"` // ERROR "struct field DuplicateJSON repeats json tag .a. also at structtag.go:52"
IgnoredJSON int `json:"-"`
OtherIgnoredJSON int `json:"-"`
OmitJSON int `json:",omitempty"`
OtherOmitJSON int `json:",omitempty"`
DuplicateOmitJSON int `json:"a,omitempty"` // ERROR "struct field DuplicateOmitJSON repeats json tag .a. also at structtag.go:52"
NonJSON int `foo:"a"`
DuplicateNonJSON int `foo:"a"`
Embedded struct {
DuplicateJSON int `json:"a"` // OK because it's not in the same struct type
}
AnonymousJSON `json:"a"` // ERROR "struct field AnonymousJSON repeats json tag .a. also at structtag.go:52"
AnonymousJSONField // ERROR "struct field DuplicateAnonJSON repeats json tag .a. also at structtag.go:52"
XML int `xml:"a"`
DuplicateXML int `xml:"a"` // ERROR "struct field DuplicateXML repeats xml tag .a. also at structtag.go:68"
IgnoredXML int `xml:"-"`
OtherIgnoredXML int `xml:"-"`
OmitXML int `xml:",omitempty"`
OtherOmitXML int `xml:",omitempty"`
DuplicateOmitXML int `xml:"a,omitempty"` // ERROR "struct field DuplicateOmitXML repeats xml tag .a. also at structtag.go:68"
NonXML int `foo:"a"`
DuplicateNonXML int `foo:"a"`
Embedded2 struct {
DuplicateXML int `xml:"a"` // OK because it's not in the same struct type
}
AnonymousXML `xml:"a"` // ERROR "struct field AnonymousXML repeats xml tag .a. also at structtag.go:68"
Attribute struct {
XMLName xml.Name `xml:"b"`
NoDup int `xml:"b"` // OK because XMLName above affects enclosing struct.
Attr int `xml:"b,attr"` // OK because <b b="0"><b>0</b></b> is valid.
DupAttr int `xml:"b,attr"` // ERROR "struct field DupAttr repeats xml attribute tag .b. also at structtag.go:84"
DupOmitAttr int `xml:"b,omitempty,attr"` // ERROR "struct field DupOmitAttr repeats xml attribute tag .b. also at structtag.go:84"
AnonymousXML `xml:"b,attr"` // ERROR "struct field AnonymousXML repeats xml attribute tag .b. also at structtag.go:84"
}
AnonymousJSONField `json:"not_anon"` // ok; fields aren't embedded in JSON
AnonymousJSONField `json:"-"` // ok; entire field is ignored in JSON
}
type UnexpectedSpacetest struct {
A int `json:"a,omitempty"`
B int `json:"b, omitempty"` // ERROR "suspicious space in struct tag value"
C int `json:"c ,omitempty"`
D int `json:"d,omitempty, string"` // ERROR "suspicious space in struct tag value"
E int `xml:"e local"`
F int `xml:"f "` // ERROR "suspicious space in struct tag value"
G int `xml:" g"` // ERROR "suspicious space in struct tag value"
H int `xml:"h ,omitempty"` // ERROR "suspicious space in struct tag value"
I int `xml:"i, omitempty"` // ERROR "suspicious space in struct tag value"
J int `xml:"j local ,omitempty"` // ERROR "suspicious space in struct tag value"
K int `xml:"k local, omitempty"` // ERROR "suspicious space in struct tag value"
L int `xml:" l local,omitempty"` // ERROR "suspicious space in struct tag value"
M int `xml:"m local,omitempty"` // ERROR "suspicious space in struct tag value"
N int `xml:" "` // ERROR "suspicious space in struct tag value"
O int `xml:""`
P int `xml:","`
Q int `foo:" doesn't care "`
}
package testdata
import (
"testing"
)
// Buf is a ...
type Buf []byte
// Append ...
func (*Buf) Append([]byte) {}
func (Buf) Reset() {}
func (Buf) Len() int { return 0 }
// DefaultBuf is a ...
var DefaultBuf Buf
func Example() {} // OK because is package-level.
func Example_goodSuffix() // OK because refers to suffix annotation.
func Example_BadSuffix() // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix"
func ExampleBuf() // OK because refers to known top-level type.
func ExampleBuf_Append() {} // OK because refers to known method.
func ExampleBuf_Clear() {} // ERROR "ExampleBuf_Clear refers to unknown field or method: Buf.Clear"
func ExampleBuf_suffix() {} // OK because refers to suffix annotation.
func ExampleBuf_Append_Bad() {} // ERROR "ExampleBuf_Append_Bad has malformed example suffix: Bad"
func ExampleBuf_Append_suffix() {} // OK because refers to known method with valid suffix.
func ExampleDefaultBuf() {} // OK because refers to top-level identifier.
func ExampleBuf_Reset() bool { return true } // ERROR "ExampleBuf_Reset should return nothing"
func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic"
// "Puffer" is German for "Buffer".
func ExamplePuffer() // ERROR "ExamplePuffer refers to unknown identifier: Puffer"
func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer"
func nonTest() {} // OK because it doesn't start with "Test".
func (Buf) TesthasReceiver() {} // OK because it has a receiver.
func TestOKSuffix(*testing.T) {} // OK because first char after "Test" is Uppercase.
func TestÜnicodeWorks(*testing.T) {} // OK because the first char after "Test" is Uppercase.
func TestbadSuffix(*testing.T) {} // ERROR "first letter after 'Test' must not be lowercase"
func TestemptyImportBadSuffix(*T) {} // ERROR "first letter after 'Test' must not be lowercase"
func Test(*testing.T) {} // OK "Test" on its own is considered a test.
func Testify() {} // OK because it takes no parameters.
func TesttooManyParams(*testing.T, string) {} // OK because it takes too many parameters.
func TesttooManyNames(a, b *testing.T) {} // OK because it takes too many names.
func TestnoTParam(string) {} // OK because it doesn't take a *testing.T
func BenchmarkbadSuffix(*testing.B) {} // ERROR "first letter after 'Benchmark' must not be lowercase"
// Copyright 2018 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 unmarshal checker.
package testdata
import (
"bytes"
"encoding/gob"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
)
func _() {
type t struct {
a int
}
var v t
var r io.Reader
json.Unmarshal([]byte{}, v) // ERROR "call of Unmarshal passes non-pointer as second argument"
json.Unmarshal([]byte{}, &v)
json.NewDecoder(r).Decode(v) // ERROR "call of Decode passes non-pointer"
json.NewDecoder(r).Decode(&v)
gob.NewDecoder(r).Decode(v) // ERROR "call of Decode passes non-pointer"
gob.NewDecoder(r).Decode(&v)
xml.Unmarshal([]byte{}, v) // ERROR "call of Unmarshal passes non-pointer as second argument"
xml.Unmarshal([]byte{}, &v)
xml.NewDecoder(r).Decode(v) // ERROR "call of Decode passes non-pointer"
xml.NewDecoder(r).Decode(&v)
var p *t
json.Unmarshal([]byte{}, p)
json.Unmarshal([]byte{}, *p) // ERROR "call of Unmarshal passes non-pointer as second argument"
json.NewDecoder(r).Decode(p)
json.NewDecoder(r).Decode(*p) // ERROR "call of Decode passes non-pointer"
gob.NewDecoder(r).Decode(p)
gob.NewDecoder(r).Decode(*p) // ERROR "call of Decode passes non-pointer"
xml.Unmarshal([]byte{}, p)
xml.Unmarshal([]byte{}, *p) // ERROR "call of Unmarshal passes non-pointer as second argument"
xml.NewDecoder(r).Decode(p)
xml.NewDecoder(r).Decode(*p) // ERROR "call of Decode passes non-pointer"
var i interface{}
json.Unmarshal([]byte{}, i)
json.NewDecoder(r).Decode(i)
json.Unmarshal([]byte{}, nil) // ERROR "call of Unmarshal passes non-pointer as second argument"
json.Unmarshal([]byte{}, []t{}) // ERROR "call of Unmarshal passes non-pointer as second argument"
json.Unmarshal([]byte{}, map[string]int{}) // ERROR "call of Unmarshal passes non-pointer as second argument"
json.NewDecoder(r).Decode(nil) // ERROR "call of Decode passes non-pointer"
json.NewDecoder(r).Decode([]t{}) // ERROR "call of Decode passes non-pointer"
json.NewDecoder(r).Decode(map[string]int{}) // ERROR "call of Decode passes non-pointer"
json.Unmarshal(func() ([]byte, interface{}) { return []byte{}, v }())
}
// 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"
x = unsafe.Pointer(uintptr(x) &^ 3)
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.
package main
import (
"go/ast"
"go/types"
"strings"
"unicode"
"unicode/utf8"
)
func init() {
register("tests",
"check for common mistaken usages of tests/documentation examples",
checkTestFunctions,
funcDecl)
}
func isExampleSuffix(s string) bool {
r, size := utf8.DecodeRuneInString(s)
return size > 0 && unicode.IsLower(r)
}
func isTestSuffix(name string) bool {
if len(name) == 0 {
// "Test" is ok.
return true
}
r, _ := utf8.DecodeRuneInString(name)
return !unicode.IsLower(r)
}
func isTestParam(typ ast.Expr, wantType string) bool {
ptr, ok := typ.(*ast.StarExpr)
if !ok {
// Not a pointer.
return false
}
// No easy way of making sure it's a *testing.T or *testing.B:
// ensure the name of the type matches.
if name, ok := ptr.X.(*ast.Ident); ok {
return name.Name == wantType
}
if sel, ok := ptr.X.(*ast.SelectorExpr); ok {
return sel.Sel.Name == wantType
}
return false
}
func lookup(name string, scopes []*types.Scope) types.Object {
for _, scope := range scopes {
if o := scope.Lookup(name); o != nil {
return o
}
}
return nil
}
func extendedScope(f *File) []*types.Scope {
scopes := []*types.Scope{f.pkg.typesPkg.Scope()}
if f.basePkg != nil {
scopes = append(scopes, f.basePkg.typesPkg.Scope())
} else {
// If basePkg is not specified (e.g. when checking a single file) try to
// find it among imports.
pkgName := f.pkg.typesPkg.Name()
if strings.HasSuffix(pkgName, "_test") {
basePkgName := strings.TrimSuffix(pkgName, "_test")
for _, p := range f.pkg.typesPkg.Imports() {
if p.Name() == basePkgName {
scopes = append(scopes, p.Scope())
break
}
}
}
}
return scopes
}
func checkExample(fn *ast.FuncDecl, f *File, report reporter) {
fnName := fn.Name.Name
if params := fn.Type.Params; len(params.List) != 0 {
report("%s should be niladic", fnName)
}
if results := fn.Type.Results; results != nil && len(results.List) != 0 {
report("%s should return nothing", fnName)
}
if filesRun && !includesNonTest {
// The coherence checks between a test and the package it tests
// will report false positives if no non-test files have
// been provided.
return
}
if fnName == "Example" {
// Nothing more to do.
return
}
var (
exName = strings.TrimPrefix(fnName, "Example")
elems = strings.SplitN(exName, "_", 3)
ident = elems[0]
obj = lookup(ident, extendedScope(f))
)
if ident != "" && obj == nil {
// Check ExampleFoo and ExampleBadFoo.
report("%s refers to unknown identifier: %s", fnName, ident)
// Abort since obj is absent and no subsequent checks can be performed.
return
}
if len(elems) < 2 {
// Nothing more to do.
return
}
if ident == "" {
// Check Example_suffix and Example_BadSuffix.
if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) {
report("%s has malformed example suffix: %s", fnName, residual)
}
return
}
mmbr := elems[1]
if !isExampleSuffix(mmbr) {
// Check ExampleFoo_Method and ExampleFoo_BadMethod.
if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj == nil {
report("%s refers to unknown field or method: %s.%s", fnName, ident, mmbr)
}
}
if len(elems) == 3 && !isExampleSuffix(elems[2]) {
// Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix.
report("%s has malformed example suffix: %s", fnName, elems[2])
}
}
func checkTest(fn *ast.FuncDecl, prefix string, report reporter) {
// Want functions with 0 results and 1 parameter.
if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
fn.Type.Params == nil ||
len(fn.Type.Params.List) != 1 ||
len(fn.Type.Params.List[0].Names) > 1 {
return
}
// The param must look like a *testing.T or *testing.B.
if !isTestParam(fn.Type.Params.List[0].Type, prefix[:1]) {
return
}
if !isTestSuffix(fn.Name.Name[len(prefix):]) {
report("%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
}
}
type reporter func(format string, args ...interface{})
// checkTestFunctions walks Test, Benchmark and Example functions checking
// malformed names, wrong signatures and examples documenting nonexistent
// identifiers.
func checkTestFunctions(f *File, node ast.Node) {
if !strings.HasSuffix(f.name, "_test.go") {
return
}
fn, ok := node.(*ast.FuncDecl)
if !ok || fn.Recv != nil {
// Ignore non-functions or functions with receivers.
return
}
report := func(format string, args ...interface{}) { f.Badf(node.Pos(), format, args...) }
switch {
case strings.HasPrefix(fn.Name.Name, "Example"):
checkExample(fn, f, report)
case strings.HasPrefix(fn.Name.Name, "Test"):
checkTest(fn, "Test", report)
case strings.HasPrefix(fn.Name.Name, "Benchmark"):
checkTest(fn, "Benchmark", report)
}
}
// 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/build"
"go/importer"
"go/token"
"go/types"
)
// stdImporter is the importer we use to import packages.
// It is shared so that all packages are imported by the same importer.
var stdImporter types.Importer
var (
errorType *types.Interface
stringerType *types.Interface // possibly nil
formatterType *types.Interface // possibly nil
)
func inittypes() {
errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
if typ := importType("fmt", "Stringer"); typ != nil {
stringerType = typ.Underlying().(*types.Interface)
}
if typ := importType("fmt", "Formatter"); typ != nil {
formatterType = typ.Underlying().(*types.Interface)
}
}
// isNamedType reports whether t is the named type path.name.
func isNamedType(t types.Type, path, name string) bool {
n, ok := t.(*types.Named)
if !ok {
return false
}
obj := n.Obj()
return obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == path
}
// importType returns the type denoted by the qualified identifier
// path.name, and adds the respective package to the imports map
// as a side effect. In case of an error, importType returns nil.
func importType(path, name string) types.Type {
pkg, err := stdImporter.Import(path)
if err != nil {
// This can happen if the package at path hasn't been compiled yet.
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 {
if stdImporter == nil {
if *source {
stdImporter = importer.For("source", nil)
} else {
stdImporter = importer.Default()
}
inittypes()
}
pkg.defs = make(map[*ast.Ident]types.Object)
pkg.uses = make(map[*ast.Ident]types.Object)
pkg.implicits = make(map[ast.Node]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)
var allErrors []error
config := types.Config{
// We use the same importer for all imports to ensure that
// everybody sees identical packages for the given paths.
Importer: stdImporter,
// By providing a Config with our own error function, it will continue
// past the first error. We collect them all for printing later.
Error: func(e error) {
allErrors = append(allErrors, e)
},
Sizes: archSizes,
}
info := &types.Info{
Selections: pkg.selectors,
Types: pkg.types,
Defs: pkg.defs,
Uses: pkg.uses,
Implicits: pkg.implicits,
}
typesPkg, err := config.Check(pkg.path, fs, astFiles, info)
if len(allErrors) == 0 && err != nil {
allErrors = append(allErrors, err)
}
pkg.typesPkg = typesPkg
// update spans
for id, obj := range pkg.defs {
// Ignore identifiers that don't denote objects
// (package names, symbolic variables such as t
// in t := x.(type) of type switch headers).
if obj != nil {
pkg.growSpan(obj, id.Pos(), id.End())
}
}
for id, obj := range pkg.uses {
pkg.growSpan(obj, id.Pos(), id.End())
}
for node, obj := range pkg.implicits {
// A type switch with a short variable declaration
// such as t := x.(type) doesn't declare the symbolic
// variable (t in the example) at the switch header;
// instead a new variable t (with specific type) is
// declared implicitly for each case. Such variables
// are found in the types.Info.Implicits (not Defs)
// map. Add them here, assuming they are declared at
// the type cases' colon ":".
if cc, ok := node.(*ast.CaseClause); ok {
pkg.growSpan(obj, cc.Colon, cc.Colon)
}
}
return allErrors
}
// 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.
if f.isFormatter(typ) {
return true
}
// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
if t&argString != 0 && isConvertibleToString(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(), 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
}
under := typ.Elem().Underlying()
switch under.(type) {
case *types.Struct: // see below
case *types.Array: // see below
case *types.Slice: // see below
case *types.Map: // see below
default:
// Check whether the rest can print pointers.
return t&argPointer != 0
}
// If it's a top-level pointer to a struct, array, slice, or
// map, that's equivalent in our analysis to whether we can
// print the type being pointed to. Pointers in nested levels
// are not supported to minimize fmt running into loops.
if len(inProgress) > 1 {
return false
}
return f.matchArgTypeInternal(t, under, arg, inProgress)
case *types.Struct:
return f.matchStructArgType(t, typ, arg, inProgress)
case *types.Interface:
// There's little we can do.
// Whether any particular verb is valid depends on the argument.
// The user may have reasonable prior knowledge of the contents of the interface.
return true
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 false
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
}
func isConvertibleToString(typ types.Type) bool {
if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
// We explicitly don't want untyped nil, which is
// convertible to both of the interfaces below, as it
// would just panic anyway.
return false
}
if types.ConvertibleTo(typ, errorType) {
return true // via .Error()
}
if stringerType != nil && types.ConvertibleTo(typ, stringerType) {
return true // via .String()
}
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++ {
typf := typ.Field(i)
if !f.matchArgTypeInternal(t, typf.Type(), arg, inProgress) {
return false
}
if t&argString != 0 && !typf.Exported() && isConvertibleToString(typf.Type()) {
// Issue #17798: unexported Stringer or error cannot be properly fomatted.
return false
}
}
return true
}
var archSizes = types.SizesFor("gc", build.Default.GOARCH)
// Copyright 2018 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 passing non-pointer or non-interface
// types to unmarshal and decode functions.
package main
import (
"go/ast"
"go/types"
"strings"
)
func init() {
register("unmarshal",
"check for passing non-pointer or non-interface types to unmarshal and decode functions",
checkUnmarshalArg,
callExpr)
}
var pointerArgFuncs = map[string]int{
"encoding/json.Unmarshal": 1,
"(*encoding/json.Decoder).Decode": 0,
"(*encoding/gob.Decoder).Decode": 0,
"encoding/xml.Unmarshal": 1,
"(*encoding/xml.Decoder).Decode": 0,
}
func checkUnmarshalArg(f *File, n ast.Node) {
call, ok := n.(*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
}
info := &types.Info{Uses: f.pkg.uses, Selections: f.pkg.selectors}
name := callName(info, call)
arg, ok := pointerArgFuncs[name]
if !ok {
return // not a function we are interested in
}
if len(call.Args) < arg+1 {
return // not enough arguments, e.g. called with return values of another function
}
typ := f.pkg.types[call.Args[arg]]
if typ.Type == nil {
return // type error prevents further analysis
}
switch typ.Type.Underlying().(type) {
case *types.Pointer, *types.Interface:
return
}
shortname := name[strings.LastIndexByte(name, '.')+1:]
switch arg {
case 0:
f.Badf(call.Lparen, "call of %s passes non-pointer", shortname)
case 1:
f.Badf(call.Lparen, "call of %s passes non-pointer as second argument", shortname)
}
}
// 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"
"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, token.AND_NOT:
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"
"go/types"
"strings"
)
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,
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)
}
}
}
}
......@@ -15,7 +15,6 @@ import (
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
......@@ -60,107 +59,69 @@ func Build(t *testing.T) {
built = true
}
func Vet(t *testing.T, files []string) {
flags := []string{
"-printfuncs=Warn:1,Warnf:1",
"-all",
"-shadow",
func vetCmd(t *testing.T, args ...string) *exec.Cmd {
cmd := exec.Command(testenv.GoToolPath(t), "vet", "-vettool="+binary)
cmd.Args = append(cmd.Args, args...)
testdata, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(binary, append(flags, files...)...)
errchk(cmd, files, t)
cmd.Env = append(os.Environ(), "GOPATH="+testdata)
return cmd
}
// TestVet is equivalent to running this:
// go build -o ./testvet
// errorCheck the output of ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s
// errorCheck the output of ./testvet -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s
// rm ./testvet
//
// TestVet tests self-contained files in testdata/*.go.
//
// If a file contains assembly or has inter-dependencies, it should be
// in its own test, like TestVetAsm, TestDivergentPackagesExamples,
// etc below.
func TestVet(t *testing.T) {
Build(t)
t.Parallel()
gos, err := filepath.Glob(filepath.Join(dataDir, "*.go"))
if err != nil {
t.Fatal(err)
}
wide := runtime.GOMAXPROCS(0)
if wide > len(gos) {
wide = len(gos)
}
batch := make([][]string, wide)
for i, file := range gos {
// The print.go test is run by TestVetPrint.
if strings.HasSuffix(file, "print.go") {
continue
}
batch[i%wide] = append(batch[i%wide], file)
}
for i, files := range batch {
if len(files) == 0 {
continue
}
files := files
t.Run(fmt.Sprint(i), func(t *testing.T) {
t.Parallel()
t.Logf("files: %q", files)
Vet(t, files)
})
}
}
func TestVetPrint(t *testing.T) {
Build(t)
file := filepath.Join("testdata", "print.go")
cmd := exec.Command(
"go", "vet", "-vettool="+binary,
"-printf",
"-printfuncs=Warn:1,Warnf:1",
file,
)
errchk(cmd, []string{file}, t)
}
func TestVetAsm(t *testing.T) {
Build(t)
asmDir := filepath.Join(dataDir, "asm")
gos, err := filepath.Glob(filepath.Join(asmDir, "*.go"))
if err != nil {
t.Fatal(err)
}
asms, err := filepath.Glob(filepath.Join(asmDir, "*.s"))
if err != nil {
t.Fatal(err)
}
t.Parallel()
Vet(t, append(gos, asms...))
}
func TestVetDirs(t *testing.T) {
t.Parallel()
Build(t)
for _, dir := range []string{
"testingpkg",
"divergent",
for _, pkg := range []string{
"asm",
"assign",
"atomic",
"bool",
"buildtag",
"incomplete", // incomplete examples
"cgo",
"composite",
"copylock",
"deadcode",
"httpresponse",
"lostcancel",
"method",
"nilfunc",
"print",
"rangeloop",
"shift",
"structtag",
"testingpkg",
// "testtag" has its own test
"unmarshal",
"unsafeptr",
"unused",
} {
dir := dir
t.Run(dir, func(t *testing.T) {
pkg := pkg
t.Run(pkg, func(t *testing.T) {
t.Parallel()
gos, err := filepath.Glob(filepath.Join("testdata", dir, "*.go"))
cmd := vetCmd(t, "-printfuncs=Warn,Warnf", pkg)
dir := filepath.Join("testdata/src", pkg)
gos, err := filepath.Glob(filepath.Join(dir, "*.go"))
if err != nil {
t.Fatal(err)
}
Vet(t, gos)
asms, err := filepath.Glob(filepath.Join(dir, "*.s"))
if err != nil {
t.Fatal(err)
}
var files []string
files = append(files, gos...)
files = append(files, asms...)
errchk(cmd, files, t)
})
}
}
......@@ -185,44 +146,35 @@ func errchk(c *exec.Cmd, files []string, t *testing.T) {
func TestTags(t *testing.T) {
t.Parallel()
Build(t)
for _, tag := range []string{"testtag", "x testtag y", "x,testtag,y"} {
tag := tag
for tag, wantFile := range map[string]int{
"testtag": 1, // file1
"x testtag y": 1,
"othertag": 2,
} {
tag, wantFile := tag, wantFile
t.Run(tag, func(t *testing.T) {
t.Parallel()
t.Logf("-tags=%s", tag)
args := []string{
"-tags=" + tag,
"-v", // We're going to look at the files it examines.
"testdata/tagtest",
}
cmd := exec.Command(binary, args...)
cmd := vetCmd(t, "-tags="+tag, "tagtest")
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatal(err)
}
want := fmt.Sprintf("file%d.go", wantFile)
dontwant := fmt.Sprintf("file%d.go", 3-wantFile)
// file1 has testtag and file2 has !testtag.
if !bytes.Contains(output, []byte(filepath.Join("tagtest", "file1.go"))) {
t.Error("file1 was excluded, should be included")
if !bytes.Contains(output, []byte(filepath.Join("tagtest", want))) {
t.Errorf("%s: %s was excluded, should be included", tag, want)
}
if bytes.Contains(output, []byte(filepath.Join("tagtest", "file2.go"))) {
t.Error("file2 was included, should be excluded")
if bytes.Contains(output, []byte(filepath.Join("tagtest", dontwant))) {
t.Errorf("%s: %s was included, should be excluded", tag, dontwant)
}
if t.Failed() {
t.Logf("err=%s, output=<<%s>>", err, output)
}
})
}
}
// Issue #21188.
func TestVetVerbose(t *testing.T) {
t.Parallel()
Build(t)
cmd := exec.Command(binary, "-v", "-all", "testdata/cgo/cgo3.go")
out, err := cmd.CombinedOutput()
if err != nil {
t.Logf("%s", out)
t.Error(err)
}
}
// All declarations below were adapted from test/run.go.
// errorCheck matches errors in outStr against comments in source files.
......
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