Skip to content

Commit fb9eaeb

Browse files
author
Nishanth Shanmugham
committed
account for anonymous interface in type params
Previously the typeparams logic did not account for anonymous interfaces in type paramters. Fix this, and add tests. A bit of housekeeping to ease debugging: The types and funcs in the testdata typeparams package are moved to separate files. This should ease debugging. To debug a problematic test case in isolation, the single function for the test case can be added to a separate new file (say funcs_debug.go), and the existing funcs in funcs.go can be disabled altogether by setting build constraint in funcs.go to "go118 && never", such that it isn't included. Also, move the check for same basic kind-ness to once at the end.
1 parent 228d3b5 commit fb9eaeb

File tree

3 files changed

+155
-92
lines changed

3 files changed

+155
-92
lines changed

common_go118.go

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,8 @@ func fromNamed(pass *analysis.Pass, t *types.Named, typeparam bool) (result []ty
3232
}
3333

3434
func fromInterface(pass *analysis.Pass, intf *types.Interface, typeparam bool) (result []typeAndMembers, all bool) {
35-
var kind types.BasicKind
36-
var kindSet bool
3735
all = true
3836

39-
// sameKind reports whether each type t that the function is called with
40-
// has the same underlying basic kind as the rest.
41-
sameBasicKind := func(t types.Type) (ok bool) {
42-
basic, ok := t.Underlying().(*types.Basic)
43-
if !ok {
44-
return false
45-
}
46-
if kindSet && kind != basic.Kind() {
47-
return false
48-
}
49-
kind = basic.Kind()
50-
kindSet = true
51-
return true
52-
}
53-
5437
for i := 0; i < intf.NumEmbeddeds(); i++ {
5538
embed := intf.EmbeddedType(i)
5639

@@ -60,24 +43,12 @@ func fromInterface(pass *analysis.Pass, intf *types.Interface, typeparam bool) (
6043
// gather from each term in the union.
6144
for i := 0; i < u.Len(); i++ {
6245
r, a := fromType(pass, u.Term(i).Type(), typeparam)
63-
for _, rr := range r {
64-
if !sameBasicKind(rr.et.TypeName.Type()) {
65-
a = false
66-
break
67-
}
68-
}
6946
result = append(result, r...)
7047
all = all && a
7148
}
7249

7350
case *types.Named:
7451
r, a := fromNamed(pass, embed.(*types.Named), typeparam)
75-
for _, rr := range r {
76-
if !sameBasicKind(rr.et.TypeName.Type()) {
77-
a = false
78-
break
79-
}
80-
}
8152
result = append(result, r...)
8253
all = all && a
8354

@@ -102,6 +73,14 @@ func fromType(pass *analysis.Pass, t types.Type, typeparam bool) (result []typeA
10273
intf := t.Constraint().Underlying().(*types.Interface)
10374
return fromInterface(pass, intf, typeparam)
10475

76+
case *types.Interface:
77+
// anonymous interface.
78+
// e.g. func foo[T interface { M } | interface { N }](v T) {}
79+
if !typeparam {
80+
return nil, true
81+
}
82+
return fromInterface(pass, t, typeparam)
83+
10584
default:
10685
// ignore these.
10786
return nil, true
@@ -110,5 +89,34 @@ func fromType(pass *analysis.Pass, t types.Type, typeparam bool) (result []typeA
11089

11190
func composingEnumTypes(pass *analysis.Pass, t types.Type) (result []typeAndMembers, ok bool) {
11291
_, typeparam := t.(*types.TypeParam)
113-
return fromType(pass, t, typeparam)
92+
result, ok = fromType(pass, t, typeparam)
93+
94+
if typeparam {
95+
var kind types.BasicKind
96+
var kindSet bool
97+
98+
// sameKind reports whether each type t that the function is called
99+
// with has the same underlying basic kind.
100+
sameBasicKind := func(t types.Type) (ok bool) {
101+
basic, ok := t.Underlying().(*types.Basic)
102+
if !ok {
103+
return false
104+
}
105+
if kindSet && kind != basic.Kind() {
106+
return false
107+
}
108+
kind = basic.Kind()
109+
kindSet = true
110+
return true
111+
}
112+
113+
for _, rr := range result {
114+
if !sameBasicKind(rr.et.TypeName.Type()) {
115+
ok = false
116+
break
117+
}
118+
}
119+
}
120+
121+
return result, ok
114122
}

testdata/src/typeparam/typeparam.go renamed to testdata/src/typeparam/funcs.go

Lines changed: 52 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,67 +3,7 @@
33

44
package typeparam
55

6-
import (
7-
y "general/y"
8-
)
9-
10-
type M uint8 // want M:"^A,B$"
11-
const (
12-
_ M = iota * 100
13-
A
14-
B
15-
)
16-
17-
func (M) String() string { return "" }
18-
19-
type N uint8 // want N:"^C,D$"
20-
const (
21-
_ N = iota * 100
22-
C
23-
D
24-
)
25-
26-
type O byte // want O:"^E1,E2$"
27-
const (
28-
E1 O = 'h'
29-
E2 O = 'e'
30-
)
31-
32-
type P float32 // want P:"^F$"
33-
const (
34-
F P = 1.1234
35-
)
36-
37-
type Q string // want Q:"^G$"
38-
const (
39-
G Q = "world"
40-
)
41-
42-
type NotEnumType uint8
43-
44-
type Stringer interface {
45-
String() string
46-
}
47-
48-
type II interface{ N | JJ }
49-
type JJ interface{ O }
50-
type KK interface {
51-
M
52-
Stringer
53-
error
54-
comparable
55-
}
56-
type LL interface {
57-
M | NotEnumType
58-
Stringer
59-
error
60-
}
61-
type MM interface {
62-
M
63-
}
64-
type QQ interface {
65-
Q
66-
}
6+
import y "general/y"
677

688
func _a[T y.Phylum | M](v T) {
699
switch v { // want `^missing cases in switch of type bar.Phylum\|typeparam.M: bar.Chordata, bar.Mollusca, typeparam.B$`
@@ -93,7 +33,17 @@ func _b[T N | MM](v T) {
9333
}
9434
}
9535

96-
func _c[T O | M | N](v T) {
36+
func _c0[T O | M | N](v T) {
37+
switch v { // want `^missing cases in switch of type typeparam.O\|typeparam.M\|typeparam.N: typeparam.E1, typeparam.E2, typeparam.A\|typeparam.C$`
38+
case T(B):
39+
}
40+
41+
_ = map[T]struct{}{ // want `^missing keys in map of key type typeparam.O\|typeparam.M\|typeparam.N: typeparam.E1, typeparam.E2, typeparam.A\|typeparam.C$`
42+
T(B): struct{}{},
43+
}
44+
}
45+
46+
func _c2[T interface{ O } | M | interface{ N }](v T) {
9747
switch v { // want `^missing cases in switch of type typeparam.O\|typeparam.M\|typeparam.N: typeparam.E1, typeparam.E2, typeparam.A\|typeparam.C$`
9848
case T(B):
9949
}
@@ -125,6 +75,16 @@ func _e[T M](v T) {
12575
}
12676
}
12777

78+
func _f[T Anon](v T) {
79+
switch v { // want `^missing cases in switch of type typeparam.M\|typeparam.N: typeparam.B\|typeparam.D$`
80+
case T(C):
81+
}
82+
83+
_ = map[T]struct{}{ // want `^missing keys in map of key type typeparam.M\|typeparam.N: typeparam.B\|typeparam.D$`
84+
T(C): struct{}{},
85+
}
86+
}
87+
12888
func repeat0[T II | O](v T) {
12989
switch v { // want `^missing cases in switch of type typeparam.N\|typeparam.O: typeparam.C, typeparam.D, typeparam.E2$`
13090
case T(E1):
@@ -145,6 +105,16 @@ func repeat1[T MM | M](v T) {
145105
}
146106
}
147107

108+
func repeat2[T interface{ M } | interface{ M }](v T) {
109+
switch v { // want `^missing cases in switch of type typeparam.M: typeparam.A$`
110+
case T(B):
111+
}
112+
113+
_ = map[T]struct{}{ // want `^missing keys in map of key type typeparam.M: typeparam.A$`
114+
T(B): struct{}{},
115+
}
116+
}
117+
148118
func _mixedTypes0[T M | QQ](v T) {
149119
// expect no diagnostic because underlying basic kinds are not same:
150120
// uint8 vs. string
@@ -167,6 +137,26 @@ func _mixedTypes1[T MM | QQ](v T) {
167137
}
168138
}
169139

140+
func _mixedTypes2[T interface{ M } | interface{ Q }](v T) {
141+
switch v {
142+
case T(A):
143+
}
144+
145+
_ = map[T]struct{}{
146+
T(A): struct{}{},
147+
}
148+
}
149+
150+
func _mixedTypes3[T interface{ M | Q }](v T) {
151+
switch v {
152+
case T(A):
153+
}
154+
155+
_ = map[T]struct{}{
156+
T(A): struct{}{},
157+
}
158+
}
159+
170160
func _notEnumType0[T M | NotEnumType](v T) {
171161
// expect no diagnostic because not type elements are enum types.
172162
switch v {

testdata/src/typeparam/types.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//go:build go1.18
2+
// +build go1.18
3+
4+
package typeparam
5+
6+
type Stringer interface {
7+
String() string
8+
}
9+
10+
type M uint8 // want M:"^A,B$"
11+
const (
12+
_ M = iota * 100
13+
A
14+
B
15+
)
16+
17+
func (M) String() string { return "" }
18+
19+
type N uint8 // want N:"^C,D$"
20+
const (
21+
_ N = iota * 100
22+
C
23+
D
24+
)
25+
26+
type O byte // want O:"^E1,E2$"
27+
const (
28+
E1 O = 'h'
29+
E2 O = 'e'
30+
)
31+
32+
type P float32 // want P:"^F$"
33+
const (
34+
F P = 1.1234
35+
)
36+
37+
type Q string // want Q:"^G$"
38+
const (
39+
G Q = "world"
40+
)
41+
42+
type NotEnumType uint8
43+
44+
type II interface{ N | JJ }
45+
type JJ interface{ O }
46+
type KK interface {
47+
M
48+
Stringer
49+
error
50+
comparable
51+
}
52+
type LL interface {
53+
M | NotEnumType
54+
Stringer
55+
error
56+
}
57+
type MM interface {
58+
M
59+
}
60+
type Anon interface {
61+
interface{ M } | interface{ N }
62+
}
63+
type QQ interface {
64+
Q
65+
}

0 commit comments

Comments
 (0)