Skip to content

Commit 4ab7496

Browse files
committed
go/analysis/passes/copylock: avoid infinite recursion via type params
We should not infinitely expand type parameters looking for a lock path. Fixes golang/go#49384 Change-Id: I8e1c952d468908aeb16751dddd6c05bb07f3c95c Reviewed-on: https://go-review.googlesource.com/c/tools/+/361456 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Tim King <[email protected]>
1 parent 1c6f3cc commit 4ab7496

File tree

2 files changed

+31
-8
lines changed

2 files changed

+31
-8
lines changed

go/analysis/passes/copylock/copylock.go

+20-8
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,15 @@ func checkCopyLocksCallExpr(pass *analysis.Pass, ce *ast.CallExpr) {
146146
func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, typ *ast.FuncType) {
147147
if recv != nil && len(recv.List) > 0 {
148148
expr := recv.List[0].Type
149-
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
149+
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type, nil); path != nil {
150150
pass.ReportRangef(expr, "%s passes lock by value: %v", name, path)
151151
}
152152
}
153153

154154
if typ.Params != nil {
155155
for _, field := range typ.Params.List {
156156
expr := field.Type
157-
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
157+
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type, nil); path != nil {
158158
pass.ReportRangef(expr, "%s passes lock by value: %v", name, path)
159159
}
160160
}
@@ -200,7 +200,7 @@ func checkCopyLocksRangeVar(pass *analysis.Pass, rtok token.Token, e ast.Expr) {
200200
if typ == nil {
201201
return
202202
}
203-
if path := lockPath(pass.Pkg, typ); path != nil {
203+
if path := lockPath(pass.Pkg, typ, nil); path != nil {
204204
pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisutil.Format(pass.Fset, e), path)
205205
}
206206
}
@@ -235,23 +235,35 @@ func lockPathRhs(pass *analysis.Pass, x ast.Expr) typePath {
235235
return nil
236236
}
237237
}
238-
return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type)
238+
return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type, nil)
239239
}
240240

241241
// lockPath returns a typePath describing the location of a lock value
242242
// contained in typ. If there is no contained lock, it returns nil.
243-
func lockPath(tpkg *types.Package, typ types.Type) typePath {
243+
//
244+
// The seenTParams map is used to short-circuit infinite recursion via type
245+
// parameters.
246+
func lockPath(tpkg *types.Package, typ types.Type, seenTParams map[*typeparams.TypeParam]bool) typePath {
244247
if typ == nil {
245248
return nil
246249
}
247250

248251
if tpar, ok := typ.(*typeparams.TypeParam); ok {
252+
if seenTParams == nil {
253+
// Lazily allocate seenTParams, since the common case will not involve
254+
// any type parameters.
255+
seenTParams = make(map[*typeparams.TypeParam]bool)
256+
}
257+
if seenTParams[tpar] {
258+
return nil
259+
}
260+
seenTParams[tpar] = true
249261
terms, err := typeparams.StructuralTerms(tpar)
250262
if err != nil {
251263
return nil // invalid type
252264
}
253265
for _, term := range terms {
254-
subpath := lockPath(tpkg, term.Type())
266+
subpath := lockPath(tpkg, term.Type(), seenTParams)
255267
if len(subpath) > 0 {
256268
if term.Tilde() {
257269
// Prepend a tilde to our lock path entry to clarify the resulting
@@ -285,7 +297,7 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath {
285297
ttyp, ok := typ.Underlying().(*types.Tuple)
286298
if ok {
287299
for i := 0; i < ttyp.Len(); i++ {
288-
subpath := lockPath(tpkg, ttyp.At(i).Type())
300+
subpath := lockPath(tpkg, ttyp.At(i).Type(), seenTParams)
289301
if subpath != nil {
290302
return append(subpath, typ.String())
291303
}
@@ -319,7 +331,7 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath {
319331
nfields := styp.NumFields()
320332
for i := 0; i < nfields; i++ {
321333
ftyp := styp.Field(i).Type()
322-
subpath := lockPath(tpkg, ftyp)
334+
subpath := lockPath(tpkg, ftyp, seenTParams)
323335
if subpath != nil {
324336
return append(subpath, typ.String())
325337
}

go/analysis/passes/copylock/testdata/src/typeparams/typeparams.go

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ package typeparams
66

77
import "sync"
88

9+
// The copylock analyzer runs despite errors. The following invalid type should
10+
// not cause an infinite recursion.
11+
type R struct{ r R }
12+
13+
func TestNoRecursion(r R) {}
14+
15+
// The following recursive type parameter definitions should not cause an
16+
// infinite recursion.
17+
func TestNoTypeParamRecursion[T1 ~[]T2, T2 ~[]T1 | string, T3 ~struct{ F T3 }](t1 T1, t2 T2, t3 T3) {
18+
}
19+
920
func OkFunc1[Struct ~*struct{ mu sync.Mutex }](s Struct) {
1021
}
1122

0 commit comments

Comments
 (0)