Bumping k8s dependencies to 1.13
This commit is contained in:
8
vendor/golang.org/x/tools/go/analysis/passes/README
generated
vendored
Normal file
8
vendor/golang.org/x/tools/go/analysis/passes/README
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
This directory does not contain a Go package,
|
||||
but acts as a container for various analyses
|
||||
that implement the golang.org/x/tools/go/analysis
|
||||
API and may be imported into an analysis tool.
|
||||
|
||||
By convention, each package foo provides the analysis,
|
||||
and each command foo/cmd/foo provides a standalone driver.
|
||||
760
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
generated
vendored
Normal file
760
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
generated
vendored
Normal file
@@ -0,0 +1,760 @@
|
||||
// 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 asmdecl defines an Analyzer that reports mismatches between
|
||||
// assembly files and Go declarations.
|
||||
package asmdecl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "asmdecl",
|
||||
Doc: "report mismatches between assembly files and Go declarations",
|
||||
Run: run,
|
||||
}
|
||||
|
||||
// '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 {
|
||||
// TODO(adonovan): fix: now that asmdecl is not in the standard
|
||||
// library we cannot assume types.SizesFor is consistent with arches.
|
||||
// For now, assume 64-bit norms and print a warning.
|
||||
// But this warning should really be deferred until we attempt to use
|
||||
// arch, which is very unlikely.
|
||||
arch.sizes = types.SizesFor("gc", "amd64")
|
||||
log.Printf("unknown architecture %s", 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]))
|
||||
}
|
||||
}
|
||||
|
||||
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 run(pass *analysis.Pass) (interface{}, error) {
|
||||
// No work if no assembly files.
|
||||
var sfiles []string
|
||||
for _, fname := range pass.OtherFiles {
|
||||
if strings.HasSuffix(fname, ".s") {
|
||||
sfiles = append(sfiles, fname)
|
||||
}
|
||||
}
|
||||
if sfiles == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Gather declarations. knownFunc[name][arch] is func description.
|
||||
knownFunc := make(map[string]map[string]*asmFunc)
|
||||
|
||||
for _, f := range pass.Files {
|
||||
for _, decl := range f.Decls {
|
||||
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
|
||||
knownFunc[decl.Name.Name] = asmParseDecl(pass, decl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Files:
|
||||
for _, fname := range sfiles {
|
||||
content, tf, err := analysisutil.ReadFile(pass.Fset, fname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine architecture from file name if possible.
|
||||
var arch string
|
||||
var archDef *asmArch
|
||||
for _, a := range arches {
|
||||
if strings.HasSuffix(fname, "_"+a.name+".s") {
|
||||
arch = a.name
|
||||
archDef = a
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
lines := strings.SplitAfter(string(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 {
|
||||
pass.Reportf(analysisutil.LineStart(tf, line), "[%s] %s: RET without writing to %d-byte ret+%d(FP)", arch, fnName, v.size, v.off)
|
||||
}
|
||||
}
|
||||
retLine = nil
|
||||
}
|
||||
for lineno, line := range lines {
|
||||
lineno++
|
||||
|
||||
badf := func(format string, args ...interface{}) {
|
||||
pass.Reportf(analysisutil.LineStart(tf, lineno), "[%s] %s: %s", 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 == "" {
|
||||
log.Printf("%s: cannot determine architecture for assembly file", fname)
|
||||
continue Files
|
||||
}
|
||||
}
|
||||
fnName = m[2]
|
||||
if pkgPath := strings.TrimSpace(m[1]); pkgPath != "" {
|
||||
// The assembler uses Unicode division slash within
|
||||
// identifiers to represent the directory separator.
|
||||
pkgPath = strings.Replace(pkgPath, "∕", "/", -1)
|
||||
if pkgPath != pass.Pkg.Path() {
|
||||
log.Printf("%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", fname, lineno, arch, fnName, pkgPath)
|
||||
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()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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 asmParseDecl(pass *analysis.Pass, 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.
|
||||
// TODO(adonovan): simplify by passing (*types.Signature).{Params,Results}
|
||||
// instead of list.
|
||||
addParams := func(list []*ast.Field, isret bool) {
|
||||
argnum := 0
|
||||
for _, fld := range list {
|
||||
t := pass.TypesInfo.Types[fld.Type].Type
|
||||
|
||||
// Work around github.com/golang/go/issues/28277.
|
||||
if t == nil {
|
||||
if ell, ok := fld.Type.(*ast.Ellipsis); ok {
|
||||
t = types.NewSlice(pass.TypesInfo.Types[ell.Elt].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())
|
||||
}
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package asmdecl_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/asmdecl"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, asmdecl.Analyzer, "a")
|
||||
}
|
||||
48
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm.go
generated
vendored
Normal file
48
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// 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 declarations to test the assembly in test_asm.s.
|
||||
|
||||
package a
|
||||
|
||||
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)
|
||||
|
||||
func fvariadic(int, ...int)
|
||||
314
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm1.s
generated
vendored
Normal file
314
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm1.s
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
// 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
|
||||
|
||||
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 // want `\[amd64\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVW y+1(FP), AX // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVL y+1(FP), AX // want `invalid MOVL of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVQ y+1(FP), AX // want `invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
TESTB x+0(FP), AX
|
||||
TESTB y+1(FP), BX
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTW y+1(FP), AX // want `invalid TESTW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTL y+1(FP), AX // want `invalid TESTL of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTQ y+1(FP), AX // want `invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
TESTB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 8(SP), AX // want `8\(SP\) should be x\+0\(FP\)`
|
||||
MOVB 9(SP), AX // want `9\(SP\) should be y\+1\(FP\)`
|
||||
MOVB 10(SP), AX // want `use of 10\(SP\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVB x+0(FP), AX // want `arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), AX // want `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 // want `invalid MOVL of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVL y+2(FP), AX // want `invalid MOVL of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVQ y+2(FP), AX // want `invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTB y+2(FP), AX // want `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 // want `invalid TESTL of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTL y+2(FP), AX // want `invalid TESTL of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTQ y+2(FP), AX // want `invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
TESTW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVW y+4(FP), AX // want `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 // want `invalid MOVQ of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTW y+4(FP), AX // want `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 // want `invalid TESTQ of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
TESTL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVL y+8(FP), AX // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTL y+8(FP), AX // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int is 8-byte value`
|
||||
MOVL y+8(FP), AX // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int is 8-byte value`
|
||||
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); uint is 8-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int is 8-byte value`
|
||||
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); uint is 8-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int is 8-byte value`
|
||||
TESTL y+8(FP), AX // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVL y+8(FP), AX // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); \*byte is 8-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); \*byte is 8-byte value`
|
||||
TESTL y+8(FP), AX // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
MOVL c+16(FP), AX // want `invalid MOVL of c\+16\(FP\); chan int is 8-byte value`
|
||||
MOVL m+24(FP), AX // want `invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value`
|
||||
MOVL f+32(FP), AX // want `invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVQ x+0(FP), AX
|
||||
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVL x_base+0(FP), AX // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+8(FP), AX // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVL x_len+8(FP), AX // want `invalid MOVL of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVQ x_len+8(FP), AX
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
|
||||
MOVQ y_len+8(FP), AX // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVQ x+0(FP), AX
|
||||
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVL x_base+0(FP), AX // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+8(FP), AX // want `invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVL x_len+8(FP), AX // want `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 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVL x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVQ x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVW x_cap+16(FP), AX // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVL x_cap+16(FP), AX // want `invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVQ x_cap+16(FP), AX
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
|
||||
MOVQ y_len+8(FP), AX // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
|
||||
MOVQ y_cap+16(FP), AX // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-32
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVQ x+0(FP), AX
|
||||
MOVW x_type+0(FP), AX // want `invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVL x_type+0(FP), AX // want `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 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVQ x_itable+1(FP), AX // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVW x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVL x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVQ x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVW x_data+8(FP), AX // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVL x_data+8(FP), AX // want `invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVQ x_data+8(FP), AX
|
||||
MOVW y+16(FP), AX // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVL y+16(FP), AX // want `invalid MOVL of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVQ y+16(FP), AX
|
||||
MOVW y_itable+16(FP), AX // want `invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVL y_itable+16(FP), AX // want `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 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
|
||||
MOVW y_data+16(FP), AX // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVL y_data+16(FP), AX // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVQ y_data+16(FP), AX // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVW y_data+24(FP), AX // want `invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVL y_data+24(FP), AX // want `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 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVSS x+0(FP), X0 // want `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 // want `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 // want `invalid MOVSD of x_real\+0\(FP\); real\(complex64\) is 4-byte value`
|
||||
MOVSS x_real+4(FP), X0 // want `invalid offset x_real\+4\(FP\); expected x_real\+0\(FP\)`
|
||||
MOVSS x_imag+4(FP), X0
|
||||
MOVSD x_imag+4(FP), X0 // want `invalid MOVSD of x_imag\+4\(FP\); imag\(complex64\) is 4-byte value`
|
||||
MOVSS x_imag+8(FP), X0 // want `invalid offset x_imag\+8\(FP\); expected x_imag\+4\(FP\)`
|
||||
MOVSD y+8(FP), X0 // want `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 // want `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 // want `invalid offset y_real\+16\(FP\); expected y_real\+8\(FP\)`
|
||||
MOVSS y_imag+16(FP), X0 // want `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 // want `invalid offset y_imag\+24\(FP\); expected y_imag\+16\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argstruct(SB),0,$64 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); a.S is 24-byte value`
|
||||
MOVQ x_i+0(FP), AX // want `invalid MOVQ of x_i\+0\(FP\); int32 is 4-byte value`
|
||||
MOVQ x_b+0(FP), AX // want `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 // want `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 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); \[2\]a.S is 48-byte value`
|
||||
MOVQ x_0_i+0(FP), AX // want `invalid MOVQ of x_0_i\+0\(FP\); int32 is 4-byte value`
|
||||
MOVQ x_0_b+0(FP), AX // want `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 // want `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 // want `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 // want `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) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVW AX, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVL AX, ret+0(FP) // want `invalid MOVL of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVQ AX, ret+0(FP)
|
||||
MOVQ AX, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVQ AX, r+0(FP) // want `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) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVL AX, ret+8(FP) // want `invalid MOVL of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVQ AX, ret+8(FP) // want `invalid MOVQ of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVB AX, ret+7(FP) // want `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) // want `invalid MOVL of r1\+8\(FP\); int is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-8
|
||||
RET // want `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
|
||||
256
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm2.s
generated
vendored
Normal file
256
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm2.s
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
// 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
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), AX
|
||||
MOVB y+1(FP), BX
|
||||
MOVW x+0(FP), AX // want `\[386\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVW y+1(FP), AX // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVL y+1(FP), AX // want `invalid MOVL of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVQ y+1(FP), AX // want `invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
TESTB x+0(FP), AX
|
||||
TESTB y+1(FP), BX
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTW y+1(FP), AX // want `invalid TESTW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTL y+1(FP), AX // want `invalid TESTL of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTQ y+1(FP), AX // want `invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
TESTB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 4(SP), AX // want `4\(SP\) should be x\+0\(FP\)`
|
||||
MOVB 5(SP), AX // want `5\(SP\) should be y\+1\(FP\)`
|
||||
MOVB 6(SP), AX // want `use of 6\(SP\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVB x+0(FP), AX // want `arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), AX // want `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 // want `invalid MOVL of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVL y+2(FP), AX // want `invalid MOVL of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVQ y+2(FP), AX // want `invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTB y+2(FP), AX // want `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 // want `invalid TESTL of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTL y+2(FP), AX // want `invalid TESTL of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTQ y+2(FP), AX // want `invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
TESTW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVW y+4(FP), AX // want `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 // want `invalid MOVQ of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTW y+4(FP), AX // want `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 // want `invalid TESTQ of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
TESTL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `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 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
TESTL x+0(FP), AX // want `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 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int is 4-byte value`
|
||||
MOVW y+4(FP), AX // want `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 // want `invalid MOVQ of x\+0\(FP\); int is 4-byte value`
|
||||
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int is 4-byte value`
|
||||
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); uint is 4-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int is 4-byte value`
|
||||
TESTW y+4(FP), AX // want `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 // want `invalid TESTQ of x\+0\(FP\); int is 4-byte value`
|
||||
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); uint is 4-byte value`
|
||||
TESTQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-20`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVW y+4(FP), AX // want `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 // want `invalid MOVQ of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); \*byte is 4-byte value`
|
||||
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); \*byte is 4-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); \*byte is 4-byte value`
|
||||
TESTW y+4(FP), AX // want `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 // want `invalid TESTQ of x\+0\(FP\); \*byte is 4-byte value`
|
||||
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); \*byte is 4-byte value`
|
||||
TESTQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
MOVW c+8(FP), AX // want `invalid MOVW of c\+8\(FP\); chan int is 4-byte value`
|
||||
MOVW m+12(FP), AX // want `invalid MOVW of m\+12\(FP\); map\[int\]int is 4-byte value`
|
||||
MOVW f+16(FP), AX // want `invalid MOVW of f\+16\(FP\); func\(\) is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$16 // want `wrong argument size 0; expected \$\.\.\.-16`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); string base is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x_base+0(FP), AX // want `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 // want `invalid MOVQ of x_base\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+4(FP), AX // want `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 // want `invalid MOVQ of x_len\+4\(FP\); string len is 4-byte value`
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+8\(FP\)`
|
||||
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); slice base is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x_base+0(FP), AX // want `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 // want `invalid MOVQ of x_base\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+4(FP), AX // want `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 // want `invalid MOVQ of x_len\+4\(FP\); slice len is 4-byte value`
|
||||
MOVW x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVL x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVQ x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVW x_cap+8(FP), AX // want `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 // want `invalid MOVQ of x_cap\+8\(FP\); slice cap is 4-byte value`
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+12\(FP\)`
|
||||
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)`
|
||||
MOVQ y_cap+8(FP), AX // want `invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-16
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); interface type is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); interface type is 4-byte value`
|
||||
MOVW x_type+0(FP), AX // want `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 // want `invalid MOVQ of x_type\+0\(FP\); interface type is 4-byte value`
|
||||
MOVQ x_itable+0(FP), AX // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVQ x_itable+1(FP), AX // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVW x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVL x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVQ x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVW x_data+4(FP), AX // want `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 // want `invalid MOVQ of x_data\+4\(FP\); interface data is 4-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVL y+8(FP), AX
|
||||
MOVQ y+8(FP), AX // want `invalid MOVQ of y\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVW y_itable+8(FP), AX // want `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 // want `invalid MOVQ of y_itable\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVQ y_type+8(FP), AX // want `unknown variable y_type; offset 8 is y_itable\+8\(FP\)`
|
||||
MOVW y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVL y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVQ y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVW y_data+12(FP), AX // want `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 // want `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) // want `invalid MOVB of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVW AX, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVL AX, ret+0(FP)
|
||||
MOVQ AX, ret+0(FP) // want `invalid MOVQ of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVQ AX, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVQ AX, r+0(FP) // want `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) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVL AX, ret+4(FP) // want `invalid MOVL of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVQ AX, ret+4(FP) // want `invalid MOVQ of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVB AX, ret+3(FP) // want `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) // want `invalid MOVQ of r1\+4\(FP\); int is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-4
|
||||
RET // want `RET without writing to 4-byte ret\+0\(FP\)`
|
||||
191
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm3.s
generated
vendored
Normal file
191
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm3.s
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
// 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
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), AX
|
||||
MOVB y+1(FP), BX
|
||||
MOVH x+0(FP), AX // want `\[arm\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVH y+1(FP), AX // want `invalid MOVH of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVW y+1(FP), AX // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 8(R13), AX // want `8\(R13\) should be x\+0\(FP\)`
|
||||
MOVB 9(R13), AX // want `9\(R13\) should be y\+1\(FP\)`
|
||||
MOVB 10(R13), AX // want `use of 10\(R13\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVB x+0(FP), AX // want `arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), AX // want `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 // want `invalid MOVW of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), AX // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVH x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), AX // want `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 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), AX // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `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 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); int is 4-byte value`
|
||||
MOVH y+4(FP), AX // want `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 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-20`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVH y+4(FP), AX // want `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 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
MOVH c+8(FP), AX // want `invalid MOVH of c\+8\(FP\); chan int is 4-byte value`
|
||||
MOVH m+12(FP), AX // want `invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value`
|
||||
MOVH f+16(FP), AX // want `invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$16 // want `wrong argument size 0; expected \$\.\.\.-16`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVH x_base+0(FP), AX // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVH x_len+4(FP), AX // want `invalid MOVH of x_len\+4\(FP\); string len is 4-byte value`
|
||||
MOVW x_len+4(FP), AX
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+8\(FP\)`
|
||||
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVH x_base+0(FP), AX // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVH x_len+4(FP), AX // want `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 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVW x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVQ x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVH x_cap+8(FP), AX // want `invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value`
|
||||
MOVW x_cap+8(FP), AX
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+12\(FP\)`
|
||||
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)`
|
||||
MOVQ y_cap+8(FP), AX // want `invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-16
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); interface type is 4-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVH x_type+0(FP), AX // want `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 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVQ x_itable+1(FP), AX // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVW x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVQ x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVH x_data+4(FP), AX // want `invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value`
|
||||
MOVW x_data+4(FP), AX
|
||||
MOVH y+8(FP), AX // want `invalid MOVH of y\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVW y+8(FP), AX
|
||||
MOVH y_itable+8(FP), AX // want `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 // want `unknown variable y_type; offset 8 is y_itable\+8\(FP\)`
|
||||
MOVH y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVW y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVQ y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVH y_data+12(FP), AX // want `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) // want `invalid MOVB of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVH AX, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVW AX, ret+0(FP)
|
||||
MOVQ AX, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVQ AX, r+0(FP) // want `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) // want `invalid MOVH of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVW AX, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVB AX, ret+3(FP) // want `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) // want `invalid MOVB of r1\+4\(FP\); int is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-4
|
||||
RET // want `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 // want `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 // want `use of 8\(R13\) points beyond argument frame`
|
||||
MOVW 12(R13), AX // want `use of 12\(R13\) points beyond argument frame`
|
||||
RET
|
||||
25
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm4.s
generated
vendored
Normal file
25
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm4.s
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// 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
|
||||
|
||||
// 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
|
||||
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm5.s
generated
vendored
Normal file
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm5.s
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
// 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
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), R1
|
||||
MOVBU y+1(FP), R2
|
||||
MOVH x+0(FP), R1 // want `\[mips64\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVHU y+1(FP), R1 // want `invalid MOVHU of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVWU y+1(FP), R1 // want `invalid MOVWU of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVV x+0(FP), R1 // want `invalid MOVV of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVV y+1(FP), R1 // want `invalid MOVV of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), R1 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVBU y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 16(R29), R1 // want `16\(R29\) should be x\+0\(FP\)`
|
||||
MOVB 17(R29), R1 // want `17\(R29\) should be y\+1\(FP\)`
|
||||
MOVB 18(R29), R1 // want `use of 18\(R29\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVBU x+0(FP), R1 // want `arg2: invalid MOVBU of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), R1 // want `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 // want `invalid MOVWU of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), R1 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVV x+0(FP), R1 // want `invalid MOVV of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVV y+2(FP), R1 // want `invalid MOVV of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHU x+2(FP), R1 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `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 // want `invalid MOVV of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVV y+4(FP), R1 // want `invalid MOVV of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVV y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVV y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVV y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
MOVW c+16(FP), R1 // want `invalid MOVW of c\+16\(FP\); chan int is 8-byte value`
|
||||
MOVW m+24(FP), R1 // want `invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value`
|
||||
MOVW f+32(FP), R1 // want `invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVV x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x_base+0(FP), R1 // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVV x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVV x_len+8(FP), R1
|
||||
MOVV y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
|
||||
MOVV y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVV x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x_base+0(FP), R1 // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVV x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVW x_len+8(FP), R1 // want `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 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVW x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVV x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVH x_cap+16(FP), R1 // want `invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVW x_cap+16(FP), R1 // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVV x_cap+16(FP), R1
|
||||
MOVV y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
|
||||
MOVV y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
|
||||
MOVV y_cap+16(FP), R1 // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-32
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVV x+0(FP), R1
|
||||
MOVH x_type+0(FP), R1 // want `invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x_type+0(FP), R1 // want `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 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVV x_itable+1(FP), R1 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVW x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVV x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVH x_data+8(FP), R1 // want `invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVW x_data+8(FP), R1 // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVV x_data+8(FP), R1
|
||||
MOVH y+16(FP), R1 // want `invalid MOVH of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y+16(FP), R1 // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVV y+16(FP), R1
|
||||
MOVH y_itable+16(FP), R1 // want `invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y_itable+16(FP), R1 // want `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 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
|
||||
MOVH y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVW y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVV y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVH y_data+24(FP), R1 // want `invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVW y_data+24(FP), R1 // want `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) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVH R1, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVW R1, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVV R1, ret+0(FP)
|
||||
MOVV R1, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVV R1, r+0(FP) // want `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) // want `invalid MOVH of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVW R1, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVV R1, ret+8(FP) // want `invalid MOVV of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVB R1, ret+7(FP) // want `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) // want `invalid MOVW of r1\+8\(FP\); int is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-8
|
||||
RET // want `RET without writing to 8-byte ret\+0\(FP\)`
|
||||
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm6.s
generated
vendored
Normal file
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm6.s
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
// 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
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), R1
|
||||
MOVBZ y+1(FP), R2
|
||||
MOVH x+0(FP), R1 // want `\[s390x\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVHZ y+1(FP), R1 // want `invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVWZ y+1(FP), R1 // want `invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVD x+0(FP), R1 // want `invalid MOVD of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVD y+1(FP), R1 // want `invalid MOVD of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), R1 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVBZ y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 16(R15), R1 // want `16\(R15\) should be x\+0\(FP\)`
|
||||
MOVB 17(R15), R1 // want `17\(R15\) should be y\+1\(FP\)`
|
||||
MOVB 18(R15), R1 // want `use of 18\(R15\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVBZ x+0(FP), R1 // want `arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), R1 // want `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 // want `invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), R1 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVD x+0(FP), R1 // want `invalid MOVD of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVD y+2(FP), R1 // want `invalid MOVD of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHZ x+2(FP), R1 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `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 // want `invalid MOVD of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVD y+4(FP), R1 // want `invalid MOVD of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
MOVW c+16(FP), R1 // want `invalid MOVW of c\+16\(FP\); chan int is 8-byte value`
|
||||
MOVW m+24(FP), R1 // want `invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value`
|
||||
MOVW f+32(FP), R1 // want `invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVD x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x_base+0(FP), R1 // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVD x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVD x_len+8(FP), R1
|
||||
MOVD y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
|
||||
MOVD y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVD x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x_base+0(FP), R1 // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVD x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVW x_len+8(FP), R1 // want `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 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVW x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVD x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVH x_cap+16(FP), R1 // want `invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVW x_cap+16(FP), R1 // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVD x_cap+16(FP), R1
|
||||
MOVD y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
|
||||
MOVD y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
|
||||
MOVD y_cap+16(FP), R1 // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-32
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVD x+0(FP), R1
|
||||
MOVH x_type+0(FP), R1 // want `invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x_type+0(FP), R1 // want `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 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVD x_itable+1(FP), R1 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVW x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVD x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVH x_data+8(FP), R1 // want `invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVW x_data+8(FP), R1 // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVD x_data+8(FP), R1
|
||||
MOVH y+16(FP), R1 // want `invalid MOVH of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y+16(FP), R1 // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVD y+16(FP), R1
|
||||
MOVH y_itable+16(FP), R1 // want `invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y_itable+16(FP), R1 // want `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 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
|
||||
MOVH y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVW y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVD y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVH y_data+24(FP), R1 // want `invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVW y_data+24(FP), R1 // want `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) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVH R1, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVW R1, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVD R1, ret+0(FP)
|
||||
MOVD R1, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVD R1, r+0(FP) // want `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) // want `invalid MOVH of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVW R1, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVD R1, ret+8(FP) // want `invalid MOVD of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVB R1, ret+7(FP) // want `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) // want `invalid MOVW of r1\+8\(FP\); int is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-8
|
||||
RET // want `RET without writing to 8-byte ret\+0\(FP\)`
|
||||
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm7.s
generated
vendored
Normal file
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm7.s
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
// 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
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), R3
|
||||
MOVBZ y+1(FP), R4
|
||||
MOVH x+0(FP), R3 // want `\[(ppc64|ppc64le)\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVHZ y+1(FP), R3 // want `invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVWZ y+1(FP), R3 // want `invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVD x+0(FP), R3 // want `invalid MOVD of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVD y+1(FP), R3 // want `invalid MOVD of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), R3 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVBZ y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 16(R1), R3 // want `16\(R1\) should be x\+0\(FP\)`
|
||||
MOVB 17(R1), R3 // want `17\(R1\) should be y\+1\(FP\)`
|
||||
MOVB 18(R1), R3 // want `use of 18\(R1\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVBZ x+0(FP), R3 // want `arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), R3 // want `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 // want `invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), R3 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVD x+0(FP), R3 // want `invalid MOVD of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVD y+2(FP), R3 // want `invalid MOVD of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHZ x+2(FP), R3 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), R3 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), R4 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), R3 // want `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 // want `invalid MOVD of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVD y+4(FP), R3 // want `invalid MOVD of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+4(FP), R3 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), R4 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), R3 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), R3 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
|
||||
MOVB y+8(FP), R4 // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); int is 8-byte value`
|
||||
MOVH y+8(FP), R3 // want `invalid MOVH of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
|
||||
MOVW y+8(FP), R3 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
|
||||
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVB y+8(FP), R4 // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVH y+8(FP), R3 // want `invalid MOVH of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVW y+8(FP), R3 // want `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 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
MOVW c+16(FP), R3 // want `invalid MOVW of c\+16\(FP\); chan int is 8-byte value`
|
||||
MOVW m+24(FP), R3 // want `invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value`
|
||||
MOVW f+32(FP), R3 // want `invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVD x+0(FP), R3
|
||||
MOVH x_base+0(FP), R3 // want `invalid MOVH of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x_base+0(FP), R3 // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVD x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R3 // want `invalid MOVH of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVW x_len+8(FP), R3 // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVD x_len+8(FP), R3
|
||||
MOVD y+0(FP), R3 // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
|
||||
MOVD y_len+8(FP), R3 // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVD x+0(FP), R3
|
||||
MOVH x_base+0(FP), R3 // want `invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x_base+0(FP), R3 // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVD x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R3 // want `invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVW x_len+8(FP), R3 // want `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 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVW x_cap+0(FP), R3 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVD x_cap+0(FP), R3 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVH x_cap+16(FP), R3 // want `invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVW x_cap+16(FP), R3 // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVD x_cap+16(FP), R3
|
||||
MOVD y+0(FP), R3 // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
|
||||
MOVD y_len+8(FP), R3 // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
|
||||
MOVD y_cap+16(FP), R3 // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-32
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVD x+0(FP), R3
|
||||
MOVH x_type+0(FP), R3 // want `invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x_type+0(FP), R3 // want `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 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVD x_itable+1(FP), R3 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), R3 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVW x_data+0(FP), R3 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVD x_data+0(FP), R3 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVH x_data+8(FP), R3 // want `invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVW x_data+8(FP), R3 // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVD x_data+8(FP), R3
|
||||
MOVH y+16(FP), R3 // want `invalid MOVH of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y+16(FP), R3 // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVD y+16(FP), R3
|
||||
MOVH y_itable+16(FP), R3 // want `invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y_itable+16(FP), R3 // want `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 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
|
||||
MOVH y_data+16(FP), R3 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVW y_data+16(FP), R3 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVD y_data+16(FP), R3 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVH y_data+24(FP), R3 // want `invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVW y_data+24(FP), R3 // want `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) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVH R3, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVW R3, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVD R3, ret+0(FP)
|
||||
MOVD R3, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVD R3, r+0(FP) // want `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) // want `invalid MOVH of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVW R3, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVD R3, ret+8(FP) // want `invalid MOVD of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVB R3, ret+7(FP) // want `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) // want `invalid MOVW of r1\+8\(FP\); int is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-8
|
||||
RET // want `RET without writing to 8-byte ret\+0\(FP\)`
|
||||
164
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm8.s
generated
vendored
Normal file
164
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm8.s
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
// 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
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), R1
|
||||
MOVBU y+1(FP), R2
|
||||
MOVH x+0(FP), R1 // want `\[mipsle\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVHU y+1(FP), R1 // want `invalid MOVHU of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVWU y+1(FP), R1 // want `invalid MOVWU of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW y+1(FP), R1 // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), R1 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVBU y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 8(R29), R1 // want `8\(R29\) should be x\+0\(FP\)`
|
||||
MOVB 9(R29), R1 // want `9\(R29\) should be y\+1\(FP\)`
|
||||
MOVB 10(R29), R1 // want `use of 10\(R29\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVBU x+0(FP), R1 // want `arg2: invalid MOVBU of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), R1 // want `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 // want `invalid MOVWU of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), R1 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHU x+2(FP), R1 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `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 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `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 // want `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 // want `wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `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 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-20`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `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 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
MOVH c+8(FP), R1 // want `invalid MOVH of c\+8\(FP\); chan int is 4-byte value`
|
||||
MOVH m+12(FP), R1 // want `invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value`
|
||||
MOVH f+16(FP), R1 // want `invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$16 // want `wrong argument size 0; expected \$\.\.\.-16`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVH x_len+4(FP), R1 // want `invalid MOVH of x_len\+4\(FP\); string len is 4-byte value`
|
||||
MOVW x_len+4(FP), R1
|
||||
MOVW y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+8\(FP\)`
|
||||
MOVW y_len+4(FP), R1 // want `invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `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 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVH x_len+4(FP), R1 // want `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 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVW x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVH x_cap+8(FP), R1 // want `invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value`
|
||||
MOVW x_cap+8(FP), R1
|
||||
MOVW y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+12\(FP\)`
|
||||
MOVW y_len+4(FP), R1 // want `invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)`
|
||||
MOVW y_cap+8(FP), R1 // want `invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-16
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); interface type is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVH x_type+0(FP), R1 // want `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 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVQ x_itable+1(FP), R1 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVW x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVQ x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVH x_data+4(FP), R1 // want `invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value`
|
||||
MOVW x_data+4(FP), R1
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVW y+8(FP), R1
|
||||
MOVH y_itable+8(FP), R1 // want `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 // want `unknown variable y_type; offset 8 is y_itable\+8\(FP\)`
|
||||
MOVH y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVW y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVH y_data+12(FP), AX // want `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) // want `invalid MOVH of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVW R1, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVB R1, ret+3(FP) // want `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) // want `invalid MOVH of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVW R1, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVB R1, ret+3(FP) // want `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) // want `invalid MOVB of r1\+4\(FP\); int is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-4
|
||||
RET // want `RET without writing to 4-byte ret\+0\(FP\)`
|
||||
68
vendor/golang.org/x/tools/go/analysis/passes/assign/assign.go
generated
vendored
Normal file
68
vendor/golang.org/x/tools/go/analysis/passes/assign/assign.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package assign defines an Analyzer that detects useless assignments.
|
||||
package assign
|
||||
|
||||
// TODO(adonovan): check also for assignments to struct fields inside
|
||||
// methods that are on T instead of *T.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for useless assignments
|
||||
|
||||
This checker reports assignments of the form x = x or a[i] = a[i].
|
||||
These are almost always useless, and even when they aren't they are
|
||||
usually a mistake.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "assign",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.AssignStmt)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
stmt := n.(*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 analysisutil.HasSideEffects(pass.TypesInfo, lhs) ||
|
||||
analysisutil.HasSideEffects(pass.TypesInfo, rhs) {
|
||||
continue // expressions may not be equal
|
||||
}
|
||||
if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
|
||||
continue // short-circuit the heavy-weight gofmt check
|
||||
}
|
||||
le := analysisutil.Format(pass.Fset, lhs)
|
||||
re := analysisutil.Format(pass.Fset, rhs)
|
||||
if le == re {
|
||||
pass.Reportf(stmt.Pos(), "self-assignment of %s to %s", re, le)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/assign/assign_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/assign/assign_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package assign_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/assign"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, assign.Analyzer, "a")
|
||||
}
|
||||
31
vendor/golang.org/x/tools/go/analysis/passes/assign/testdata/src/a/a.go
generated
vendored
Normal file
31
vendor/golang.org/x/tools/go/analysis/passes/assign/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file contains tests for the useless-assignment checker.
|
||||
|
||||
package testdata
|
||||
|
||||
import "math/rand"
|
||||
|
||||
type ST struct {
|
||||
x int
|
||||
l []int
|
||||
}
|
||||
|
||||
func (s *ST) SetX(x int, ch chan int) {
|
||||
// Accidental self-assignment; it should be "s.x = x"
|
||||
x = x // want "self-assignment of x to x"
|
||||
// Another mistake
|
||||
s.x = s.x // want "self-assignment of s.x to s.x"
|
||||
|
||||
s.l[0] = s.l[0] // want "self-assignment of s.l.0. to s.l.0."
|
||||
|
||||
// Bail on any potential side effects to avoid false positives
|
||||
s.l[num()] = s.l[num()]
|
||||
rng := rand.New(rand.NewSource(0))
|
||||
s.l[rng.Intn(len(s.l))] = s.l[rng.Intn(len(s.l))]
|
||||
s.l[<-ch] = s.l[<-ch]
|
||||
}
|
||||
|
||||
func num() int { return 2 }
|
||||
96
vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic.go
generated
vendored
Normal file
96
vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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 atomic defines an Analyzer that checks for common mistakes
|
||||
// using the sync/atomic package.
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for common mistakes using the sync/atomic package
|
||||
|
||||
The atomic checker looks for assignment statements of the form:
|
||||
|
||||
x = atomic.AddUint64(&x, 1)
|
||||
|
||||
which are not atomic.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "atomic",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
RunDespiteErrors: true,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.AssignStmt)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(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 := pass.TypesInfo.Uses[pkgIdent].(*types.PkgName)
|
||||
if !ok || pkgName.Imported().Path() != "sync/atomic" {
|
||||
continue
|
||||
}
|
||||
|
||||
switch sel.Sel.Name {
|
||||
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
||||
checkAtomicAddAssignment(pass, n.Lhs[i], call)
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// checkAtomicAddAssignment walks the atomic.Add* method calls checking
|
||||
// for assigning the return value to the same variable being used in the
|
||||
// operation
|
||||
func checkAtomicAddAssignment(pass *analysis.Pass, left ast.Expr, call *ast.CallExpr) {
|
||||
if len(call.Args) != 2 {
|
||||
return
|
||||
}
|
||||
arg := call.Args[0]
|
||||
broken := false
|
||||
|
||||
gofmt := func(e ast.Expr) string { return analysisutil.Format(pass.Fset, e) }
|
||||
|
||||
if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
|
||||
broken = gofmt(left) == gofmt(uarg.X)
|
||||
} else if star, ok := left.(*ast.StarExpr); ok {
|
||||
broken = gofmt(star.X) == gofmt(arg)
|
||||
}
|
||||
|
||||
if broken {
|
||||
pass.Reportf(left.Pos(), "direct assignment to atomic value")
|
||||
}
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package atomic_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/atomic"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, atomic.Analyzer, "a")
|
||||
}
|
||||
62
vendor/golang.org/x/tools/go/analysis/passes/atomic/testdata/src/a/a.go
generated
vendored
Normal file
62
vendor/golang.org/x/tools/go/analysis/passes/atomic/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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 a
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Counter uint64
|
||||
|
||||
func AtomicTests() {
|
||||
x := uint64(1)
|
||||
x = atomic.AddUint64(&x, 1) // want "direct assignment to atomic value"
|
||||
_, x = 10, atomic.AddUint64(&x, 1) // want "direct assignment to atomic value"
|
||||
x, _ = atomic.AddUint64(&x, 1), 10 // want "direct assignment to atomic value"
|
||||
|
||||
y := &x
|
||||
*y = atomic.AddUint64(y, 1) // want "direct assignment to atomic value"
|
||||
|
||||
var su struct{ Counter uint64 }
|
||||
su.Counter = atomic.AddUint64(&su.Counter, 1) // want "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) // want "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) // want "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) // want "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)
|
||||
|
||||
// Re-declaration assigns a new value.
|
||||
x, w := atomic.AddUint64(&x, 1), 10 // want "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
|
||||
}
|
||||
214
vendor/golang.org/x/tools/go/analysis/passes/bools/bools.go
generated
vendored
Normal file
214
vendor/golang.org/x/tools/go/analysis/passes/bools/bools.go
generated
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
// 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 bools defines an Analyzer that detects common mistakes
|
||||
// involving boolean operators.
|
||||
package bools
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "bools",
|
||||
Doc: "check for common mistakes involving boolean operators",
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.BinaryExpr)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(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
|
||||
}
|
||||
|
||||
// TODO(adonovan): this reports n(n-1)/2 errors for an
|
||||
// expression e||...||e of depth n. Fix.
|
||||
// See https://github.com/golang/go/issues/28086.
|
||||
comm := op.commutativeSets(pass.TypesInfo, e)
|
||||
for _, exprs := range comm {
|
||||
op.checkRedundant(pass, exprs)
|
||||
op.checkSuspect(pass, exprs)
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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(info *types.Info, 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(info, 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(pass *analysis.Pass, exprs []ast.Expr) {
|
||||
seen := make(map[string]bool)
|
||||
for _, e := range exprs {
|
||||
efmt := analysisutil.Format(pass.Fset, e)
|
||||
if seen[efmt] {
|
||||
pass.Reportf(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(pass *analysis.Pass, 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 pass.TypesInfo.Types[bin.Y].Value != nil:
|
||||
x = bin.X
|
||||
case pass.TypesInfo.Types[bin.X].Value != nil:
|
||||
x = bin.Y
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
// e is of the form 'x != c' or 'x == c'.
|
||||
xfmt := analysisutil.Format(pass.Fset, x)
|
||||
efmt := analysisutil.Format(pass.Fset, e)
|
||||
if prev, found := seen[xfmt]; found {
|
||||
// checkRedundant handles the case in which efmt == prev.
|
||||
if efmt != prev {
|
||||
pass.Reportf(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(info *types.Info, e ast.Expr) bool {
|
||||
safe := true
|
||||
ast.Inspect(e, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
case *ast.CallExpr:
|
||||
typVal := info.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
|
||||
}
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/bools/bools_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/bools/bools_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package bools_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/bools"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, bools.Analyzer, "a")
|
||||
}
|
||||
137
vendor/golang.org/x/tools/go/analysis/passes/bools/testdata/src/a/a.go
generated
vendored
Normal file
137
vendor/golang.org/x/tools/go/analysis/passes/bools/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// 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 a
|
||||
|
||||
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 { // want `redundant or: v == w \|\| v == w`
|
||||
}
|
||||
_ = f == nil || f == nil // want `redundant or: f == nil \|\| f == nil`
|
||||
|
||||
var B byte
|
||||
_ = B == byte(1) || B == byte(1) // want `redundant or: B == byte\(1\) \|\| B == byte\(1\)`
|
||||
_ = t == T(2) || t == T(2) // want `redundant or: t == T\(2\) \|\| t == T\(2\)`
|
||||
_ = FT(f) == nil || FT(f) == nil // want `redundant or: FT\(f\) == nil \|\| FT\(f\) == nil`
|
||||
|
||||
_ = (func() int)(f) == nil || (func() int)(f) == nil // want `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 { // want `redundant or: i == j \|\| i == j`
|
||||
}
|
||||
|
||||
var i, j, k int
|
||||
_ = i+1 == 1 || i+1 == 1 // want `redundant or: i\+1 == 1 \|\| i\+1 == 1`
|
||||
_ = i == 1 || j+1 == i || i == 1 // want `redundant or: i == 1 \|\| i == 1`
|
||||
|
||||
// The various r.* patterns are intended to match duplicate
|
||||
// diagnostics reported for the same underlying problem.
|
||||
// See github.com/golang/go/issues/28086.
|
||||
// TODO(adonovan): fix the checker.
|
||||
|
||||
_ = i == 1 || i == 1 || f() == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*`
|
||||
_ = i == 1 || f() == 1 || i == 1 // OK f may alter i as a side effect
|
||||
_ = f() == 1 || i == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1`
|
||||
|
||||
// Test partition edge cases
|
||||
_ = f() == 1 || i == 1 || i == 1 || j == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*`
|
||||
_ = f() == 1 || j == 1 || i == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1`
|
||||
_ = i == 1 || f() == 1 || i == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1`
|
||||
_ = i == 1 || i == 1 || f() == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*`
|
||||
_ = i == 1 || i == 1 || j == 1 || f() == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*`
|
||||
_ = j == 1 || i == 1 || i == 1 || f() == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*`
|
||||
_ = i == 1 || f() == 1 || f() == 1 || i == 1
|
||||
|
||||
_ = i == 1 || (i == 1 || i == 2) // want `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) // want `redundant or: i == 1 \|\| i == 1`
|
||||
_ = i == 1 || (i == 2 || (i == 1 || i == 3)) // want `redundant or: i == 1 \|\| i == 1`
|
||||
|
||||
var a, b bool
|
||||
_ = i == 1 || (a || (i == 1 || b)) // want `redundant or: i == 1 \|\| i == 1`
|
||||
|
||||
// Check that all redundant ors are flagged
|
||||
_ = j == 0 ||
|
||||
i == 1 ||
|
||||
f() == 1 ||
|
||||
j == 0 || // want `redundant or: j == 0 \|\| j == 0` `r.*`
|
||||
i == 1 || // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*` `r.*`
|
||||
i == 1 || // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*`
|
||||
i == 1 ||
|
||||
j == 0 ||
|
||||
k == 0
|
||||
|
||||
_ = i == 1*2*3 || i == 1*2*3 // want `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 // want `redundant or: i != 0 \|\| i != 0`
|
||||
_ = i == 0 && i == 0 // want `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 // want `redundant and: f != nil && f != nil`
|
||||
_ = i != 1 && i != 1 && f() != 1 // want `redundant and: i != 1 && i != 1` `r.*`
|
||||
_ = i != 1 && f() != 1 && i != 1 // OK f may alter i as a side effect
|
||||
_ = f() != 1 && i != 1 && i != 1 // want `redundant and: i != 1 && i != 1`
|
||||
}
|
||||
|
||||
func RoyallySuspectConditions() {
|
||||
var i, j int
|
||||
|
||||
_ = i == 0 || i == 1 // OK
|
||||
_ = i != 0 || i != 1 // want `suspect or: i != 0 \|\| i != 1`
|
||||
_ = i != 0 || 1 != i // want `suspect or: i != 0 \|\| 1 != i`
|
||||
_ = 0 != i || 1 != i // want `suspect or: 0 != i \|\| 1 != i`
|
||||
_ = 0 != i || i != 1 // want `suspect or: 0 != i \|\| i != 1`
|
||||
|
||||
_ = (0 != i) || i != 1 // want `suspect or: 0 != i \|\| i != 1`
|
||||
|
||||
_ = i+3 != 7 || j+5 == 0 || i+3 != 9 // want `suspect or: i\+3 != 7 \|\| i\+3 != 9`
|
||||
|
||||
_ = i != 0 || j == 0 || i != 1 // want `suspect or: i != 0 \|\| i != 1`
|
||||
|
||||
_ = i != 0 || i != 1<<4 // want `suspect or: i != 0 \|\| i != 1<<4`
|
||||
|
||||
_ = i != 0 || j != 0
|
||||
_ = 0 != i || 0 != j
|
||||
|
||||
var s string
|
||||
_ = s != "one" || s != "the other" // want `suspect or: s != .one. \|\| s != .the other.`
|
||||
|
||||
_ = "et" != "alii" || "et" != "cetera" // want `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 // want `suspect and: i == 0 && i == 1`
|
||||
_ = i == 0 && 1 == i // want `suspect and: i == 0 && 1 == i`
|
||||
_ = 0 == i && 1 == i // want `suspect and: 0 == i && 1 == i`
|
||||
_ = 0 == i && i == 1 // want `suspect and: 0 == i && i == 1`
|
||||
}
|
||||
117
vendor/golang.org/x/tools/go/analysis/passes/buildssa/buildssa.go
generated
vendored
Normal file
117
vendor/golang.org/x/tools/go/analysis/passes/buildssa/buildssa.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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.
|
||||
|
||||
// Package buildssa defines an Analyzer that constructs the SSA
|
||||
// representation of an error-free package and returns the set of all
|
||||
// functions within it. It does not report any diagnostics itself but
|
||||
// may be used as an input to other analyzers.
|
||||
//
|
||||
// THIS INTERFACE IS EXPERIMENTAL AND MAY BE SUBJECT TO INCOMPATIBLE CHANGE.
|
||||
package buildssa
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "buildssa",
|
||||
Doc: "build SSA-form IR for later passes",
|
||||
Run: run,
|
||||
ResultType: reflect.TypeOf(new(SSA)),
|
||||
}
|
||||
|
||||
// SSA provides SSA-form intermediate representation for all the
|
||||
// non-blank source functions in the current package.
|
||||
type SSA struct {
|
||||
Pkg *ssa.Package
|
||||
SrcFuncs []*ssa.Function
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
// Plundered from ssautil.BuildPackage.
|
||||
|
||||
// We must create a new Program for each Package because the
|
||||
// analysis API provides no place to hang a Program shared by
|
||||
// all Packages. Consequently, SSA Packages and Functions do not
|
||||
// have a canonical representation across an analysis session of
|
||||
// multiple packages. This is unlikely to be a problem in
|
||||
// practice because the analysis API essentially forces all
|
||||
// packages to be analysed independently, so any given call to
|
||||
// Analysis.Run on a package will see only SSA objects belonging
|
||||
// to a single Program.
|
||||
|
||||
// Some Analyzers may need GlobalDebug, in which case we'll have
|
||||
// to set it globally, but let's wait till we need it.
|
||||
mode := ssa.BuilderMode(0)
|
||||
|
||||
prog := ssa.NewProgram(pass.Fset, mode)
|
||||
|
||||
// Create SSA packages for all imports.
|
||||
// Order is not significant.
|
||||
created := make(map[*types.Package]bool)
|
||||
var createAll func(pkgs []*types.Package)
|
||||
createAll = func(pkgs []*types.Package) {
|
||||
for _, p := range pkgs {
|
||||
if !created[p] {
|
||||
created[p] = true
|
||||
prog.CreatePackage(p, nil, nil, true)
|
||||
createAll(p.Imports())
|
||||
}
|
||||
}
|
||||
}
|
||||
createAll(pass.Pkg.Imports())
|
||||
|
||||
// Create and build the primary package.
|
||||
ssapkg := prog.CreatePackage(pass.Pkg, pass.Files, pass.TypesInfo, false)
|
||||
ssapkg.Build()
|
||||
|
||||
// Compute list of source functions, including literals,
|
||||
// in source order.
|
||||
var funcs []*ssa.Function
|
||||
for _, f := range pass.Files {
|
||||
for _, decl := range f.Decls {
|
||||
if fdecl, ok := decl.(*ast.FuncDecl); ok {
|
||||
|
||||
// SSA will not build a Function
|
||||
// for a FuncDecl named blank.
|
||||
// That's arguably too strict but
|
||||
// relaxing it would break uniqueness of
|
||||
// names of package members.
|
||||
if fdecl.Name.Name == "_" {
|
||||
continue
|
||||
}
|
||||
|
||||
// (init functions have distinct Func
|
||||
// objects named "init" and distinct
|
||||
// ssa.Functions named "init#1", ...)
|
||||
|
||||
fn := pass.TypesInfo.Defs[fdecl.Name].(*types.Func)
|
||||
if fn == nil {
|
||||
panic(fn)
|
||||
}
|
||||
|
||||
f := ssapkg.Prog.FuncValue(fn)
|
||||
if f == nil {
|
||||
panic(fn)
|
||||
}
|
||||
|
||||
var addAnons func(f *ssa.Function)
|
||||
addAnons = func(f *ssa.Function) {
|
||||
funcs = append(funcs, f)
|
||||
for _, anon := range f.AnonFuncs {
|
||||
addAnons(anon)
|
||||
}
|
||||
}
|
||||
addAnons(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &SSA{Pkg: ssapkg, SrcFuncs: funcs}, nil
|
||||
}
|
||||
29
vendor/golang.org/x/tools/go/analysis/passes/buildssa/buildssa_test.go
generated
vendored
Normal file
29
vendor/golang.org/x/tools/go/analysis/passes/buildssa/buildssa_test.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
package buildssa_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/buildssa"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
result := analysistest.Run(t, testdata, buildssa.Analyzer, "a")[0].Result
|
||||
|
||||
ssainfo := result.(*buildssa.SSA)
|
||||
got := fmt.Sprint(ssainfo.SrcFuncs)
|
||||
want := `[a.Fib (a.T).fib]`
|
||||
if got != want {
|
||||
t.Errorf("SSA.SrcFuncs = %s, want %s", got, want)
|
||||
for _, f := range ssainfo.SrcFuncs {
|
||||
f.WriteTo(os.Stderr)
|
||||
}
|
||||
}
|
||||
}
|
||||
16
vendor/golang.org/x/tools/go/analysis/passes/buildssa/testdata/src/a/a.go
generated
vendored
Normal file
16
vendor/golang.org/x/tools/go/analysis/passes/buildssa/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package a
|
||||
|
||||
func Fib(x int) int {
|
||||
if x < 2 {
|
||||
return x
|
||||
}
|
||||
return Fib(x-1) + Fib(x-2)
|
||||
}
|
||||
|
||||
type T int
|
||||
|
||||
func (T) fib(x int) int { return Fib(x) }
|
||||
|
||||
func _() {
|
||||
print("hi")
|
||||
}
|
||||
159
vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go
generated
vendored
Normal file
159
vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
// 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 buildtag defines an Analyzer that checks build tags.
|
||||
package buildtag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "buildtag",
|
||||
Doc: "check that +build tags are well-formed and correctly located",
|
||||
Run: runBuildTag,
|
||||
}
|
||||
|
||||
func runBuildTag(pass *analysis.Pass) (interface{}, error) {
|
||||
for _, f := range pass.Files {
|
||||
checkGoFile(pass, f)
|
||||
}
|
||||
for _, name := range pass.OtherFiles {
|
||||
if err := checkOtherFile(pass, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func checkGoFile(pass *analysis.Pass, f *ast.File) {
|
||||
pastCutoff := false
|
||||
for _, group := range f.Comments {
|
||||
// A +build comment is ignored after or adjoining the package declaration.
|
||||
if group.End()+1 >= f.Package {
|
||||
pastCutoff = true
|
||||
}
|
||||
|
||||
// "+build" is ignored within or after a /*...*/ comment.
|
||||
if !strings.HasPrefix(group.List[0].Text, "//") {
|
||||
pastCutoff = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Check each line of a //-comment.
|
||||
for _, c := range group.List {
|
||||
if !strings.Contains(c.Text, "+build") {
|
||||
continue
|
||||
}
|
||||
if err := checkLine(c.Text, pastCutoff); err != nil {
|
||||
pass.Reportf(c.Pos(), "%s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkOtherFile(pass *analysis.Pass, filename string) error {
|
||||
content, tf, err := analysisutil.ReadFile(pass.Fset, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We must look at the raw lines, as build tags may appear in non-Go
|
||||
// files such as assembly files.
|
||||
lines := bytes.SplitAfter(content, nl)
|
||||
|
||||
// Determine cutpoint where +build comments are no longer valid.
|
||||
// They are valid in leading // comments in the file followed by
|
||||
// a blank line.
|
||||
//
|
||||
// This must be done as a separate pass because of the
|
||||
// requirement that the comment be followed by a blank line.
|
||||
var cutoff int
|
||||
for i, line := range lines {
|
||||
line = bytes.TrimSpace(line)
|
||||
if !bytes.HasPrefix(line, slashSlash) {
|
||||
if len(line) > 0 {
|
||||
break
|
||||
}
|
||||
cutoff = i
|
||||
}
|
||||
}
|
||||
|
||||
for i, line := range lines {
|
||||
line = bytes.TrimSpace(line)
|
||||
if !bytes.HasPrefix(line, slashSlash) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Contains(line, []byte("+build")) {
|
||||
continue
|
||||
}
|
||||
if err := checkLine(string(line), i >= cutoff); err != nil {
|
||||
pass.Reportf(analysisutil.LineStart(tf, i+1), "%s", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkLine checks a line that starts with "//" and contains "+build".
|
||||
func checkLine(line string, pastCutoff bool) error {
|
||||
line = strings.TrimPrefix(line, "//")
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if strings.HasPrefix(line, "+build") {
|
||||
fields := strings.Fields(line)
|
||||
if fields[0] != "+build" {
|
||||
// Comment is something like +buildasdf not +build.
|
||||
return fmt.Errorf("possible malformed +build comment")
|
||||
}
|
||||
if pastCutoff {
|
||||
return fmt.Errorf("+build comment must appear before package clause and be followed by a blank line")
|
||||
}
|
||||
if err := checkArguments(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Comment with +build but not at beginning.
|
||||
if !pastCutoff {
|
||||
return fmt.Errorf("possible malformed +build comment")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkArguments(fields []string) error {
|
||||
// The original version of this checker in vet could examine
|
||||
// files with malformed build tags that would cause the file to
|
||||
// be always ignored by "go build". However, drivers for the new
|
||||
// analysis API will analyze only the files selected to form a
|
||||
// package, so these checks will never fire.
|
||||
// TODO(adonovan): rethink this.
|
||||
|
||||
for _, arg := range fields[1:] {
|
||||
for _, elem := range strings.Split(arg, ",") {
|
||||
if strings.HasPrefix(elem, "!!") {
|
||||
return fmt.Errorf("invalid double negative in build constraint: %s", arg)
|
||||
}
|
||||
elem = strings.TrimPrefix(elem, "!")
|
||||
for _, c := range elem {
|
||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
|
||||
return fmt.Errorf("invalid non-alphanumeric build constraint: %s", arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
nl = []byte("\n")
|
||||
slashSlash = []byte("//")
|
||||
)
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package buildtag_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/buildtag"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, buildtag.Analyzer, "a")
|
||||
}
|
||||
21
vendor/golang.org/x/tools/go/analysis/passes/buildtag/testdata/src/a/buildtag.go
generated
vendored
Normal file
21
vendor/golang.org/x/tools/go/analysis/passes/buildtag/testdata/src/a/buildtag.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file contains tests for the buildtag checker.
|
||||
|
||||
// +builder // want `possible malformed \+build comment`
|
||||
// +build !ignore
|
||||
|
||||
// Mention +build // want `possible malformed \+build comment`
|
||||
|
||||
// +build nospace // want "build comment must appear before package clause and be followed by a blank line"
|
||||
package a
|
||||
|
||||
// +build toolate // want "build comment must appear before package clause and be followed by a blank line$"
|
||||
|
||||
var _ = 3
|
||||
|
||||
var _ = `
|
||||
// +build notacomment
|
||||
`
|
||||
390
vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go
generated
vendored
Normal file
390
vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go
generated
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
// 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 cgocall defines an Analyzer that detects some violations of
|
||||
// the cgo pointer passing rules.
|
||||
package cgocall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
)
|
||||
|
||||
const debug = false
|
||||
|
||||
const doc = `detect some violations of the cgo pointer passing rules
|
||||
|
||||
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.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "cgocall",
|
||||
Doc: doc,
|
||||
RunDespiteErrors: true,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
if imports(pass.Pkg, "runtime/cgo") == nil {
|
||||
return nil, nil // doesn't use cgo
|
||||
}
|
||||
|
||||
cgofiles, info, err := typeCheckCgoSourceFiles(pass.Fset, pass.Pkg, pass.Files, pass.TypesInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, f := range cgofiles {
|
||||
checkCgo(pass.Fset, f, info, pass.Reportf)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func checkCgo(fset *token.FileSet, f *ast.File, info *types.Info, reportf func(token.Pos, string, ...interface{})) {
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
call, ok := n.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// Is this a C.f() call?
|
||||
var name string
|
||||
if sel, ok := analysisutil.Unparen(call.Fun).(*ast.SelectorExpr); ok {
|
||||
if id, ok := sel.X.(*ast.Ident); ok && id.Name == "C" {
|
||||
name = sel.Sel.Name
|
||||
}
|
||||
}
|
||||
if name == "" {
|
||||
return true // not a call we need to check
|
||||
}
|
||||
|
||||
// A call to C.CBytes passes a pointer but is always safe.
|
||||
if name == "CBytes" {
|
||||
return true
|
||||
}
|
||||
|
||||
if debug {
|
||||
log.Printf("%s: call to C.%s", fset.Position(call.Lparen), name)
|
||||
}
|
||||
|
||||
for _, arg := range call.Args {
|
||||
if !typeOKForCgoCall(cgoBaseType(info, arg), make(map[types.Type]bool)) {
|
||||
reportf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
|
||||
break
|
||||
}
|
||||
|
||||
// Check for passing the address of a bad type.
|
||||
if conv, ok := arg.(*ast.CallExpr); ok && len(conv.Args) == 1 &&
|
||||
isUnsafePointer(info, conv.Fun) {
|
||||
arg = conv.Args[0]
|
||||
}
|
||||
if u, ok := arg.(*ast.UnaryExpr); ok && u.Op == token.AND {
|
||||
if !typeOKForCgoCall(cgoBaseType(info, u.X), make(map[types.Type]bool)) {
|
||||
reportf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// typeCheckCgoSourceFiles returns type-checked syntax trees for the raw
|
||||
// cgo files of a package (those that import "C"). Such files are not
|
||||
// Go, so there may be gaps in type information around C.f references.
|
||||
//
|
||||
// This checker was initially written in vet to inpect raw cgo source
|
||||
// files using partial type information. However, Analyzers in the new
|
||||
// analysis API are presented with the type-checked, "cooked" Go ASTs
|
||||
// resulting from cgo-processing files, so we must choose between
|
||||
// working with the cooked file generated by cgo (which was tried but
|
||||
// proved fragile) or locating the raw cgo file (e.g. from //line
|
||||
// directives) and working with that, as we now do.
|
||||
//
|
||||
// Specifically, we must type-check the raw cgo source files (or at
|
||||
// least the subtrees needed for this analyzer) in an environment that
|
||||
// simulates the rest of the already type-checked package.
|
||||
//
|
||||
// For example, for each raw cgo source file in the original package,
|
||||
// such as this one:
|
||||
//
|
||||
// package p
|
||||
// import "C"
|
||||
// import "fmt"
|
||||
// type T int
|
||||
// const k = 3
|
||||
// var x, y = fmt.Println()
|
||||
// func f() { ... }
|
||||
// func g() { ... C.malloc(k) ... }
|
||||
// func (T) f(int) string { ... }
|
||||
//
|
||||
// we synthesize a new ast.File, shown below, that dot-imports the
|
||||
// orginal "cooked" package using a special name ("·this·"), so that all
|
||||
// references to package members resolve correctly. (References to
|
||||
// unexported names cause an "unexported" error, which we ignore.)
|
||||
//
|
||||
// To avoid shadowing names imported from the cooked package,
|
||||
// package-level declarations in the new source file are modified so
|
||||
// that they do not declare any names.
|
||||
// (The cgocall analysis is concerned with uses, not declarations.)
|
||||
// Specifically, type declarations are discarded;
|
||||
// all names in each var and const declaration are blanked out;
|
||||
// each method is turned into a regular function by turning
|
||||
// the receiver into the first parameter;
|
||||
// and all functions are renamed to "_".
|
||||
//
|
||||
// package p
|
||||
// import . "·this·" // declares T, k, x, y, f, g, T.f
|
||||
// import "C"
|
||||
// import "fmt"
|
||||
// const _ = 3
|
||||
// var _, _ = fmt.Println()
|
||||
// func _() { ... }
|
||||
// func _() { ... C.malloc(k) ... }
|
||||
// func _(T, int) string { ... }
|
||||
//
|
||||
// In this way, the raw function bodies and const/var initializer
|
||||
// expressions are preserved but refer to the "cooked" objects imported
|
||||
// from "·this·", and none of the transformed package-level declarations
|
||||
// actually declares anything. In the example above, the reference to k
|
||||
// in the argument of the call to C.malloc resolves to "·this·".k, which
|
||||
// has an accurate type.
|
||||
//
|
||||
// This approach could in principle be generalized to more complex
|
||||
// analyses on raw cgo files. One could synthesize a "C" package so that
|
||||
// C.f would resolve to "·this·"._C_func_f, for example. But we have
|
||||
// limited ourselves here to preserving function bodies and initializer
|
||||
// expressions since that is all that the cgocall analyzer needs.
|
||||
//
|
||||
func typeCheckCgoSourceFiles(fset *token.FileSet, pkg *types.Package, files []*ast.File, info *types.Info) ([]*ast.File, *types.Info, error) {
|
||||
const thispkg = "·this·"
|
||||
|
||||
// Which files are cgo files?
|
||||
var cgoFiles []*ast.File
|
||||
importMap := map[string]*types.Package{thispkg: pkg}
|
||||
for _, raw := range files {
|
||||
// If f is a cgo-generated file, Position reports
|
||||
// the original file, honoring //line directives.
|
||||
filename := fset.Position(raw.Pos()).Filename
|
||||
f, err := parser.ParseFile(fset, filename, nil, parser.Mode(0))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("can't parse raw cgo file: %v", err)
|
||||
}
|
||||
found := false
|
||||
for _, spec := range f.Imports {
|
||||
if spec.Path.Value == `"C"` {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
continue // not a cgo file
|
||||
}
|
||||
|
||||
// Record the original import map.
|
||||
for _, spec := range raw.Imports {
|
||||
path, _ := strconv.Unquote(spec.Path.Value)
|
||||
importMap[path] = imported(info, spec)
|
||||
}
|
||||
|
||||
// Add special dot-import declaration:
|
||||
// import . "·this·"
|
||||
var decls []ast.Decl
|
||||
decls = append(decls, &ast.GenDecl{
|
||||
Tok: token.IMPORT,
|
||||
Specs: []ast.Spec{
|
||||
&ast.ImportSpec{
|
||||
Name: &ast.Ident{Name: "."},
|
||||
Path: &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: strconv.Quote(thispkg),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Transform declarations from the raw cgo file.
|
||||
for _, decl := range f.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch decl.Tok {
|
||||
case token.TYPE:
|
||||
// Discard type declarations.
|
||||
continue
|
||||
case token.IMPORT:
|
||||
// Keep imports.
|
||||
case token.VAR, token.CONST:
|
||||
// Blank the declared var/const names.
|
||||
for _, spec := range decl.Specs {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
for i := range spec.Names {
|
||||
spec.Names[i].Name = "_"
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
// Blank the declared func name.
|
||||
decl.Name.Name = "_"
|
||||
|
||||
// Turn a method receiver: func (T) f(P) R {...}
|
||||
// into regular parameter: func _(T, P) R {...}
|
||||
if decl.Recv != nil {
|
||||
var params []*ast.Field
|
||||
params = append(params, decl.Recv.List...)
|
||||
params = append(params, decl.Type.Params.List...)
|
||||
decl.Type.Params.List = params
|
||||
decl.Recv = nil
|
||||
}
|
||||
}
|
||||
decls = append(decls, decl)
|
||||
}
|
||||
f.Decls = decls
|
||||
if debug {
|
||||
format.Node(os.Stderr, fset, f) // debugging
|
||||
}
|
||||
cgoFiles = append(cgoFiles, f)
|
||||
}
|
||||
if cgoFiles == nil {
|
||||
return nil, nil, nil // nothing to do (can't happen?)
|
||||
}
|
||||
|
||||
// Type-check the synthetic files.
|
||||
tc := &types.Config{
|
||||
FakeImportC: true,
|
||||
Importer: importerFunc(func(path string) (*types.Package, error) {
|
||||
return importMap[path], nil
|
||||
}),
|
||||
// TODO(adonovan): Sizes should probably be provided by analysis.Pass.
|
||||
Sizes: types.SizesFor("gc", build.Default.GOARCH),
|
||||
Error: func(error) {}, // ignore errors (e.g. unused import)
|
||||
}
|
||||
|
||||
// It's tempting to record the new types in the
|
||||
// existing pass.TypesInfo, but we don't own it.
|
||||
altInfo := &types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
}
|
||||
tc.Check(pkg.Path(), fset, cgoFiles, altInfo)
|
||||
|
||||
return cgoFiles, altInfo, nil
|
||||
}
|
||||
|
||||
// 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(info *types.Info, arg ast.Expr) types.Type {
|
||||
switch arg := arg.(type) {
|
||||
case *ast.CallExpr:
|
||||
if len(arg.Args) == 1 && isUnsafePointer(info, arg.Fun) {
|
||||
return cgoBaseType(info, 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 := info.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 !isUnsafePointer(info, call.Fun) {
|
||||
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(info, u.X)
|
||||
}
|
||||
|
||||
return info.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
|
||||
}
|
||||
|
||||
func isUnsafePointer(info *types.Info, e ast.Expr) bool {
|
||||
t := info.Types[e].Type
|
||||
return t != nil && t.Underlying() == types.Typ[types.UnsafePointer]
|
||||
}
|
||||
|
||||
type importerFunc func(path string) (*types.Package, error)
|
||||
|
||||
func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
|
||||
|
||||
// TODO(adonovan): make this a library function or method of Info.
|
||||
func imported(info *types.Info, spec *ast.ImportSpec) *types.Package {
|
||||
obj, ok := info.Implicits[spec]
|
||||
if !ok {
|
||||
obj = info.Defs[spec.Name] // renaming import
|
||||
}
|
||||
return obj.(*types.PkgName).Imported()
|
||||
}
|
||||
|
||||
// imports reports whether pkg has path among its direct imports.
|
||||
// It returns the imported package if so, or nil if not.
|
||||
// TODO(adonovan): move to analysisutil.
|
||||
func imports(pkg *types.Package, path string) *types.Package {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp.Path() == path {
|
||||
return imp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package cgocall_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/cgocall"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, cgocall.Analyzer, "a", "b", "c")
|
||||
}
|
||||
71
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/a/cgo.go
generated
vendored
Normal file
71
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/a/cgo.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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 a
|
||||
|
||||
// void f(void *ptr) {}
|
||||
import "C"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func CgoTests() {
|
||||
var c chan bool
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&c))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&c)) // want "embedded pointer"
|
||||
|
||||
var m map[string]string
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&m))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&m)) // want "embedded pointer"
|
||||
|
||||
var f func()
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&f))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&f)) // want "embedded pointer"
|
||||
|
||||
var s []int
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&s)) // want "embedded pointer"
|
||||
|
||||
var a [1][]int
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&a)) // want "embedded pointer"
|
||||
|
||||
var st struct{ f []int }
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&st)) // want "embedded pointer"
|
||||
|
||||
var st3 S
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st3))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&st3)) // want "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))
|
||||
|
||||
var st4 S2
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st4)))
|
||||
C.f(unsafe.Pointer(&st4))
|
||||
|
||||
type cgoStruct struct{ p *cgoStruct }
|
||||
C.f(unsafe.Pointer(&cgoStruct{}))
|
||||
|
||||
C.CBytes([]byte("hello"))
|
||||
}
|
||||
|
||||
type S struct{ slice []int }
|
||||
|
||||
type S2 struct{ int int }
|
||||
21
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/a/cgo3.go
generated
vendored
Normal file
21
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/a/cgo3.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// 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.
|
||||
|
||||
package a
|
||||
|
||||
// The purpose of this inherited test is unclear.
|
||||
|
||||
import "C"
|
||||
|
||||
const x = 1
|
||||
|
||||
var a, b = 1, 2
|
||||
|
||||
func F() {
|
||||
}
|
||||
|
||||
func FAD(int, string) bool {
|
||||
C.malloc(3)
|
||||
return true
|
||||
}
|
||||
20
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/b/b.go
generated
vendored
Normal file
20
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/b/b.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// 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 b
|
||||
|
||||
import C "fmt"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func init() {
|
||||
var f func()
|
||||
C.Println(unsafe.Pointer(&f))
|
||||
|
||||
// Passing a pointer (via a slice), but C is fmt, not cgo.
|
||||
C.Println([]int{3})
|
||||
}
|
||||
14
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/c/c.go
generated
vendored
Normal file
14
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/c/c.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// 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.
|
||||
|
||||
// Test the cgo checker on a file that doesn't use cgo.
|
||||
|
||||
package c
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Passing a pointer (via the slice), but C isn't cgo.
|
||||
var _ = C.f(unsafe.Pointer(new([]int)))
|
||||
|
||||
var C struct{ f func(interface{}) int }
|
||||
108
vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go
generated
vendored
Normal file
108
vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// 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.
|
||||
|
||||
// Package composite defines an Analyzer that checks for unkeyed
|
||||
// composite literals.
|
||||
package composite
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `checked for unkeyed composite literals
|
||||
|
||||
This analyzer reports a diagnostic for composite literals of struct
|
||||
types imported from another package that do not use the field-keyed
|
||||
syntax. Such literals are fragile because the addition of a new field
|
||||
(even if unexported) to the struct will cause compilation to fail.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "composites",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
RunDespiteErrors: true,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
var whitelist = true
|
||||
|
||||
func init() {
|
||||
Analyzer.Flags.BoolVar(&whitelist, "whitelist", whitelist, "use composite white list; for testing only")
|
||||
}
|
||||
|
||||
// runUnkeyedLiteral checks if a composite literal is a struct literal with
|
||||
// unkeyed fields.
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.CompositeLit)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
cl := n.(*ast.CompositeLit)
|
||||
|
||||
typ := pass.TypesInfo.Types[cl].Type
|
||||
if typ == nil {
|
||||
// cannot determine composite literals' type, skip it
|
||||
return
|
||||
}
|
||||
typeName := typ.String()
|
||||
if 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(pass, 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
|
||||
}
|
||||
|
||||
pass.Reportf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName)
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func isLocalType(pass *analysis.Pass, typ types.Type) bool {
|
||||
switch x := typ.(type) {
|
||||
case *types.Struct:
|
||||
// struct literals are local types
|
||||
return true
|
||||
case *types.Pointer:
|
||||
return isLocalType(pass, 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(pass.Pkg.Path(), "_test")
|
||||
}
|
||||
return false
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/composite/composite_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/composite/composite_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package composite_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/composite"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, composite.Analyzer, "a")
|
||||
}
|
||||
121
vendor/golang.org/x/tools/go/analysis/passes/composite/testdata/src/a/a.go
generated
vendored
Normal file
121
vendor/golang.org/x/tools/go/analysis/passes/composite/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
// 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 a
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"image"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
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{ // want "unkeyed fields"
|
||||
"Name",
|
||||
"Usage",
|
||||
nil, // Value
|
||||
"DefValue",
|
||||
}
|
||||
|
||||
var delta [3]rune
|
||||
|
||||
// SpecialCase is a named slice of CaseRange to test issue 9171.
|
||||
var goodNamedSliceLiteral = unicode.SpecialCase{
|
||||
{Lo: 1, Hi: 2, Delta: delta},
|
||||
unicode.CaseRange{Lo: 1, Hi: 2, Delta: delta},
|
||||
}
|
||||
var badNamedSliceLiteral = unicode.SpecialCase{
|
||||
{1, 2, delta}, // want "unkeyed fields"
|
||||
unicode.CaseRange{1, 2, delta}, // want "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{token.Position{}, "foobar"}, // want "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 = unicode.NoSuchType{"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, delta}, // want "unkeyed fields"
|
||||
&unicode.CaseRange{1, 2, delta}, // want "unkeyed fields"
|
||||
}
|
||||
33
vendor/golang.org/x/tools/go/analysis/passes/composite/whitelist.go
generated
vendored
Normal file
33
vendor/golang.org/x/tools/go/analysis/passes/composite/whitelist.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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 composite
|
||||
|
||||
// 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,
|
||||
|
||||
// These three structs are used in generated test main files,
|
||||
// but the generator can be trusted.
|
||||
"testing.InternalBenchmark": true,
|
||||
"testing.InternalExample": true,
|
||||
"testing.InternalTest": true,
|
||||
}
|
||||
300
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
generated
vendored
Normal file
300
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
generated
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
// 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 copylock defines an Analyzer that checks for locks
|
||||
// erroneously passed by value.
|
||||
package copylock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for locks erroneously passed by value
|
||||
|
||||
Inadvertently copying a value containing a lock, such as sync.Mutex or
|
||||
sync.WaitGroup, may cause both copies to malfunction. Generally such
|
||||
values should be referred to through a pointer.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "copylocks",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
RunDespiteErrors: true,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.AssignStmt)(nil),
|
||||
(*ast.CallExpr)(nil),
|
||||
(*ast.CompositeLit)(nil),
|
||||
(*ast.FuncDecl)(nil),
|
||||
(*ast.FuncLit)(nil),
|
||||
(*ast.GenDecl)(nil),
|
||||
(*ast.RangeStmt)(nil),
|
||||
(*ast.ReturnStmt)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(node ast.Node) {
|
||||
switch node := node.(type) {
|
||||
case *ast.RangeStmt:
|
||||
checkCopyLocksRange(pass, node)
|
||||
case *ast.FuncDecl:
|
||||
checkCopyLocksFunc(pass, node.Name.Name, node.Recv, node.Type)
|
||||
case *ast.FuncLit:
|
||||
checkCopyLocksFunc(pass, "func", nil, node.Type)
|
||||
case *ast.CallExpr:
|
||||
checkCopyLocksCallExpr(pass, node)
|
||||
case *ast.AssignStmt:
|
||||
checkCopyLocksAssign(pass, node)
|
||||
case *ast.GenDecl:
|
||||
checkCopyLocksGenDecl(pass, node)
|
||||
case *ast.CompositeLit:
|
||||
checkCopyLocksCompositeLit(pass, node)
|
||||
case *ast.ReturnStmt:
|
||||
checkCopyLocksReturnStmt(pass, node)
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// checkCopyLocksAssign checks whether an assignment
|
||||
// copies a lock.
|
||||
func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) {
|
||||
for i, x := range as.Rhs {
|
||||
if path := lockPathRhs(pass, x); path != nil {
|
||||
pass.Reportf(x.Pos(), "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, as.Lhs[i]), path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkCopyLocksGenDecl checks whether lock is copied
|
||||
// in variable declaration.
|
||||
func checkCopyLocksGenDecl(pass *analysis.Pass, 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(pass, x); path != nil {
|
||||
pass.Reportf(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(pass *analysis.Pass, cl *ast.CompositeLit) {
|
||||
for _, x := range cl.Elts {
|
||||
if node, ok := x.(*ast.KeyValueExpr); ok {
|
||||
x = node.Value
|
||||
}
|
||||
if path := lockPathRhs(pass, x); path != nil {
|
||||
pass.Reportf(x.Pos(), "literal copies lock value from %v: %v", analysisutil.Format(pass.Fset, x), path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkCopyLocksReturnStmt detects lock copy in return statement
|
||||
func checkCopyLocksReturnStmt(pass *analysis.Pass, rs *ast.ReturnStmt) {
|
||||
for _, x := range rs.Results {
|
||||
if path := lockPathRhs(pass, x); path != nil {
|
||||
pass.Reportf(x.Pos(), "return copies lock value: %v", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkCopyLocksCallExpr detects lock copy in the arguments to a function call
|
||||
func checkCopyLocksCallExpr(pass *analysis.Pass, 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 := pass.TypesInfo.Uses[id].(*types.Builtin); ok {
|
||||
switch fun.Name() {
|
||||
case "new", "len", "cap", "Sizeof":
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, x := range ce.Args {
|
||||
if path := lockPathRhs(pass, x); path != nil {
|
||||
pass.Reportf(x.Pos(), "call of %s copies lock value: %v", analysisutil.Format(pass.Fset, 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(pass *analysis.Pass, name string, recv *ast.FieldList, typ *ast.FuncType) {
|
||||
if recv != nil && len(recv.List) > 0 {
|
||||
expr := recv.List[0].Type
|
||||
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
|
||||
pass.Reportf(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(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
|
||||
pass.Reportf(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(pass *analysis.Pass, r *ast.RangeStmt) {
|
||||
checkCopyLocksRangeVar(pass, r.Tok, r.Key)
|
||||
checkCopyLocksRangeVar(pass, r.Tok, r.Value)
|
||||
}
|
||||
|
||||
func checkCopyLocksRangeVar(pass *analysis.Pass, 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 := pass.TypesInfo.Defs[id]
|
||||
if obj == nil {
|
||||
return
|
||||
}
|
||||
typ = obj.Type()
|
||||
} else {
|
||||
typ = pass.TypesInfo.Types[e].Type
|
||||
}
|
||||
|
||||
if typ == nil {
|
||||
return
|
||||
}
|
||||
if path := lockPath(pass.Pkg, typ); path != nil {
|
||||
pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisutil.Format(pass.Fset, 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(pass *analysis.Pass, 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(pass.Pkg, pass.TypesInfo.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}
|
||||
}
|
||||
|
||||
// In go1.10, sync.noCopy did not implement Locker.
|
||||
// (The Unlock method was added only in CL 121876.)
|
||||
// TODO(adonovan): remove workaround when we drop go1.10.
|
||||
if named, ok := typ.(*types.Named); ok &&
|
||||
named.Obj().Name() == "noCopy" &&
|
||||
named.Obj().Pkg().Path() == "sync" {
|
||||
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()
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package copylock_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/copylock"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, copylock.Analyzer, "a")
|
||||
}
|
||||
188
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock.go
generated
vendored
Normal file
188
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock.go
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
package a
|
||||
|
||||
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 // want `assignment copies lock value to \*p: sync.Mutex`
|
||||
|
||||
var t Tlock
|
||||
var tp *Tlock
|
||||
tp = &t
|
||||
*tp = t // want `assignment copies lock value to \*tp: a.Tlock contains sync.Once contains sync.Mutex`
|
||||
t = *tp // want "assignment copies lock value to t: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
|
||||
y := *x // want "assignment copies lock value to y: sync.Mutex"
|
||||
var z = t // want "variable declaration copies lock value to z: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
|
||||
w := struct{ L sync.Mutex }{
|
||||
L: *x, // want `literal copies lock value from \*x: sync.Mutex`
|
||||
}
|
||||
var q = map[int]Tlock{
|
||||
1: t, // want "literal copies lock value from t: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
2: *tp, // want `literal copies lock value from \*tp: a.Tlock contains sync.Once contains sync.Mutex`
|
||||
}
|
||||
yy := []Tlock{
|
||||
t, // want "literal copies lock value from t: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
*tp, // want `literal copies lock value from \*tp: a.Tlock contains sync.Once contains sync.Mutex`
|
||||
}
|
||||
|
||||
// override 'new' keyword
|
||||
new := func(interface{}) {}
|
||||
new(t) // want "call of new copies lock value: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
|
||||
// copy of array of locks
|
||||
var muA [5]sync.Mutex
|
||||
muB := muA // want "assignment copies lock value to muB: sync.Mutex"
|
||||
muA = muB // want "assignment copies lock value to muA: sync.Mutex"
|
||||
muSlice := muA[:] // OK
|
||||
|
||||
// multidimensional array
|
||||
var mmuA [5][5]sync.Mutex
|
||||
mmuB := mmuA // want "assignment copies lock value to mmuB: sync.Mutex"
|
||||
mmuA = mmuB // want "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) // want "call of len copies lock value: sync.Mutex"
|
||||
|
||||
cap := func(interface{}) {}
|
||||
cap(a) // want "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) // want "call of unsafe.Sizeof copies lock value: sync.Mutex"
|
||||
Sizeof := func(interface{}) {}
|
||||
Sizeof(mu) // want "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 // want "assignment copies lock value to rwmuY: sync.RWMutex"
|
||||
rwmuY = rwmuX // want "assignment copies lock value to rwmuY: sync.RWMutex"
|
||||
var rwmuYY = rwmuX // want "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 // want "assignment copies lock value to condY: sync.Cond contains sync.noCopy"
|
||||
condY = condX // want "assignment copies lock value to condY: sync.Cond contains sync.noCopy"
|
||||
var condYY = condX // want "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 // want "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy"
|
||||
wgY = wgX // want "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy"
|
||||
var wgYY = wgX // want "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 // want "assignment copies lock value to poolY: sync.Pool contains sync.noCopy"
|
||||
poolY = poolX // want "assignment copies lock value to poolY: sync.Pool contains sync.noCopy"
|
||||
var poolYY = poolX // want "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 // want "assignment copies lock value to onceY: sync.Once contains sync.Mutex"
|
||||
onceY = onceX // want "assignment copies lock value to onceY: sync.Once contains sync.Mutex"
|
||||
var onceYY = onceX // want "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{}
|
||||
}
|
||||
136
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock_func.go
generated
vendored
Normal file
136
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock_func.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
// 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 a
|
||||
|
||||
import "sync"
|
||||
|
||||
func OkFunc(*sync.Mutex) {}
|
||||
func BadFunc(sync.Mutex) {} // want "BadFunc passes lock by value: sync.Mutex"
|
||||
func BadFunc2(sync.Map) {} // want "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) {} // want "func passes lock by value: sync.Mutex"
|
||||
BadClosure2 = func(sync.Map) {} // want "func passes lock by value: sync.Map contains sync.Mutex"
|
||||
)
|
||||
|
||||
type EmbeddedRWMutex struct {
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (*EmbeddedRWMutex) OkMeth() {}
|
||||
func (EmbeddedRWMutex) BadMeth() {} // want "BadMeth passes lock by value: a.EmbeddedRWMutex"
|
||||
func OkFunc(e *EmbeddedRWMutex) {}
|
||||
func BadFunc(EmbeddedRWMutex) {} // want "BadFunc passes lock by value: a.EmbeddedRWMutex"
|
||||
func OkRet() *EmbeddedRWMutex {}
|
||||
func BadRet() EmbeddedRWMutex {} // Don't warn about results
|
||||
|
||||
type FieldMutex struct {
|
||||
s sync.Mutex
|
||||
}
|
||||
|
||||
func (*FieldMutex) OkMeth() {}
|
||||
func (FieldMutex) BadMeth() {} // want "BadMeth passes lock by value: a.FieldMutex contains sync.Mutex"
|
||||
func OkFunc(*FieldMutex) {}
|
||||
func BadFunc(FieldMutex, int) {} // want "BadFunc passes lock by value: a.FieldMutex contains sync.Mutex"
|
||||
|
||||
type L0 struct {
|
||||
L1
|
||||
}
|
||||
|
||||
type L1 struct {
|
||||
l L2
|
||||
}
|
||||
|
||||
type L2 struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (*L0) Ok() {}
|
||||
func (L0) Bad() {} // want "Bad passes lock by value: a.L0 contains a.L1 contains a.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) {} // want "Bad passes lock by value: a.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) // want "call of f copies lock value: sync.Mutex"
|
||||
f(5, t) // want "call of f copies lock value: struct.lock sync.Mutex. contains sync.Mutex"
|
||||
var fntab []func(t)
|
||||
fntab[0](t) // want "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 // want "return copies lock value: sync.Mutex"
|
||||
default:
|
||||
return 3, t // want "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() {} // want "Bad passes lock by value: a.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 :(
|
||||
67
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock_range.go
generated
vendored
Normal file
67
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock_range.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file contains tests for the copylock checker's
|
||||
// range statement analysis.
|
||||
|
||||
package a
|
||||
|
||||
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 { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for _, m := range s { // want "range var m copies lock: sync.Mutex"
|
||||
}
|
||||
for i, mu = range s { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for i, m := range s { // want "range var m copies lock: sync.Mutex"
|
||||
}
|
||||
|
||||
var a [3]sync.Mutex
|
||||
for _, m := range a { // want "range var m copies lock: sync.Mutex"
|
||||
}
|
||||
|
||||
var m map[sync.Mutex]sync.Mutex
|
||||
for k := range m { // want "range var k copies lock: sync.Mutex"
|
||||
}
|
||||
for mu, _ = range m { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for k, _ := range m { // want "range var k copies lock: sync.Mutex"
|
||||
}
|
||||
for _, mu = range m { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for _, v := range m { // want "range var v copies lock: sync.Mutex"
|
||||
}
|
||||
|
||||
var c chan sync.Mutex
|
||||
for range c {
|
||||
}
|
||||
for mu = range c { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for v := range c { // want "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 { // want "range var t.mu copies lock: sync.Mutex"
|
||||
}
|
||||
}
|
||||
225
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
generated
vendored
Normal file
225
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
// 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.
|
||||
|
||||
// Package ctrlflow is an analysis that provides a syntactic
|
||||
// control-flow graph (CFG) for the body of a function.
|
||||
// It records whether a function cannot return.
|
||||
// By itself, it does not report any diagnostics.
|
||||
package ctrlflow
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"log"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/cfg"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "ctrlflow",
|
||||
Doc: "build a control-flow graph",
|
||||
Run: run,
|
||||
ResultType: reflect.TypeOf(new(CFGs)),
|
||||
FactTypes: []analysis.Fact{new(noReturn)},
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
}
|
||||
|
||||
// noReturn is a fact indicating that a function does not return.
|
||||
type noReturn struct{}
|
||||
|
||||
func (*noReturn) AFact() {}
|
||||
|
||||
func (*noReturn) String() string { return "noReturn" }
|
||||
|
||||
// A CFGs holds the control-flow graphs
|
||||
// for all the functions of the current package.
|
||||
type CFGs struct {
|
||||
defs map[*ast.Ident]types.Object // from Pass.TypesInfo.Defs
|
||||
funcDecls map[*types.Func]*declInfo
|
||||
funcLits map[*ast.FuncLit]*litInfo
|
||||
pass *analysis.Pass // transient; nil after construction
|
||||
}
|
||||
|
||||
// CFGs has two maps: funcDecls for named functions and funcLits for
|
||||
// unnamed ones. Unlike funcLits, the funcDecls map is not keyed by its
|
||||
// syntax node, *ast.FuncDecl, because callMayReturn needs to do a
|
||||
// look-up by *types.Func, and you can get from an *ast.FuncDecl to a
|
||||
// *types.Func but not the other way.
|
||||
|
||||
type declInfo struct {
|
||||
decl *ast.FuncDecl
|
||||
cfg *cfg.CFG // iff decl.Body != nil
|
||||
started bool // to break cycles
|
||||
noReturn bool
|
||||
}
|
||||
|
||||
type litInfo struct {
|
||||
cfg *cfg.CFG
|
||||
noReturn bool
|
||||
}
|
||||
|
||||
// FuncDecl returns the control-flow graph for a named function.
|
||||
// It returns nil if decl.Body==nil.
|
||||
func (c *CFGs) FuncDecl(decl *ast.FuncDecl) *cfg.CFG {
|
||||
if decl.Body == nil {
|
||||
return nil
|
||||
}
|
||||
fn := c.defs[decl.Name].(*types.Func)
|
||||
return c.funcDecls[fn].cfg
|
||||
}
|
||||
|
||||
// FuncLit returns the control-flow graph for a literal function.
|
||||
func (c *CFGs) FuncLit(lit *ast.FuncLit) *cfg.CFG {
|
||||
return c.funcLits[lit].cfg
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
// Because CFG construction consumes and produces noReturn
|
||||
// facts, CFGs for exported FuncDecls must be built before 'run'
|
||||
// returns; we cannot construct them lazily.
|
||||
// (We could build CFGs for FuncLits lazily,
|
||||
// but the benefit is marginal.)
|
||||
|
||||
// Pass 1. Map types.Funcs to ast.FuncDecls in this package.
|
||||
funcDecls := make(map[*types.Func]*declInfo) // functions and methods
|
||||
funcLits := make(map[*ast.FuncLit]*litInfo)
|
||||
|
||||
var decls []*types.Func // keys(funcDecls), in order
|
||||
var lits []*ast.FuncLit // keys(funcLits), in order
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.FuncDecl)(nil),
|
||||
(*ast.FuncLit)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
switch n := n.(type) {
|
||||
case *ast.FuncDecl:
|
||||
fn := pass.TypesInfo.Defs[n.Name].(*types.Func)
|
||||
funcDecls[fn] = &declInfo{decl: n}
|
||||
decls = append(decls, fn)
|
||||
|
||||
case *ast.FuncLit:
|
||||
funcLits[n] = new(litInfo)
|
||||
lits = append(lits, n)
|
||||
}
|
||||
})
|
||||
|
||||
c := &CFGs{
|
||||
defs: pass.TypesInfo.Defs,
|
||||
funcDecls: funcDecls,
|
||||
funcLits: funcLits,
|
||||
pass: pass,
|
||||
}
|
||||
|
||||
// Pass 2. Build CFGs.
|
||||
|
||||
// Build CFGs for named functions.
|
||||
// Cycles in the static call graph are broken
|
||||
// arbitrarily but deterministically.
|
||||
// We create noReturn facts as discovered.
|
||||
for _, fn := range decls {
|
||||
c.buildDecl(fn, funcDecls[fn])
|
||||
}
|
||||
|
||||
// Build CFGs for literal functions.
|
||||
// These aren't relevant to facts (since they aren't named)
|
||||
// but are required for the CFGs.FuncLit API.
|
||||
for _, lit := range lits {
|
||||
li := funcLits[lit]
|
||||
if li.cfg == nil {
|
||||
li.cfg = cfg.New(lit.Body, c.callMayReturn)
|
||||
if !hasReachableReturn(li.cfg) {
|
||||
li.noReturn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All CFGs are now built.
|
||||
c.pass = nil
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// di.cfg may be nil on return.
|
||||
func (c *CFGs) buildDecl(fn *types.Func, di *declInfo) {
|
||||
// buildDecl may call itself recursively for the same function,
|
||||
// because cfg.New is passed the callMayReturn method, which
|
||||
// builds the CFG of the callee, leading to recursion.
|
||||
// The buildDecl call tree thus resembles the static call graph.
|
||||
// We mark each node when we start working on it to break cycles.
|
||||
|
||||
if !di.started { // break cycle
|
||||
di.started = true
|
||||
|
||||
if isIntrinsicNoReturn(fn) {
|
||||
di.noReturn = true
|
||||
}
|
||||
if di.decl.Body != nil {
|
||||
di.cfg = cfg.New(di.decl.Body, c.callMayReturn)
|
||||
if !hasReachableReturn(di.cfg) {
|
||||
di.noReturn = true
|
||||
}
|
||||
}
|
||||
if di.noReturn {
|
||||
c.pass.ExportObjectFact(fn, new(noReturn))
|
||||
}
|
||||
|
||||
// debugging
|
||||
if false {
|
||||
log.Printf("CFG for %s:\n%s (noreturn=%t)\n", fn, di.cfg.Format(c.pass.Fset), di.noReturn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// callMayReturn reports whether the called function may return.
|
||||
// It is passed to the CFG builder.
|
||||
func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
|
||||
if id, ok := call.Fun.(*ast.Ident); ok && c.pass.TypesInfo.Uses[id] == panicBuiltin {
|
||||
return false // panic never returns
|
||||
}
|
||||
|
||||
// Is this a static call?
|
||||
fn := typeutil.StaticCallee(c.pass.TypesInfo, call)
|
||||
if fn == nil {
|
||||
return true // callee not statically known; be conservative
|
||||
}
|
||||
|
||||
// Function or method declared in this package?
|
||||
if di, ok := c.funcDecls[fn]; ok {
|
||||
c.buildDecl(fn, di)
|
||||
return !di.noReturn
|
||||
}
|
||||
|
||||
// Not declared in this package.
|
||||
// Is there a fact from another package?
|
||||
return !c.pass.ImportObjectFact(fn, new(noReturn))
|
||||
}
|
||||
|
||||
var panicBuiltin = types.Universe.Lookup("panic").(*types.Builtin)
|
||||
|
||||
func hasReachableReturn(g *cfg.CFG) bool {
|
||||
for _, b := range g.Blocks {
|
||||
if b.Live && b.Return() != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isIntrinsicNoReturn reports whether a function intrinsically never
|
||||
// returns because it stops execution of the calling thread.
|
||||
// It is the base case in the recursion.
|
||||
func isIntrinsicNoReturn(fn *types.Func) bool {
|
||||
// Add functions here as the need arises, but don't allocate memory.
|
||||
path, name := fn.Pkg().Path(), fn.Name()
|
||||
return path == "syscall" && (name == "Exit" || name == "ExitProcess" || name == "ExitThread") ||
|
||||
path == "runtime" && name == "Goexit"
|
||||
}
|
||||
35
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow_test.go
generated
vendored
Normal file
35
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow_test.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
|
||||
package ctrlflow_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/ctrlflow"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
|
||||
// load testdata/src/a/a.go
|
||||
results := analysistest.Run(t, testdata, ctrlflow.Analyzer, "a")
|
||||
|
||||
// Perform a minimal smoke test on
|
||||
// the result (CFG) computed by ctrlflow.
|
||||
for _, result := range results {
|
||||
cfgs := result.Result.(*ctrlflow.CFGs)
|
||||
|
||||
for _, decl := range result.Pass.Files[0].Decls {
|
||||
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body != nil {
|
||||
if cfgs.FuncDecl(decl) == nil {
|
||||
t.Errorf("%s: no CFG for func %s",
|
||||
result.Pass.Fset.Position(decl.Pos()), decl.Name.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/testdata/src/a/a.go
generated
vendored
Normal file
109
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
package a
|
||||
|
||||
// This file tests facts produced by ctrlflow.
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"lib"
|
||||
)
|
||||
|
||||
var cond bool
|
||||
|
||||
func a() { // want a:"noReturn"
|
||||
if cond {
|
||||
b()
|
||||
} else {
|
||||
for {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func b() { // want b:"noReturn"
|
||||
select {}
|
||||
}
|
||||
|
||||
func f(x int) { // no fact here
|
||||
switch x {
|
||||
case 0:
|
||||
os.Exit(0)
|
||||
case 1:
|
||||
panic(0)
|
||||
}
|
||||
// default case returns
|
||||
}
|
||||
|
||||
type T int
|
||||
|
||||
func (T) method1() { // want method1:"noReturn"
|
||||
a()
|
||||
}
|
||||
|
||||
func (T) method2() { // (may return)
|
||||
if cond {
|
||||
a()
|
||||
}
|
||||
}
|
||||
|
||||
// Checking for the noreturn fact associated with F ensures that
|
||||
// ctrlflow proved each of the listed functions was "noReturn".
|
||||
//
|
||||
func standardFunctions(x int) { // want standardFunctions:"noReturn"
|
||||
t := new(testing.T)
|
||||
switch x {
|
||||
case 0:
|
||||
t.FailNow()
|
||||
case 1:
|
||||
t.Fatal()
|
||||
case 2:
|
||||
t.Fatalf("")
|
||||
case 3:
|
||||
t.Skip()
|
||||
case 4:
|
||||
t.SkipNow()
|
||||
case 5:
|
||||
t.Skipf("")
|
||||
case 6:
|
||||
log.Fatal()
|
||||
case 7:
|
||||
log.Fatalf("")
|
||||
case 8:
|
||||
log.Fatalln()
|
||||
case 9:
|
||||
os.Exit(0)
|
||||
case 10:
|
||||
syscall.Exit(0)
|
||||
case 11:
|
||||
runtime.Goexit()
|
||||
case 12:
|
||||
log.Panic()
|
||||
case 13:
|
||||
log.Panicln()
|
||||
case 14:
|
||||
log.Panicf("")
|
||||
default:
|
||||
panic(0)
|
||||
}
|
||||
}
|
||||
|
||||
// False positives are possible.
|
||||
// This function is marked noReturn but in fact returns.
|
||||
//
|
||||
func spurious() { // want spurious:"noReturn"
|
||||
defer func() { recover() }()
|
||||
panic(nil)
|
||||
}
|
||||
|
||||
func noBody()
|
||||
|
||||
func g() {
|
||||
lib.CanReturn()
|
||||
}
|
||||
|
||||
func h() { // want h:"noReturn"
|
||||
lib.NoReturn()
|
||||
}
|
||||
8
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/testdata/src/lib/lib.go
generated
vendored
Normal file
8
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/testdata/src/lib/lib.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package lib
|
||||
|
||||
func CanReturn() {}
|
||||
|
||||
func NoReturn() {
|
||||
for {
|
||||
}
|
||||
}
|
||||
9
vendor/golang.org/x/tools/go/analysis/passes/findcall/cmd/findcall/main.go
generated
vendored
Normal file
9
vendor/golang.org/x/tools/go/analysis/passes/findcall/cmd/findcall/main.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// The findcall command runs the findcall analyzer.
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/passes/findcall"
|
||||
"golang.org/x/tools/go/analysis/singlechecker"
|
||||
)
|
||||
|
||||
func main() { singlechecker.Main(findcall.Analyzer) }
|
||||
80
vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall.go
generated
vendored
Normal file
80
vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// 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 findcall package defines an Analyzer that serves as a trivial
|
||||
// example and test of the Analysis API. It reports a diagnostic for
|
||||
// every call to a function or method of the name specified by its
|
||||
// -name flag.
|
||||
package findcall
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
const Doc = `find calls to a particular function
|
||||
|
||||
The findcall analysis reports calls to functions or methods
|
||||
of a particular name.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "findcall",
|
||||
Doc: Doc,
|
||||
Run: run,
|
||||
RunDespiteErrors: true,
|
||||
FactTypes: []analysis.Fact{new(foundFact)},
|
||||
}
|
||||
|
||||
var name string // -name flag
|
||||
|
||||
func init() {
|
||||
Analyzer.Flags.StringVar(&name, "name", name, "name of the function to find")
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
for _, f := range pass.Files {
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
if call, ok := n.(*ast.CallExpr); ok {
|
||||
var id *ast.Ident
|
||||
switch fun := call.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
id = fun
|
||||
case *ast.SelectorExpr:
|
||||
id = fun.Sel
|
||||
}
|
||||
if id != nil && !pass.TypesInfo.Types[id].IsType() && id.Name == name {
|
||||
pass.Reportf(call.Lparen, "call of %s(...)", id.Name)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Export a fact for each matching function.
|
||||
//
|
||||
// These facts are produced only to test the testing
|
||||
// infrastructure in the analysistest package.
|
||||
// They are not consumed by the findcall Analyzer
|
||||
// itself, as would happen in a more realistic example.
|
||||
for _, f := range pass.Files {
|
||||
for _, decl := range f.Decls {
|
||||
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Name.Name == name {
|
||||
if obj, ok := pass.TypesInfo.Defs[decl.Name].(*types.Func); ok {
|
||||
pass.ExportObjectFact(obj, new(foundFact))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// foundFact is a fact associated with functions that match -name.
|
||||
// We use it to exercise the fact machinery in tests.
|
||||
type foundFact struct{}
|
||||
|
||||
func (*foundFact) String() string { return "found" }
|
||||
func (*foundFact) AFact() {}
|
||||
64
vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall_test.go
generated
vendored
Normal file
64
vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall_test.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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.
|
||||
|
||||
package findcall_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/findcall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
findcall.Analyzer.Flags.Set("name", "println")
|
||||
}
|
||||
|
||||
// TestFromStringLiterals demonstrates how to test an analysis using
|
||||
// a table of string literals for each test case.
|
||||
//
|
||||
// Such tests are typically quite compact.
|
||||
func TestFromStringLiterals(t *testing.T) {
|
||||
|
||||
for _, test := range [...]struct {
|
||||
desc string
|
||||
pkgpath string
|
||||
files map[string]string
|
||||
}{
|
||||
{
|
||||
desc: "SimpleTest",
|
||||
pkgpath: "main",
|
||||
files: map[string]string{"main/main.go": `package main
|
||||
|
||||
func main() {
|
||||
println("hello") // want "call of println"
|
||||
print("goodbye") // not a call of println
|
||||
}`,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
dir, cleanup, err := analysistest.WriteFiles(test.files)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
analysistest.Run(t, dir, findcall.Analyzer, test.pkgpath)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestFromFileSystem demonstrates how to test an analysis using input
|
||||
// files stored in the file system.
|
||||
//
|
||||
// These tests have the advantages that test data can be edited
|
||||
// directly, and that files named in error messages can be opened.
|
||||
// However, they tend to spread a small number of lines of text across a
|
||||
// rather deep directory hierarchy, and obscure similarities among
|
||||
// related tests, especially when tests involve multiple packages, or
|
||||
// multiple variants of a single scenario.
|
||||
func TestFromFileSystem(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, findcall.Analyzer, "a") // loads testdata/src/a/a.go.
|
||||
}
|
||||
6
vendor/golang.org/x/tools/go/analysis/passes/findcall/testdata/src/a/a.go
generated
vendored
Normal file
6
vendor/golang.org/x/tools/go/analysis/passes/findcall/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("hi") // want "call of println"
|
||||
print("hi") // not a call of println
|
||||
}
|
||||
177
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse.go
generated
vendored
Normal file
177
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse.go
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
// 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 httpresponse defines an Analyzer that checks for mistakes
|
||||
// using HTTP responses.
|
||||
package httpresponse
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for mistakes using HTTP responses
|
||||
|
||||
A common mistake when using the net/http package is to defer a function
|
||||
call to close the http.Response Body before checking the error that
|
||||
determines whether the response is valid:
|
||||
|
||||
resp, err := http.Head(url)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// (defer statement belongs here)
|
||||
|
||||
This checker helps uncover latent nil dereference bugs by reporting a
|
||||
diagnostic for such mistakes.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "httpresponse",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
// Fast path: if the package doesn't import net/http,
|
||||
// skip the traversal.
|
||||
if !imports(pass.Pkg, "net/http") {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.CallExpr)(nil),
|
||||
}
|
||||
inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool {
|
||||
if !push {
|
||||
return true
|
||||
}
|
||||
call := n.(*ast.CallExpr)
|
||||
if !isHTTPFuncOrMethodOnClient(pass.TypesInfo, call) {
|
||||
return true // the function call is not related to this check.
|
||||
}
|
||||
|
||||
// Find the innermost containing block, and get the list
|
||||
// of statements starting with the one containing call.
|
||||
stmts := restOfBlock(stack)
|
||||
if len(stmts) < 2 {
|
||||
return true // the call to the http function is the last statement of the block.
|
||||
}
|
||||
|
||||
asg, ok := stmts[0].(*ast.AssignStmt)
|
||||
if !ok {
|
||||
return true // the first statement is not assignment.
|
||||
}
|
||||
resp := rootIdent(asg.Lhs[0])
|
||||
if resp == nil {
|
||||
return true // could not find the http.Response in the assignment.
|
||||
}
|
||||
|
||||
def, ok := stmts[1].(*ast.DeferStmt)
|
||||
if !ok {
|
||||
return true // the following statement is not a defer.
|
||||
}
|
||||
root := rootIdent(def.Call.Fun)
|
||||
if root == nil {
|
||||
return true // could not find the receiver of the defer call.
|
||||
}
|
||||
|
||||
if resp.Obj == root.Obj {
|
||||
pass.Reportf(root.Pos(), "using %s before checking for errors", resp.Name)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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(info *types.Info, expr *ast.CallExpr) bool {
|
||||
fun, _ := expr.Fun.(*ast.SelectorExpr)
|
||||
sig, _ := info.Types[fun].Type.(*types.Signature)
|
||||
if sig == nil {
|
||||
return false // the call is not 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.
|
||||
}
|
||||
|
||||
errorType := types.Universe.Lookup("error").Type()
|
||||
if !types.Identical(res.At(1).Type(), errorType) {
|
||||
return false // the second return type is not error
|
||||
}
|
||||
|
||||
typ := info.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.
|
||||
}
|
||||
|
||||
// restOfBlock, given a traversal stack, finds the innermost containing
|
||||
// block and returns the suffix of its statements starting with the
|
||||
// current node (the last element of stack).
|
||||
func restOfBlock(stack []ast.Node) []ast.Stmt {
|
||||
for i := len(stack) - 1; i >= 0; i-- {
|
||||
if b, ok := stack[i].(*ast.BlockStmt); ok {
|
||||
for j, v := range b.List {
|
||||
if v == stack[i+1] {
|
||||
return b.List[j:]
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func imports(pkg *types.Package, path string) bool {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp.Path() == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package httpresponse_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/httpresponse"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, httpresponse.Analyzer, "a")
|
||||
}
|
||||
85
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/testdata/src/a/a.go
generated
vendored
Normal file
85
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
package a
|
||||
|
||||
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() // want "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() // want "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() // want "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() // want "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() // want "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() // want "using resp before checking for errors"
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
45
vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go
generated
vendored
Normal file
45
vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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.
|
||||
|
||||
// Package inspect defines an Analyzer that provides an AST inspector
|
||||
// (golang.org/x/tools/go/ast/inspect.Inspect) for the syntax trees of a
|
||||
// package. It is only a building block for other analyzers.
|
||||
//
|
||||
// Example of use in another analysis:
|
||||
//
|
||||
// import "golang.org/x/tools/go/analysis/passes/inspect"
|
||||
//
|
||||
// var Analyzer = &analysis.Analyzer{
|
||||
// ...
|
||||
// Requires: reflect.TypeOf(new(inspect.Analyzer)),
|
||||
// }
|
||||
//
|
||||
// func run(pass *analysis.Pass) (interface{}, error) {
|
||||
// inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
// inspect.Preorder(nil, func(n ast.Node) {
|
||||
// ...
|
||||
// })
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
package inspect
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "inspect",
|
||||
Doc: "optimize AST traversal for later passes",
|
||||
Run: run,
|
||||
RunDespiteErrors: true,
|
||||
ResultType: reflect.TypeOf(new(inspector.Inspector)),
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
return inspector.New(pass.Files), nil
|
||||
}
|
||||
106
vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go
generated
vendored
Normal file
106
vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// Package analysisutil defines various helper functions
|
||||
// used by two or more packages beneath go/analysis.
|
||||
package analysisutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Format returns a string representation of the expression.
|
||||
func Format(fset *token.FileSet, x ast.Expr) string {
|
||||
var b bytes.Buffer
|
||||
printer.Fprint(&b, fset, x)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// HasSideEffects reports whether evaluation of e has side effects.
|
||||
func HasSideEffects(info *types.Info, e ast.Expr) bool {
|
||||
safe := true
|
||||
ast.Inspect(e, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
case *ast.CallExpr:
|
||||
typVal := info.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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFile reads a file and adds it to the FileSet
|
||||
// so that we can report errors against it using lineStart.
|
||||
func ReadFile(fset *token.FileSet, filename string) ([]byte, *token.File, error) {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
tf := fset.AddFile(filename, -1, len(content))
|
||||
tf.SetLinesForContent(content)
|
||||
return content, tf, nil
|
||||
}
|
||||
|
||||
// LineStart returns the position of the start of the specified line
|
||||
// within file f, or NoPos if there is no line of that number.
|
||||
func LineStart(f *token.File, line int) token.Pos {
|
||||
// Use binary search to find the start offset of this line.
|
||||
//
|
||||
// TODO(adonovan): eventually replace this function with the
|
||||
// simpler and more efficient (*go/token.File).LineStart, added
|
||||
// in go1.12.
|
||||
|
||||
min := 0 // inclusive
|
||||
max := f.Size() // exclusive
|
||||
for {
|
||||
offset := (min + max) / 2
|
||||
pos := f.Pos(offset)
|
||||
posn := f.Position(pos)
|
||||
if posn.Line == line {
|
||||
return pos - (token.Pos(posn.Column) - 1)
|
||||
}
|
||||
|
||||
if min+1 >= max {
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
if posn.Line < line {
|
||||
min = offset
|
||||
} else {
|
||||
max = offset
|
||||
}
|
||||
}
|
||||
}
|
||||
130
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure.go
generated
vendored
Normal file
130
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
// 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.
|
||||
|
||||
// Package loopclosure defines an Analyzer that checks for references to
|
||||
// enclosing loop variables from within nested functions.
|
||||
package loopclosure
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
// TODO(adonovan): also report an error for the following structure,
|
||||
// which is often used to ensure that deferred calls do not accumulate
|
||||
// in a loop:
|
||||
//
|
||||
// for i, x := range c {
|
||||
// func() {
|
||||
// ...reference to i or x...
|
||||
// }()
|
||||
// }
|
||||
|
||||
const Doc = `check references to loop variables from within nested functions
|
||||
|
||||
This analyzer checks for references to loop variables from within a
|
||||
function literal inside the loop body. It checks only instances where
|
||||
the function literal is called in a defer or go statement that 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`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "loopclosure",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.RangeStmt)(nil),
|
||||
(*ast.ForStmt)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n 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 := n.(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 pass.TypesInfo.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 {
|
||||
pass.Reportf(id.Pos(), "loop variable %s captured by func literal",
|
||||
id.Name)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package loopclosure_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/loopclosure"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, loopclosure.Analyzer, "a")
|
||||
}
|
||||
90
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/testdata/src/a/a.go
generated
vendored
Normal file
90
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// 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 loopclosure checker.
|
||||
|
||||
package testdata
|
||||
|
||||
func _() {
|
||||
var s []int
|
||||
for i, v := range s {
|
||||
go func() {
|
||||
println(i) // want "loop variable i captured by func literal"
|
||||
println(v) // want "loop variable v captured by func literal"
|
||||
}()
|
||||
}
|
||||
for i, v := range s {
|
||||
defer func() {
|
||||
println(i) // want "loop variable i captured by func literal"
|
||||
println(v) // want "loop variable v captured by func literal"
|
||||
}()
|
||||
}
|
||||
for i := range s {
|
||||
go func() {
|
||||
println(i) // want "loop variable i captured by func literal"
|
||||
}()
|
||||
}
|
||||
for _, v := range s {
|
||||
go func() {
|
||||
println(v) // want "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 // want "loop variable f captured by func literal"
|
||||
}()
|
||||
}
|
||||
type T struct {
|
||||
v int
|
||||
}
|
||||
for _, v := range s {
|
||||
go func() {
|
||||
_ = T{v: 1}
|
||||
_ = map[int]int{v: 1} // want "loop variable v captured by func literal"
|
||||
}()
|
||||
}
|
||||
|
||||
// ordinary for-loops
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
print(i) // want "loop variable i captured by func literal"
|
||||
}()
|
||||
}
|
||||
for i, j := 0, 1; i < 100; i, j = j, i+j {
|
||||
go func() {
|
||||
print(j) // want "loop variable j captured by func literal"
|
||||
}()
|
||||
}
|
||||
type cons struct {
|
||||
car int
|
||||
cdr *cons
|
||||
}
|
||||
var head *cons
|
||||
for p := head; p != nil; p = p.cdr {
|
||||
go func() {
|
||||
print(p.car) // want "loop variable p captured by func literal"
|
||||
}()
|
||||
}
|
||||
}
|
||||
10
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/cmd/lostcancel/main.go
generated
vendored
Normal file
10
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/cmd/lostcancel/main.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// The nilness command applies the golang.org/x/tools/go/analysis/passes/lostcancel
|
||||
// analysis to the specified packages of Go source code.
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/passes/lostcancel"
|
||||
"golang.org/x/tools/go/analysis/singlechecker"
|
||||
)
|
||||
|
||||
func main() { singlechecker.Main(lostcancel.Analyzer) }
|
||||
310
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go
generated
vendored
Normal file
310
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go
generated
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
// 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 lostcancel defines an Analyzer that checks for failure to
|
||||
// call a context cancelation function.
|
||||
package lostcancel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/ctrlflow"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/cfg"
|
||||
)
|
||||
|
||||
const Doc = `check cancel func returned by context.WithCancel is called
|
||||
|
||||
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.)`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "lostcancel",
|
||||
Doc: Doc,
|
||||
Run: run,
|
||||
Requires: []*analysis.Analyzer{
|
||||
inspect.Analyzer,
|
||||
ctrlflow.Analyzer,
|
||||
},
|
||||
}
|
||||
|
||||
const debug = 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 run(pass *analysis.Pass) (interface{}, error) {
|
||||
// Fast path: bypass check if file doesn't use context.WithCancel.
|
||||
if !hasImport(pass.Pkg, contextPackage) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Call runFunc for each Func{Decl,Lit}.
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
nodeTypes := []ast.Node{
|
||||
(*ast.FuncLit)(nil),
|
||||
(*ast.FuncDecl)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeTypes, func(n ast.Node) {
|
||||
runFunc(pass, n)
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func runFunc(pass *analysis.Pass, node ast.Node) {
|
||||
// Maps each cancel variable to its defining ValueSpec/AssignStmt.
|
||||
cancelvars := make(map[*types.Var]ast.Node)
|
||||
|
||||
// TODO(adonovan): opt: refactor to make a single pass
|
||||
// over the AST using inspect.WithStack and node types
|
||||
// {FuncDecl,FuncLit,CallExpr,SelectorExpr}.
|
||||
|
||||
// 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(pass.TypesInfo, 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 == "_" {
|
||||
pass.Reportf(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 := pass.TypesInfo.Uses[id].(*types.Var); ok {
|
||||
cancelvars[v] = stmt
|
||||
} else if v, ok := pass.TypesInfo.Defs[id].(*types.Var); ok {
|
||||
cancelvars[v] = stmt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if len(cancelvars) == 0 {
|
||||
return // no need to inspect CFG
|
||||
}
|
||||
|
||||
// Obtain the CFG.
|
||||
cfgs := pass.ResultOf[ctrlflow.Analyzer].(*ctrlflow.CFGs)
|
||||
var g *cfg.CFG
|
||||
var sig *types.Signature
|
||||
switch node := node.(type) {
|
||||
case *ast.FuncDecl:
|
||||
sig, _ = pass.TypesInfo.Defs[node.Name].Type().(*types.Signature)
|
||||
if node.Name.Name == "main" && sig.Recv() == nil && pass.Pkg.Name() == "main" {
|
||||
// Returning from main.main terminates the process,
|
||||
// so there's no need to cancel contexts.
|
||||
return
|
||||
}
|
||||
g = cfgs.FuncDecl(node)
|
||||
|
||||
case *ast.FuncLit:
|
||||
sig, _ = pass.TypesInfo.Types[node.Type].Type.(*types.Signature)
|
||||
g = cfgs.FuncLit(node)
|
||||
}
|
||||
if sig == nil {
|
||||
return // missing type information
|
||||
}
|
||||
|
||||
// Print CFG.
|
||||
if debug {
|
||||
fmt.Println(g.Format(pass.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(pass, g, v, stmt, sig); ret != nil {
|
||||
lineno := pass.Fset.Position(stmt.Pos()).Line
|
||||
pass.Reportf(stmt.Pos(), "the %s function is not used on all paths (possible context leak)", v.Name())
|
||||
pass.Reportf(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(pkg *types.Package, path string) bool {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp.Path() == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isContextWithCancel reports whether n is one of the qualified identifiers
|
||||
// context.With{Cancel,Timeout,Deadline}.
|
||||
func isContextWithCancel(info *types.Info, 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 := info.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(pass *analysis.Pass, 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(pass *analysis.Pass, 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 pass.TypesInfo.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(pass *analysis.Pass, v *types.Var, b *cfg.Block) bool {
|
||||
res, ok := memo[b]
|
||||
if !ok {
|
||||
res = uses(pass, 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(pass, 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(pass, v, b) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Found path to return statement?
|
||||
if ret := b.Return(); ret != nil {
|
||||
if debug {
|
||||
fmt.Printf("found path to return in block %s\n", b)
|
||||
}
|
||||
return ret // found
|
||||
}
|
||||
|
||||
// Recur
|
||||
if ret := search(b.Succs); ret != nil {
|
||||
if debug {
|
||||
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
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package lostcancel_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/lostcancel"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, lostcancel.Analyzer, "a", "b")
|
||||
}
|
||||
173
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/testdata/src/a/a.go
generated
vendored
Normal file
173
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
// 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 a
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var bg = context.Background()
|
||||
|
||||
// Check the three functions and assignment forms (var, :=, =) we look for.
|
||||
// (Do these early: line numbers are fragile.)
|
||||
func _() {
|
||||
var _, cancel = context.WithCancel(bg) // want `the cancel function is not used on all paths \(possible context leak\)`
|
||||
if false {
|
||||
_ = cancel
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel var defined on line 20"
|
||||
|
||||
func _() {
|
||||
_, cancel2 := context.WithDeadline(bg, time.Time{}) // want "the cancel2 function is not used..."
|
||||
if false {
|
||||
_ = cancel2
|
||||
}
|
||||
} // want "may be reached without using the cancel2 var defined on line 27"
|
||||
|
||||
func _() {
|
||||
var cancel3 func()
|
||||
_, cancel3 = context.WithTimeout(bg, 0) // want "function is not used..."
|
||||
if false {
|
||||
_ = cancel3
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel3 var defined on line 35"
|
||||
|
||||
func _() {
|
||||
ctx, _ := context.WithCancel(bg) // want "the cancel function returned by context.WithCancel should be called, not discarded, to avoid a context leak"
|
||||
ctx, _ = context.WithTimeout(bg, 0) // want "the cancel function returned by context.WithTimeout should be called, not discarded, to avoid a context leak"
|
||||
ctx, _ = context.WithDeadline(bg, time.Time{}) // want "the cancel function returned by context.WithDeadline should be called, not discarded, to avoid a context leak"
|
||||
_ = ctx
|
||||
}
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg)
|
||||
defer cancel() // ok
|
||||
}
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg) // want "not used on all paths"
|
||||
if condition {
|
||||
cancel()
|
||||
}
|
||||
return // want "this return statement may be reached without using the cancel var"
|
||||
}
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg)
|
||||
if condition {
|
||||
cancel()
|
||||
} else {
|
||||
// ok: infinite loop
|
||||
for {
|
||||
print(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg) // want "not used on all paths"
|
||||
if condition {
|
||||
cancel()
|
||||
} else {
|
||||
for i := 0; i < 10; i++ {
|
||||
print(0)
|
||||
}
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel var"
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg)
|
||||
// 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 _() {
|
||||
_, cancel := context.WithCancel(bg) // want "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)
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel var"
|
||||
|
||||
func _(ch chan int) {
|
||||
_, cancel := context.WithCancel(bg) // want "not used on all paths"
|
||||
select {
|
||||
case <-ch:
|
||||
new(testing.T).FailNow()
|
||||
case ch <- 2:
|
||||
print("hi") // falls through to implicit return
|
||||
case ch <- 1:
|
||||
cancel()
|
||||
default:
|
||||
os.Exit(1)
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel var"
|
||||
|
||||
func _(ch chan int) {
|
||||
_, cancel := context.WithCancel(bg)
|
||||
// A blocking select must execute one of its cases.
|
||||
select {
|
||||
case <-ch:
|
||||
panic(0)
|
||||
}
|
||||
if false {
|
||||
_ = cancel
|
||||
}
|
||||
}
|
||||
|
||||
func _() {
|
||||
go func() {
|
||||
ctx, cancel := context.WithCancel(bg) // want "not used on all paths"
|
||||
if false {
|
||||
_ = cancel
|
||||
}
|
||||
print(ctx)
|
||||
}() // want "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(bg)
|
||||
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(bg)
|
||||
return
|
||||
}
|
||||
26
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/testdata/src/b/b.go
generated
vendored
Normal file
26
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/testdata/src/b/b.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import "context"
|
||||
|
||||
// Return from main is handled specially.
|
||||
// Since the program exits, there's no need to call cancel.
|
||||
func main() {
|
||||
_, cancel := context.WithCancel(nil)
|
||||
if maybe {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func notMain() {
|
||||
_, cancel := context.WithCancel(nil) // want "cancel function.*not used"
|
||||
|
||||
if maybe {
|
||||
cancel()
|
||||
}
|
||||
} // want "return statement.*reached without using the cancel"
|
||||
|
||||
var maybe bool
|
||||
74
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go
generated
vendored
Normal file
74
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// 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 nilfunc defines an Analyzer that checks for useless
|
||||
// comparisons against nil.
|
||||
package nilfunc
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for useless comparisons between functions and nil
|
||||
|
||||
A useless comparison is one like f == nil as opposed to f() == nil.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "nilfunc",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.BinaryExpr)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
e := n.(*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 pass.TypesInfo.Types[e.X].IsNil():
|
||||
e2 = e.Y
|
||||
case pass.TypesInfo.Types[e.Y].IsNil():
|
||||
e2 = e.X
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
// Only want identifiers or selector expressions.
|
||||
var obj types.Object
|
||||
switch v := e2.(type) {
|
||||
case *ast.Ident:
|
||||
obj = pass.TypesInfo.Uses[v]
|
||||
case *ast.SelectorExpr:
|
||||
obj = pass.TypesInfo.Uses[v.Sel]
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
// Only want functions.
|
||||
if _, ok := obj.(*types.Func); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
pass.Reportf(e.Pos(), "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ)
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package nilfunc_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/nilfunc"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, nilfunc.Analyzer, "a")
|
||||
}
|
||||
35
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/testdata/src/a/a.go
generated
vendored
Normal file
35
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package a
|
||||
|
||||
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 { // want "comparison of function F == nil is always false"
|
||||
panic("can't happen")
|
||||
}
|
||||
if t.M == nil { // want "comparison of function M == nil is always false"
|
||||
panic("can't happen")
|
||||
}
|
||||
if F != nil { // want "comparison of function F != nil is always true"
|
||||
if t.M != nil { // want "comparison of function M != nil is always true"
|
||||
return
|
||||
}
|
||||
}
|
||||
panic("can't happen")
|
||||
}
|
||||
10
vendor/golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness/main.go
generated
vendored
Normal file
10
vendor/golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness/main.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// The nilness command applies the golang.org/x/tools/go/analysis/passes/nilness
|
||||
// analysis to the specified packages of Go source code.
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/passes/nilness"
|
||||
"golang.org/x/tools/go/analysis/singlechecker"
|
||||
)
|
||||
|
||||
func main() { singlechecker.Main(nilness.Analyzer) }
|
||||
271
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go
generated
vendored
Normal file
271
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
// 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.
|
||||
|
||||
// Package nilness inspects the control-flow graph of an SSA function
|
||||
// and reports errors such as nil pointer dereferences and degenerate
|
||||
// nil pointer comparisons.
|
||||
package nilness
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/buildssa"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
const Doc = `check for redundant or impossible nil comparisons
|
||||
|
||||
The nilness checker inspects the control-flow graph of each function in
|
||||
a package and reports nil pointer dereferences and degenerate nil
|
||||
pointers. A degenerate comparison is of the form x==nil or x!=nil where x
|
||||
is statically known to be nil or non-nil. These are often a mistake,
|
||||
especially in control flow related to errors.
|
||||
|
||||
This check reports conditions such as:
|
||||
|
||||
if f == nil { // impossible condition (f is a function)
|
||||
}
|
||||
|
||||
and:
|
||||
|
||||
p := &v
|
||||
...
|
||||
if p != nil { // tautological condition
|
||||
}
|
||||
|
||||
and:
|
||||
|
||||
if p == nil {
|
||||
print(*p) // nil dereference
|
||||
}
|
||||
`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "nilness",
|
||||
Doc: Doc,
|
||||
Run: run,
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
|
||||
for _, fn := range ssainput.SrcFuncs {
|
||||
runFunc(pass, fn)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func runFunc(pass *analysis.Pass, fn *ssa.Function) {
|
||||
reportf := func(category string, pos token.Pos, format string, args ...interface{}) {
|
||||
pass.Report(analysis.Diagnostic{
|
||||
Pos: pos,
|
||||
Category: category,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
})
|
||||
}
|
||||
|
||||
// notNil reports an error if v is provably nil.
|
||||
notNil := func(stack []fact, instr ssa.Instruction, v ssa.Value, descr string) {
|
||||
if nilnessOf(stack, v) == isnil {
|
||||
reportf("nilderef", instr.Pos(), "nil dereference in "+descr)
|
||||
}
|
||||
}
|
||||
|
||||
// visit visits reachable blocks of the CFG in dominance order,
|
||||
// maintaining a stack of dominating nilness facts.
|
||||
//
|
||||
// By traversing the dom tree, we can pop facts off the stack as
|
||||
// soon as we've visited a subtree. Had we traversed the CFG,
|
||||
// we would need to retain the set of facts for each block.
|
||||
seen := make([]bool, len(fn.Blocks)) // seen[i] means visit should ignore block i
|
||||
var visit func(b *ssa.BasicBlock, stack []fact)
|
||||
visit = func(b *ssa.BasicBlock, stack []fact) {
|
||||
if seen[b.Index] {
|
||||
return
|
||||
}
|
||||
seen[b.Index] = true
|
||||
|
||||
// Report nil dereferences.
|
||||
for _, instr := range b.Instrs {
|
||||
switch instr := instr.(type) {
|
||||
case ssa.CallInstruction:
|
||||
notNil(stack, instr, instr.Common().Value,
|
||||
instr.Common().Description())
|
||||
case *ssa.FieldAddr:
|
||||
notNil(stack, instr, instr.X, "field selection")
|
||||
case *ssa.IndexAddr:
|
||||
notNil(stack, instr, instr.X, "index operation")
|
||||
case *ssa.MapUpdate:
|
||||
notNil(stack, instr, instr.Map, "map update")
|
||||
case *ssa.Slice:
|
||||
// A nilcheck occurs in ptr[:] iff ptr is a pointer to an array.
|
||||
if _, ok := instr.X.Type().Underlying().(*types.Pointer); ok {
|
||||
notNil(stack, instr, instr.X, "slice operation")
|
||||
}
|
||||
case *ssa.Store:
|
||||
notNil(stack, instr, instr.Addr, "store")
|
||||
case *ssa.TypeAssert:
|
||||
notNil(stack, instr, instr.X, "type assertion")
|
||||
case *ssa.UnOp:
|
||||
if instr.Op == token.MUL { // *X
|
||||
notNil(stack, instr, instr.X, "load")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For nil comparison blocks, report an error if the condition
|
||||
// is degenerate, and push a nilness fact on the stack when
|
||||
// visiting its true and false successor blocks.
|
||||
if binop, tsucc, fsucc := eq(b); binop != nil {
|
||||
xnil := nilnessOf(stack, binop.X)
|
||||
ynil := nilnessOf(stack, binop.Y)
|
||||
|
||||
if ynil != unknown && xnil != unknown && (xnil == isnil || ynil == isnil) {
|
||||
// Degenerate condition:
|
||||
// the nilness of both operands is known,
|
||||
// and at least one of them is nil.
|
||||
var adj string
|
||||
if (xnil == ynil) == (binop.Op == token.EQL) {
|
||||
adj = "tautological"
|
||||
} else {
|
||||
adj = "impossible"
|
||||
}
|
||||
reportf("cond", binop.Pos(), "%s condition: %s %s %s", adj, xnil, binop.Op, ynil)
|
||||
|
||||
// If tsucc's or fsucc's sole incoming edge is impossible,
|
||||
// it is unreachable. Prune traversal of it and
|
||||
// all the blocks it dominates.
|
||||
// (We could be more precise with full dataflow
|
||||
// analysis of control-flow joins.)
|
||||
var skip *ssa.BasicBlock
|
||||
if xnil == ynil {
|
||||
skip = fsucc
|
||||
} else {
|
||||
skip = tsucc
|
||||
}
|
||||
for _, d := range b.Dominees() {
|
||||
if d == skip && len(d.Preds) == 1 {
|
||||
continue
|
||||
}
|
||||
visit(d, stack)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// "if x == nil" or "if nil == y" condition; x, y are unknown.
|
||||
if xnil == isnil || ynil == isnil {
|
||||
var f fact
|
||||
if xnil == isnil {
|
||||
// x is nil, y is unknown:
|
||||
// t successor learns y is nil.
|
||||
f = fact{binop.Y, isnil}
|
||||
} else {
|
||||
// x is nil, y is unknown:
|
||||
// t successor learns x is nil.
|
||||
f = fact{binop.X, isnil}
|
||||
}
|
||||
|
||||
for _, d := range b.Dominees() {
|
||||
// Successor blocks learn a fact
|
||||
// only at non-critical edges.
|
||||
// (We could do be more precise with full dataflow
|
||||
// analysis of control-flow joins.)
|
||||
s := stack
|
||||
if len(d.Preds) == 1 {
|
||||
if d == tsucc {
|
||||
s = append(s, f)
|
||||
} else if d == fsucc {
|
||||
s = append(s, f.negate())
|
||||
}
|
||||
}
|
||||
visit(d, s)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, d := range b.Dominees() {
|
||||
visit(d, stack)
|
||||
}
|
||||
}
|
||||
|
||||
// Visit the entry block. No need to visit fn.Recover.
|
||||
if fn.Blocks != nil {
|
||||
visit(fn.Blocks[0], make([]fact, 0, 20)) // 20 is plenty
|
||||
}
|
||||
}
|
||||
|
||||
// A fact records that a block is dominated
|
||||
// by the condition v == nil or v != nil.
|
||||
type fact struct {
|
||||
value ssa.Value
|
||||
nilness nilness
|
||||
}
|
||||
|
||||
func (f fact) negate() fact { return fact{f.value, -f.nilness} }
|
||||
|
||||
type nilness int
|
||||
|
||||
const (
|
||||
isnonnil = -1
|
||||
unknown nilness = 0
|
||||
isnil = 1
|
||||
)
|
||||
|
||||
var nilnessStrings = []string{"non-nil", "unknown", "nil"}
|
||||
|
||||
func (n nilness) String() string { return nilnessStrings[n+1] }
|
||||
|
||||
// nilnessOf reports whether v is definitely nil, definitely not nil,
|
||||
// or unknown given the dominating stack of facts.
|
||||
func nilnessOf(stack []fact, v ssa.Value) nilness {
|
||||
// Is value intrinsically nil or non-nil?
|
||||
switch v := v.(type) {
|
||||
case *ssa.Alloc,
|
||||
*ssa.FieldAddr,
|
||||
*ssa.FreeVar,
|
||||
*ssa.Function,
|
||||
*ssa.Global,
|
||||
*ssa.IndexAddr,
|
||||
*ssa.MakeChan,
|
||||
*ssa.MakeClosure,
|
||||
*ssa.MakeInterface,
|
||||
*ssa.MakeMap,
|
||||
*ssa.MakeSlice:
|
||||
return isnonnil
|
||||
case *ssa.Const:
|
||||
if v.IsNil() {
|
||||
return isnil
|
||||
} else {
|
||||
return isnonnil
|
||||
}
|
||||
}
|
||||
|
||||
// Search dominating control-flow facts.
|
||||
for _, f := range stack {
|
||||
if f.value == v {
|
||||
return f.nilness
|
||||
}
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
// If b ends with an equality comparison, eq returns the operation and
|
||||
// its true (equal) and false (not equal) successors.
|
||||
func eq(b *ssa.BasicBlock) (op *ssa.BinOp, tsucc, fsucc *ssa.BasicBlock) {
|
||||
if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok {
|
||||
if binop, ok := If.Cond.(*ssa.BinOp); ok {
|
||||
switch binop.Op {
|
||||
case token.EQL:
|
||||
return binop, b.Succs[0], b.Succs[1]
|
||||
case token.NEQ:
|
||||
return binop, b.Succs[1], b.Succs[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package nilness_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/nilness"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, nilness.Analyzer, "a")
|
||||
}
|
||||
91
vendor/golang.org/x/tools/go/analysis/passes/nilness/testdata/src/a/a.go
generated
vendored
Normal file
91
vendor/golang.org/x/tools/go/analysis/passes/nilness/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package a
|
||||
|
||||
type X struct{ f, g int }
|
||||
|
||||
func f(x, y *X) {
|
||||
if x == nil {
|
||||
print(x.f) // want "nil dereference in field selection"
|
||||
} else {
|
||||
print(x.f)
|
||||
}
|
||||
|
||||
if x == nil {
|
||||
if nil != y {
|
||||
print(1)
|
||||
panic(0)
|
||||
}
|
||||
x.f = 1 // want "nil dereference in field selection"
|
||||
y.f = 1 // want "nil dereference in field selection"
|
||||
}
|
||||
|
||||
var f func()
|
||||
if f == nil { // want "tautological condition: nil == nil"
|
||||
go f() // want "nil dereference in dynamic function call"
|
||||
} else {
|
||||
// This block is unreachable,
|
||||
// so we don't report an error for the
|
||||
// nil dereference in the call.
|
||||
defer f()
|
||||
}
|
||||
}
|
||||
|
||||
func f2(ptr *[3]int, i interface{}) {
|
||||
if ptr != nil {
|
||||
print(ptr[:])
|
||||
*ptr = [3]int{}
|
||||
print(*ptr)
|
||||
} else {
|
||||
print(ptr[:]) // want "nil dereference in slice operation"
|
||||
*ptr = [3]int{} // want "nil dereference in store"
|
||||
print(*ptr) // want "nil dereference in load"
|
||||
|
||||
if ptr != nil { // want "impossible condition: nil != nil"
|
||||
// Dominated by ptr==nil and ptr!=nil,
|
||||
// this block is unreachable.
|
||||
// We do not report errors within it.
|
||||
print(*ptr)
|
||||
}
|
||||
}
|
||||
|
||||
if i != nil {
|
||||
print(i.(interface{ f() }))
|
||||
} else {
|
||||
print(i.(interface{ f() })) // want "nil dereference in type assertion"
|
||||
}
|
||||
}
|
||||
|
||||
func g() error
|
||||
|
||||
func f3() error {
|
||||
err := g()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil && err.Error() == "foo" { // want "impossible condition: nil != nil"
|
||||
print(0)
|
||||
}
|
||||
ch := make(chan int)
|
||||
if ch == nil { // want "impossible condition: non-nil == nil"
|
||||
print(0)
|
||||
}
|
||||
if ch != nil { // want "tautological condition: non-nil != nil"
|
||||
print(0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func h(err error, b bool) {
|
||||
if err != nil && b {
|
||||
return
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func i(*int) error {
|
||||
for {
|
||||
if err := g(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
127
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact.go
generated
vendored
Normal file
127
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
// 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 pkgfact package is a demonstration and test of the package fact
|
||||
// mechanism.
|
||||
//
|
||||
// The output of the pkgfact analysis is a set of key/values pairs
|
||||
// gathered from the analyzed package and its imported dependencies.
|
||||
// Each key/value pair comes from a top-level constant declaration
|
||||
// whose name starts and ends with "_". For example:
|
||||
//
|
||||
// package p
|
||||
//
|
||||
// const _greeting_ = "hello"
|
||||
// const _audience_ = "world"
|
||||
//
|
||||
// the pkgfact analysis output for package p would be:
|
||||
//
|
||||
// {"greeting": "hello", "audience": "world"}.
|
||||
//
|
||||
// In addition, the analysis reports a diagnostic at each import
|
||||
// showing which key/value pairs it contributes.
|
||||
package pkgfact
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "pkgfact",
|
||||
Doc: "gather name/value pairs from constant declarations",
|
||||
Run: run,
|
||||
FactTypes: []analysis.Fact{new(pairsFact)},
|
||||
ResultType: reflect.TypeOf(map[string]string{}),
|
||||
}
|
||||
|
||||
// A pairsFact is a package-level fact that records
|
||||
// an set of key=value strings accumulated from constant
|
||||
// declarations in this package and its dependencies.
|
||||
// Elements are ordered by keys, which are unique.
|
||||
type pairsFact []string
|
||||
|
||||
func (f *pairsFact) AFact() {}
|
||||
func (f *pairsFact) String() string { return "pairs(" + strings.Join(*f, ", ") + ")" }
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
result := make(map[string]string)
|
||||
|
||||
// At each import, print the fact from the imported
|
||||
// package and accumulate its information into the result.
|
||||
// (Warning: accumulation leads to quadratic growth of work.)
|
||||
doImport := func(spec *ast.ImportSpec) {
|
||||
pkg := imported(pass.TypesInfo, spec)
|
||||
var fact pairsFact
|
||||
if pass.ImportPackageFact(pkg, &fact) {
|
||||
for _, pair := range fact {
|
||||
eq := strings.IndexByte(pair, '=')
|
||||
result[pair[:eq]] = pair[1+eq:]
|
||||
}
|
||||
pass.Reportf(spec.Pos(), "%s", strings.Join(fact, " "))
|
||||
}
|
||||
}
|
||||
|
||||
// At each "const _name_ = value", add a fact into env.
|
||||
doConst := func(spec *ast.ValueSpec) {
|
||||
if len(spec.Names) == len(spec.Values) {
|
||||
for i := range spec.Names {
|
||||
name := spec.Names[i].Name
|
||||
if strings.HasPrefix(name, "_") && strings.HasSuffix(name, "_") {
|
||||
|
||||
if key := strings.Trim(name, "_"); key != "" {
|
||||
value := pass.TypesInfo.Types[spec.Values[i]].Value.String()
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range pass.Files {
|
||||
for _, decl := range f.Decls {
|
||||
if decl, ok := decl.(*ast.GenDecl); ok {
|
||||
for _, spec := range decl.Specs {
|
||||
switch decl.Tok {
|
||||
case token.IMPORT:
|
||||
doImport(spec.(*ast.ImportSpec))
|
||||
case token.CONST:
|
||||
doConst(spec.(*ast.ValueSpec))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort/deduplicate the result and save it as a package fact.
|
||||
keys := make([]string, 0, len(result))
|
||||
for key := range result {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
var fact pairsFact
|
||||
for _, key := range keys {
|
||||
fact = append(fact, fmt.Sprintf("%s=%s", key, result[key]))
|
||||
}
|
||||
if len(fact) > 0 {
|
||||
pass.ExportPackageFact(&fact)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func imported(info *types.Info, spec *ast.ImportSpec) *types.Package {
|
||||
obj, ok := info.Implicits[spec]
|
||||
if !ok {
|
||||
obj = info.Defs[spec.Name] // renaming import
|
||||
}
|
||||
return obj.(*types.PkgName).Imported()
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package pkgfact_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/pkgfact"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, pkgfact.Analyzer, "c")
|
||||
}
|
||||
4
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/a/a.go
generated
vendored
Normal file
4
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
package a
|
||||
|
||||
const _greeting_ = "hello"
|
||||
const _audience_ = "world"
|
||||
5
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/b/b.go
generated
vendored
Normal file
5
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/b/b.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package b
|
||||
|
||||
import _ "a"
|
||||
|
||||
const _pi_ = 3.14159
|
||||
5
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/c/c.go
generated
vendored
Normal file
5
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/c/c.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// want package:`pairs\(audience="world", greeting="hello", pi=3.14159\)`
|
||||
|
||||
package c
|
||||
|
||||
import _ "b" // want `audience="world" greeting="hello" pi=3.14159`
|
||||
1017
vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
Normal file
1017
vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
14
vendor/golang.org/x/tools/go/analysis/passes/printf/printf_test.go
generated
vendored
Normal file
14
vendor/golang.org/x/tools/go/analysis/passes/printf/printf_test.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package printf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/printf"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
printf.Analyzer.Flags.Set("funcs", "Warn,Warnf")
|
||||
analysistest.Run(t, testdata, printf.Analyzer, "a", "b")
|
||||
}
|
||||
710
vendor/golang.org/x/tools/go/analysis/passes/printf/testdata/src/a/a.go
generated
vendored
Normal file
710
vendor/golang.org/x/tools/go/analysis/passes/printf/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,710 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file contains tests for the printf checker.
|
||||
|
||||
package a
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
logpkg "log" // renamed to make it harder to see
|
||||
"math"
|
||||
"os"
|
||||
"testing"
|
||||
"unsafe" // just for test case printing unsafe.Pointer
|
||||
|
||||
// For testing printf-like functions from external package.
|
||||
// "github.com/foobar/externalprintf"
|
||||
"b"
|
||||
)
|
||||
|
||||
func UnsafePointerPrintfTest() {
|
||||
var up unsafe.Pointer
|
||||
fmt.Printf("%p, %x %X", up, up, up)
|
||||
}
|
||||
|
||||
// Error methods that do not satisfy the Error interface and should be checked.
|
||||
type errorTest1 int
|
||||
|
||||
func (errorTest1) Error(...interface{}) string {
|
||||
return "hi"
|
||||
}
|
||||
|
||||
type errorTest2 int // Analogous to testing's *T type.
|
||||
func (errorTest2) Error(...interface{}) {
|
||||
}
|
||||
|
||||
type errorTest3 int
|
||||
|
||||
func (errorTest3) Error() { // No return value.
|
||||
}
|
||||
|
||||
type errorTest4 int
|
||||
|
||||
func (errorTest4) Error() int { // Different return type.
|
||||
return 3
|
||||
}
|
||||
|
||||
type errorTest5 int
|
||||
|
||||
func (errorTest5) error() { // niladic; don't complain if no args (was bug)
|
||||
}
|
||||
|
||||
// This function never executes, but it serves as a simple test for the program.
|
||||
// Test with make test.
|
||||
func PrintfTests() {
|
||||
var b bool
|
||||
var i int
|
||||
var r rune
|
||||
var s string
|
||||
var x float64
|
||||
var p *int
|
||||
var imap map[int]int
|
||||
var fslice []float64
|
||||
var c complex64
|
||||
// Some good format/argtypes
|
||||
fmt.Printf("")
|
||||
fmt.Printf("%b %b %b", 3, i, x)
|
||||
fmt.Printf("%c %c %c %c", 3, i, 'x', r)
|
||||
fmt.Printf("%d %d %d", 3, i, imap)
|
||||
fmt.Printf("%e %e %e %e", 3e9, x, fslice, c)
|
||||
fmt.Printf("%E %E %E %E", 3e9, x, fslice, c)
|
||||
fmt.Printf("%f %f %f %f", 3e9, x, fslice, c)
|
||||
fmt.Printf("%F %F %F %F", 3e9, x, fslice, c)
|
||||
fmt.Printf("%g %g %g %g", 3e9, x, fslice, c)
|
||||
fmt.Printf("%G %G %G %G", 3e9, x, fslice, c)
|
||||
fmt.Printf("%b %b %b %b", 3e9, x, fslice, c)
|
||||
fmt.Printf("%o %o", 3, i)
|
||||
fmt.Printf("%p", p)
|
||||
fmt.Printf("%q %q %q %q", 3, i, 'x', r)
|
||||
fmt.Printf("%s %s %s", "hi", s, []byte{65})
|
||||
fmt.Printf("%t %t", true, b)
|
||||
fmt.Printf("%T %T", 3, i)
|
||||
fmt.Printf("%U %U", 3, i)
|
||||
fmt.Printf("%v %v", 3, i)
|
||||
fmt.Printf("%x %x %x %x", 3, i, "hi", s)
|
||||
fmt.Printf("%X %X %X %X", 3, i, "hi", s)
|
||||
fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
|
||||
fmt.Printf("%s", &stringerv)
|
||||
fmt.Printf("%v", &stringerv)
|
||||
fmt.Printf("%T", &stringerv)
|
||||
fmt.Printf("%s", &embeddedStringerv)
|
||||
fmt.Printf("%v", &embeddedStringerv)
|
||||
fmt.Printf("%T", &embeddedStringerv)
|
||||
fmt.Printf("%v", notstringerv)
|
||||
fmt.Printf("%T", notstringerv)
|
||||
fmt.Printf("%q", stringerarrayv)
|
||||
fmt.Printf("%v", stringerarrayv)
|
||||
fmt.Printf("%s", stringerarrayv)
|
||||
fmt.Printf("%v", notstringerarrayv)
|
||||
fmt.Printf("%T", notstringerarrayv)
|
||||
fmt.Printf("%d", new(fmt.Formatter))
|
||||
fmt.Printf("%*%", 2) // Ridiculous but allowed.
|
||||
fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say.
|
||||
|
||||
fmt.Printf("%g", 1+2i)
|
||||
fmt.Printf("%#e %#E %#f %#F %#g %#G", 1.2, 1.2, 1.2, 1.2, 1.2, 1.2) // OK since Go 1.9
|
||||
// Some bad format/argTypes
|
||||
fmt.Printf("%b", "hi") // want "Printf format %b has arg \x22hi\x22 of wrong type string"
|
||||
fmt.Printf("%t", c) // want "Printf format %t has arg c of wrong type complex64"
|
||||
fmt.Printf("%t", 1+2i) // want `Printf format %t has arg 1 \+ 2i of wrong type complex128`
|
||||
fmt.Printf("%c", 2.3) // want "Printf format %c has arg 2.3 of wrong type float64"
|
||||
fmt.Printf("%d", 2.3) // want "Printf format %d has arg 2.3 of wrong type float64"
|
||||
fmt.Printf("%e", "hi") // want `Printf format %e has arg "hi" of wrong type string`
|
||||
fmt.Printf("%E", true) // want "Printf format %E has arg true of wrong type bool"
|
||||
fmt.Printf("%f", "hi") // want "Printf format %f has arg \x22hi\x22 of wrong type string"
|
||||
fmt.Printf("%F", 'x') // want "Printf format %F has arg 'x' of wrong type rune"
|
||||
fmt.Printf("%g", "hi") // want `Printf format %g has arg "hi" of wrong type string`
|
||||
fmt.Printf("%g", imap) // want `Printf format %g has arg imap of wrong type map\[int\]int`
|
||||
fmt.Printf("%G", i) // want "Printf format %G has arg i of wrong type int"
|
||||
fmt.Printf("%o", x) // want "Printf format %o has arg x of wrong type float64"
|
||||
fmt.Printf("%p", nil) // want "Printf format %p has arg nil of wrong type untyped nil"
|
||||
fmt.Printf("%p", 23) // want "Printf format %p has arg 23 of wrong type int"
|
||||
fmt.Printf("%q", x) // want "Printf format %q has arg x of wrong type float64"
|
||||
fmt.Printf("%s", b) // want "Printf format %s has arg b of wrong type bool"
|
||||
fmt.Printf("%s", byte(65)) // want `Printf format %s has arg byte\(65\) of wrong type byte`
|
||||
fmt.Printf("%t", 23) // want "Printf format %t has arg 23 of wrong type int"
|
||||
fmt.Printf("%U", x) // want "Printf format %U has arg x of wrong type float64"
|
||||
fmt.Printf("%x", nil) // want "Printf format %x has arg nil of wrong type untyped nil"
|
||||
fmt.Printf("%X", 2.3) // want "Printf format %X has arg 2.3 of wrong type float64"
|
||||
fmt.Printf("%s", stringerv) // want "Printf format %s has arg stringerv of wrong type a.ptrStringer"
|
||||
fmt.Printf("%t", stringerv) // want "Printf format %t has arg stringerv of wrong type a.ptrStringer"
|
||||
fmt.Printf("%s", embeddedStringerv) // want "Printf format %s has arg embeddedStringerv of wrong type a.embeddedStringer"
|
||||
fmt.Printf("%t", embeddedStringerv) // want "Printf format %t has arg embeddedStringerv of wrong type a.embeddedStringer"
|
||||
fmt.Printf("%q", notstringerv) // want "Printf format %q has arg notstringerv of wrong type a.notstringer"
|
||||
fmt.Printf("%t", notstringerv) // want "Printf format %t has arg notstringerv of wrong type a.notstringer"
|
||||
fmt.Printf("%t", stringerarrayv) // want "Printf format %t has arg stringerarrayv of wrong type a.stringerarray"
|
||||
fmt.Printf("%t", notstringerarrayv) // want "Printf format %t has arg notstringerarrayv of wrong type a.notstringerarray"
|
||||
fmt.Printf("%q", notstringerarrayv) // want "Printf format %q has arg notstringerarrayv of wrong type a.notstringerarray"
|
||||
fmt.Printf("%d", BoolFormatter(true)) // want `Printf format %d has arg BoolFormatter\(true\) of wrong type a.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)
|
||||
fmt.Printf("%.*s %d %6g", 3, "hi", 23, 'x') // want "Printf format %6g has arg 'x' of wrong type rune"
|
||||
fmt.Println() // not an error
|
||||
fmt.Println("%s", "hi") // want "Println call has possible formatting directive %s"
|
||||
fmt.Println("%v", "hi") // want "Println call has possible formatting directive %v"
|
||||
fmt.Println("%T", "hi") // want "Println call has possible formatting directive %T"
|
||||
fmt.Println("0.0%") // correct (trailing % couldn't be a formatting directive)
|
||||
fmt.Printf("%s", "hi", 3) // want "Printf call needs 1 arg but has 2 args"
|
||||
_ = fmt.Sprintf("%"+("s"), "hi", 3) // want "Sprintf call needs 1 arg but has 2 args"
|
||||
fmt.Printf("%s%%%d", "hi", 3) // correct
|
||||
fmt.Printf("%08s", "woo") // correct
|
||||
fmt.Printf("% 8s", "woo") // correct
|
||||
fmt.Printf("%.*d", 3, 3) // correct
|
||||
fmt.Printf("%.*d x", 3, 3, 3, 3) // want "Printf call needs 2 args but has 4 args"
|
||||
fmt.Printf("%.*d x", "hi", 3) // want `Printf format %.*d uses non-int "hi" as argument of \*`
|
||||
fmt.Printf("%.*d x", i, 3) // correct
|
||||
fmt.Printf("%.*d x", s, 3) // want `Printf format %.\*d uses non-int s as argument of \*`
|
||||
fmt.Printf("%*% x", 0.22) // want `Printf format %\*% uses non-int 0.22 as argument of \*`
|
||||
fmt.Printf("%q %q", multi()...) // ok
|
||||
fmt.Printf("%#q", `blah`) // ok
|
||||
// printf("now is the time", "buddy") // no error "printf call has arguments but no formatting directives"
|
||||
Printf("now is the time", "buddy") // want "Printf call has arguments but no formatting directives"
|
||||
Printf("hi") // ok
|
||||
const format = "%s %s\n"
|
||||
Printf(format, "hi", "there")
|
||||
Printf(format, "hi") // want "Printf format %s reads arg #2, but call has 1 arg$"
|
||||
Printf("%s %d %.3v %q", "str", 4) // want "Printf format %.3v reads arg #3, but call has 2 args"
|
||||
f := new(ptrStringer)
|
||||
f.Warn(0, "%s", "hello", 3) // want "Warn call has possible formatting directive %s"
|
||||
f.Warnf(0, "%s", "hello", 3) // want "Warnf call needs 1 arg but has 2 args"
|
||||
f.Warnf(0, "%r", "hello") // want "Warnf format %r has unknown verb r"
|
||||
f.Warnf(0, "%#s", "hello") // want "Warnf format %#s has unrecognized flag #"
|
||||
f.Warn2(0, "%s", "hello", 3) // want "Warn2 call has possible formatting directive %s"
|
||||
f.Warnf2(0, "%s", "hello", 3) // want "Warnf2 call needs 1 arg but has 2 args"
|
||||
f.Warnf2(0, "%r", "hello") // want "Warnf2 format %r has unknown verb r"
|
||||
f.Warnf2(0, "%#s", "hello") // want "Warnf2 format %#s has unrecognized flag #"
|
||||
f.Wrap(0, "%s", "hello", 3) // want "Wrap call has possible formatting directive %s"
|
||||
f.Wrapf(0, "%s", "hello", 3) // want "Wrapf call needs 1 arg but has 2 args"
|
||||
f.Wrapf(0, "%r", "hello") // want "Wrapf format %r has unknown verb r"
|
||||
f.Wrapf(0, "%#s", "hello") // want "Wrapf format %#s has unrecognized flag #"
|
||||
f.Wrap2(0, "%s", "hello", 3) // want "Wrap2 call has possible formatting directive %s"
|
||||
f.Wrapf2(0, "%s", "hello", 3) // want "Wrapf2 call needs 1 arg but has 2 args"
|
||||
f.Wrapf2(0, "%r", "hello") // want "Wrapf2 format %r has unknown verb r"
|
||||
f.Wrapf2(0, "%#s", "hello") // want "Wrapf2 format %#s has unrecognized flag #"
|
||||
fmt.Printf("%#s", FormatterVal(true)) // correct (the type is responsible for formatting)
|
||||
Printf("d%", 2) // want "Printf format % is missing verb at end of string"
|
||||
Printf("%d", percentDV)
|
||||
Printf("%d", &percentDV)
|
||||
Printf("%d", notPercentDV) // want "Printf format %d has arg notPercentDV of wrong type a.notPercentDStruct"
|
||||
Printf("%d", ¬PercentDV) // want `Printf format %d has arg ¬PercentDV of wrong type \*a.notPercentDStruct`
|
||||
Printf("%p", ¬PercentDV) // Works regardless: we print it as a pointer.
|
||||
Printf("%q", &percentDV) // want `Printf format %q has arg &percentDV of wrong type \*a.percentDStruct`
|
||||
Printf("%s", percentSV)
|
||||
Printf("%s", &percentSV)
|
||||
// Good argument reorderings.
|
||||
Printf("%[1]d", 3)
|
||||
Printf("%[1]*d", 3, 1)
|
||||
Printf("%[2]*[1]d", 1, 3)
|
||||
Printf("%[2]*.[1]*[3]d", 2, 3, 4)
|
||||
fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.
|
||||
// Bad argument reorderings.
|
||||
Printf("%[xd", 3) // want `Printf format %\[xd is missing closing \]`
|
||||
Printf("%[x]d x", 3) // want `Printf format has invalid argument index \[x\]`
|
||||
Printf("%[3]*s x", "hi", 2) // want `Printf format has invalid argument index \[3\]`
|
||||
_ = fmt.Sprintf("%[3]d x", 2) // want `Sprintf format has invalid argument index \[3\]`
|
||||
Printf("%[2]*.[1]*[3]d x", 2, "hi", 4) // want `Printf format %\[2]\*\.\[1\]\*\[3\]d uses non-int \x22hi\x22 as argument of \*`
|
||||
Printf("%[0]s x", "arg1") // want `Printf format has invalid argument index \[0\]`
|
||||
Printf("%[0]d x", 1) // want `Printf format has invalid argument index \[0\]`
|
||||
// Something that satisfies the error interface.
|
||||
var e error
|
||||
fmt.Println(e.Error()) // ok
|
||||
// Something that looks like an error interface but isn't, such as the (*T).Error method
|
||||
// in the testing package.
|
||||
var et1 *testing.T
|
||||
et1.Error() // ok
|
||||
et1.Error("hi") // ok
|
||||
et1.Error("%d", 3) // want "Error call has possible formatting directive %d"
|
||||
et1.Errorf("%s", 1) // want "Errorf format %s has arg 1 of wrong type int"
|
||||
var et3 errorTest3
|
||||
et3.Error() // ok, not an error method.
|
||||
var et4 errorTest4
|
||||
et4.Error() // ok, not an error method.
|
||||
var et5 errorTest5
|
||||
et5.error() // ok, not an error method.
|
||||
// Interfaces can be used with any verb.
|
||||
var iface interface {
|
||||
ToTheMadness() bool // Method ToTheMadness usually returns false
|
||||
}
|
||||
fmt.Printf("%f", iface) // ok: fmt treats interfaces as transparent and iface may well have a float concrete type
|
||||
// Can't print a function.
|
||||
Printf("%d", someFunction) // want "Printf format %d arg someFunction is a func value, not called"
|
||||
Printf("%v", someFunction) // want "Printf format %v arg someFunction is a func value, not called"
|
||||
Println(someFunction) // want "Println arg someFunction is a func value, not called"
|
||||
Printf("%p", someFunction) // ok: maybe someone wants to see the pointer
|
||||
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) // want `Printf format %x has arg recursiveStruct1V\.next of wrong type \*a\.RecursiveStruct2`
|
||||
Printf("%p %x", recursiveSliceV, recursiveSliceV)
|
||||
Printf("%p %x", recursiveMapV, recursiveMapV)
|
||||
// Special handling for Log.
|
||||
math.Log(3) // OK
|
||||
var t *testing.T
|
||||
t.Log("%d", 3) // want "Log call has possible formatting directive %d"
|
||||
t.Logf("%d", 3)
|
||||
t.Logf("%d", "hi") // want `Logf format %d has arg "hi" of wrong type string`
|
||||
|
||||
Errorf(1, "%d", 3) // OK
|
||||
Errorf(1, "%d", "hi") // want `Errorf format %d has arg "hi" of wrong type string`
|
||||
|
||||
// Multiple string arguments before variadic args
|
||||
errorf("WARNING", "foobar") // OK
|
||||
errorf("INFO", "s=%s, n=%d", "foo", 1) // OK
|
||||
errorf("ERROR", "%d") // want "errorf format %d reads arg #1, but call has 0 args"
|
||||
|
||||
var tb testing.TB
|
||||
tb.Errorf("%s", 1) // want "Errorf format %s has arg 1 of wrong type int"
|
||||
|
||||
// Printf from external package
|
||||
// externalprintf.Printf("%d", 42) // OK
|
||||
// externalprintf.Printf("foobar") // OK
|
||||
// level := 123
|
||||
// externalprintf.Logf(level, "%d", 42) // OK
|
||||
// externalprintf.Errorf(level, level, "foo %q bar", "foobar") // OK
|
||||
// externalprintf.Logf(level, "%d") // no error "Logf format %d reads arg #1, but call has 0 args"
|
||||
// var formatStr = "%s %s"
|
||||
// externalprintf.Sprintf(formatStr, "a", "b") // OK
|
||||
// externalprintf.Logf(level, formatStr, "a", "b") // OK
|
||||
|
||||
// user-defined Println-like functions
|
||||
ss := &someStruct{}
|
||||
ss.Log(someFunction, "foo") // OK
|
||||
ss.Error(someFunction, someFunction) // OK
|
||||
ss.Println() // OK
|
||||
ss.Println(1.234, "foo") // OK
|
||||
ss.Println(1, someFunction) // no error "Println arg someFunction is a func value, not called"
|
||||
ss.log(someFunction) // OK
|
||||
ss.log(someFunction, "bar", 1.33) // OK
|
||||
ss.log(someFunction, someFunction) // no error "log arg someFunction is a func value, not called"
|
||||
|
||||
// indexed arguments
|
||||
Printf("%d %[3]d %d %[2]d x", 1, 2, 3, 4) // OK
|
||||
Printf("%d %[0]d %d %[2]d x", 1, 2, 3, 4) // want `Printf format has invalid argument index \[0\]`
|
||||
Printf("%d %[3]d %d %[-2]d x", 1, 2, 3, 4) // want `Printf format has invalid argument index \[-2\]`
|
||||
Printf("%d %[3]d %d %[2234234234234]d x", 1, 2, 3, 4) // want `Printf format has invalid argument index \[2234234234234\]`
|
||||
Printf("%d %[3]d %-10d %[2]d x", 1, 2, 3) // want "Printf format %-10d reads arg #4, but call has 3 args"
|
||||
Printf("%[1][3]d x", 1, 2) // want `Printf format %\[1\]\[ has unknown verb \[`
|
||||
Printf("%[1]d x", 1, 2) // OK
|
||||
Printf("%d %[3]d %d %[2]d x", 1, 2, 3, 4, 5) // OK
|
||||
|
||||
// wrote Println but meant Fprintln
|
||||
Printf("%p\n", os.Stdout) // OK
|
||||
Println(os.Stdout, "hello") // want "Println does not take io.Writer but has first arg os.Stdout"
|
||||
|
||||
Printf(someString(), "hello") // OK
|
||||
|
||||
// Printf wrappers in package log should be detected automatically
|
||||
logpkg.Fatal("%d", 1) // want "Fatal call has possible formatting directive %d"
|
||||
logpkg.Fatalf("%d", "x") // want `Fatalf format %d has arg "x" of wrong type string`
|
||||
logpkg.Fatalln("%d", 1) // want "Fatalln call has possible formatting directive %d"
|
||||
logpkg.Panic("%d", 1) // want "Panic call has possible formatting directive %d"
|
||||
logpkg.Panicf("%d", "x") // want `Panicf format %d has arg "x" of wrong type string`
|
||||
logpkg.Panicln("%d", 1) // want "Panicln call has possible formatting directive %d"
|
||||
logpkg.Print("%d", 1) // want "Print call has possible formatting directive %d"
|
||||
logpkg.Printf("%d", "x") // want `Printf format %d has arg "x" of wrong type string`
|
||||
logpkg.Println("%d", 1) // want "Println call has possible formatting directive %d"
|
||||
|
||||
// Methods too.
|
||||
var l *logpkg.Logger
|
||||
l.Fatal("%d", 1) // want "Fatal call has possible formatting directive %d"
|
||||
l.Fatalf("%d", "x") // want `Fatalf format %d has arg "x" of wrong type string`
|
||||
l.Fatalln("%d", 1) // want "Fatalln call has possible formatting directive %d"
|
||||
l.Panic("%d", 1) // want "Panic call has possible formatting directive %d"
|
||||
l.Panicf("%d", "x") // want `Panicf format %d has arg "x" of wrong type string`
|
||||
l.Panicln("%d", 1) // want "Panicln call has possible formatting directive %d"
|
||||
l.Print("%d", 1) // want "Print call has possible formatting directive %d"
|
||||
l.Printf("%d", "x") // want `Printf format %d has arg "x" of wrong type string`
|
||||
l.Println("%d", 1) // want "Println call has possible formatting directive %d"
|
||||
|
||||
// Issue 26486
|
||||
dbg("", 1) // no error "call has arguments but no formatting directive"
|
||||
}
|
||||
|
||||
func someString() string { return "X" }
|
||||
|
||||
type someStruct struct{}
|
||||
|
||||
// Log is non-variadic user-define Println-like function.
|
||||
// Calls to this func must be skipped when checking
|
||||
// for Println-like arguments.
|
||||
func (ss *someStruct) Log(f func(), s string) {}
|
||||
|
||||
// Error is variadic user-define Println-like function.
|
||||
// Calls to this func mustn't be checked for Println-like arguments,
|
||||
// since variadic arguments type isn't interface{}.
|
||||
func (ss *someStruct) Error(args ...func()) {}
|
||||
|
||||
// Println is variadic user-defined Println-like function.
|
||||
// Calls to this func must be checked for Println-like arguments.
|
||||
func (ss *someStruct) Println(args ...interface{}) {}
|
||||
|
||||
// log is variadic user-defined Println-like function.
|
||||
// Calls to this func must be checked for Println-like arguments.
|
||||
func (ss *someStruct) log(f func(), args ...interface{}) {}
|
||||
|
||||
// A function we use as a function value; it has no other purpose.
|
||||
func someFunction() {}
|
||||
|
||||
// Printf is used by the test so we must declare it.
|
||||
func Printf(format string, args ...interface{}) { // want Printf:"printfWrapper"
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Println is used by the test so we must declare it.
|
||||
func Println(args ...interface{}) { // want Println:"printWrapper"
|
||||
fmt.Println(args...)
|
||||
}
|
||||
|
||||
// printf is used by the test so we must declare it.
|
||||
func printf(format string, args ...interface{}) { // want printf:"printfWrapper"
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Errorf is used by the test for a case in which the first parameter
|
||||
// is not a format string.
|
||||
func Errorf(i int, format string, args ...interface{}) { // want Errorf:"printfWrapper"
|
||||
_ = fmt.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// errorf is used by the test for a case in which the function accepts multiple
|
||||
// string parameters before variadic arguments
|
||||
func errorf(level, format string, args ...interface{}) { // want errorf:"printfWrapper"
|
||||
_ = fmt.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// multi is used by the test.
|
||||
func multi() []interface{} {
|
||||
panic("don't call - testing only")
|
||||
}
|
||||
|
||||
type stringer int
|
||||
|
||||
func (stringer) String() string { return "string" }
|
||||
|
||||
type ptrStringer float64
|
||||
|
||||
var stringerv ptrStringer
|
||||
|
||||
func (*ptrStringer) String() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
func (p *ptrStringer) Warn2(x int, args ...interface{}) string { // want Warn2:"printWrapper"
|
||||
return p.Warn(x, args...)
|
||||
}
|
||||
|
||||
func (p *ptrStringer) Warnf2(x int, format string, args ...interface{}) string { // want Warnf2:"printfWrapper"
|
||||
return p.Warnf(x, format, args...)
|
||||
}
|
||||
|
||||
// During testing -printf.funcs flag matches Warn.
|
||||
func (*ptrStringer) Warn(x int, args ...interface{}) string {
|
||||
return "warn"
|
||||
}
|
||||
|
||||
// During testing -printf.funcs flag matches Warnf.
|
||||
func (*ptrStringer) Warnf(x int, format string, args ...interface{}) string {
|
||||
return "warnf"
|
||||
}
|
||||
|
||||
func (p *ptrStringer) Wrap2(x int, args ...interface{}) string { // want Wrap2:"printWrapper"
|
||||
return p.Wrap(x, args...)
|
||||
}
|
||||
|
||||
func (p *ptrStringer) Wrapf2(x int, format string, args ...interface{}) string { // want Wrapf2:"printfWrapper"
|
||||
return p.Wrapf(x, format, args...)
|
||||
}
|
||||
|
||||
func (*ptrStringer) Wrap(x int, args ...interface{}) string { // want Wrap:"printWrapper"
|
||||
return fmt.Sprint(args...)
|
||||
}
|
||||
|
||||
func (*ptrStringer) Wrapf(x int, format string, args ...interface{}) string { // want Wrapf:"printfWrapper"
|
||||
return fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
func (*ptrStringer) BadWrap(x int, args ...interface{}) string {
|
||||
return fmt.Sprint(args) // want "missing ... in args forwarded to print-like function"
|
||||
}
|
||||
|
||||
func (*ptrStringer) BadWrapf(x int, format string, args ...interface{}) string {
|
||||
return fmt.Sprintf(format, args) // want "missing ... in args forwarded to printf-like function"
|
||||
}
|
||||
|
||||
func (*ptrStringer) WrapfFalsePositive(x int, arg1 string, arg2 ...interface{}) string {
|
||||
return fmt.Sprintf("%s %v", arg1, arg2)
|
||||
}
|
||||
|
||||
type embeddedStringer struct {
|
||||
foo string
|
||||
ptrStringer
|
||||
bar int
|
||||
}
|
||||
|
||||
var embeddedStringerv embeddedStringer
|
||||
|
||||
type notstringer struct {
|
||||
f float64
|
||||
}
|
||||
|
||||
var notstringerv notstringer
|
||||
|
||||
type stringerarray [4]float64
|
||||
|
||||
func (stringerarray) String() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
var stringerarrayv stringerarray
|
||||
|
||||
type notstringerarray [4]float64
|
||||
|
||||
var notstringerarrayv notstringerarray
|
||||
|
||||
var nonemptyinterface = interface {
|
||||
f()
|
||||
}(nil)
|
||||
|
||||
// A data type we can print with "%d".
|
||||
type percentDStruct struct {
|
||||
a int
|
||||
b []byte
|
||||
c *float64
|
||||
}
|
||||
|
||||
var percentDV percentDStruct
|
||||
|
||||
// A data type we cannot print correctly with "%d".
|
||||
type notPercentDStruct struct {
|
||||
a int
|
||||
b []byte
|
||||
c bool
|
||||
}
|
||||
|
||||
var notPercentDV notPercentDStruct
|
||||
|
||||
// A data type we can print with "%s".
|
||||
type percentSStruct struct {
|
||||
a string
|
||||
b []byte
|
||||
C stringerarray
|
||||
}
|
||||
|
||||
var percentSV percentSStruct
|
||||
|
||||
type recursiveStringer int
|
||||
|
||||
func (s recursiveStringer) String() string {
|
||||
_ = fmt.Sprintf("%d", s)
|
||||
_ = fmt.Sprintf("%#v", s)
|
||||
_ = fmt.Sprintf("%v", s) // want "Sprintf format %v with arg s causes recursive String method call"
|
||||
_ = fmt.Sprintf("%v", &s) // want "Sprintf format %v with arg &s causes recursive String method call"
|
||||
_ = fmt.Sprintf("%T", s) // ok; does not recursively call String
|
||||
return fmt.Sprintln(s) // want "Sprintln arg s causes recursive call to String method"
|
||||
}
|
||||
|
||||
type recursivePtrStringer int
|
||||
|
||||
func (p *recursivePtrStringer) String() string {
|
||||
_ = fmt.Sprintf("%v", *p)
|
||||
_ = fmt.Sprint(&p) // ok; prints address
|
||||
return fmt.Sprintln(p) // want "Sprintln arg p causes recursive call to String method"
|
||||
}
|
||||
|
||||
type cons struct {
|
||||
car int
|
||||
cdr *cons
|
||||
}
|
||||
|
||||
func (cons *cons) String() string {
|
||||
if cons == nil {
|
||||
return "nil"
|
||||
}
|
||||
_ = fmt.Sprint(cons.cdr) // don't want "recursive call" diagnostic
|
||||
return fmt.Sprintf("(%d . %v)", cons.car, cons.cdr) // don't want "recursive call" diagnostic
|
||||
}
|
||||
|
||||
type BoolFormatter bool
|
||||
|
||||
func (*BoolFormatter) Format(fmt.State, rune) {
|
||||
}
|
||||
|
||||
// Formatter with value receiver
|
||||
type FormatterVal bool
|
||||
|
||||
func (FormatterVal) Format(fmt.State, rune) {
|
||||
}
|
||||
|
||||
type RecursiveSlice []RecursiveSlice
|
||||
|
||||
var recursiveSliceV = &RecursiveSlice{}
|
||||
|
||||
type RecursiveMap map[int]RecursiveMap
|
||||
|
||||
var recursiveMapV = make(RecursiveMap)
|
||||
|
||||
type RecursiveStruct struct {
|
||||
next *RecursiveStruct
|
||||
}
|
||||
|
||||
var recursiveStructV = &RecursiveStruct{}
|
||||
|
||||
type RecursiveStruct1 struct {
|
||||
next *RecursiveStruct2
|
||||
}
|
||||
|
||||
type RecursiveStruct2 struct {
|
||||
next *RecursiveStruct1
|
||||
}
|
||||
|
||||
var recursiveStruct1V = &RecursiveStruct1{}
|
||||
|
||||
type unexportedInterface struct {
|
||||
f interface{}
|
||||
}
|
||||
|
||||
// Issue 17798: unexported ptrStringer cannot be formatted.
|
||||
type unexportedStringer struct {
|
||||
t ptrStringer
|
||||
}
|
||||
type unexportedStringerOtherFields struct {
|
||||
s string
|
||||
t ptrStringer
|
||||
S string
|
||||
}
|
||||
|
||||
// Issue 17798: unexported error cannot be formatted.
|
||||
type unexportedError struct {
|
||||
e error
|
||||
}
|
||||
type unexportedErrorOtherFields struct {
|
||||
s string
|
||||
e error
|
||||
S string
|
||||
}
|
||||
|
||||
type errorer struct{}
|
||||
|
||||
func (e errorer) Error() string { return "errorer" }
|
||||
|
||||
type unexportedCustomError struct {
|
||||
e errorer
|
||||
}
|
||||
|
||||
type errorInterface interface {
|
||||
error
|
||||
ExtraMethod()
|
||||
}
|
||||
|
||||
type unexportedErrorInterface struct {
|
||||
e errorInterface
|
||||
}
|
||||
|
||||
func UnexportedStringerOrError() {
|
||||
fmt.Printf("%s", unexportedInterface{"foo"}) // ok; prints {foo}
|
||||
fmt.Printf("%s", unexportedInterface{3}) // ok; we can't see the problem
|
||||
|
||||
us := unexportedStringer{}
|
||||
fmt.Printf("%s", us) // want "Printf format %s has arg us of wrong type a.unexportedStringer"
|
||||
fmt.Printf("%s", &us) // want "Printf format %s has arg &us of wrong type [*]a.unexportedStringer"
|
||||
|
||||
usf := unexportedStringerOtherFields{
|
||||
s: "foo",
|
||||
S: "bar",
|
||||
}
|
||||
fmt.Printf("%s", usf) // want "Printf format %s has arg usf of wrong type a.unexportedStringerOtherFields"
|
||||
fmt.Printf("%s", &usf) // want "Printf format %s has arg &usf of wrong type [*]a.unexportedStringerOtherFields"
|
||||
|
||||
ue := unexportedError{
|
||||
e: &errorer{},
|
||||
}
|
||||
fmt.Printf("%s", ue) // want "Printf format %s has arg ue of wrong type a.unexportedError"
|
||||
fmt.Printf("%s", &ue) // want "Printf format %s has arg &ue of wrong type [*]a.unexportedError"
|
||||
|
||||
uef := unexportedErrorOtherFields{
|
||||
s: "foo",
|
||||
e: &errorer{},
|
||||
S: "bar",
|
||||
}
|
||||
fmt.Printf("%s", uef) // want "Printf format %s has arg uef of wrong type a.unexportedErrorOtherFields"
|
||||
fmt.Printf("%s", &uef) // want "Printf format %s has arg &uef of wrong type [*]a.unexportedErrorOtherFields"
|
||||
|
||||
uce := unexportedCustomError{
|
||||
e: errorer{},
|
||||
}
|
||||
fmt.Printf("%s", uce) // want "Printf format %s has arg uce of wrong type a.unexportedCustomError"
|
||||
|
||||
uei := unexportedErrorInterface{}
|
||||
fmt.Printf("%s", uei) // want "Printf format %s has arg uei of wrong type a.unexportedErrorInterface"
|
||||
fmt.Println("foo\n", "bar") // not an error
|
||||
|
||||
fmt.Println("foo\n") // want "Println arg list ends with redundant newline"
|
||||
fmt.Println("foo\\n") // not an error
|
||||
fmt.Println(`foo\n`) // not an error
|
||||
|
||||
intSlice := []int{3, 4}
|
||||
fmt.Printf("%s", intSlice) // want `Printf format %s has arg intSlice of wrong type \[\]int`
|
||||
nonStringerArray := [1]unexportedStringer{{}}
|
||||
fmt.Printf("%s", nonStringerArray) // want `Printf format %s has arg nonStringerArray of wrong type \[1\]a.unexportedStringer`
|
||||
fmt.Printf("%s", []stringer{3, 4}) // not an error
|
||||
fmt.Printf("%s", [2]stringer{3, 4}) // not an error
|
||||
}
|
||||
|
||||
// TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11.
|
||||
// See issues 23598 and 23605.
|
||||
func DisableErrorForFlag0() {
|
||||
fmt.Printf("%0t", true)
|
||||
}
|
||||
|
||||
// Issue 26486.
|
||||
func dbg(format string, args ...interface{}) {
|
||||
if format == "" {
|
||||
format = "%v"
|
||||
}
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
func PointersToCompoundTypes() {
|
||||
stringSlice := []string{"a", "b"}
|
||||
fmt.Printf("%s", &stringSlice) // not an error
|
||||
|
||||
intSlice := []int{3, 4}
|
||||
fmt.Printf("%s", &intSlice) // want `Printf format %s has arg &intSlice of wrong type \*\[\]int`
|
||||
|
||||
stringArray := [2]string{"a", "b"}
|
||||
fmt.Printf("%s", &stringArray) // not an error
|
||||
|
||||
intArray := [2]int{3, 4}
|
||||
fmt.Printf("%s", &intArray) // want `Printf format %s has arg &intArray of wrong type \*\[2\]int`
|
||||
|
||||
stringStruct := struct{ F string }{"foo"}
|
||||
fmt.Printf("%s", &stringStruct) // not an error
|
||||
|
||||
intStruct := struct{ F int }{3}
|
||||
fmt.Printf("%s", &intStruct) // want `Printf format %s has arg &intStruct of wrong type \*struct{F int}`
|
||||
|
||||
stringMap := map[string]string{"foo": "bar"}
|
||||
fmt.Printf("%s", &stringMap) // not an error
|
||||
|
||||
intMap := map[int]int{3: 4}
|
||||
fmt.Printf("%s", &intMap) // want `Printf format %s has arg &intMap of wrong type \*map\[int\]int`
|
||||
|
||||
type T2 struct {
|
||||
X string
|
||||
}
|
||||
type T1 struct {
|
||||
X *T2
|
||||
}
|
||||
fmt.Printf("%s\n", T1{&T2{"x"}}) // want `Printf format %s has arg T1{&T2{.x.}} of wrong type a\.T1`
|
||||
}
|
||||
|
||||
// Printf wrappers from external package
|
||||
func externalPackage() {
|
||||
b.Wrapf("%s", 1) // want "Wrapf format %s has arg 1 of wrong type int"
|
||||
b.Wrap("%s", 1) // want "Wrap call has possible formatting directive %s"
|
||||
b.NoWrap("%s", 1)
|
||||
b.Wrapf2("%s", 1) // want "Wrapf2 format %s has arg 1 of wrong type int"
|
||||
}
|
||||
33
vendor/golang.org/x/tools/go/analysis/passes/printf/testdata/src/b/b.go
generated
vendored
Normal file
33
vendor/golang.org/x/tools/go/analysis/passes/printf/testdata/src/b/b.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package b
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Wrapf is a printf wrapper.
|
||||
func Wrapf(format string, args ...interface{}) { // want Wrapf:"printfWrapper"
|
||||
fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
// Wrap is a print wrapper.
|
||||
func Wrap(args ...interface{}) { // want Wrap:"printWrapper"
|
||||
fmt.Sprint(args...)
|
||||
}
|
||||
|
||||
// NoWrap is not a wrapper.
|
||||
func NoWrap(format string, args ...interface{}) {
|
||||
}
|
||||
|
||||
// Wrapf2 is another printf wrapper.
|
||||
func Wrapf2(format string, args ...interface{}) string { // want Wrapf2:"printfWrapper"
|
||||
|
||||
// This statement serves as an assertion that this function is a
|
||||
// printf wrapper and that calls to it should be checked
|
||||
// accordingly, even though the delegation below is obscured by
|
||||
// the "("+format+")" operations.
|
||||
if false {
|
||||
fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
// Effectively a printf delegation,
|
||||
// but the printf checker can't see it.
|
||||
return fmt.Sprintf("("+format+")", args...)
|
||||
}
|
||||
236
vendor/golang.org/x/tools/go/analysis/passes/printf/types.go
generated
vendored
Normal file
236
vendor/golang.org/x/tools/go/analysis/passes/printf/types.go
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
package printf
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
)
|
||||
|
||||
var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
|
||||
|
||||
// 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 matchArgType(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr) bool {
|
||||
return matchArgTypeInternal(pass, 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 matchArgTypeInternal(pass *analysis.Pass, 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 = pass.TypesInfo.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 isFormatter(pass, typ) {
|
||||
return true
|
||||
}
|
||||
// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
|
||||
if t&argString != 0 && isConvertibleToString(pass, 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 ||
|
||||
(matchArgTypeInternal(pass, t, typ.Key(), arg, inProgress) && matchArgTypeInternal(pass, 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 || matchArgTypeInternal(pass, 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 || matchArgTypeInternal(pass, 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 false {
|
||||
pass.Reportf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", analysisutil.Format(pass.Fset, 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 matchArgTypeInternal(pass, t, under, arg, inProgress)
|
||||
|
||||
case *types.Struct:
|
||||
return matchStructArgType(pass, 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 false {
|
||||
pass.Reportf(arg.Pos(), "printf argument %v has invalid or unknown type", analysisutil.Format(pass.Fset, arg))
|
||||
}
|
||||
return true // Probably a type check problem.
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isConvertibleToString(pass *analysis.Pass, 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()
|
||||
}
|
||||
|
||||
// Does it implement fmt.Stringer?
|
||||
if obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "String"); obj != nil {
|
||||
if fn, ok := obj.(*types.Func); ok {
|
||||
sig := fn.Type().(*types.Signature)
|
||||
if sig.Params().Len() == 0 &&
|
||||
sig.Results().Len() == 1 &&
|
||||
sig.Results().At(0).Type() == types.Typ[types.String] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// hasBasicType reports whether x's type is a types.Basic with the given kind.
|
||||
func hasBasicType(pass *analysis.Pass, x ast.Expr, kind types.BasicKind) bool {
|
||||
t := pass.TypesInfo.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 matchStructArgType(pass *analysis.Pass, 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 !matchArgTypeInternal(pass, t, typf.Type(), arg, inProgress) {
|
||||
return false
|
||||
}
|
||||
if t&argString != 0 && !typf.Exported() && isConvertibleToString(pass, typf.Type()) {
|
||||
// Issue #17798: unexported Stringer or error cannot be properly fomatted.
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var archSizes = types.SizesFor("gc", build.Default.GOARCH)
|
||||
9
vendor/golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow/main.go
generated
vendored
Normal file
9
vendor/golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow/main.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// The shadow command runs the shadow analyzer.
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/passes/shadow"
|
||||
"golang.org/x/tools/go/analysis/singlechecker"
|
||||
)
|
||||
|
||||
func main() { singlechecker.Main(shadow.Analyzer) }
|
||||
288
vendor/golang.org/x/tools/go/analysis/passes/shadow/shadow.go
generated
vendored
Normal file
288
vendor/golang.org/x/tools/go/analysis/passes/shadow/shadow.go
generated
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
// 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 shadow
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
// NOTE: Experimental. Not part of the vet suite.
|
||||
|
||||
const Doc = `check for possible unintended shadowing of variables
|
||||
|
||||
This analyzer 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
|
||||
}
|
||||
`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "shadow",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
// flags
|
||||
var strict = false
|
||||
|
||||
func init() {
|
||||
Analyzer.Flags.BoolVar(&strict, "strict", strict, "whether to be strict about shadowing; can be noisy")
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
spans := make(map[types.Object]span)
|
||||
for id, obj := range pass.TypesInfo.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 {
|
||||
growSpan(spans, obj, id.Pos(), id.End())
|
||||
}
|
||||
}
|
||||
for id, obj := range pass.TypesInfo.Uses {
|
||||
growSpan(spans, obj, id.Pos(), id.End())
|
||||
}
|
||||
for node, obj := range pass.TypesInfo.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 {
|
||||
growSpan(spans, obj, cc.Colon, cc.Colon)
|
||||
}
|
||||
}
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.AssignStmt)(nil),
|
||||
(*ast.GenDecl)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
switch n := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
checkShadowAssignment(pass, spans, n)
|
||||
case *ast.GenDecl:
|
||||
checkShadowDecl(pass, spans, n)
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// A 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 strict 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 growSpan(spans map[types.Object]span, obj types.Object, pos, end token.Pos) {
|
||||
if strict {
|
||||
return // No need
|
||||
}
|
||||
s, ok := spans[obj]
|
||||
if ok {
|
||||
if s.min > pos {
|
||||
s.min = pos
|
||||
}
|
||||
if s.max < end {
|
||||
s.max = end
|
||||
}
|
||||
} else {
|
||||
s = span{pos, end}
|
||||
}
|
||||
spans[obj] = s
|
||||
}
|
||||
|
||||
// checkShadowAssignment checks for shadowing in a short variable declaration.
|
||||
func checkShadowAssignment(pass *analysis.Pass, spans map[types.Object]span, a *ast.AssignStmt) {
|
||||
if a.Tok != token.DEFINE {
|
||||
return
|
||||
}
|
||||
if idiomaticShortRedecl(pass, a) {
|
||||
return
|
||||
}
|
||||
for _, expr := range a.Lhs {
|
||||
ident, ok := expr.(*ast.Ident)
|
||||
if !ok {
|
||||
pass.Reportf(expr.Pos(), "invalid AST: short variable declaration of non-identifier")
|
||||
return
|
||||
}
|
||||
checkShadowing(pass, spans, 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 idiomaticShortRedecl(pass *analysis.Pass, 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 {
|
||||
pass.Reportf(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 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(pass *analysis.Pass, spans map[types.Object]span, d *ast.GenDecl) {
|
||||
if d.Tok != token.VAR {
|
||||
return
|
||||
}
|
||||
for _, spec := range d.Specs {
|
||||
valueSpec, ok := spec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
pass.Reportf(spec.Pos(), "invalid AST: var GenDecl not ValueSpec")
|
||||
return
|
||||
}
|
||||
// Don't complain about deliberate redeclarations of the form
|
||||
// var i = i
|
||||
if idiomaticRedecl(valueSpec) {
|
||||
return
|
||||
}
|
||||
for _, ident := range valueSpec.Names {
|
||||
checkShadowing(pass, spans, ident)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkShadowing checks whether the identifier shadows an identifier in an outer scope.
|
||||
func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast.Ident) {
|
||||
if ident.Name == "_" {
|
||||
// Can't shadow the blank identifier.
|
||||
return
|
||||
}
|
||||
obj := pass.TypesInfo.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 strict {
|
||||
// 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 := spans[shadowed]
|
||||
if !ok {
|
||||
pass.Reportf(ident.Pos(), "internal error: no range for %q", ident.Name)
|
||||
return
|
||||
}
|
||||
if !span.contains(ident.Pos()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Don't complain if the types differ: that implies the programmer really wants two different things.
|
||||
if types.Identical(obj.Type(), shadowed.Type()) {
|
||||
line := pass.Fset.Position(shadowed.Pos()).Line
|
||||
pass.Reportf(ident.Pos(), "declaration of %q shadows declaration at line %d", obj.Name(), line)
|
||||
}
|
||||
}
|
||||
13
vendor/golang.org/x/tools/go/analysis/passes/shadow/shadow_test.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/go/analysis/passes/shadow/shadow_test.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package shadow_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/shadow"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, shadow.Analyzer, "a")
|
||||
}
|
||||
91
vendor/golang.org/x/tools/go/analysis/passes/shadow/testdata/src/a/a.go
generated
vendored
Normal file
91
vendor/golang.org/x/tools/go/analysis/passes/shadow/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// 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 a
|
||||
|
||||
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) // want "declaration of .err. shadows declaration at line 13"
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i := 3 // OK
|
||||
_ = i
|
||||
}
|
||||
if f != nil {
|
||||
x := one() // want "declaration of .x. shadows declaration at line 14"
|
||||
var _, err = f.Read(buf) // want "declaration of .err. shadows declaration at line 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 // want "declaration of .x. shadows declaration at line 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 // want "declaration of .t. shadows declaration at line 78"
|
||||
_ = t
|
||||
}
|
||||
_ = t
|
||||
case uint:
|
||||
{
|
||||
t := uint(0) // OK because t is not mentioned later in this function
|
||||
_ = t
|
||||
}
|
||||
}
|
||||
}
|
||||
101
vendor/golang.org/x/tools/go/analysis/passes/shift/dead.go
generated
vendored
Normal file
101
vendor/golang.org/x/tools/go/analysis/passes/shift/dead.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// 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.
|
||||
|
||||
package shift
|
||||
|
||||
// Simplified dead code detector.
|
||||
// Used for skipping shift checks on unreachable arch-specific code.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// updateDead puts unreachable "if" and "case" nodes into dead.
|
||||
func updateDead(info *types.Info, dead map[ast.Node]bool, node ast.Node) {
|
||||
if dead[node] {
|
||||
// The node is already marked as dead.
|
||||
return
|
||||
}
|
||||
|
||||
// setDead marks the node and all the children as dead.
|
||||
setDead := func(n ast.Node) {
|
||||
ast.Inspect(n, func(node ast.Node) bool {
|
||||
if node != nil {
|
||||
dead[node] = true
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
switch stmt := node.(type) {
|
||||
case *ast.IfStmt:
|
||||
// "if" branch is dead if its condition evaluates
|
||||
// to constant false.
|
||||
v := info.Types[stmt.Cond].Value
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
if !constant.BoolVal(v) {
|
||||
setDead(stmt.Body)
|
||||
return
|
||||
}
|
||||
if stmt.Else != nil {
|
||||
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 := info.Types[expr].Value
|
||||
if v == nil || v.Kind() != constant.Bool || constant.BoolVal(v) {
|
||||
continue BodyLoopBool
|
||||
}
|
||||
}
|
||||
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 := info.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 := info.Types[expr].Value
|
||||
if v == nil {
|
||||
continue BodyLoopInt
|
||||
}
|
||||
n, ok := constant.Uint64Val(v)
|
||||
if !ok || tagN == n {
|
||||
continue BodyLoopInt
|
||||
}
|
||||
}
|
||||
setDead(cc)
|
||||
}
|
||||
}
|
||||
}
|
||||
128
vendor/golang.org/x/tools/go/analysis/passes/shift/shift.go
generated
vendored
Normal file
128
vendor/golang.org/x/tools/go/analysis/passes/shift/shift.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
// 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 shift defines an Analyzer that checks for shifts that exceed
|
||||
// the width of an integer.
|
||||
package shift
|
||||
|
||||
// TODO(adonovan): integrate with ctrflow (CFG-based) dead code analysis. May
|
||||
// have impedance mismatch due to its (non-)treatment of constant
|
||||
// expressions (such as runtime.GOARCH=="386").
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "shift",
|
||||
Doc: "check for shifts that equal or exceed the width of the integer",
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
// Do a complete pass to compute dead nodes.
|
||||
dead := make(map[ast.Node]bool)
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.IfStmt)(nil),
|
||||
(*ast.SwitchStmt)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
// TODO(adonovan): move updateDead into this file.
|
||||
updateDead(pass.TypesInfo, dead, n)
|
||||
})
|
||||
|
||||
nodeFilter = []ast.Node{
|
||||
(*ast.AssignStmt)(nil),
|
||||
(*ast.BinaryExpr)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(node ast.Node) {
|
||||
if 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(pass, 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(pass, node, node.Lhs[0], node.Rhs[0])
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// checkLongShift checks if shift or shift-assign operations shift by more than
|
||||
// the length of the underlying variable.
|
||||
func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
|
||||
if pass.TypesInfo.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 := pass.TypesInfo.Types[y].Value
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
amt, ok := constant.Int64Val(v)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
t := pass.TypesInfo.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 := analysisutil.Format(pass.Fset, x)
|
||||
pass.Reportf(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])
|
||||
)
|
||||
|
||||
var archSizes = types.SizesFor("gc", build.Default.GOARCH)
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/shift/shift_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/shift/shift_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package shift_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/shift"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, shift.Analyzer, "a")
|
||||
}
|
||||
155
vendor/golang.org/x/tools/go/analysis/passes/shift/testdata/src/a/a.go
generated
vendored
Normal file
155
vendor/golang.org/x/tools/go/analysis/passes/shift/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func ShiftTest() {
|
||||
var i8 int8
|
||||
_ = i8 << 7
|
||||
_ = (i8 + 1) << 8 // want ".i8 . 1. .8 bits. too small for shift of 8"
|
||||
_ = i8 << (7 + 1) // want "i8 .8 bits. too small for shift of 8"
|
||||
_ = i8 >> 8 // want "i8 .8 bits. too small for shift of 8"
|
||||
i8 <<= 8 // want "i8 .8 bits. too small for shift of 8"
|
||||
i8 >>= 8 // want "i8 .8 bits. too small for shift of 8"
|
||||
var i16 int16
|
||||
_ = i16 << 15
|
||||
_ = i16 << 16 // want "i16 .16 bits. too small for shift of 16"
|
||||
_ = i16 >> 16 // want "i16 .16 bits. too small for shift of 16"
|
||||
i16 <<= 16 // want "i16 .16 bits. too small for shift of 16"
|
||||
i16 >>= 16 // want "i16 .16 bits. too small for shift of 16"
|
||||
var i32 int32
|
||||
_ = i32 << 31
|
||||
_ = i32 << 32 // want "i32 .32 bits. too small for shift of 32"
|
||||
_ = i32 >> 32 // want "i32 .32 bits. too small for shift of 32"
|
||||
i32 <<= 32 // want "i32 .32 bits. too small for shift of 32"
|
||||
i32 >>= 32 // want "i32 .32 bits. too small for shift of 32"
|
||||
var i64 int64
|
||||
_ = i64 << 63
|
||||
_ = i64 << 64 // want "i64 .64 bits. too small for shift of 64"
|
||||
_ = i64 >> 64 // want "i64 .64 bits. too small for shift of 64"
|
||||
i64 <<= 64 // want "i64 .64 bits. too small for shift of 64"
|
||||
i64 >>= 64 // want "i64 .64 bits. too small for shift of 64"
|
||||
var u8 uint8
|
||||
_ = u8 << 7
|
||||
_ = u8 << 8 // want "u8 .8 bits. too small for shift of 8"
|
||||
_ = u8 >> 8 // want "u8 .8 bits. too small for shift of 8"
|
||||
u8 <<= 8 // want "u8 .8 bits. too small for shift of 8"
|
||||
u8 >>= 8 // want "u8 .8 bits. too small for shift of 8"
|
||||
var u16 uint16
|
||||
_ = u16 << 15
|
||||
_ = u16 << 16 // want "u16 .16 bits. too small for shift of 16"
|
||||
_ = u16 >> 16 // want "u16 .16 bits. too small for shift of 16"
|
||||
u16 <<= 16 // want "u16 .16 bits. too small for shift of 16"
|
||||
u16 >>= 16 // want "u16 .16 bits. too small for shift of 16"
|
||||
var u32 uint32
|
||||
_ = u32 << 31
|
||||
_ = u32 << 32 // want "u32 .32 bits. too small for shift of 32"
|
||||
_ = u32 >> 32 // want "u32 .32 bits. too small for shift of 32"
|
||||
u32 <<= 32 // want "u32 .32 bits. too small for shift of 32"
|
||||
u32 >>= 32 // want "u32 .32 bits. too small for shift of 32"
|
||||
var u64 uint64
|
||||
_ = u64 << 63
|
||||
_ = u64 << 64 // want "u64 .64 bits. too small for shift of 64"
|
||||
_ = u64 >> 64 // want "u64 .64 bits. too small for shift of 64"
|
||||
u64 <<= 64 // want "u64 .64 bits. too small for shift of 64"
|
||||
u64 >>= 64 // want "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 // want "too small for shift"
|
||||
_ = i >> in // want "too small for shift"
|
||||
i <<= in // want "too small for shift"
|
||||
i >>= in // want "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 // want "too small for shift"
|
||||
_ = u >> un // want "too small for shift"
|
||||
u <<= un // want "too small for shift"
|
||||
u >>= un // want "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 // want "too small for shift"
|
||||
_ = p >> pn // want "too small for shift"
|
||||
p <<= pn // want "too small for shift"
|
||||
p >>= pn // want "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) // want "too small for shift"
|
||||
h >>= 7 * unsafe.Alignof(h)
|
||||
h >>= 8 * unsafe.Alignof(h) // want "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 // want "too small for shift"
|
||||
}
|
||||
}
|
||||
182
vendor/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go
generated
vendored
Normal file
182
vendor/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package stdmethods defines an Analyzer that checks for misspellings
|
||||
// in the signatures of methods similar to well-known interfaces.
|
||||
package stdmethods
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check signature of methods of well-known interfaces
|
||||
|
||||
Sometimes a type may be intended to satisfy an interface but may fail to
|
||||
do so because of a mistake in its method signature.
|
||||
For example, the result of this WriteTo method should be (int64, error),
|
||||
not error, to satisfy io.WriterTo:
|
||||
|
||||
type myWriterTo struct{...}
|
||||
func (myWriterTo) WriteTo(w io.Writer) error { ... }
|
||||
|
||||
This check ensures that each method whose name matches one of several
|
||||
well-known interface methods from the standard library has the correct
|
||||
signature for that interface.
|
||||
|
||||
Checked method names include:
|
||||
Format GobEncode GobDecode MarshalJSON MarshalXML
|
||||
Peek ReadByte ReadFrom ReadRune Scan Seek
|
||||
UnmarshalJSON UnreadByte UnreadRune WriteByte
|
||||
WriteTo
|
||||
`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "stdmethods",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
// 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]struct{ args, results []string }{
|
||||
// "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 run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.FuncDecl)(nil),
|
||||
(*ast.InterfaceType)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
switch n := n.(type) {
|
||||
case *ast.FuncDecl:
|
||||
if n.Recv != nil {
|
||||
canonicalMethod(pass, n.Name)
|
||||
}
|
||||
case *ast.InterfaceType:
|
||||
for _, field := range n.Methods.List {
|
||||
for _, id := range field.Names {
|
||||
canonicalMethod(pass, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func canonicalMethod(pass *analysis.Pass, id *ast.Ident) {
|
||||
// Expected input/output.
|
||||
expect, ok := canonicalMethods[id.Name]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Actual input/output
|
||||
sign := pass.TypesInfo.Defs[id].Type().(*types.Signature)
|
||||
args := sign.Params()
|
||||
results := sign.Results()
|
||||
|
||||
// Do the =s (if any) all match?
|
||||
if !matchParams(pass, expect.args, args, "=") || !matchParams(pass, expect.results, results, "=") {
|
||||
return
|
||||
}
|
||||
|
||||
// Everything must match.
|
||||
if !matchParams(pass, expect.args, args, "") || !matchParams(pass, 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 := types.TypeString(sign, (*types.Package).Name)
|
||||
actual = strings.TrimPrefix(actual, "func")
|
||||
actual = id.Name + actual
|
||||
|
||||
pass.Reportf(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 matchParams(pass *analysis.Pass, 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 !matchParamType(pass.Fset, pass.Pkg, x, actual.At(i).Type()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if prefix == "" && actual.Len() > len(expect) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Does this one type match?
|
||||
func matchParamType(fset *token.FileSet, pkg *types.Package, expect string, actual types.Type) bool {
|
||||
expect = strings.TrimPrefix(expect, "=")
|
||||
// Strip package name if we're in that package.
|
||||
if n := len(pkg.Name()); len(expect) > n && expect[:n] == pkg.Name() && expect[n] == '.' {
|
||||
expect = expect[n+1:]
|
||||
}
|
||||
|
||||
// Overkill but easy.
|
||||
return actual.String() == expect
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package stdmethods_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/stdmethods"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, stdmethods.Analyzer, "a")
|
||||
}
|
||||
29
vendor/golang.org/x/tools/go/analysis/passes/stdmethods/testdata/src/a/a.go
generated
vendored
Normal file
29
vendor/golang.org/x/tools/go/analysis/passes/stdmethods/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
package a
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type T int
|
||||
|
||||
func (T) Scan(x fmt.ScanState, c byte) {} // want `should have signature Scan\(fmt\.ScanState, rune\) error`
|
||||
|
||||
func (T) Format(fmt.State, byte) {} // want `should have signature Format\(fmt.State, rune\)`
|
||||
|
||||
type U int
|
||||
|
||||
func (U) Format(byte) {} // no error: first parameter must be fmt.State to trigger check
|
||||
|
||||
func (U) GobDecode() {} // want `should have signature GobDecode\(\[\]byte\) error`
|
||||
|
||||
// Test rendering of type names such as xml.Encoder in diagnostic.
|
||||
func (U) MarshalXML(*xml.Encoder) {} // want `method MarshalXML\(\*xml.Encoder\) should...`
|
||||
|
||||
type I interface {
|
||||
ReadByte() byte // want `should have signature ReadByte\(\) \(byte, error\)`
|
||||
}
|
||||
260
vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go
generated
vendored
Normal file
260
vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
// 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.
|
||||
|
||||
// Package structtag defines an Analyzer that checks struct field tags
|
||||
// are well formed.
|
||||
package structtag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check that struct field tags conform to reflect.StructTag.Get
|
||||
|
||||
Also report certain struct tags (json, xml) used with unexported fields.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "structtag",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
RunDespiteErrors: true,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.StructType)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
styp := pass.TypesInfo.Types[n.(*ast.StructType)].Type.(*types.Struct)
|
||||
var seen map[[2]string]token.Pos
|
||||
for i := 0; i < styp.NumFields(); i++ {
|
||||
field := styp.Field(i)
|
||||
tag := styp.Tag(i)
|
||||
checkCanonicalFieldTag(pass, field, tag, &seen)
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var checkTagDups = []string{"json", "xml"}
|
||||
var checkTagSpaces = map[string]bool{"json": true, "xml": true, "asn1": true}
|
||||
|
||||
// checkCanonicalFieldTag checks a single struct field tag.
|
||||
func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, seen *map[[2]string]token.Pos) {
|
||||
for _, key := range checkTagDups {
|
||||
checkTagDuplicates(pass, tag, key, field, field, seen)
|
||||
}
|
||||
|
||||
if err := validateStructTag(tag); err != nil {
|
||||
pass.Reportf(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.
|
||||
// TODO(adonovan): investigate, now that that issue is fixed.
|
||||
if field.Anonymous() {
|
||||
return
|
||||
}
|
||||
|
||||
if field.Exported() {
|
||||
return
|
||||
}
|
||||
|
||||
for _, enc := range [...]string{"json", "xml"} {
|
||||
if reflect.StructTag(tag).Get(enc) != "" {
|
||||
pass.Reportf(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(pass *analysis.Pass, 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(pass, 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 {
|
||||
posn := pass.Fset.Position(pos)
|
||||
posn.Filename = filepath.Base(posn.Filename)
|
||||
posn.Column = 0
|
||||
pass.Reportf(nearest.Pos(), "struct field %s repeats %s tag %q also at %s", field.Name(), key, val, posn)
|
||||
} 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
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
package structtag_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/structtag"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, structtag.Analyzer, "a")
|
||||
}
|
||||
113
vendor/golang.org/x/tools/go/analysis/passes/structtag/testdata/src/a/a.go
generated
vendored
Normal file
113
vendor/golang.org/x/tools/go/analysis/passes/structtag/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
// 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 a
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
type StructTagTest struct {
|
||||
A int "hello" // want "`hello` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair"
|
||||
B int "\tx:\"y\"" // want "not compatible with reflect.StructTag.Get: bad syntax for struct tag key"
|
||||
C int "x:\"y\"\tx:\"y\"" // want "not compatible with reflect.StructTag.Get"
|
||||
D int "x:`y`" // want "not compatible with reflect.StructTag.Get: bad syntax for struct tag value"
|
||||
E int "ct\brl:\"char\"" // want "not compatible with reflect.StructTag.Get: bad syntax for struct tag pair"
|
||||
F int `:"emptykey"` // want "not compatible with reflect.StructTag.Get: bad syntax for struct tag key"
|
||||
G int `x:"noEndQuote` // want "not compatible with reflect.StructTag.Get: bad syntax for struct tag value"
|
||||
H int `x:"trunc\x0"` // want "not compatible with reflect.StructTag.Get: bad syntax for struct tag value"
|
||||
I int `x:"foo",y:"bar"` // want "not compatible with reflect.StructTag.Get: key:.value. pairs not separated by spaces"
|
||||
J int `x:"foo"y:"bar"` // want "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"` // want "struct field x has json tag but is not exported"
|
||||
y int `xml:"yy"` // want "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" // want "`hello` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair"
|
||||
}
|
||||
|
||||
type DuplicateJSONFields struct {
|
||||
JSON int `json:"a"`
|
||||
DuplicateJSON int `json:"a"` // want "struct field DuplicateJSON repeats json tag .a. also at a.go:52"
|
||||
IgnoredJSON int `json:"-"`
|
||||
OtherIgnoredJSON int `json:"-"`
|
||||
OmitJSON int `json:",omitempty"`
|
||||
OtherOmitJSON int `json:",omitempty"`
|
||||
DuplicateOmitJSON int `json:"a,omitempty"` // want "struct field DuplicateOmitJSON repeats json tag .a. also at a.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"` // want "struct field AnonymousJSON repeats json tag .a. also at a.go:52"
|
||||
|
||||
AnonymousJSONField // want "struct field DuplicateAnonJSON repeats json tag .a. also at a.go:52"
|
||||
|
||||
XML int `xml:"a"`
|
||||
DuplicateXML int `xml:"a"` // want "struct field DuplicateXML repeats xml tag .a. also at a.go:68"
|
||||
IgnoredXML int `xml:"-"`
|
||||
OtherIgnoredXML int `xml:"-"`
|
||||
OmitXML int `xml:",omitempty"`
|
||||
OtherOmitXML int `xml:",omitempty"`
|
||||
DuplicateOmitXML int `xml:"a,omitempty"` // want "struct field DuplicateOmitXML repeats xml tag .a. also at a.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"` // want "struct field AnonymousXML repeats xml tag .a. also at a.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"` // want "struct field DupAttr repeats xml attribute tag .b. also at a.go:84"
|
||||
DupOmitAttr int `xml:"b,omitempty,attr"` // want "struct field DupOmitAttr repeats xml attribute tag .b. also at a.go:84"
|
||||
|
||||
AnonymousXML `xml:"b,attr"` // want "struct field AnonymousXML repeats xml attribute tag .b. also at a.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"` // want "suspicious space in struct tag value"
|
||||
C int `json:"c ,omitempty"`
|
||||
D int `json:"d,omitempty, string"` // want "suspicious space in struct tag value"
|
||||
E int `xml:"e local"`
|
||||
F int `xml:"f "` // want "suspicious space in struct tag value"
|
||||
G int `xml:" g"` // want "suspicious space in struct tag value"
|
||||
H int `xml:"h ,omitempty"` // want "suspicious space in struct tag value"
|
||||
I int `xml:"i, omitempty"` // want "suspicious space in struct tag value"
|
||||
J int `xml:"j local ,omitempty"` // want "suspicious space in struct tag value"
|
||||
K int `xml:"k local, omitempty"` // want "suspicious space in struct tag value"
|
||||
L int `xml:" l local,omitempty"` // want "suspicious space in struct tag value"
|
||||
M int `xml:"m local,omitempty"` // want "suspicious space in struct tag value"
|
||||
N int `xml:" "` // want "suspicious space in struct tag value"
|
||||
O int `xml:""`
|
||||
P int `xml:","`
|
||||
Q int `foo:" doesn't care "`
|
||||
}
|
||||
3
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/a/a.go
generated
vendored
Normal file
3
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
package a
|
||||
|
||||
func Foo() {}
|
||||
78
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/a/a_test.go
generated
vendored
Normal file
78
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/a/a_test.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
package a
|
||||
|
||||
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() {} // want "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() {} // want "ExampleBuf_Clear refers to unknown field or method: Buf.Clear"
|
||||
|
||||
func ExampleBuf_suffix() {} // OK because refers to suffix annotation.
|
||||
|
||||
func ExampleBuf_Append_Bad() {} // want "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 } // want "ExampleBuf_Reset should return nothing"
|
||||
|
||||
func ExampleBuf_Len(i int) {} // want "ExampleBuf_Len should be niladic"
|
||||
|
||||
// "Puffer" is German for "Buffer".
|
||||
|
||||
func ExamplePuffer() {} // want "ExamplePuffer refers to unknown identifier: Puffer"
|
||||
|
||||
func ExamplePuffer_Append() {} // want "ExamplePuffer_Append refers to unknown identifier: Puffer"
|
||||
|
||||
func ExamplePuffer_suffix() {} // want "ExamplePuffer_suffix refers to unknown identifier: Puffer"
|
||||
|
||||
func ExampleFoo() {} // OK because a.Foo exists
|
||||
|
||||
func ExampleBar() {} // want "ExampleBar refers to unknown identifier: Bar"
|
||||
|
||||
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) {} // want "first letter after 'Test' must not be lowercase"
|
||||
|
||||
func TestemptyImportBadSuffix(*testing.T) {} // want "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) {} // want "first letter after 'Benchmark' must not be lowercase"
|
||||
7
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/a/ax_test.go
generated
vendored
Normal file
7
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/a/ax_test.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package a_test
|
||||
|
||||
import _ "a"
|
||||
|
||||
func ExampleFoo() {} // OK because a.Foo exists
|
||||
|
||||
func ExampleBar() {} // want "ExampleBar refers to unknown identifier: Bar"
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/divergent/buf.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/divergent/buf.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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
|
||||
35
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/divergent/buf_test.go
generated
vendored
Normal file
35
vendor/golang.org/x/tools/go/analysis/passes/tests/testdata/src/divergent/buf_test.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Test of examples with divergent packages.
|
||||
|
||||
package buf
|
||||
|
||||
func Example() {} // OK because is package-level.
|
||||
|
||||
func Example_suffix() {} // OK because refers to suffix annotation.
|
||||
|
||||
func Example_BadSuffix() {} // want "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() {} // want "ExampleBuf_Clear refers to unknown field or method: Buf.Clear"
|
||||
|
||||
func ExampleBuf_suffix() {} // OK because refers to suffix annotation.
|
||||
|
||||
func ExampleBuf_Append_Bad() {} // want "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 } // want "ExampleBuf_Reset should return nothing"
|
||||
|
||||
func ExampleBuf_Len(i int) {} // want "ExampleBuf_Len should be niladic"
|
||||
|
||||
// "Puffer" is German for "Buffer".
|
||||
|
||||
func ExamplePuffer() {} // want "ExamplePuffer refers to unknown identifier: Puffer"
|
||||
|
||||
func ExamplePuffer_Append() {} // want "ExamplePuffer_Append refers to unknown identifier: Puffer"
|
||||
|
||||
func ExamplePuffer_suffix() {} // want "ExamplePuffer_suffix refers to unknown identifier: Puffer"
|
||||
175
vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go
generated
vendored
Normal file
175
vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
// 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 tests defines an Analyzer that checks for common mistaken
|
||||
// usages of tests and examples.
|
||||
package tests
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
const Doc = `check for common mistaken usages of tests and examples
|
||||
|
||||
The tests checker walks Test, Benchmark and Example functions checking
|
||||
malformed names, wrong signatures and examples documenting non-existent
|
||||
identifiers.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "tests",
|
||||
Doc: Doc,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
for _, f := range pass.Files {
|
||||
if !strings.HasSuffix(pass.Fset.File(f.Pos()).Name(), "_test.go") {
|
||||
continue
|
||||
}
|
||||
for _, decl := range f.Decls {
|
||||
fn, ok := decl.(*ast.FuncDecl)
|
||||
if !ok || fn.Recv != nil {
|
||||
// Ignore non-functions or functions with receivers.
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(fn.Name.Name, "Example"):
|
||||
checkExample(pass, fn)
|
||||
case strings.HasPrefix(fn.Name.Name, "Test"):
|
||||
checkTest(pass, fn, "Test")
|
||||
case strings.HasPrefix(fn.Name.Name, "Benchmark"):
|
||||
checkTest(pass, fn, "Benchmark")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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(pkg *types.Package, name string) types.Object {
|
||||
if o := pkg.Scope().Lookup(name); o != nil {
|
||||
return o
|
||||
}
|
||||
|
||||
// If this package is ".../foo_test" and it imports a package
|
||||
// ".../foo", try looking in the latter package.
|
||||
// This heuristic should work even on build systems that do not
|
||||
// record any special link between the packages.
|
||||
if basePath := strings.TrimSuffix(pkg.Path(), "_test"); basePath != pkg.Path() {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp.Path() == basePath {
|
||||
return imp.Scope().Lookup(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) {
|
||||
fnName := fn.Name.Name
|
||||
if params := fn.Type.Params; len(params.List) != 0 {
|
||||
pass.Reportf(fn.Pos(), "%s should be niladic", fnName)
|
||||
}
|
||||
if results := fn.Type.Results; results != nil && len(results.List) != 0 {
|
||||
pass.Reportf(fn.Pos(), "%s should return nothing", fnName)
|
||||
}
|
||||
|
||||
if fnName == "Example" {
|
||||
// Nothing more to do.
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
exName = strings.TrimPrefix(fnName, "Example")
|
||||
elems = strings.SplitN(exName, "_", 3)
|
||||
ident = elems[0]
|
||||
obj = lookup(pass.Pkg, ident)
|
||||
)
|
||||
if ident != "" && obj == nil {
|
||||
// Check ExampleFoo and ExampleBadFoo.
|
||||
pass.Reportf(fn.Pos(), "%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) {
|
||||
pass.Reportf(fn.Pos(), "%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 {
|
||||
pass.Reportf(fn.Pos(), "%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.
|
||||
pass.Reportf(fn.Pos(), "%s has malformed example suffix: %s", fnName, elems[2])
|
||||
}
|
||||
}
|
||||
|
||||
func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
|
||||
// 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):]) {
|
||||
pass.Reportf(fn.Pos(), "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user