Skip to content

Commit

Permalink
go/analysis/passes/printf: fix %w for non-fmt.Errorf functions
Browse files Browse the repository at this point in the history
Previously all functions that were named Errorf have been treated like a
fmt.Errorf-based function. But only fmt.Errorf (and functions based on
fmt.Errorf) accept the %w verb. Fix that.

Updates golang/go#47641.

Change-Id: Iec5d0ae674c7dc817e85291dcfa064303eafba7e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/340409
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
Trust: Damien Neil <dneil@google.com>
  • Loading branch information
chressie authored and neild committed Aug 17, 2021
1 parent 96f147c commit e32504c
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 12 deletions.
2 changes: 1 addition & 1 deletion go/analysis/passes/printf/printf.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
_, ok = isPrint[strings.ToLower(fn.Name())]
}
if ok {
if fn.Name() == "Errorf" {
if fn.FullName() == "fmt.Errorf" {
kind = KindErrorf
} else if strings.HasSuffix(fn.Name(), "f") {
kind = KindPrintf
Expand Down
57 changes: 46 additions & 11 deletions go/analysis/passes/printf/testdata/src/a/a.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,14 +327,26 @@ func PrintfTests() {
dbg("", 1) // no error "call has arguments but no formatting directive"

// %w
_ = fmt.Errorf("%w", err)
_ = fmt.Errorf("%#w", err)
_ = fmt.Errorf("%[2]w %[1]s", "x", err)
_ = fmt.Errorf("%[2]w %[1]s", e, "x") // want `fmt.Errorf format %\[2\]w has arg "x" of wrong type string`
_ = fmt.Errorf("%w", "x") // want `fmt.Errorf format %w has arg "x" of wrong type string`
_ = fmt.Errorf("%w %w", err, err) // want `fmt.Errorf call has more than one error-wrapping directive %w`
fmt.Printf("%w", err) // want `fmt.Printf does not support error-wrapping directive %w`
Errorf(0, "%w", err)
_ = fmt.Errorf("%w", err) // OK
_ = fmt.Errorf("%#w", err) // OK
_ = fmt.Errorf("%[2]w %[1]s", "x", err) // OK
_ = fmt.Errorf("%[2]w %[1]s", e, "x") // want `fmt.Errorf format %\[2\]w has arg "x" of wrong type string`
_ = fmt.Errorf("%w", "x") // want `fmt.Errorf format %w has arg "x" of wrong type string`
_ = fmt.Errorf("%w %w", err, err) // want `fmt.Errorf call has more than one error-wrapping directive %w`
fmt.Printf("%w", err) // want `fmt.Printf does not support error-wrapping directive %w`
var wt *testing.T
wt.Errorf("%w", err) // want `\(\*testing.common\).Errorf does not support error-wrapping directive %w`
wt.Errorf("%[1][3]d x", 1, 2) // want `\(\*testing.common\).Errorf format %\[1\]\[ has unknown verb \[`
wt.Errorf("%[1]d x", 1, 2) // OK
// Errorf is a printfWrapper, not an errorfWrapper.
Errorf(0, "%w", err) // want `a.Errorf does not support error-wrapping directive %w`
// %w should work on fmt.Errorf-based wrappers.
var es errorfStruct
var eis errorfIntStruct
var ess errorfStringStruct
es.Errorf("%w", err) // OK
eis.Errorf(0, "%w", err) // OK
ess.Errorf("ERROR", "%w", err) // OK
}

func someString() string { return "X" }
Expand Down Expand Up @@ -379,13 +391,36 @@ func printf(format string, args ...interface{}) { // want printf:"printfWrapper"

// Errorf is used by the test for a case in which the first parameter
// is not a format string.
func Errorf(i int, format string, args ...interface{}) { // want Errorf:"errorfWrapper"
_ = fmt.Errorf(format, args...)
func Errorf(i int, format string, args ...interface{}) { // want Errorf:"printfWrapper"
fmt.Sprintf(format, args...)
}

// errorf is used by the test for a case in which the function accepts multiple
// string parameters before variadic arguments
func errorf(level, format string, args ...interface{}) { // want errorf:"errorfWrapper"
func errorf(level, format string, args ...interface{}) { // want errorf:"printfWrapper"
fmt.Sprintf(format, args...)
}

type errorfStruct struct{}

// Errorf is used to test %w works on errorf wrappers.
func (errorfStruct) Errorf(format string, args ...interface{}) { // want Errorf:"errorfWrapper"
_ = fmt.Errorf(format, args...)
}

type errorfStringStruct struct{}

// Errorf is used by the test for a case in which the function accepts multiple
// string parameters before variadic arguments
func (errorfStringStruct) Errorf(level, format string, args ...interface{}) { // want Errorf:"errorfWrapper"
_ = fmt.Errorf(format, args...)
}

type errorfIntStruct struct{}

// Errorf is used by the test for a case in which the first parameter
// is not a format string.
func (errorfIntStruct) Errorf(i int, format string, args ...interface{}) { // want Errorf:"errorfWrapper"
_ = fmt.Errorf(format, args...)
}

Expand Down

0 comments on commit e32504c

Please sign in to comment.