Commit fc768da8 authored by Russ Cox's avatar Russ Cox

cmd/vet: tighten printf format error messages

Every time I see an error that begins `missing argument for Fprintf("%s")`
my mental type-checker goes off, since obviously "%s" is not a valid first
argument to Fprintf. Writing Printf("%s") to report an error in Printf("hello %s")
is almost as confusing.

This CL rewords the errors reported by vet's printf check to be more
consistent with each other, avoid placing context like "in printf call"
in the middle of the message, and to avoid the imprecisions above by
not quoting the format string at all.

Before:

	bad.go:9: no formatting directive in Printf call
	bad.go:10: missing argument for Printf("%s"): format reads arg 1, have only 0 args
	bad.go:11: wrong number of args for format in Printf call: 1 needed but 2 args
	bad.go:12: bad syntax for printf argument index: [1]
	bad.go:13: index value [0] for Printf("%[0]s"); indexes start at 1
	bad.go:14: missing argument for Printf("%[2]s"): format reads arg 2, have only 1 args
	bad.go:15: bad syntax for printf argument index: [abc]
	bad.go:16: unrecognized printf verb 'z'
	bad.go:17: arg "hello" for * in printf format not of type int
	bad.go:18: arg fmt.Sprint in printf call is a function value, not a function call
	bad.go:19: arg fmt.Sprint in Print call is a function value, not a function call
	bad.go:20: arg "world" for printf verb %d of wrong type: string
	bad.go:21: missing argument for Printf("%q"): format reads arg 2, have only 1 args
	bad.go:22: first argument to Print is os.Stderr
	bad.go:23: Println call ends with newline
	bad.go:32: arg r in Sprint call causes recursive call to String method
	bad.go:34: arg r for printf causes recursive call to String method

After:

	bad.go:9: Printf call has arguments but no formatting directives
	bad.go:10: Printf format %s reads arg #1, but have only 0 args
	bad.go:11: Printf call needs 1 args but has 2 args
	bad.go:12: Printf format %[1 is missing closing ]
	bad.go:13: Printf format has invalid argument index [0]
	bad.go:14: Printf format has invalid argument index [2]
	bad.go:15: Printf format has invalid argument index [abc]
	bad.go:16: Printf format %.234z has unknown verb z
	bad.go:17: Printf format %.*s uses non-int "hello" as argument of *
	bad.go:18: Printf format %s arg fmt.Sprint is a func value, not called
	bad.go:19: Print arg fmt.Sprint is a func value, not called
	bad.go:20: Printf format %d has arg "world" of wrong type string
	bad.go:21: Printf format %q reads arg #2, but have only 1 args
	bad.go:22: Print does not take io.Writer but has first arg os.Stderr
	bad.go:23: Println args end with redundant newline
	bad.go:32: Sprint arg r causes recursive call to String method
	bad.go:34: Sprintf format %s with arg r causes recursive String method call

Change-Id: I5719f0fb9f2cd84df8ad4c7754ab9b79c691b060
Reviewed-on: https://go-review.googlesource.com/74352
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRob Pike <r@golang.org>
parent 9aa6f80e
...@@ -343,7 +343,7 @@ func (t *tester) registerTests() { ...@@ -343,7 +343,7 @@ func (t *tester) registerTests() {
osarch := k osarch := k
t.tests = append(t.tests, distTest{ t.tests = append(t.tests, distTest{
name: "vet/" + osarch, name: "vet/" + osarch,
heading: "go vet std cmd", heading: "cmd/vet/all",
fn: func(dt *distTest) error { fn: func(dt *distTest) error {
t.addCmd(dt, "src/cmd/vet/all", "go", "run", "main.go", "-p="+osarch) t.addCmd(dt, "src/cmd/vet/all", "go", "run", "main.go", "-p="+osarch)
return nil return nil
......
...@@ -2909,7 +2909,7 @@ func TestGoVetWithExternalTests(t *testing.T) { ...@@ -2909,7 +2909,7 @@ func TestGoVetWithExternalTests(t *testing.T) {
tg.run("install", "cmd/vet") tg.run("install", "cmd/vet")
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.runFail("vet", "vetpkg") tg.runFail("vet", "vetpkg")
tg.grepBoth("missing argument for Printf", "go vet vetpkg did not find missing argument for Printf") tg.grepBoth("Printf", "go vet vetpkg did not find missing argument for Printf")
} }
func TestGoVetWithTags(t *testing.T) { func TestGoVetWithTags(t *testing.T) {
...@@ -2919,7 +2919,7 @@ func TestGoVetWithTags(t *testing.T) { ...@@ -2919,7 +2919,7 @@ func TestGoVetWithTags(t *testing.T) {
tg.run("install", "cmd/vet") tg.run("install", "cmd/vet")
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.runFail("vet", "-tags", "tagtest", "vetpkg") tg.runFail("vet", "-tags", "tagtest", "vetpkg")
tg.grepBoth(`c\.go.*wrong number of args for format`, "go vet vetpkg did not run scan tagged file") tg.grepBoth(`c\.go.*Printf`, "go vet vetpkg did not run scan tagged file")
} }
func TestGoVetWithFlagsOn(t *testing.T) { func TestGoVetWithFlagsOn(t *testing.T) {
...@@ -2929,7 +2929,7 @@ func TestGoVetWithFlagsOn(t *testing.T) { ...@@ -2929,7 +2929,7 @@ func TestGoVetWithFlagsOn(t *testing.T) {
tg.run("install", "cmd/vet") tg.run("install", "cmd/vet")
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.runFail("vet", "-printf", "vetpkg") tg.runFail("vet", "-printf", "vetpkg")
tg.grepBoth("missing argument for Printf", "go vet -printf vetpkg did not find missing argument for Printf") tg.grepBoth("Printf", "go vet -printf vetpkg did not find missing argument for Printf")
} }
func TestGoVetWithFlagsOff(t *testing.T) { func TestGoVetWithFlagsOff(t *testing.T) {
......
...@@ -3,7 +3,5 @@ ...@@ -3,7 +3,5 @@
// Issue 18609 // Issue 18609
crypto/x509/root_windows.go: unreachable code crypto/x509/root_windows.go: unreachable code
path/filepath/path_windows_test.go: possible formatting directive in Fatal call
runtime/sys_windows_ARCHSUFF.s: [GOARCH] sigtramp: function sigtramp missing Go declaration runtime/sys_windows_ARCHSUFF.s: [GOARCH] sigtramp: function sigtramp missing Go declaration
runtime/sys_windows_ARCHSUFF.s: [GOARCH] onosstack: unknown variable usec; offset 0 is fn+0(FP) runtime/sys_windows_ARCHSUFF.s: [GOARCH] onosstack: unknown variable usec; offset 0 is fn+0(FP)
...@@ -9,10 +9,12 @@ package main ...@@ -9,10 +9,12 @@ package main
import ( import (
"bytes" "bytes"
"flag" "flag"
"fmt"
"go/ast" "go/ast"
"go/constant" "go/constant"
"go/token" "go/token"
"go/types" "go/types"
"regexp"
"strconv" "strconv"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
...@@ -88,8 +90,8 @@ var isPrint = map[string]bool{ ...@@ -88,8 +90,8 @@ var isPrint = map[string]bool{
// The first string literal or string constant is assumed to be a format string // The first string literal or string constant is assumed to be a format string
// if the call's signature cannot be determined. // if the call's signature cannot be determined.
// //
// If it cannot find any format string parameter, it returns ("", -1). // If it cannot find any format string parameter, it returns ("", -1).
func formatString(f *File, call *ast.CallExpr) (string, int) { func formatString(f *File, call *ast.CallExpr) (format string, idx int) {
typ := f.pkg.types[call.Fun].Type typ := f.pkg.types[call.Fun].Type
if typ != nil { if typ != nil {
if sig, ok := typ.(*types.Signature); ok { if sig, ok := typ.(*types.Signature); ok {
...@@ -228,7 +230,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) { ...@@ -228,7 +230,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) {
firstArg := idx + 1 // Arguments are immediately after format string. firstArg := idx + 1 // Arguments are immediately after format string.
if !strings.Contains(format, "%") { if !strings.Contains(format, "%") {
if len(call.Args) > firstArg { if len(call.Args) > firstArg {
f.Badf(call.Pos(), "no formatting directive in %s call", name) f.Badf(call.Pos(), "%s call has arguments but no formatting directives", name)
} }
return return
} }
...@@ -266,7 +268,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) { ...@@ -266,7 +268,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) {
if maxArgNum != len(call.Args) { if maxArgNum != len(call.Args) {
expect := maxArgNum - firstArg expect := maxArgNum - firstArg
numArgs := len(call.Args) - firstArg numArgs := len(call.Args) - firstArg
f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) f.Badf(call.Pos(), "%s call needs %v but has %v", name, count(expect, "arg"), count(numArgs, "arg"))
} }
} }
...@@ -302,17 +304,18 @@ func (s *formatState) parseIndex() bool { ...@@ -302,17 +304,18 @@ func (s *formatState) parseIndex() bool {
s.nbytes++ // skip '[' s.nbytes++ // skip '['
start := s.nbytes start := s.nbytes
s.scanNum() s.scanNum()
ok := true
if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' { if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' {
end := strings.Index(s.format, "]") ok = false
if end < 0 { s.nbytes = strings.Index(s.format, "]")
end = len(s.format) if s.nbytes < 0 {
s.file.Badf(s.call.Pos(), "%s format %s is missing closing ]", s.name, s.format)
return false
} }
s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: [%s]", s.format[start:end])
return false
} }
arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32) arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
if err != nil { if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) {
s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: %s", err) s.file.Badf(s.call.Pos(), "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes])
return false return false
} }
s.nbytes++ // skip ']' s.nbytes++ // skip ']'
...@@ -388,7 +391,7 @@ func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg ...@@ -388,7 +391,7 @@ func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg
return nil return nil
} }
if state.nbytes == len(state.format) { if state.nbytes == len(state.format) {
f.Badf(call.Pos(), "missing verb at end of format string in %s call", name) f.Badf(call.Pos(), "%s format %s is missing verb at end of string", name, state.format)
return nil return nil
} }
verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:])
...@@ -481,12 +484,12 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { ...@@ -481,12 +484,12 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
} }
if !found && !formatter { if !found && !formatter {
f.Badf(call.Pos(), "unrecognized printf verb %q", state.verb) f.Badf(call.Pos(), "%s format %s has unknown verb %c", state.name, state.format, state.verb)
return false return false
} }
for _, flag := range state.flags { for _, flag := range state.flags {
if !strings.ContainsRune(v.flags, rune(flag)) { if !strings.ContainsRune(v.flags, rune(flag)) {
f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", state.verb, flag) f.Badf(call.Pos(), "%s format %s has unrecognized flag %c", state.name, state.format, flag)
return false return false
} }
} }
...@@ -504,7 +507,7 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { ...@@ -504,7 +507,7 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
} }
arg := call.Args[argNum] arg := call.Args[argNum]
if !f.matchArgType(argInt, nil, arg) { if !f.matchArgType(argInt, nil, arg) {
f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(arg)) f.Badf(call.Pos(), "%s format %s uses non-int %s as argument of *", state.name, state.format, f.gofmt(arg))
return false return false
} }
} }
...@@ -517,7 +520,7 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { ...@@ -517,7 +520,7 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
} }
arg := call.Args[argNum] arg := call.Args[argNum]
if f.isFunctionValue(arg) && state.verb != 'p' && state.verb != 'T' { if f.isFunctionValue(arg) && state.verb != 'p' && state.verb != 'T' {
f.Badf(call.Pos(), "arg %s in printf call is a function value, not a function call", f.gofmt(arg)) f.Badf(call.Pos(), "%s format %s arg %s is a func value, not called", state.name, state.format, f.gofmt(arg))
return false return false
} }
if !f.matchArgType(v.typ, nil, arg) { if !f.matchArgType(v.typ, nil, arg) {
...@@ -525,11 +528,11 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { ...@@ -525,11 +528,11 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
if typ := f.pkg.types[arg].Type; typ != nil { if typ := f.pkg.types[arg].Type; typ != nil {
typeString = typ.String() typeString = typ.String()
} }
f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), state.verb, typeString) f.Badf(call.Pos(), "%s format %s has arg %s of wrong type %s", state.name, state.format, f.gofmt(arg), typeString)
return false return false
} }
if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && f.recursiveStringer(arg) { if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && f.recursiveStringer(arg) {
f.Badf(call.Pos(), "arg %s for printf causes recursive call to String method", f.gofmt(arg)) f.Badf(call.Pos(), "%s format %s with arg %s causes recursive String method call", state.name, state.format, f.gofmt(arg))
return false return false
} }
return true return true
...@@ -580,14 +583,10 @@ func (f *File) isFunctionValue(e ast.Expr) bool { ...@@ -580,14 +583,10 @@ func (f *File) isFunctionValue(e ast.Expr) bool {
// means we can't see it. // means we can't see it.
func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, state *formatState) bool { func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, state *formatState) bool {
argNum := state.argNums[formatArg] argNum := state.argNums[formatArg]
if argNum < 0 { if argNum <= 0 {
// Shouldn't happen, so catch it with prejudice. // Shouldn't happen, so catch it with prejudice.
panic("negative arg num") panic("negative arg num")
} }
if argNum == 0 {
f.Badf(call.Pos(), `index value [0] for %s("%s"); indexes start at 1`, state.name, state.format)
return false
}
if argNum < len(call.Args)-1 { if argNum < len(call.Args)-1 {
return true // Always OK. return true // Always OK.
} }
...@@ -600,10 +599,22 @@ func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, state *formatS ...@@ -600,10 +599,22 @@ func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, state *formatS
// There are bad indexes in the format or there are fewer arguments than the format needs. // There are bad indexes in the format or there are fewer arguments than the format needs.
// This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed.
f.Badf(call.Pos(), `missing argument for %s("%s"): format reads arg %d, have only %d args`, state.name, state.format, arg, len(call.Args)-state.firstArg) f.Badf(call.Pos(), "%s format %s reads arg #%d, but call has only %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg"))
return false return false
} }
// printFormatRE is the regexp we match and report as a possible format string
// in the first argument to unformatted prints like fmt.Print.
// We exclude the space flag, so that printing a string like "x % y" is not reported as a format.
var printFormatRE = regexp.MustCompile(`%` + flagsRE + numOptRE + `\.?` + numOptRE + indexOptRE + verbRE)
const (
flagsRE = `[+\-#]*`
indexOptRE = `(\[[0-9]+\])?`
numOptRE = `([0-9]+|` + indexOptRE + `\*)?`
verbRE = `[bcdefgopqstxEFGUX]`
)
// checkPrint checks a call to an unformatted print routine such as Println. // checkPrint checks a call to an unformatted print routine such as Println.
func (f *File) checkPrint(call *ast.CallExpr, name string) { func (f *File) checkPrint(call *ast.CallExpr, name string) {
firstArg := 0 firstArg := 0
...@@ -635,23 +646,26 @@ func (f *File) checkPrint(call *ast.CallExpr, name string) { ...@@ -635,23 +646,26 @@ func (f *File) checkPrint(call *ast.CallExpr, name string) {
} }
args = args[firstArg:] args = args[firstArg:]
// check for Println(os.Stderr, ...)
if firstArg == 0 { if firstArg == 0 {
if sel, ok := args[0].(*ast.SelectorExpr); ok { if sel, ok := call.Args[0].(*ast.SelectorExpr); ok {
if x, ok := sel.X.(*ast.Ident); ok { if x, ok := sel.X.(*ast.Ident); ok {
if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
f.Badf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) f.Badf(call.Pos(), "%s does not take io.Writer but has first arg %s", name, f.gofmt(call.Args[0]))
} }
} }
} }
} }
arg := args[0] arg := args[0]
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
// Ignore trailing % character in lit.Value. // Ignore trailing % character in lit.Value.
// The % in "abc 0.0%" couldn't be a formatting directive. // The % in "abc 0.0%" couldn't be a formatting directive.
s := strings.TrimSuffix(lit.Value, `%"`) s := strings.TrimSuffix(lit.Value, `%"`)
if strings.Contains(s, "%") { if strings.Contains(s, "%") {
f.Badf(call.Pos(), "possible formatting directive in %s call", name) m := printFormatRE.FindStringSubmatch(s)
if m != nil {
f.Badf(call.Pos(), "%s call has possible formatting directive %s", name, m[0])
}
} }
} }
if strings.HasSuffix(name, "ln") { if strings.HasSuffix(name, "ln") {
...@@ -659,16 +673,25 @@ func (f *File) checkPrint(call *ast.CallExpr, name string) { ...@@ -659,16 +673,25 @@ func (f *File) checkPrint(call *ast.CallExpr, name string) {
arg = args[len(args)-1] arg = args[len(args)-1]
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
if strings.HasSuffix(lit.Value, `\n"`) { if strings.HasSuffix(lit.Value, `\n"`) {
f.Badf(call.Pos(), "%s call ends with newline", name) f.Badf(call.Pos(), "%s args end with redundant newline", name)
} }
} }
} }
for _, arg := range args { for _, arg := range args {
if f.isFunctionValue(arg) { if f.isFunctionValue(arg) {
f.Badf(call.Pos(), "arg %s in %s call is a function value, not a function call", f.gofmt(arg), name) f.Badf(call.Pos(), "%s arg %s is a func value, not called", name, f.gofmt(arg))
} }
if f.recursiveStringer(arg) { if f.recursiveStringer(arg) {
f.Badf(call.Pos(), "arg %s in %s call causes recursive call to String method", f.gofmt(arg), name) f.Badf(call.Pos(), "%s arg %s causes recursive call to String method", name, f.gofmt(arg))
} }
} }
} }
// count(n, what) returns "1 what" or "N whats"
// (assuming the plural of what is whats).
func count(n int, what string) string {
if n == 1 {
return "1 " + what
}
return fmt.Sprintf("%d %ss", n, what)
}
...@@ -104,74 +104,74 @@ func PrintfTests() { ...@@ -104,74 +104,74 @@ func PrintfTests() {
fmt.Printf("%g", 1+2i) 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 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 // Some bad format/argTypes
fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type" fmt.Printf("%b", "hi") // ERROR "Printf format %b has arg \x22hi\x22 of wrong type string"
fmt.Printf("%t", c) // ERROR "arg c for printf verb %t of wrong type" fmt.Printf("%t", c) // ERROR "Printf format %t has arg c of wrong type complex64"
fmt.Printf("%t", 1+2i) // ERROR "arg 1 \+ 2i for printf verb %t of wrong type" fmt.Printf("%t", 1+2i) // ERROR "Printf format %t has arg 1 \+ 2i of wrong type complex128"
fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type" fmt.Printf("%c", 2.3) // ERROR "Printf format %c has arg 2.3 of wrong type float64"
fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type" fmt.Printf("%d", 2.3) // ERROR "Printf format %d has arg 2.3 of wrong type float64"
fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type" fmt.Printf("%e", "hi") // ERROR "Printf format %e has arg \x22hi\x22 of wrong type string"
fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type" fmt.Printf("%E", true) // ERROR "Printf format %E has arg true of wrong type bool"
fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type" fmt.Printf("%f", "hi") // ERROR "Printf format %f has arg \x22hi\x22 of wrong type string"
fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type" fmt.Printf("%F", 'x') // ERROR "Printf format %F has arg 'x' of wrong type rune"
fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type" fmt.Printf("%g", "hi") // ERROR "Printf format %g has arg \x22hi\x22 of wrong type string"
fmt.Printf("%g", imap) // ERROR "arg imap for printf verb %g of wrong type" fmt.Printf("%g", imap) // ERROR "Printf format %g has arg imap of wrong type map\[int\]int"
fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type" fmt.Printf("%G", i) // ERROR "Printf format %G has arg i of wrong type int"
fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type" fmt.Printf("%o", x) // ERROR "Printf format %o has arg x of wrong type float64"
fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type" fmt.Printf("%p", 23) // ERROR "Printf format %p has arg 23 of wrong type int"
fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type" fmt.Printf("%q", x) // ERROR "Printf format %q has arg x of wrong type float64"
fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type" fmt.Printf("%s", b) // ERROR "Printf format %s has arg b of wrong type bool"
fmt.Printf("%s", byte(65)) // ERROR "arg byte\(65\) for printf verb %s of wrong type" fmt.Printf("%s", byte(65)) // ERROR "Printf format %s has arg byte\(65\) of wrong type byte"
fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type" fmt.Printf("%t", 23) // ERROR "Printf format %t has arg 23 of wrong type int"
fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type" fmt.Printf("%U", x) // ERROR "Printf format %U has arg x of wrong type float64"
fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type" fmt.Printf("%x", nil) // ERROR "Printf format %x has arg nil of wrong type untyped nil"
fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type" fmt.Printf("%X", 2.3) // ERROR "Printf format %X has arg 2.3 of wrong type float64"
fmt.Printf("%s", stringerv) // ERROR "arg stringerv for printf verb %s of wrong type" fmt.Printf("%s", stringerv) // ERROR "Printf format %s has arg stringerv of wrong type testdata.stringer"
fmt.Printf("%t", stringerv) // ERROR "arg stringerv for printf verb %t of wrong type" fmt.Printf("%t", stringerv) // ERROR "Printf format %t has arg stringerv of wrong type testdata.stringer"
fmt.Printf("%s", embeddedStringerv) // ERROR "arg embeddedStringerv for printf verb %s of wrong type" fmt.Printf("%s", embeddedStringerv) // ERROR "Printf format %s has arg embeddedStringerv of wrong type testdata.embeddedStringer"
fmt.Printf("%t", embeddedStringerv) // ERROR "arg embeddedStringerv for printf verb %t of wrong type" fmt.Printf("%t", embeddedStringerv) // ERROR "Printf format %t has arg embeddedStringerv of wrong type testdata.embeddedStringer"
fmt.Printf("%q", notstringerv) // ERROR "arg notstringerv for printf verb %q of wrong type" fmt.Printf("%q", notstringerv) // ERROR "Printf format %q has arg notstringerv of wrong type testdata.notstringer"
fmt.Printf("%t", notstringerv) // ERROR "arg notstringerv for printf verb %t of wrong type" fmt.Printf("%t", notstringerv) // ERROR "Printf format %t has arg notstringerv of wrong type testdata.notstringer"
fmt.Printf("%t", stringerarrayv) // ERROR "arg stringerarrayv for printf verb %t of wrong type" fmt.Printf("%t", stringerarrayv) // ERROR "Printf format %t has arg stringerarrayv of wrong type testdata.stringerarray"
fmt.Printf("%t", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %t of wrong type" fmt.Printf("%t", notstringerarrayv) // ERROR "Printf format %t has arg notstringerarrayv of wrong type testdata.notstringerarray"
fmt.Printf("%q", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %q of wrong type" fmt.Printf("%q", notstringerarrayv) // ERROR "Printf format %q has arg notstringerarrayv of wrong type testdata.notstringerarray"
fmt.Printf("%d", Formatter(true)) // ERROR "arg Formatter\(true\) for printf verb %d of wrong type: testdata.Formatter" fmt.Printf("%d", Formatter(true)) // ERROR "Printf format %d has arg Formatter\(true\) of wrong type testdata.Formatter"
fmt.Printf("%z", FormatterVal(true)) // correct (the type is responsible for formatting) 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("%d", FormatterVal(true)) // correct (the type is responsible for formatting)
fmt.Printf("%s", nonemptyinterface) // correct (the type is responsible for formatting) fmt.Printf("%s", nonemptyinterface) // correct (the type is responsible for formatting)
fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type" fmt.Printf("%.*s %d %6g", 3, "hi", 23, 'x') // ERROR "Printf format %6g has arg 'x' of wrong type rune"
fmt.Println() // not an error fmt.Println() // not an error
fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" fmt.Println("%s", "hi") // ERROR "Println call has possible formatting directive %s"
fmt.Println("0.0%") // correct (trailing % couldn't be a formatting directive) fmt.Println("0.0%") // correct (trailing % couldn't be a formatting directive)
fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call" fmt.Printf("%s", "hi", 3) // ERROR "Printf call needs 1 arg but has 2 args"
_ = fmt.Sprintf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Sprintf call" _ = fmt.Sprintf("%"+("s"), "hi", 3) // ERROR "Sprintf call needs 1 arg but has 2 args"
fmt.Printf("%s%%%d", "hi", 3) // correct fmt.Printf("%s%%%d", "hi", 3) // correct
fmt.Printf("%08s", "woo") // correct fmt.Printf("%08s", "woo") // correct
fmt.Printf("% 8s", "woo") // correct fmt.Printf("% 8s", "woo") // correct
fmt.Printf("%.*d", 3, 3) // correct fmt.Printf("%.*d", 3, 3) // correct
fmt.Printf("%.*d", 3, 3, 3, 3) // ERROR "wrong number of args for format in Printf call.*4 args" fmt.Printf("%.*d x", 3, 3, 3, 3) // ERROR "Printf call needs 2 args but has 4 args"
fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int" fmt.Printf("%.*d x", "hi", 3) // ERROR "Printf format %.*d uses non-int \x22hi\x22 as argument of \*"
fmt.Printf("%.*d", i, 3) // correct fmt.Printf("%.*d x", i, 3) // correct
fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int" fmt.Printf("%.*d x", s, 3) // ERROR "Printf format %.\*d uses non-int s as argument of \*"
fmt.Printf("%*%", 0.22) // ERROR "arg 0.22 for \* in printf format not of type int" fmt.Printf("%*% x", 0.22) // ERROR "Printf format %\*% uses non-int 0.22 as argument of \*"
fmt.Printf("%q %q", multi()...) // ok fmt.Printf("%q %q", multi()...) // ok
fmt.Printf("%#q", `blah`) // ok fmt.Printf("%#q", `blah`) // ok
printf("now is the time", "buddy") // ERROR "no formatting directive" printf("now is the time", "buddy") // ERROR "printf call has arguments but no formatting directives"
Printf("now is the time", "buddy") // ERROR "no formatting directive" Printf("now is the time", "buddy") // ERROR "Printf call has arguments but no formatting directives"
Printf("hi") // ok Printf("hi") // ok
const format = "%s %s\n" const format = "%s %s\n"
Printf(format, "hi", "there") Printf(format, "hi", "there")
Printf(format, "hi") // ERROR "missing argument for Printf..%s..: format reads arg 2, have only 1" Printf(format, "hi") // ERROR "Printf format %s reads arg #2, but call has only 1 arg$"
Printf("%s %d %.3v %q", "str", 4) // ERROR "missing argument for Printf..%.3v..: format reads arg 3, have only 2" Printf("%s %d %.3v %q", "str", 4) // ERROR "Printf format %.3v reads arg #3, but call has only 2 args"
f := new(stringer) f := new(stringer)
f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call" f.Warn(0, "%s", "hello", 3) // ERROR "Warn call has possible formatting directive %s"
f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call" f.Warnf(0, "%s", "hello", 3) // ERROR "Warnf call needs 1 arg but has 2 args"
f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb" f.Warnf(0, "%r", "hello") // ERROR "Warnf format %r has unknown verb r"
f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag" f.Warnf(0, "%#s", "hello") // ERROR "Warnf format %#s has unrecognized flag #"
Printf("d%", 2) // ERROR "missing verb at end of format string in Printf call" Printf("d%", 2) // ERROR "Printf format % is missing verb at end of string"
Printf("%d", percentDV) Printf("%d", percentDV)
Printf("%d", &percentDV) Printf("%d", &percentDV)
Printf("%d", notPercentDV) // ERROR "arg notPercentDV for printf verb %d of wrong type" Printf("%d", notPercentDV) // ERROR "Printf format %d has arg notPercentDV of wrong type testdata.notPercentDStruct"
Printf("%d", &notPercentDV) // ERROR "arg &notPercentDV for printf verb %d of wrong type" Printf("%d", &notPercentDV) // ERROR "Printf format %d has arg &notPercentDV of wrong type \*testdata.notPercentDStruct"
Printf("%p", &notPercentDV) // Works regardless: we print it as a pointer. Printf("%p", &notPercentDV) // Works regardless: we print it as a pointer.
Printf("%s", percentSV) Printf("%s", percentSV)
Printf("%s", &percentSV) Printf("%s", &percentSV)
...@@ -182,13 +182,13 @@ func PrintfTests() { ...@@ -182,13 +182,13 @@ func PrintfTests() {
Printf("%[2]*.[1]*[3]d", 2, 3, 4) 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. fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.
// Bad argument reorderings. // Bad argument reorderings.
Printf("%[xd", 3) // ERROR "bad syntax for printf argument index: \[xd\]" Printf("%[xd", 3) // ERROR "Printf format %\[xd is missing closing \]"
Printf("%[x]d", 3) // ERROR "bad syntax for printf argument index: \[x\]" Printf("%[x]d x", 3) // ERROR "Printf format has invalid argument index \[x\]"
Printf("%[3]*s", "hi", 2) // ERROR "missing argument for Printf.* reads arg 3, have only 2" Printf("%[3]*s x", "hi", 2) // ERROR "Printf format has invalid argument index \[3\]"
_ = fmt.Sprintf("%[3]d", 2) // ERROR "missing argument for Sprintf.* reads arg 3, have only 1" _ = fmt.Sprintf("%[3]d x", 2) // ERROR "Sprintf format has invalid argument index \[3\]"
Printf("%[2]*.[1]*[3]d", 2, "hi", 4) // ERROR "arg .hi. for \* in printf format not of type int" Printf("%[2]*.[1]*[3]d x", 2, "hi", 4) // ERROR "Printf format %\[2]\*\.\[1\]\*\[3\]d uses non-int \x22hi\x22 as argument of \*"
Printf("%[0]s", "arg1") // ERROR "index value \[0\] for Printf.*; indexes start at 1" Printf("%[0]s x", "arg1") // ERROR "Printf format has invalid argument index \[0\]"
Printf("%[0]d", 1) // ERROR "index value \[0\] for Printf.*; indexes start at 1" Printf("%[0]d x", 1) // ERROR "Printf format has invalid argument index \[0\]"
// Something that satisfies the error interface. // Something that satisfies the error interface.
var e error var e error
fmt.Println(e.Error()) // ok fmt.Println(e.Error()) // ok
...@@ -197,11 +197,11 @@ func PrintfTests() { ...@@ -197,11 +197,11 @@ func PrintfTests() {
var et1 errorTest1 var et1 errorTest1
fmt.Println(et1.Error()) // ok fmt.Println(et1.Error()) // ok
fmt.Println(et1.Error("hi")) // ok fmt.Println(et1.Error("hi")) // ok
fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call" fmt.Println(et1.Error("%d", 3)) // ERROR "Error call has possible formatting directive %d"
var et2 errorTest2 var et2 errorTest2
et2.Error() // ok et2.Error() // ok
et2.Error("hi") // ok, not an error method. et2.Error("hi") // ok, not an error method.
et2.Error("%d", 3) // ERROR "possible formatting directive in Error call" et2.Error("%d", 3) // ERROR "Error call has possible formatting directive %d"
var et3 errorTest3 var et3 errorTest3
et3.Error() // ok, not an error method. et3.Error() // ok, not an error method.
var et4 errorTest4 var et4 errorTest4
...@@ -214,9 +214,9 @@ func PrintfTests() { ...@@ -214,9 +214,9 @@ func PrintfTests() {
} }
fmt.Printf("%f", iface) // ok: fmt treats interfaces as transparent and iface may well have a float concrete type fmt.Printf("%f", iface) // ok: fmt treats interfaces as transparent and iface may well have a float concrete type
// Can't print a function. // Can't print a function.
Printf("%d", someFunction) // ERROR "arg someFunction in printf call is a function value, not a function call" Printf("%d", someFunction) // ERROR "Printf format %d arg someFunction is a func value, not called"
Printf("%v", someFunction) // ERROR "arg someFunction in printf call is a function value, not a function call" Printf("%v", someFunction) // ERROR "Printf format %v arg someFunction is a func value, not called"
Println(someFunction) // ERROR "arg someFunction in Println call is a function value, not a function call" Println(someFunction) // ERROR "Println arg someFunction is a func value, not called"
Printf("%p", someFunction) // ok: maybe someone wants to see the pointer Printf("%p", someFunction) // ok: maybe someone wants to see the pointer
Printf("%T", someFunction) // ok: maybe someone wants to see the type Printf("%T", someFunction) // ok: maybe someone wants to see the type
// Bug: used to recur forever. // Bug: used to recur forever.
...@@ -227,17 +227,17 @@ func PrintfTests() { ...@@ -227,17 +227,17 @@ func PrintfTests() {
// Special handling for Log. // Special handling for Log.
math.Log(3) // OK math.Log(3) // OK
Log(3) // OK Log(3) // OK
Log("%d", 3) // ERROR "possible formatting directive in Log call" Log("%d", 3) // ERROR "Log call has possible formatting directive %d"
Logf("%d", 3) Logf("%d", 3)
Logf("%d", "hi") // ERROR "arg .hi. for printf verb %d of wrong type: string" Logf("%d", "hi") // ERROR "Logf format %d has arg \x22hi\x22 of wrong type string"
Errorf(1, "%d", 3) // OK Errorf(1, "%d", 3) // OK
Errorf(1, "%d", "hi") // ERROR "arg .hi. for printf verb %d of wrong type: string" Errorf(1, "%d", "hi") // ERROR "Errorf format %d has arg \x22hi\x22 of wrong type string"
// Multiple string arguments before variadic args // Multiple string arguments before variadic args
errorf("WARNING", "foobar") // OK errorf("WARNING", "foobar") // OK
errorf("INFO", "s=%s, n=%d", "foo", 1) // OK errorf("INFO", "s=%s, n=%d", "foo", 1) // OK
errorf("ERROR", "%d") // ERROR "format reads arg 1, have only 0 args" errorf("ERROR", "%d") // ERROR "errorf format %d reads arg #1, but call has only 0 args"
// Printf from external package // Printf from external package
externalprintf.Printf("%d", 42) // OK externalprintf.Printf("%d", 42) // OK
...@@ -245,7 +245,7 @@ func PrintfTests() { ...@@ -245,7 +245,7 @@ func PrintfTests() {
level := 123 level := 123
externalprintf.Logf(level, "%d", 42) // OK externalprintf.Logf(level, "%d", 42) // OK
externalprintf.Errorf(level, level, "foo %q bar", "foobar") // OK externalprintf.Errorf(level, level, "foo %q bar", "foobar") // OK
externalprintf.Logf(level, "%d") // ERROR "format reads arg 1, have only 0 args" externalprintf.Logf(level, "%d") // ERROR "Logf format %d reads arg #1, but call has only 0 args"
var formatStr = "%s %s" var formatStr = "%s %s"
externalprintf.Sprintf(formatStr, "a", "b") // OK externalprintf.Sprintf(formatStr, "a", "b") // OK
externalprintf.Logf(level, formatStr, "a", "b") // OK externalprintf.Logf(level, formatStr, "a", "b") // OK
...@@ -256,21 +256,30 @@ func PrintfTests() { ...@@ -256,21 +256,30 @@ func PrintfTests() {
ss.Error(someFunction, someFunction) // OK ss.Error(someFunction, someFunction) // OK
ss.Println() // OK ss.Println() // OK
ss.Println(1.234, "foo") // OK ss.Println(1.234, "foo") // OK
ss.Println(1, someFunction) // ERROR "arg someFunction in Println call is a function value, not a function call" ss.Println(1, someFunction) // ERROR "Println arg someFunction is a func value, not called"
ss.log(someFunction) // OK ss.log(someFunction) // OK
ss.log(someFunction, "bar", 1.33) // OK ss.log(someFunction, "bar", 1.33) // OK
ss.log(someFunction, someFunction) // ERROR "arg someFunction in log call is a function value, not a function call" ss.log(someFunction, someFunction) // ERROR "log arg someFunction is a func value, not called"
// indexed arguments // indexed arguments
Printf("%d %[3]d %d %[2]d", 1, 2, 3, 4) // OK Printf("%d %[3]d %d %[2]d x", 1, 2, 3, 4) // OK
Printf("%d %[0]d %d %[2]d", 1, 2, 3, 4) // ERROR "indexes start at 1" Printf("%d %[0]d %d %[2]d x", 1, 2, 3, 4) // ERROR "Printf format has invalid argument index \[0\]"
Printf("%d %[3]d %d %[-2]d", 1, 2, 3, 4) // ERROR "bad syntax for printf argument index: \[-2\]" Printf("%d %[3]d %d %[-2]d x", 1, 2, 3, 4) // ERROR "Printf format has invalid argument index \[-2\]"
Printf("%d %[3]d %d %[2234234234234]d", 1, 2, 3, 4) // ERROR "bad syntax for printf argument index: .+ value out of range" Printf("%d %[3]d %d %[2234234234234]d x", 1, 2, 3, 4) // ERROR "Printf format has invalid argument index \[2234234234234\]"
Printf("%d %[3]d %d %[2]d", 1, 2, 3) // ERROR "format reads arg 4, have only 3 args" Printf("%d %[3]d %-10d %[2]d x", 1, 2, 3) // ERROR "Printf format %-10d reads arg #4, but call has only 3 args"
Printf("%d %[3]d %d %[2]d", 1, 2, 3, 4, 5) // ERROR "wrong number of args for format in Printf call: 4 needed but 5 args" Printf("%d %[3]d %d %[2]d x", 1, 2, 3, 4, 5) // ERROR "Printf call needs 4 args but has 5 args"
Printf("%[1][3]d", 1, 2) // ERROR "unrecognized printf verb '\['" Printf("%[1][3]d x", 1, 2) // ERROR "Printf format %\[1\]\[ has unknown verb \["
// wrote Println but meant Fprintln
Printf("%p\n", os.Stdout) // OK
Println(os.Stdout, "hello") // ERROR "Println does not take io.Writer but has first arg os.Stdout"
Printf(someString(), "hello") // OK
} }
func someString() string
type someStruct struct{} type someStruct struct{}
// Log is non-variadic user-define Println-like function. // Log is non-variadic user-define Println-like function.
...@@ -414,17 +423,17 @@ type recursiveStringer int ...@@ -414,17 +423,17 @@ type recursiveStringer int
func (s recursiveStringer) String() string { func (s recursiveStringer) String() string {
_ = fmt.Sprintf("%d", s) _ = fmt.Sprintf("%d", s)
_ = fmt.Sprintf("%#v", s) _ = fmt.Sprintf("%#v", s)
_ = fmt.Sprintf("%v", s) // ERROR "arg s for printf causes recursive call to String method" _ = fmt.Sprintf("%v", s) // ERROR "Sprintf format %v with arg s causes recursive String method call"
_ = fmt.Sprintf("%v", &s) // ERROR "arg &s for printf causes recursive call to String method" _ = fmt.Sprintf("%v", &s) // ERROR "Sprintf format %v with arg &s causes recursive String method call"
_ = fmt.Sprintf("%T", s) // ok; does not recursively call String _ = fmt.Sprintf("%T", s) // ok; does not recursively call String
return fmt.Sprintln(s) // ERROR "arg s in Sprintln call causes recursive call to String method" return fmt.Sprintln(s) // ERROR "Sprintln arg s causes recursive call to String method"
} }
type recursivePtrStringer int type recursivePtrStringer int
func (p *recursivePtrStringer) String() string { func (p *recursivePtrStringer) String() string {
_ = fmt.Sprintf("%v", *p) _ = fmt.Sprintf("%v", *p)
return fmt.Sprintln(p) // ERROR "arg p in Sprintln call causes recursive call to String method" return fmt.Sprintln(p) // ERROR "Sprintln arg p causes recursive call to String method"
} }
type Formatter bool type Formatter bool
...@@ -500,27 +509,27 @@ func (e errorer) Error() string { return "errorer" } ...@@ -500,27 +509,27 @@ func (e errorer) Error() string { return "errorer" }
func UnexportedStringerOrError() { func UnexportedStringerOrError() {
us := unexportedStringer{} us := unexportedStringer{}
fmt.Printf("%s", us) // ERROR "arg us for printf verb %s of wrong type" fmt.Printf("%s", us) // ERROR "Printf format %s has arg us of wrong type testdata.unexportedStringer"
fmt.Printf("%s", &us) // ERROR "arg &us for printf verb %s of wrong type" fmt.Printf("%s", &us) // ERROR "Printf format %s has arg &us of wrong type [*]testdata.unexportedStringer"
usf := unexportedStringerOtherFields{ usf := unexportedStringerOtherFields{
s: "foo", s: "foo",
S: "bar", S: "bar",
} }
fmt.Printf("%s", usf) // ERROR "arg usf for printf verb %s of wrong type" fmt.Printf("%s", usf) // ERROR "Printf format %s has arg usf of wrong type testdata.unexportedStringerOtherFields"
fmt.Printf("%s", &usf) // ERROR "arg &usf for printf verb %s of wrong type" fmt.Printf("%s", &usf) // ERROR "Printf format %s has arg &usf of wrong type [*]testdata.unexportedStringerOtherFields"
ue := unexportedError{ ue := unexportedError{
e: &errorer{}, e: &errorer{},
} }
fmt.Printf("%s", ue) // ERROR "arg ue for printf verb %s of wrong type" fmt.Printf("%s", ue) // ERROR "Printf format %s has arg ue of wrong type testdata.unexportedError"
fmt.Printf("%s", &ue) // ERROR "arg &ue for printf verb %s of wrong type" fmt.Printf("%s", &ue) // ERROR "Printf format %s has arg &ue of wrong type [*]testdata.unexportedError"
uef := unexportedErrorOtherFields{ uef := unexportedErrorOtherFields{
s: "foo", s: "foo",
e: &errorer{}, e: &errorer{},
S: "bar", S: "bar",
} }
fmt.Printf("%s", uef) // ERROR "arg uef for printf verb %s of wrong type" fmt.Printf("%s", uef) // ERROR "Printf format %s has arg uef of wrong type testdata.unexportedErrorOtherFields"
fmt.Printf("%s", &uef) // ERROR "arg &uef for printf verb %s of wrong type" fmt.Printf("%s", &uef) // ERROR "Printf format %s has arg &uef of wrong type [*]testdata.unexportedErrorOtherFields"
} }
...@@ -6,7 +6,10 @@ ...@@ -6,7 +6,10 @@
package testdata package testdata
import "unsafe" import (
"fmt"
"unsafe"
)
func ShiftTest() { func ShiftTest() {
var i8 int8 var i8 int8
...@@ -154,6 +157,6 @@ func ShiftDeadCode() { ...@@ -154,6 +157,6 @@ func ShiftDeadCode() {
// Make sure other vet checks work in dead code. // Make sure other vet checks work in dead code.
if iBits == 1024 { if iBits == 1024 {
_ = i << 512 // OK _ = i << 512 // OK
fmt.Printf("foo %s bar", 123) // ERROR "arg 123 for printf verb %s of wrong type: untyped int" fmt.Printf("foo %s bar", 123) // ERROR "Printf"
} }
} }
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