Commit 249842c8 authored by Rob Pike's avatar Rob Pike

cmd/vet: delete; it now lives in the go.tools subrepo

R=golang-dev, dsymonds, rsc, iant, dave
CC=golang-dev
https://golang.org/cl/9496043
parent 60214492
# 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.
# Assumes go/types is installed
test testshort:
go build -tags 'vet_test gotypes'
../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' test_*.go test_*.s
test_notypes:
go build -tags 'vet_test'
# Only those tests that do not depend on types.
# Excluded: test_print.go
../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' test_asm.go test_assign.go test_atomic.go test_buildtag.go test_buildtag_bad.go test_deadcode.go test_method.go test_rangeloop.go test_structtag.go test_taglit.go test_*.s
This diff is collapsed.
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
This file contains the code to check for useless assignments.
*/
package main
import (
"go/ast"
"go/token"
"reflect"
)
// TODO: should also check for assignments to struct fields inside methods
// that are on T instead of *T.
// checkAssignStmt checks for assignments of the form "<expr> = <expr>".
// These are almost always useless, and even when they aren't they are usually a mistake.
func (f *File) checkAssignStmt(stmt *ast.AssignStmt) {
if !vet("assign") {
return
}
if stmt.Tok != token.ASSIGN {
return // ignore :=
}
if len(stmt.Lhs) != len(stmt.Rhs) {
// If LHS and RHS have different cardinality, they can't be the same.
return
}
for i, lhs := range stmt.Lhs {
rhs := stmt.Rhs[i]
if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
continue // short-circuit the heavy-weight gofmt check
}
le := f.gofmt(lhs)
re := f.gofmt(rhs)
if le == re {
f.Warnf(stmt.Pos(), "self-assignment of %s to %s", re, le)
}
}
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"go/ast"
"go/token"
)
// checkAtomicAssignment walks the assignment statement checking for common
// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1)
func (f *File) checkAtomicAssignment(n *ast.AssignStmt) {
if !vet("atomic") {
return
}
if len(n.Lhs) != len(n.Rhs) {
return
}
for i, right := range n.Rhs {
call, ok := right.(*ast.CallExpr)
if !ok {
continue
}
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
continue
}
pkg, ok := sel.X.(*ast.Ident)
if !ok || pkg.Name != "atomic" {
continue
}
switch sel.Sel.Name {
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
f.checkAtomicAddAssignment(n.Lhs[i], call)
}
}
}
// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value
// to the same variable being used in the operation
func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) {
arg := call.Args[0]
broken := false
if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
broken = f.gofmt(left) == f.gofmt(uarg.X)
} else if star, ok := left.(*ast.StarExpr); ok {
broken = f.gofmt(star.X) == f.gofmt(arg)
}
if broken {
f.Warn(left.Pos(), "direct assignment to atomic value")
}
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"fmt"
"os"
"strings"
"unicode"
)
var (
nl = []byte("\n")
slashSlash = []byte("//")
plusBuild = []byte("+build")
)
// checkBuildTag checks that build tags are in the correct location and well-formed.
func checkBuildTag(name string, data []byte) {
if !vet("buildtags") {
return
}
lines := bytes.SplitAfter(data, nl)
// Determine cutpoint where +build comments are no longer valid.
// They are valid in leading // comments in the file followed by
// a blank line.
var cutoff int
for i, line := range lines {
line = bytes.TrimSpace(line)
if len(line) == 0 {
cutoff = i
continue
}
if bytes.HasPrefix(line, slashSlash) {
continue
}
break
}
for i, line := range lines {
line = bytes.TrimSpace(line)
if !bytes.HasPrefix(line, slashSlash) {
continue
}
text := bytes.TrimSpace(line[2:])
if bytes.HasPrefix(text, plusBuild) {
fields := bytes.Fields(text)
if !bytes.Equal(fields[0], plusBuild) {
// Comment is something like +buildasdf not +build.
fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
continue
}
if i >= cutoff {
fmt.Fprintf(os.Stderr, "%s:%d: +build comment appears too late in file\n", name, i+1)
setExit(1)
continue
}
// Check arguments.
Args:
for _, arg := range fields[1:] {
for _, elem := range strings.Split(string(arg), ",") {
if strings.HasPrefix(elem, "!!") {
fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg)
setExit(1)
break Args
}
if strings.HasPrefix(elem, "!") {
elem = elem[1:]
}
for _, c := range elem {
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg)
setExit(1)
break Args
}
}
}
}
continue
}
// Comment with +build but not at beginning.
if bytes.Contains(line, plusBuild) && i < cutoff {
fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1)
continue
}
}
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Check for syntactically unreachable code.
package main
import (
"go/ast"
"go/token"
)
type deadState struct {
f *File
hasBreak map[ast.Stmt]bool
hasGoto map[string]bool
labels map[string]ast.Stmt
breakTarget ast.Stmt
reachable bool
}
// checkUnreachable checks a function body for dead code.
func (f *File) checkUnreachable(body *ast.BlockStmt) {
if !vet("unreachable") || body == nil {
return
}
d := &deadState{
f: f,
hasBreak: make(map[ast.Stmt]bool),
hasGoto: make(map[string]bool),
labels: make(map[string]ast.Stmt),
}
d.findLabels(body)
d.reachable = true
d.findDead(body)
}
// findLabels gathers information about the labels defined and used by stmt
// and about which statements break, whether a label is involved or not.
func (d *deadState) findLabels(stmt ast.Stmt) {
switch x := stmt.(type) {
default:
d.f.Warnf(x.Pos(), "internal error in findLabels: unexpected statement %T", x)
case *ast.AssignStmt,
*ast.BadStmt,
*ast.DeclStmt,
*ast.DeferStmt,
*ast.EmptyStmt,
*ast.ExprStmt,
*ast.GoStmt,
*ast.IncDecStmt,
*ast.ReturnStmt,
*ast.SendStmt:
// no statements inside
case *ast.BlockStmt:
for _, stmt := range x.List {
d.findLabels(stmt)
}
case *ast.BranchStmt:
switch x.Tok {
case token.GOTO:
d.hasGoto[x.Label.Name] = true
case token.BREAK:
stmt := d.breakTarget
if x.Label != nil {
stmt = d.labels[x.Label.Name]
}
if stmt != nil {
d.hasBreak[stmt] = true
}
}
case *ast.IfStmt:
d.findLabels(x.Body)
if x.Else != nil {
d.findLabels(x.Else)
}
case *ast.LabeledStmt:
d.labels[x.Label.Name] = x.Stmt
d.findLabels(x.Stmt)
// These cases are all the same, but the x.Body only works
// when the specific type of x is known, so the cases cannot
// be merged.
case *ast.ForStmt:
outer := d.breakTarget
d.breakTarget = x
d.findLabels(x.Body)
d.breakTarget = outer
case *ast.RangeStmt:
outer := d.breakTarget
d.breakTarget = x
d.findLabels(x.Body)
d.breakTarget = outer
case *ast.SelectStmt:
outer := d.breakTarget
d.breakTarget = x
d.findLabels(x.Body)
d.breakTarget = outer
case *ast.SwitchStmt:
outer := d.breakTarget
d.breakTarget = x
d.findLabels(x.Body)
d.breakTarget = outer
case *ast.TypeSwitchStmt:
outer := d.breakTarget
d.breakTarget = x
d.findLabels(x.Body)
d.breakTarget = outer
case *ast.CommClause:
for _, stmt := range x.Body {
d.findLabels(stmt)
}
case *ast.CaseClause:
for _, stmt := range x.Body {
d.findLabels(stmt)
}
}
}
// findDead walks the statement looking for dead code.
// If d.reachable is false on entry, stmt itself is dead.
// When findDead returns, d.reachable tells whether the
// statement following stmt is reachable.
func (d *deadState) findDead(stmt ast.Stmt) {
// Is this a labeled goto target?
// If so, assume it is reachable due to the goto.
// This is slightly conservative, in that we don't
// check that the goto is reachable, so
// L: goto L
// will not provoke a warning.
// But it's good enough.
if x, isLabel := stmt.(*ast.LabeledStmt); isLabel && d.hasGoto[x.Label.Name] {
d.reachable = true
}
if !d.reachable {
switch stmt.(type) {
case *ast.EmptyStmt:
// do not warn about unreachable empty statements
default:
d.f.Warnf(stmt.Pos(), "unreachable code")
d.reachable = true // silence error about next statement
}
}
switch x := stmt.(type) {
default:
d.f.Warnf(x.Pos(), "internal error in findDead: unexpected statement %T", x)
case *ast.AssignStmt,
*ast.BadStmt,
*ast.DeclStmt,
*ast.DeferStmt,
*ast.EmptyStmt,
*ast.GoStmt,
*ast.IncDecStmt,
*ast.SendStmt:
// no control flow
case *ast.BlockStmt:
for _, stmt := range x.List {
d.findDead(stmt)
}
case *ast.BranchStmt:
switch x.Tok {
case token.BREAK, token.GOTO, token.FALLTHROUGH:
d.reachable = false
case token.CONTINUE:
// NOTE: We accept "continue" statements as terminating.
// They are not necessary in the spec definition of terminating,
// because a continue statement cannot be the final statement
// before a return. But for the more general problem of syntactically
// identifying dead code, continue redirects control flow just
// like the other terminating statements.
d.reachable = false
}
case *ast.ExprStmt:
// Call to panic?
call, ok := x.X.(*ast.CallExpr)
if ok {
name, ok := call.Fun.(*ast.Ident)
if ok && name.Name == "panic" && name.Obj == nil {
d.reachable = false
}
}
case *ast.ForStmt:
d.findDead(x.Body)
d.reachable = x.Cond != nil || d.hasBreak[x]
case *ast.IfStmt:
d.findDead(x.Body)
if x.Else != nil {
r := d.reachable
d.reachable = true
d.findDead(x.Else)
d.reachable = d.reachable || r
} else {
// might not have executed if statement
d.reachable = true
}
case *ast.LabeledStmt:
d.findDead(x.Stmt)
case *ast.RangeStmt:
d.findDead(x.Body)
d.reachable = true
case *ast.ReturnStmt:
d.reachable = false
case *ast.SelectStmt:
// NOTE: Unlike switch and type switch below, we don't care
// whether a select has a default, because a select without a
// default blocks until one of the cases can run. That's different
// from a switch without a default, which behaves like it has
// a default with an empty body.
anyReachable := false
for _, comm := range x.Body.List {
d.reachable = true
for _, stmt := range comm.(*ast.CommClause).Body {
d.findDead(stmt)
}
anyReachable = anyReachable || d.reachable
}
d.reachable = anyReachable || d.hasBreak[x]
case *ast.SwitchStmt:
anyReachable := false
hasDefault := false
for _, cas := range x.Body.List {
cc := cas.(*ast.CaseClause)
if cc.List == nil {
hasDefault = true
}
d.reachable = true
for _, stmt := range cc.Body {
d.findDead(stmt)
}
anyReachable = anyReachable || d.reachable
}
d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
case *ast.TypeSwitchStmt:
anyReachable := false
hasDefault := false
for _, cas := range x.Body.List {
cc := cas.(*ast.CaseClause)
if cc.List == nil {
hasDefault = true
}
d.reachable = true
for _, stmt := range cc.Body {
d.findDead(stmt)
}
anyReachable = anyReachable || d.reachable
}
d.reachable = anyReachable || d.hasBreak[x] || !hasDefault
}
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Vet examines Go source code and reports suspicious constructs, such as Printf
calls whose arguments do not align with the format string. Vet uses heuristics
that do not guarantee all reports are genuine problems, but it can find errors
not caught by the compilers.
Its exit code is 2 for erroneous invocation of the tool, 1 if a
problem was reported, and 0 otherwise. Note that the tool does not
check every possible problem and depends on unreliable heuristics
so it should be used as guidance only, not as a firm indicator of
program correctness.
By default all checks are performed, but if explicit flags are provided, only
those identified by the flags are performed.
Available checks:
1. Printf family, flag -printf
Suspicious calls to functions in the Printf family, including any functions
with these names:
Print Printf Println
Fprint Fprintf Fprintln
Sprint Sprintf Sprintln
Error Errorf
Fatal Fatalf
Panic Panicf Panicln
If the function name ends with an 'f', the function is assumed to take
a format descriptor string in the manner of fmt.Printf. If not, vet
complains about arguments that look like format descriptor strings.
It also checks for errors such as using a Writer as the first argument of
Printf.
2. Methods, flag -methods
Non-standard signatures for methods with familiar names, including:
Format GobEncode GobDecode MarshalJSON MarshalXML
Peek ReadByte ReadFrom ReadRune Scan Seek
UnmarshalJSON UnreadByte UnreadRune WriteByte
WriteTo
3. Struct tags, flag -structtags
Struct tags that do not follow the format understood by reflect.StructTag.Get.
4. Untagged composite literals, flag -composites
Composite struct literals that do not use the type-tagged syntax.
Usage:
go tool vet [flag] [file.go ...]
go tool vet [flag] [directory ...] # Scan all .go files under directory, recursively
The other flags are:
-v
Verbose mode
-printfuncs
A comma-separated list of print-like functions to supplement
the standard list. Each entry is in the form Name:N where N
is the zero-based argument position of the first argument
involved in the print: either the format or the first print
argument for non-formatted prints. For example,
if you have Warn and Warnf functions that take an
io.Writer as their first argument, like Fprintf,
-printfuncs=Warn:1,Warnf:1
*/
package main
This diff is collapsed.
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains the code to check canonical methods.
package main
import (
"fmt"
"go/ast"
"go/printer"
"strings"
)
type MethodSig struct {
args []string
results []string
}
// canonicalMethods lists the input and output types for Go methods
// that are checked using dynamic interface checks. Because the
// checks are dynamic, such methods would not cause a compile error
// if they have the wrong signature: instead the dynamic check would
// fail, sometimes mysteriously. If a method is found with a name listed
// here but not the input/output types listed here, vet complains.
//
// A few of the canonical methods have very common names.
// For example, a type might implement a Scan method that
// has nothing to do with fmt.Scanner, but we still want to check
// the methods that are intended to implement fmt.Scanner.
// To do that, the arguments that have a = prefix are treated as
// signals that the canonical meaning is intended: if a Scan
// method doesn't have a fmt.ScanState as its first argument,
// we let it go. But if it does have a fmt.ScanState, then the
// rest has to match.
var canonicalMethods = map[string]MethodSig{
// "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
"Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter
"GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder
"GobEncode": {[]string{}, []string{"[]byte", "error"}}, // gob.GobEncoder
"MarshalJSON": {[]string{}, []string{"[]byte", "error"}}, // json.Marshaler
"MarshalXML": {[]string{}, []string{"[]byte", "error"}}, // xml.Marshaler
"Peek": {[]string{"=int"}, []string{"[]byte", "error"}}, // image.reader (matching bufio.Reader)
"ReadByte": {[]string{}, []string{"byte", "error"}}, // io.ByteReader
"ReadFrom": {[]string{"=io.Reader"}, []string{"int64", "error"}}, // io.ReaderFrom
"ReadRune": {[]string{}, []string{"rune", "int", "error"}}, // io.RuneReader
"Scan": {[]string{"=fmt.ScanState", "rune"}, []string{"error"}}, // fmt.Scanner
"Seek": {[]string{"=int64", "int"}, []string{"int64", "error"}}, // io.Seeker
"UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}}, // json.Unmarshaler
"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 (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) {
if !vet("methods") {
return
}
// Expected input/output.
expect, ok := canonicalMethods[id.Name]
if !ok {
return
}
// Actual input/output
args := typeFlatten(t.Params.List)
var results []ast.Expr
if t.Results != nil {
results = typeFlatten(t.Results.List)
}
// Do the =s (if any) all match?
if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") {
return
}
// Everything must match.
if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") {
expectFmt := id.Name + "(" + argjoin(expect.args) + ")"
if len(expect.results) == 1 {
expectFmt += " " + argjoin(expect.results)
} else if len(expect.results) > 1 {
expectFmt += " (" + argjoin(expect.results) + ")"
}
f.b.Reset()
if err := printer.Fprint(&f.b, f.fset, t); err != nil {
fmt.Fprintf(&f.b, "<%s>", err)
}
actual := f.b.String()
actual = strings.TrimPrefix(actual, "func")
actual = id.Name + actual
f.Badf(id.Pos(), "method %s should have signature %s", actual, expectFmt)
}
}
func argjoin(x []string) string {
y := make([]string, len(x))
for i, s := range x {
if s[0] == '=' {
s = s[1:]
}
y[i] = s
}
return strings.Join(y, ", ")
}
// Turn parameter list into slice of types
// (in the ast, types are Exprs).
// Have to handle f(int, bool) and f(x, y, z int)
// so not a simple 1-to-1 conversion.
func typeFlatten(l []*ast.Field) []ast.Expr {
var t []ast.Expr
for _, f := range l {
if len(f.Names) == 0 {
t = append(t, f.Type)
continue
}
for _ = range f.Names {
t = append(t, f.Type)
}
}
return t
}
// Does each type in expect with the given prefix match the corresponding type in actual?
func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bool {
for i, x := range expect {
if !strings.HasPrefix(x, prefix) {
continue
}
if i >= len(actual) {
return false
}
if !f.matchParamType(x, actual[i]) {
return false
}
}
if prefix == "" && len(actual) > len(expect) {
return false
}
return true
}
// Does this one type match?
func (f *File) matchParamType(expect string, actual ast.Expr) bool {
if strings.HasPrefix(expect, "=") {
expect = expect[1:]
}
// Strip package name if we're in that package.
if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' {
expect = expect[n+1:]
}
// Overkill but easy.
f.b.Reset()
printer.Fprint(&f.b, f.fset, actual)
return f.b.String() == expect
}
This diff is collapsed.
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
This file contains the code to check range loop variables bound inside function
literals that are deferred or launched in new goroutines. We only check
instances where the defer or go statement is the last statement in the loop
body, as otherwise we would need whole program analysis.
For example:
for i, v := range s {
go func() {
println(i, v) // not what you might expect
}()
}
See: http://golang.org/doc/go_faq.html#closures_and_goroutines
*/
package main
import "go/ast"
// checkRangeLoop walks the body of the provided range statement, checking if
// its index or value variables are used unsafely inside goroutines or deferred
// function literals.
func checkRangeLoop(f *File, n *ast.RangeStmt) {
if !vet("rangeloops") {
return
}
key, _ := n.Key.(*ast.Ident)
val, _ := n.Value.(*ast.Ident)
if key == nil && val == nil {
return
}
sl := n.Body.List
if len(sl) == 0 {
return
}
var last *ast.CallExpr
switch s := sl[len(sl)-1].(type) {
case *ast.GoStmt:
last = s.Call
case *ast.DeferStmt:
last = s.Call
default:
return
}
lit, ok := last.Fun.(*ast.FuncLit)
if !ok {
return
}
ast.Inspect(lit.Body, func(n ast.Node) bool {
id, ok := n.(*ast.Ident)
if !ok || id.Obj == nil {
return true
}
if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj {
f.Warn(id.Pos(), "range variable", id.Name, "enclosed by function")
}
return true
})
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains the test for canonical struct tags.
package main
import (
"go/ast"
"reflect"
"strconv"
)
// checkField checks a struct field tag.
func (f *File) checkCanonicalFieldTag(field *ast.Field) {
if !vet("structtags") {
return
}
if field.Tag == nil {
return
}
tag, err := strconv.Unquote(field.Tag.Value)
if err != nil {
f.Badf(field.Pos(), "unable to read struct tag %s", field.Tag.Value)
return
}
// Check tag for validity by appending
// new key:value to end and checking that
// the tag parsing code can find it.
if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" {
f.Badf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value)
return
}
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains the test for untagged struct literals.
package main
import (
"flag"
"go/ast"
"strings"
)
var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only")
// checkUntaggedLiteral checks if a composite literal is a struct literal with
// untagged fields.
func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) {
if !vet("composites") {
return
}
typ := c.Type
for {
if typ1, ok := c.Type.(*ast.ParenExpr); ok {
typ = typ1
continue
}
break
}
switch typ.(type) {
case *ast.ArrayType:
return
case *ast.MapType:
return
case *ast.StructType:
return // a literal struct type does not need to use tags
case *ast.Ident:
// A simple type name like t or T does not need tags either,
// since it is almost certainly declared in the current package.
// (The exception is names being used via import . "pkg", but
// those are already breaking the Go 1 compatibility promise,
// so not reporting potential additional breakage seems okay.)
return
}
// Otherwise the type is a selector like pkg.Name.
// We only care if pkg.Name is a struct, not if it's a map, array, or slice.
isStruct, typeString := f.pkg.isStruct(c)
if !isStruct {
return
}
if typeString == "" { // isStruct doesn't know
typeString = f.gofmt(typ)
}
// It's a struct, or we can't tell it's not a struct because we don't have types.
// Check if the CompositeLit contains an untagged field.
allKeyValue := true
for _, e := range c.Elts {
if _, ok := e.(*ast.KeyValueExpr); !ok {
allKeyValue = false
break
}
}
if allKeyValue {
return
}
// Check that the CompositeLit's type has the form pkg.Typ.
s, ok := c.Type.(*ast.SelectorExpr)
if !ok {
return
}
pkg, ok := s.X.(*ast.Ident)
if !ok {
return
}
// Convert the package name to an import path, and compare to a whitelist.
path := pkgPath(f, pkg.Name)
if path == "" {
f.Badf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name)
return
}
typeName := path + "." + s.Sel.Name
if *compositeWhiteList && untaggedLiteralWhitelist[typeName] {
return
}
f.Warn(c.Pos(), typeString+" composite literal uses untagged fields")
}
// pkgPath returns the import path "image/png" for the package name "png".
//
// This is based purely on syntax and convention, and not on the imported
// package's contents. It will be incorrect if a package name differs from the
// leaf element of the import path, or if the package was a dot import.
func pkgPath(f *File, pkgName string) (path string) {
for _, x := range f.file.Imports {
s := strings.Trim(x.Path.Value, `"`)
if x.Name != nil {
// Catch `import pkgName "foo/bar"`.
if x.Name.Name == pkgName {
return s
}
} else {
// Catch `import "pkgName"` or `import "foo/bar/pkgName"`.
if s == pkgName || strings.HasSuffix(s, "/"+pkgName) {
return s
}
}
}
return ""
}
var untaggedLiteralWhitelist = map[string]bool{
/*
These types are actually slices. Syntactically, we cannot tell
whether the Typ in pkg.Typ{1, 2, 3} is a slice or a struct, so we
whitelist all the standard package library's exported slice types.
find $GOROOT/src/pkg -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \
grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/pkg/,,' | \
sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \
sort | awk '{ print "\"" $0 "\": true," }'
*/
"crypto/x509/pkix.RDNSequence": true,
"crypto/x509/pkix.RelativeDistinguishedNameSET": true,
"database/sql.RawBytes": true,
"debug/macho.LoadBytes": true,
"encoding/asn1.ObjectIdentifier": true,
"encoding/asn1.RawContent": true,
"encoding/json.RawMessage": true,
"encoding/xml.CharData": true,
"encoding/xml.Comment": true,
"encoding/xml.Directive": true,
"go/scanner.ErrorList": true,
"image/color.Palette": true,
"net.HardwareAddr": true,
"net.IP": true,
"net.IPMask": true,
"sort.Float64Slice": true,
"sort.IntSlice": true,
"sort.StringSlice": true,
"unicode.SpecialCase": true,
// These image and image/color struct types are frozen. We will never add fields to them.
"image/color.Alpha16": true,
"image/color.Alpha": true,
"image/color.Gray16": true,
"image/color.Gray": true,
"image/color.NRGBA64": true,
"image/color.NRGBA": true,
"image/color.RGBA64": true,
"image/color.RGBA": true,
"image/color.YCbCr": true,
"image.Point": true,
"image.Rectangle": true,
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
// This file contains declarations to test the assembly in test_asm.s.
package main
func arg1(x int8, y uint8)
func arg2(x int16, y uint16)
func arg4(x int32, y uint32)
func arg8(x int64, y uint64)
func argint(x int, y uint)
func argptr(x *byte, y *byte, c chan int, m map[int]int, f func())
func argstring(x, y string)
func argslice(x, y []string)
func argiface(x interface{}, y interface {
m()
})
func returnint() int
func returnbyte(x int) byte
func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte)
This diff is collapsed.
This diff is collapsed.
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build arm
// +build vet_test
TEXT ·arg1(SB),0,$0-2
MOVB x+0(FP), AX
MOVB y+1(FP), BX
MOVH x+0(FP), AX // ERROR "\[arm\] invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
MOVH y+1(FP), AX // ERROR "invalid MOVH of y\+1\(FP\); uint8 is 1-byte value"
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
MOVW y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
MOVB x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
MOVB y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
RET
TEXT ·arg2(SB),0,$0-4
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
MOVB y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
MOVH x+0(FP), AX
MOVH y+2(FP), BX
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int16 is 2-byte value"
MOVW y+2(FP), AX // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
MOVH x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
MOVH y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
RET
TEXT ·arg4(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
MOVW x+0(FP), AX
MOVW y+4(FP), AX
MOVW x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
MOVW y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
RET
TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
MOVB y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
MOVW x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
MOVW x_lo+0(FP), AX
MOVW x_hi+4(FP), AX
MOVW y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
MOVW y_lo+8(FP), AX
MOVW y_hi+12(FP), AX
MOVQ x+0(FP), AX
MOVQ y+8(FP), AX
MOVQ x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
RET
TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value"
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value"
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); int is 4-byte value"
MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); uint is 4-byte value"
MOVW x+0(FP), AX
MOVW y+4(FP), AX
MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
RET
TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20"
MOVB x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value"
MOVB y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value"
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 4-byte value"
MOVH y+4(FP), AX // ERROR "invalid MOVH of y\+4\(FP\); \*byte is 4-byte value"
MOVW x+0(FP), AX
MOVW y+4(FP), AX
MOVQ x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
MOVQ y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
MOVH c+8(FP), AX // ERROR "invalid MOVH of c\+8\(FP\); chan int is 4-byte value"
MOVH m+12(FP), AX // ERROR "invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value"
MOVH f+16(FP), AX // ERROR "invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value"
RET
TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16"
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); string base is 4-byte value"
MOVW x+0(FP), AX
MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 4-byte value"
MOVW x_base+0(FP), AX
MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); string len is 4-byte value"
MOVW x_len+4(FP), AX
MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)"
MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)"
RET
TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); slice base is 4-byte value"
MOVW x+0(FP), AX
MOVH x_base+0(FP), AX // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value"
MOVW x_base+0(FP), AX
MOVH x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVW x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVQ x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
MOVH x_len+4(FP), AX // ERROR "invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value"
MOVW x_len+4(FP), AX
MOVH x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
MOVW x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
MOVQ x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
MOVH x_cap+8(FP), AX // ERROR "invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value"
MOVW x_cap+8(FP), AX
MOVQ y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)"
MOVQ y_len+4(FP), AX // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)"
MOVQ y_cap+8(FP), AX // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)"
RET
TEXT ·argiface(SB),0,$0-16
MOVH x+0(FP), AX // ERROR "invalid MOVH of x\+0\(FP\); interface type is 4-byte value"
MOVW x+0(FP), AX
MOVH x_type+0(FP), AX // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value"
MOVW x_type+0(FP), AX
MOVQ x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
MOVQ x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
MOVH x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
MOVW x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
MOVQ x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
MOVH x_data+4(FP), AX // ERROR "invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value"
MOVW x_data+4(FP), AX
MOVH y+8(FP), AX // ERROR "invalid MOVH of y\+8\(FP\); interface itable is 4-byte value"
MOVW y+8(FP), AX
MOVH y_itable+8(FP), AX // ERROR "invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value"
MOVW y_itable+8(FP), AX
MOVQ y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)"
MOVH y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
MOVW y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
MOVQ y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
MOVH y_data+12(FP), AX // ERROR "invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value"
MOVW y_data+12(FP), AX
RET
TEXT ·returnint(SB),0,$0-4
MOVB AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 4-byte value"
MOVH AX, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 4-byte value"
MOVW AX, ret+0(FP)
MOVQ AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
MOVQ AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
RET
TEXT ·returnbyte(SB),0,$0-5
MOVW x+0(FP), AX
MOVB AX, ret+4(FP)
MOVH AX, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value"
MOVW AX, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
MOVB AX, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
RET
TEXT ·returnnamed(SB),0,$0-21
MOVB x+0(FP), AX
MOVW AX, r1+4(FP)
MOVH AX, r2+8(FP)
MOVW AX, r3+12(FP)
MOVW AX, r3_base+12(FP)
MOVW AX, r3_len+16(FP)
MOVB AX, r4+20(FP)
MOVB AX, r1+4(FP) // ERROR "invalid MOVB of r1\+4\(FP\); int is 4-byte value"
RET
// 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.
// +build vet_test
package main
type ST struct {
x int
}
func (s *ST) SetX(x int) {
// Accidental self-assignment; it should be "s.x = x"
x = x // ERROR "self-assignment of x to x"
// Another mistake
s.x = s.x // ERROR "self-assignment of s.x to s.x"
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build vet_test
// This file contains tests for the atomic checker.
package main
import (
"sync/atomic"
)
type Counter uint64
func AtomicTests() {
x := uint64(1)
x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
_, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value"
y := &x
*y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value"
var su struct{ Counter uint64 }
su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value"
z1 := atomic.AddUint64(&su.Counter, 1)
_ = z1 // Avoid err "z declared and not used"
var sp struct{ Counter *uint64 }
*sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value"
z2 := atomic.AddUint64(sp.Counter, 1)
_ = z2 // Avoid err "z declared and not used"
au := []uint64{10, 20}
au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value"
au[1] = atomic.AddUint64(&au[0], 1)
ap := []*uint64{&au[0], &au[1]}
*ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value"
*ap[1] = atomic.AddUint64(ap[0], 1)
}
// 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.
// +build vet_test
// +builder // ERROR "possible malformed \+build comment"
// +build !ignore
package main
// +build toolate // ERROR "build comment appears too late in file"
var _ = 3
// This file contains misplaced or malformed build constraints.
// The Go tool will skip it, because the constraints are invalid.
// It serves only to test the tag checker during make test.
// Mention +build // ERROR "possible malformed \+build comment"
// +build !!bang // ERROR "invalid double negative in build constraint"
// +build @#$ // ERROR "invalid non-alphanumeric build constraint"
// +build toolate // ERROR "build comment appears too late in file"
package bad
// This is package 'bad' rather than 'main' so the erroneous build
// tag doesn't end up looking like a package doc for the vet command
// when examined by godoc.
This diff is collapsed.
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains tests for the canonical method checker.
// +build vet_test
// This file contains the code to check canonical methods.
package main
import (
"fmt"
)
type MethodTest int
func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan"
}
type MethodTestInterface interface {
ReadByte() byte // ERROR "should have signature ReadByte"
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build vet_test
// This file contains tests for the printf checker.
package main
import (
"fmt"
"unsafe" // just for test case printing unsafe.Pointer
)
func UnsafePointerPrintfTest() {
var up unsafe.Pointer
fmt.Printf("%p, %x %X", up, up, up)
}
// Error methods that do not satisfy the Error interface and should be checked.
type errorTest1 int
func (errorTest1) Error(...interface{}) string {
return "hi"
}
type errorTest2 int // Analogous to testing's *T type.
func (errorTest2) Error(...interface{}) {
}
type errorTest3 int
func (errorTest3) Error() { // No return value.
}
type errorTest4 int
func (errorTest4) Error() int { // Different return type.
return 3
}
type errorTest5 int
func (errorTest5) error() { // niladic; don't complain if no args (was bug)
}
// This function never executes, but it serves as a simple test for the program.
// Test with make test.
func PrintfTests() {
var b bool
var i int
var r rune
var s string
var x float64
var p *int
// Some good format/argtypes
fmt.Printf("")
fmt.Printf("%b %b", 3, i)
fmt.Printf("%c %c %c %c", 3, i, 'x', r)
fmt.Printf("%d %d", 3, i)
fmt.Printf("%e %e", 3e9, x)
fmt.Printf("%E %E", 3e9, x)
fmt.Printf("%f %f", 3e9, x)
fmt.Printf("%F %F", 3e9, x)
fmt.Printf("%g %g", 3e9, x)
fmt.Printf("%G %G", 3e9, x)
fmt.Printf("%o %o", 3, i)
fmt.Printf("%p %p", p, nil)
fmt.Printf("%q %q %q %q", 3, i, 'x', r)
fmt.Printf("%s %s", "hi", s)
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)
// Some bad format/argTypes
fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type"
fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type"
fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type"
fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type"
fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type"
fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type"
fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type"
fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type"
fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type"
fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type"
fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type"
fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type"
fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type"
fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type"
fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type"
fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type"
fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type"
fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
// TODO
fmt.Println() // not an error
fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call"
fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call"
fmt.Printf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Printf call"
fmt.Printf("%s%%%d", "hi", 3) // correct
fmt.Printf("%08s", "woo") // correct
fmt.Printf("% 8s", "woo") // correct
fmt.Printf("%.*d", 3, 3) // correct
fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args for format in Printf call"
fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int"
fmt.Printf("%.*d", i, 3) // correct
fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int"
fmt.Printf("%q %q", multi()...) // ok
fmt.Printf("%#q", `blah`) // ok
printf("now is the time", "buddy") // ERROR "no formatting directive"
Printf("now is the time", "buddy") // ERROR "no formatting directive"
Printf("hi") // ok
const format = "%s %s\n"
Printf(format, "hi", "there")
Printf(format, "hi") // ERROR "wrong number of args for format in Printf call"
f := new(File)
f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call"
f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call"
f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb"
f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag"
// Something that satisfies the error interface.
var e error
fmt.Println(e.Error()) // ok
// Something that looks like an error interface but isn't, such as the (*T).Error method
// in the testing package.
var et1 errorTest1
fmt.Println(et1.Error()) // ERROR "no args in Error call"
fmt.Println(et1.Error("hi")) // ok
fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call"
var et2 errorTest2
et2.Error() // ERROR "no args in Error call"
et2.Error("hi") // ok, not an error method.
et2.Error("%d", 3) // ERROR "possible formatting directive in Error call"
var et3 errorTest3
et3.Error() // ok, not an error method.
var et4 errorTest4
et4.Error() // ok, not an error method.
var et5 errorTest5
et5.error() // ok, not an error method.
}
// printf is used by the test.
func printf(format string, args ...interface{}) {
panic("don't call - testing only")
}
// multi is used by the test.
func multi() []interface{} {
panic("don't call - testing only")
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains tests for the rangeloop checker.
// +build vet_test
package main
func RangeLoopTests() {
var s []int
for i, v := range s {
go func() {
println(i) // ERROR "range variable i enclosed by function"
println(v) // ERROR "range variable v enclosed by function"
}()
}
for i, v := range s {
defer func() {
println(i) // ERROR "range variable i enclosed by function"
println(v) // ERROR "range variable v enclosed by function"
}()
}
for i := range s {
go func() {
println(i) // ERROR "range variable i enclosed by function"
}()
}
for _, v := range s {
go func() {
println(v) // ERROR "range variable v enclosed by function"
}()
}
for i, v := range s {
go func() {
println(i, v)
}()
println("unfortunately, we don't catch the error above because of this statement")
}
for i, v := range s {
go func(i, v int) {
println(i, v)
}(i, v)
}
for i, v := range s {
i, v := i, v
go func() {
println(i, v)
}()
}
// If the key of the range statement is not an identifier
// the code should not panic (it used to).
var x [2]int
var f int
for x[0], f = range s {
go func() {
_ = f // ERROR "range variable f enclosed by function"
}()
}
}
// 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 structtag checker.
// +build vet_test
// This file contains the test for canonical struct tags.
package main
type StructTagTest struct {
X int "hello" // ERROR "not compatible with reflect.StructTag.Get"
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains tests for the untagged struct literal checker.
// +build vet_test
// This file contains the test for untagged struct literals.
package main
import (
"flag"
"go/scanner"
)
var Okay1 = []string{
"Name",
"Usage",
"DefValue",
}
var Okay2 = map[string]bool{
"Name": true,
"Usage": true,
"DefValue": true,
}
var Okay3 = struct {
X string
Y string
Z string
}{
"Name",
"Usage",
"DefValue",
}
type MyStruct struct {
X string
Y string
Z string
}
var Okay4 = MyStruct{
"Name",
"Usage",
"DefValue",
}
// Testing is awkward because we need to reference things from a separate package
// to trigger the warnings.
var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "untagged fields"
"Name",
"Usage",
nil, // Value
"DefValue",
}
// Used to test the check for slices and arrays: If that test is disabled and
// vet is run with --compositewhitelist=false, this line triggers an error.
// Clumsy but sufficient.
var scannerErrorListTest = scanner.ErrorList{nil, nil}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build gotypes
// This file contains the pieces of the tool that require the go/types package.
// To compile this file, you must first run
// $ go get code.google.com/p/go.exp/go/types
package main
import (
"go/ast"
"go/token"
"code.google.com/p/go.exp/go/exact"
"code.google.com/p/go.exp/go/types"
)
// Type is equivalent to types.Type. Repeating it here allows us to avoid
// having main depend on the go/types package.
type Type interface {
String() string
}
// ExactValue is equivalent to exact.Value. Repeating it here allows us to
// avoid having main depend on the go/exact package.
type ExactValue interface {
Kind() exact.Kind
String() string
}
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
pkg.types = make(map[ast.Expr]Type)
pkg.values = make(map[ast.Expr]ExactValue)
exprFn := func(x ast.Expr, typ types.Type, val exact.Value) {
pkg.types[x] = typ
if val != nil {
pkg.values[x] = val
}
}
// By providing the Context with our own error function, it will continue
// past the first error. There is no need for that function to do anything.
context := types.Context{
Expr: exprFn,
Error: func(error) {},
}
_, err := context.Check(fs, astFiles)
return err
}
// isStruct reports whether the composite literal c is a struct.
// If it is not (probably a struct), it returns a printable form of the type.
func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
// Check that the CompositeLit's type is a slice or array (which needs no tag), if possible.
typ := pkg.types[c]
// If it's a named type, pull out the underlying type.
actual := typ
if namedType, ok := typ.(*types.NamedType); ok {
actual = namedType.Underlying
}
if actual == nil {
// No type information available. Assume true, so we do the check.
return true, ""
}
switch actual.(type) {
case *types.Struct:
return true, typ.String()
default:
return false, ""
}
}
func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
// TODO: for now, we can only test builtin types and untyped constants.
typ := f.pkg.types[arg]
if typ == nil {
return true
}
basic, ok := typ.(*types.Basic)
if !ok {
return true
}
switch basic.Kind {
case types.Bool:
return t&argBool != 0
case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
fallthrough
case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
return t&argInt != 0
case types.Float32, types.Float64, types.Complex64, types.Complex128:
return t&argFloat != 0
case types.String:
return t&argString != 0
case types.UnsafePointer:
return t&(argPointer|argInt) != 0
case types.UntypedBool:
return t&argBool != 0
case types.UntypedComplex:
return t&argFloat != 0
case types.UntypedFloat:
// If it's integral, we can use an int format.
switch f.pkg.values[arg].Kind() {
case exact.Int:
return t&(argInt|argFloat) != 0
}
return t&argFloat != 0
case types.UntypedInt:
return t&argInt != 0
case types.UntypedRune:
return t&(argInt|argRune) != 0
case types.UntypedString:
return t&argString != 0
case types.UntypedNil:
return t&argPointer != 0 // TODO?
case types.Invalid:
if *verbose {
f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg)
}
return true // Probably a type check problem.
}
return false
}
// numArgsInSignature tells how many formal arguments the function type
// being called has.
func (f *File) numArgsInSignature(call *ast.CallExpr) int {
// Check the type of the function or method declaration
typ := f.pkg.types[call.Fun]
if typ == nil {
return 0
}
// The type must be a signature, but be sure for safety.
sig, ok := typ.(*types.Signature)
if !ok {
return 0
}
return len(sig.Params)
}
// isErrorMethodCall reports whether the call is of a method with signature
// func Error() string
// where "string" is the universe's string type. We know the method is called "Error".
func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
// Is it a selector expression? Otherwise it's a function call, not a method call.
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return false
}
// The package is type-checked, so if there are no arguments, we're done.
if len(call.Args) > 0 {
return false
}
// Check the type of the method declaration
typ := f.pkg.types[sel]
if typ == nil {
return false
}
// The type must be a signature, but be sure for safety.
sig, ok := typ.(*types.Signature)
if !ok {
return false
}
// There must be a receiver for it to be a method call. Otherwise it is
// a function, not something that satisfies the error interface.
if sig.Recv == nil {
return false
}
// There must be no arguments. Already verified by type checking, but be thorough.
if len(sig.Params) > 0 {
return false
}
// Finally the real questions.
// There must be one result.
if len(sig.Results) != 1 {
return false
}
// It must have return type "string" from the universe.
result := sig.Results[0].Type
if types.IsIdentical(result, types.Typ[types.String]) {
return true
}
return false
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !gotypes
// This file contains stubs for the pieces of the tool that require the go/types package,
// to be used if go/types is not available.
package main
import (
"go/ast"
"go/token"
)
// Type is equivalent to go/types.Type. Repeating it here allows us to avoid
// having main depend on the go/types package.
type Type interface {
String() string
}
// ExactValue is a stub for exact.Value. Stubbing it here allows us to
// avoid having main depend on the go/exact package.
type ExactValue interface {
}
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
return nil
}
func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
return true, "" // Assume true, so we do the check.
}
func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
return true // We can't tell without types.
}
func (f *File) numArgsInSignature(call *ast.CallExpr) int {
return 0 // We don't know.
}
func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
// Is it a selector expression? Otherwise it's a function call, not a method call.
if _, ok := call.Fun.(*ast.SelectorExpr); !ok {
return false
}
return true // Best guess we can make without types.
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment