Skip to content

Commit 6176384

Browse files
findleyrgopherbot
authored andcommitted
go/types/objectpath: break cycles through interface methods
As demonstrated in golang/go#68046, it was possible to have infinite recursion in objectpath.For searching for objects within recursive interface definitions. Fix this by breaking cycles. Refactor somewhat to simplify the recursive search. Fixes golang/go#68046 Change-Id: Idb65e305ec5949d212ec8dafff824486178652df Reviewed-on: https://go-review.googlesource.com/c/tools/+/621818 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Robert Findley <[email protected]>
1 parent 9e6388a commit 6176384

File tree

2 files changed

+68
-36
lines changed

2 files changed

+68
-36
lines changed

go/types/objectpath/objectpath.go

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -281,25 +281,25 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
281281

282282
T := o.Type()
283283
if alias, ok := T.(*types.Alias); ok {
284-
if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam, nil); r != nil {
284+
if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam); r != nil {
285285
return Path(r), nil
286286
}
287-
if r := find(obj, aliases.Rhs(alias), append(path, opRhs), nil); r != nil {
287+
if r := find(obj, aliases.Rhs(alias), append(path, opRhs)); r != nil {
288288
return Path(r), nil
289289
}
290290

291291
} else if tname.IsAlias() {
292292
// legacy alias
293-
if r := find(obj, T, path, nil); r != nil {
293+
if r := find(obj, T, path); r != nil {
294294
return Path(r), nil
295295
}
296296

297297
} else if named, ok := T.(*types.Named); ok {
298298
// defined (named) type
299-
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam, nil); r != nil {
299+
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam); r != nil {
300300
return Path(r), nil
301301
}
302-
if r := find(obj, named.Underlying(), append(path, opUnderlying), nil); r != nil {
302+
if r := find(obj, named.Underlying(), append(path, opUnderlying)); r != nil {
303303
return Path(r), nil
304304
}
305305
}
@@ -312,7 +312,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
312312
if _, ok := o.(*types.TypeName); !ok {
313313
if o.Exported() {
314314
// exported non-type (const, var, func)
315-
if r := find(obj, o.Type(), append(path, opType), nil); r != nil {
315+
if r := find(obj, o.Type(), append(path, opType)); r != nil {
316316
return Path(r), nil
317317
}
318318
}
@@ -332,7 +332,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
332332
if m == obj {
333333
return Path(path2), nil // found declared method
334334
}
335-
if r := find(obj, m.Type(), append(path2, opType), nil); r != nil {
335+
if r := find(obj, m.Type(), append(path2, opType)); r != nil {
336336
return Path(r), nil
337337
}
338338
}
@@ -447,46 +447,64 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
447447
//
448448
// The seen map is used to short circuit cycles through type parameters. If
449449
// nil, it will be allocated as necessary.
450-
func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
450+
//
451+
// The seenMethods map is used internally to short circuit cycles through
452+
// interface methods, such as occur in the following example:
453+
//
454+
// type I interface { f() interface{I} }
455+
//
456+
// See golang/go#68046 for details.
457+
func find(obj types.Object, T types.Type, path []byte) []byte {
458+
return (&finder{obj: obj}).find(T, path)
459+
}
460+
461+
// finder closes over search state for a call to find.
462+
type finder struct {
463+
obj types.Object // the sought object
464+
seenTParamNames map[*types.TypeName]bool // for cycle breaking through type parameters
465+
seenMethods map[*types.Func]bool // for cycle breaking through recursive interfaces
466+
}
467+
468+
func (f *finder) find(T types.Type, path []byte) []byte {
451469
switch T := T.(type) {
452470
case *types.Alias:
453-
return find(obj, types.Unalias(T), path, seen)
471+
return f.find(types.Unalias(T), path)
454472
case *types.Basic, *types.Named:
455473
// Named types belonging to pkg were handled already,
456474
// so T must belong to another package. No path.
457475
return nil
458476
case *types.Pointer:
459-
return find(obj, T.Elem(), append(path, opElem), seen)
477+
return f.find(T.Elem(), append(path, opElem))
460478
case *types.Slice:
461-
return find(obj, T.Elem(), append(path, opElem), seen)
479+
return f.find(T.Elem(), append(path, opElem))
462480
case *types.Array:
463-
return find(obj, T.Elem(), append(path, opElem), seen)
481+
return f.find(T.Elem(), append(path, opElem))
464482
case *types.Chan:
465-
return find(obj, T.Elem(), append(path, opElem), seen)
483+
return f.find(T.Elem(), append(path, opElem))
466484
case *types.Map:
467-
if r := find(obj, T.Key(), append(path, opKey), seen); r != nil {
485+
if r := f.find(T.Key(), append(path, opKey)); r != nil {
468486
return r
469487
}
470-
return find(obj, T.Elem(), append(path, opElem), seen)
488+
return f.find(T.Elem(), append(path, opElem))
471489
case *types.Signature:
472-
if r := findTypeParam(obj, T.RecvTypeParams(), path, opRecvTypeParam, nil); r != nil {
490+
if r := f.findTypeParam(T.RecvTypeParams(), path, opRecvTypeParam); r != nil {
473491
return r
474492
}
475-
if r := findTypeParam(obj, T.TypeParams(), path, opTypeParam, seen); r != nil {
493+
if r := f.findTypeParam(T.TypeParams(), path, opTypeParam); r != nil {
476494
return r
477495
}
478-
if r := find(obj, T.Params(), append(path, opParams), seen); r != nil {
496+
if r := f.find(T.Params(), append(path, opParams)); r != nil {
479497
return r
480498
}
481-
return find(obj, T.Results(), append(path, opResults), seen)
499+
return f.find(T.Results(), append(path, opResults))
482500
case *types.Struct:
483501
for i := 0; i < T.NumFields(); i++ {
484502
fld := T.Field(i)
485503
path2 := appendOpArg(path, opField, i)
486-
if fld == obj {
504+
if fld == f.obj {
487505
return path2 // found field var
488506
}
489-
if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil {
507+
if r := f.find(fld.Type(), append(path2, opType)); r != nil {
490508
return r
491509
}
492510
}
@@ -495,51 +513,62 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]
495513
for i := 0; i < T.Len(); i++ {
496514
v := T.At(i)
497515
path2 := appendOpArg(path, opAt, i)
498-
if v == obj {
516+
if v == f.obj {
499517
return path2 // found param/result var
500518
}
501-
if r := find(obj, v.Type(), append(path2, opType), seen); r != nil {
519+
if r := f.find(v.Type(), append(path2, opType)); r != nil {
502520
return r
503521
}
504522
}
505523
return nil
506524
case *types.Interface:
507525
for i := 0; i < T.NumMethods(); i++ {
508526
m := T.Method(i)
527+
if f.seenMethods[m] {
528+
return nil
529+
}
509530
path2 := appendOpArg(path, opMethod, i)
510-
if m == obj {
531+
if m == f.obj {
511532
return path2 // found interface method
512533
}
513-
if r := find(obj, m.Type(), append(path2, opType), seen); r != nil {
534+
if f.seenMethods == nil {
535+
f.seenMethods = make(map[*types.Func]bool)
536+
}
537+
f.seenMethods[m] = true
538+
if r := f.find(m.Type(), append(path2, opType)); r != nil {
514539
return r
515540
}
516541
}
517542
return nil
518543
case *types.TypeParam:
519544
name := T.Obj()
520-
if name == obj {
521-
return append(path, opObj)
522-
}
523-
if seen[name] {
545+
if f.seenTParamNames[name] {
524546
return nil
525547
}
526-
if seen == nil {
527-
seen = make(map[*types.TypeName]bool)
548+
if name == f.obj {
549+
return append(path, opObj)
528550
}
529-
seen[name] = true
530-
if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil {
551+
if f.seenTParamNames == nil {
552+
f.seenTParamNames = make(map[*types.TypeName]bool)
553+
}
554+
f.seenTParamNames[name] = true
555+
if r := f.find(T.Constraint(), append(path, opConstraint)); r != nil {
531556
return r
532557
}
533558
return nil
534559
}
535560
panic(T)
536561
}
537562

538-
func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte, seen map[*types.TypeName]bool) []byte {
563+
func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte) []byte {
564+
return (&finder{obj: obj}).findTypeParam(list, path, op)
565+
}
566+
567+
func (f *finder) findTypeParam(list *types.TypeParamList, path []byte, op byte) []byte {
539568
for i := 0; i < list.Len(); i++ {
540569
tparam := list.At(i)
541570
path2 := appendOpArg(path, op, i)
542-
if r := find(obj, tparam, path2, seen); r != nil {
571+
if r := f.find(tparam, path2); r != nil {
543572
return r
544573
}
545574
}

go/types/objectpath/objectpath_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ package a
8282
type Int int
8383
8484
type T struct{x, y int}
85+
86+
type Issue68046 interface { f(x int) interface{Issue68046} }
8587
`
8688

8789
pkgmap := loadPackages(t, src, "./a", "./b")
@@ -123,7 +125,8 @@ type T struct{x, y int}
123125
{"b", "R.UEF0", "field y int", ""},
124126
{"b", "Q.UEF0", "field z int", ""},
125127
{"a", "T", "type a.T struct{x int; y int}", ""},
126-
{"a", "T.UF0", "field x int", ""},
128+
{"a", "Issue68046.UM0", "func (a.Issue68046).f(x int) interface{a.Issue68046}", ""},
129+
{"a", "Issue68046.UM0.PA0", "var x int", ""},
127130

128131
// Bad paths
129132
{"b", "", "", "empty path"},

0 commit comments

Comments
 (0)