Skip to content

Commit 26cf12e

Browse files
committed
staticcheck: don't chase infinite chain of instantiations
(cherry picked from commit 2746778)
1 parent a285988 commit 26cf12e

File tree

4 files changed

+50
-19
lines changed

4 files changed

+50
-19
lines changed

staticcheck/fakejson/encode.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"unicode"
1818

1919
"golang.org/x/exp/typeparams"
20+
"honnef.co/go/tools/go/types/typeutil"
2021
"honnef.co/go/tools/knowledge"
2122
"honnef.co/go/tools/staticcheck/fakereflect"
2223
)
@@ -31,9 +32,7 @@ func parseTag(tag string) string {
3132
}
3233

3334
func Marshal(v types.Type) *UnsupportedTypeError {
34-
enc := encoder{
35-
seen: map[fakereflect.TypeAndCanAddr]struct{}{},
36-
}
35+
enc := encoder{}
3736
return enc.newTypeEncoder(fakereflect.TypeAndCanAddr{Type: v}, "x")
3837
}
3938

@@ -45,14 +44,23 @@ type UnsupportedTypeError struct {
4544
}
4645

4746
type encoder struct {
48-
seen map[fakereflect.TypeAndCanAddr]struct{}
47+
// TODO we track addressable and non-addressable instances separately out of an abundance of caution. We don't know
48+
// if this is actually required for correctness.
49+
seenCanAddr typeutil.Map
50+
seenCantAddr typeutil.Map
4951
}
5052

5153
func (enc *encoder) newTypeEncoder(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {
52-
if _, ok := enc.seen[t]; ok {
54+
var m *typeutil.Map
55+
if t.CanAddr() {
56+
m = &enc.seenCanAddr
57+
} else {
58+
m = &enc.seenCantAddr
59+
}
60+
if ok := m.At(t.Type); ok != nil {
5361
return nil
5462
}
55-
enc.seen[t] = struct{}{}
63+
m.Set(t.Type, struct{}{})
5664

5765
if t.Implements(knowledge.Interfaces["encoding/json.Marshaler"]) {
5866
return nil

staticcheck/fakexml/marshal.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ func Marshal(v types.Type) error {
2626
}
2727

2828
type Encoder struct {
29-
seen map[fakereflect.TypeAndCanAddr]struct{}
29+
// TODO we track addressable and non-addressable instances separately out of an abundance of caution. We don't know
30+
// if this is actually required for correctness.
31+
seenCanAddr typeutil.Map
32+
seenCantAddr typeutil.Map
3033
}
3134

3235
func NewEncoder() *Encoder {
33-
e := &Encoder{
34-
seen: map[fakereflect.TypeAndCanAddr]struct{}{},
35-
}
36+
e := &Encoder{}
3637
return e
3738
}
3839

@@ -113,10 +114,16 @@ func (err *CyclicTypeError) Error() string {
113114
// marshalValue writes one or more XML elements representing val.
114115
// If val was obtained from a struct field, finfo must have its details.
115116
func (e *Encoder) marshalValue(val fakereflect.TypeAndCanAddr, finfo *fieldInfo, startTemplate *StartElement, stack string) error {
116-
if _, ok := e.seen[val]; ok {
117+
var m *typeutil.Map
118+
if val.CanAddr() {
119+
m = &e.seenCanAddr
120+
} else {
121+
m = &e.seenCantAddr
122+
}
123+
if ok := m.At(val.Type); ok != nil {
117124
return nil
118125
}
119-
e.seen[val] = struct{}{}
126+
m.Set(val.Type, struct{}{})
120127

121128
// Drill into interfaces and pointers.
122129
seen := map[fakereflect.TypeAndCanAddr]struct{}{}

staticcheck/lint.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -524,18 +524,16 @@ func checkPrintfCallImpl(carg *Argument, f ir.Value, args []ir.Value) {
524524
return true
525525
}
526526

527-
seen := map[types.Type]bool{}
527+
var seen typeutil.Map
528528
var checkType func(verb rune, T types.Type, top bool) bool
529529
checkType = func(verb rune, T types.Type, top bool) bool {
530530
if top {
531-
for k := range seen {
532-
delete(seen, k)
533-
}
531+
seen = typeutil.Map{}
534532
}
535-
if seen[T] {
533+
if ok := seen.At(T); ok != nil {
536534
return true
537535
}
538-
seen[T] = true
536+
seen.Set(T, struct{}{})
539537
if int(verb) >= len(verbs) {
540538
// Unknown verb
541539
return true

staticcheck/testdata/src/CheckUnsupportedMarshal/generics.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
package pkg
44

5-
import "encoding/json"
5+
import (
6+
"encoding/json"
7+
"encoding/xml"
8+
)
69

710
type LMap[K comparable, V any] struct {
811
M1 map[K]V
@@ -13,3 +16,18 @@ func (lm *LMap[K, V]) MarshalJSON() {
1316
json.Marshal(lm.M1)
1417
json.Marshal(lm.M2) //@ diag(`unsupported type`)
1518
}
19+
20+
func recursiveGeneric() {
21+
// don't recurse infinitely
22+
var t Tree[int]
23+
json.Marshal(t)
24+
xml.Marshal(t)
25+
}
26+
27+
type Tree[T any] struct {
28+
Node *Node[T]
29+
}
30+
31+
type Node[T any] struct {
32+
Tree *Tree[T]
33+
}

0 commit comments

Comments
 (0)