From 199742a5a6ce5eea240b95050127c1b464df7368 Mon Sep 17 00:00:00 2001 From: Zvonimir Pavlinovic Date: Fri, 23 Sep 2022 15:41:06 -0700 Subject: [PATCH] go/analysis/passes/printf: check for nil scope of instance methods Fixes golang/go#55350 Change-Id: I28784b42de72e9d489e40d546e171af23b9e5c13 Reviewed-on: https://go-review.googlesource.com/c/tools/+/433755 gopls-CI: kokoro TryBot-Result: Gopher Robot Reviewed-by: Tim King Run-TryBot: Zvonimir Pavlinovic Reviewed-by: Robert Findley --- go/analysis/passes/printf/printf.go | 9 ++++++-- .../testdata/src/typeparams/diagnostics.go | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/go/analysis/passes/printf/printf.go b/go/analysis/passes/printf/printf.go index 19ca4527af..3ac4fcaa28 100644 --- a/go/analysis/passes/printf/printf.go +++ b/go/analysis/passes/printf/printf.go @@ -945,11 +945,16 @@ func recursiveStringer(pass *analysis.Pass, e ast.Expr) (string, bool) { return "", false } + // inScope returns true if e is in the scope of f. + inScope := func(e ast.Expr, f *types.Func) bool { + return f.Scope() != nil && f.Scope().Contains(e.Pos()) + } + // Is the expression e within the body of that String or Error method? var method *types.Func - if strOk && strMethod.Pkg() == pass.Pkg && strMethod.Scope().Contains(e.Pos()) { + if strOk && strMethod.Pkg() == pass.Pkg && inScope(e, strMethod) { method = strMethod - } else if errOk && errMethod.Pkg() == pass.Pkg && errMethod.Scope().Contains(e.Pos()) { + } else if errOk && errMethod.Pkg() == pass.Pkg && inScope(e, errMethod) { method = errMethod } else { return "", false diff --git a/go/analysis/passes/printf/testdata/src/typeparams/diagnostics.go b/go/analysis/passes/printf/testdata/src/typeparams/diagnostics.go index 76a9a205a7..c4d7e530d9 100644 --- a/go/analysis/passes/printf/testdata/src/typeparams/diagnostics.go +++ b/go/analysis/passes/printf/testdata/src/typeparams/diagnostics.go @@ -121,3 +121,25 @@ func TestTermReduction[T1 interface{ ~int | string }, T2 interface { fmt.Printf("%d", t2) fmt.Printf("%s", t2) // want "wrong type.*contains typeparams.myInt" } + +type U[T any] struct{} + +func (u U[T]) String() string { + fmt.Println(u) // want `fmt.Println arg u causes recursive call to \(typeparams.U\[T\]\).String method` + return "" +} + +type S[T comparable] struct { + t T +} + +func (s S[T]) String() T { + fmt.Println(s) // Not flagged. We currently do not consider String() T to implement fmt.Stringer (see #55928). + return s.t +} + +func TestInstanceStringer() { + // Tests String method with nil Scope (#55350) + fmt.Println(&S[string]{}) + fmt.Println(&U[string]{}) +}