Skip to content

Commit 1f85917

Browse files
mvdanianlancetaylor
authored andcommitted
cmd/vet: **T is not Stringer if *T has a String method
vet recorded what types had String methods defined on them, but it did not record whether the receivers were pointer types. That information is important, as the following program is valid: type T string func (t *T) String() string { return fmt.Sprint(&t) // prints address } Teach vet that, if *T is Stringer, **T is not. Fixes #23550. Change-Id: I1062e60e6d82e789af9cca396546db6bfc3541e8 Reviewed-on: https://go-review.googlesource.com/90417 Run-TryBot: Daniel Martí <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Rob Pike <[email protected]>
1 parent 8c1f21d commit 1f85917

File tree

3 files changed

+23
-7
lines changed

3 files changed

+23
-7
lines changed

src/cmd/vet/main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,11 @@ type File struct {
195195
// Parsed package "foo" when checking package "foo_test"
196196
basePkg *Package
197197

198-
// The objects that are receivers of a "String() string" method.
198+
// The keys are the objects that are receivers of a "String()
199+
// string" method. The value reports whether the method has a
200+
// pointer receiver.
199201
// This is used by the recursiveStringer method in print.go.
200-
stringers map[*ast.Object]bool
202+
stringerPtrs map[*ast.Object]bool
201203

202204
// Registered checkers to run.
203205
checkers map[ast.Node][]func(*File, ast.Node)

src/cmd/vet/print.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,12 +187,14 @@ func checkFmtPrintfCall(f *File, node ast.Node) {
187187

188188
if d, ok := node.(*ast.FuncDecl); ok && isStringer(f, d) {
189189
// Remember we saw this.
190-
if f.stringers == nil {
191-
f.stringers = make(map[*ast.Object]bool)
190+
if f.stringerPtrs == nil {
191+
f.stringerPtrs = make(map[*ast.Object]bool)
192192
}
193193
if l := d.Recv.List; len(l) == 1 {
194194
if n := l[0].Names; len(n) == 1 {
195-
f.stringers[n[0].Obj] = true
195+
typ := f.pkg.types[l[0].Type]
196+
_, ptrRecv := typ.Type.(*types.Pointer)
197+
f.stringerPtrs[n[0].Obj] = ptrRecv
196198
}
197199
}
198200
return
@@ -628,16 +630,18 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
628630
// recursiveStringer reports whether the provided argument is r or &r for the
629631
// fmt.Stringer receiver identifier r.
630632
func (f *File) recursiveStringer(e ast.Expr) bool {
631-
if len(f.stringers) == 0 {
633+
if len(f.stringerPtrs) == 0 {
632634
return false
633635
}
636+
ptr := false
634637
var obj *ast.Object
635638
switch e := e.(type) {
636639
case *ast.Ident:
637640
obj = e.Obj
638641
case *ast.UnaryExpr:
639642
if id, ok := e.X.(*ast.Ident); ok && e.Op == token.AND {
640643
obj = id.Obj
644+
ptr = true
641645
}
642646
}
643647

@@ -652,7 +656,16 @@ func (f *File) recursiveStringer(e ast.Expr) bool {
652656
// We compare the underlying Object, which checks that the identifier
653657
// is the one we declared as the receiver for the String method in
654658
// which this printf appears.
655-
return f.stringers[obj]
659+
ptrRecv, exist := f.stringerPtrs[obj]
660+
if !exist {
661+
return false
662+
}
663+
// We also need to check that using &t when we declared String
664+
// on (t *T) is ok; in such a case, the address is printed.
665+
if ptr && ptrRecv {
666+
return false
667+
}
668+
return true
656669
}
657670

658671
// isFunctionValue reports whether the expression is a function as opposed to a function call.

src/cmd/vet/testdata/print.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ type recursivePtrStringer int
440440

441441
func (p *recursivePtrStringer) String() string {
442442
_ = fmt.Sprintf("%v", *p)
443+
_ = fmt.Sprint(&p) // ok; prints address
443444
return fmt.Sprintln(p) // ERROR "Sprintln arg p causes recursive call to String method"
444445
}
445446

0 commit comments

Comments
 (0)