GoPLS Viewer

Home|gopls/go/types/typeutil/map_test.go
1// Copyright 2014 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package typeutil_test
6
7// TODO(adonovan):
8// - test use of explicit hasher across two maps.
9// - test hashcodes are consistent with equals for a range of types
10//   (e.g. all types generated by type-checking some body of real code).
11
12import (
13    "go/ast"
14    "go/parser"
15    "go/token"
16    "go/types"
17    "testing"
18
19    "golang.org/x/tools/go/types/typeutil"
20    "golang.org/x/tools/internal/typeparams"
21)
22
23var (
24    tStr      = types.Typ[types.String]             // string
25    tPStr1    = types.NewPointer(tStr)              // *string
26    tPStr2    = types.NewPointer(tStr)              // *string, again
27    tInt      = types.Typ[types.Int]                // int
28    tChanInt1 = types.NewChan(types.RecvOnlytInt// <-chan int
29    tChanInt2 = types.NewChan(types.RecvOnlytInt// <-chan int, again
30)
31
32func checkEqualButNotIdentical(t *testing.Txy types.Typecomment string) {
33    if !types.Identical(xy) {
34        t.Errorf("%s: not equal: %s, %s"commentxy)
35    }
36    if x == y {
37        t.Errorf("%s: identical: %v, %v"commentxy)
38    }
39}
40
41func TestAxioms(t *testing.T) {
42    checkEqualButNotIdentical(ttPStr1tPStr2"tPstr{1,2}")
43    checkEqualButNotIdentical(ttChanInt1tChanInt2"tChanInt{1,2}")
44}
45
46func TestMap(t *testing.T) {
47    var tmap *typeutil.Map
48
49    // All methods but Set are safe on on (*T)(nil).
50    tmap.Len()
51    tmap.At(tPStr1)
52    tmap.Delete(tPStr1)
53    tmap.KeysString()
54    _ = tmap.String()
55
56    tmap = new(typeutil.Map)
57
58    // Length of empty map.
59    if l := tmap.Len(); l != 0 {
60        t.Errorf("Len() on empty Map: got %d, want 0"l)
61    }
62    // At of missing key.
63    if v := tmap.At(tPStr1); v != nil {
64        t.Errorf("At() on empty Map: got %v, want nil"v)
65    }
66    // Deletion of missing key.
67    if tmap.Delete(tPStr1) {
68        t.Errorf("Delete() on empty Map: got true, want false")
69    }
70    // Set of new key.
71    if prev := tmap.Set(tPStr1"*string"); prev != nil {
72        t.Errorf("Set() on empty Map returned non-nil previous value %s"prev)
73    }
74
75    // Now: {*string: "*string"}
76
77    // Length of non-empty map.
78    if l := tmap.Len(); l != 1 {
79        t.Errorf("Len(): got %d, want 1"l)
80    }
81    // At via insertion key.
82    if v := tmap.At(tPStr1); v != "*string" {
83        t.Errorf("At(): got %q, want \"*string\""v)
84    }
85    // At via equal key.
86    if v := tmap.At(tPStr2); v != "*string" {
87        t.Errorf("At(): got %q, want \"*string\""v)
88    }
89    // Iteration over sole entry.
90    tmap.Iterate(func(key types.Typevalue interface{}) {
91        if key != tPStr1 {
92            t.Errorf("Iterate: key: got %s, want %s"keytPStr1)
93        }
94        if want := "*string"value != want {
95            t.Errorf("Iterate: value: got %s, want %s"valuewant)
96        }
97    })
98
99    // Setion with key equal to present one.
100    if prev := tmap.Set(tPStr2"*string again"); prev != "*string" {
101        t.Errorf("Set() previous value: got %s, want \"*string\""prev)
102    }
103
104    // Setion of another association.
105    if prev := tmap.Set(tChanInt1"<-chan int"); prev != nil {
106        t.Errorf("Set() previous value: got %s, want nil"prev)
107    }
108
109    // Now: {*string: "*string again", <-chan int: "<-chan int"}
110
111    want1 := "{*string: \"*string again\", <-chan int: \"<-chan int\"}"
112    want2 := "{<-chan int: \"<-chan int\", *string: \"*string again\"}"
113    if s := tmap.String(); s != want1 && s != want2 {
114        t.Errorf("String(): got %s, want %s"swant1)
115    }
116
117    want1 = "{*string, <-chan int}"
118    want2 = "{<-chan int, *string}"
119    if s := tmap.KeysString(); s != want1 && s != want2 {
120        t.Errorf("KeysString(): got %s, want %s"swant1)
121    }
122
123    // Keys().
124    I := types.Identical
125    switch k := tmap.Keys(); {
126    case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok
127    case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok
128    default:
129        t.Errorf("Keys(): got %v, want %s"kwant2)
130    }
131
132    if l := tmap.Len(); l != 2 {
133        t.Errorf("Len(): got %d, want 1"l)
134    }
135    // At via original key.
136    if v := tmap.At(tPStr1); v != "*string again" {
137        t.Errorf("At(): got %q, want \"*string again\""v)
138    }
139    hamming := 1
140    tmap.Iterate(func(key types.Typevalue interface{}) {
141        switch {
142        case I(keytChanInt1):
143            hamming *= 2 // ok
144        case I(keytPStr1):
145            hamming *= 3 // ok
146        }
147    })
148    if hamming != 6 {
149        t.Errorf("Iterate: hamming: got %d, want %d"hamming6)
150    }
151
152    if v := tmap.At(tChanInt2); v != "<-chan int" {
153        t.Errorf("At(): got %q, want \"<-chan int\""v)
154    }
155    // Deletion with key equal to present one.
156    if !tmap.Delete(tChanInt2) {
157        t.Errorf("Delete() of existing key: got false, want true")
158    }
159
160    // Now: {*string: "*string again"}
161
162    if l := tmap.Len(); l != 1 {
163        t.Errorf("Len(): got %d, want 1"l)
164    }
165    // Deletion again.
166    if !tmap.Delete(tPStr2) {
167        t.Errorf("Delete() of existing key: got false, want true")
168    }
169
170    // Now: {}
171
172    if l := tmap.Len(); l != 0 {
173        t.Errorf("Len(): got %d, want %d"l0)
174    }
175    if s := tmap.String(); s != "{}" {
176        t.Errorf("Len(): got %q, want %q"s"")
177    }
178}
179
180func TestMapGenerics(t *testing.T) {
181    if !typeparams.Enabled {
182        t.Skip("type params are not enabled at this Go version")
183    }
184
185    const src = `
186package p
187
188// Basic defined types.
189type T1 int
190type T2 int
191
192// Identical methods.
193func (T1) M(int) {}
194func (T2) M(int) {}
195
196// A constraint interface.
197type C interface {
198    ~int | string
199}
200
201type I interface {
202}
203
204// A generic type.
205type G[P C] int
206
207// Generic functions with identical signature.
208func Fa1[P C](p P) {}
209func Fa2[Q C](q Q) {}
210
211// Fb1 and Fb2 are identical and should be mapped to the same entry, even if we
212// map their arguments first.
213func Fb1[P any](x *P) {
214    var y *P // Map this first.
215    _ = y
216}
217func Fb2[Q any](x *Q) {
218}
219
220// G1 and G2 are mutally recursive, and have identical methods.
221type G1[P any] struct{
222    Field *G2[P]
223}
224func (G1[P]) M(G1[P], G2[P]) {}
225type G2[Q any] struct{
226    Field *G1[Q]
227}
228func (G2[P]) M(G1[P], G2[P]) {}
229
230// Method type expressions on different generic types are different.
231var ME1 = G1[int].M
232var ME2 = G2[int].M
233
234// ME1Type should have identical type as ME1.
235var ME1Type func(G1[int], G1[int], G2[int])
236
237// Examples from issue #51314
238type Constraint[T any] interface{}
239func Foo[T Constraint[T]]() {}
240func Fn[T1 ~*T2, T2 ~*T1](t1 T1, t2 T2) {}
241
242// Bar and Baz are identical to Foo.
243func Bar[P Constraint[P]]() {}
244func Baz[Q any]() {} // The underlying type of Constraint[P] is any.
245// But Quux is not.
246func Quux[Q interface{ quux() }]() {}
247
248
249type Issue56048_I interface{ m() interface { Issue56048_I } }
250var Issue56048 = Issue56048_I.m
251
252type Issue56048_Ib interface{ m() chan []*interface { Issue56048_Ib } }
253var Issue56048b = Issue56048_Ib.m
254
255`
256
257    fset := token.NewFileSet()
258    fileerr := parser.ParseFile(fset"p.go"src0)
259    if err != nil {
260        t.Fatal(err)
261    }
262
263    var conf types.Config
264    pkgerr := conf.Check(""fset, []*ast.File{file}, nil)
265    if err != nil {
266        t.Fatal(err)
267    }
268
269    // Collect types.
270    scope := pkg.Scope()
271    var (
272        T1      = scope.Lookup("T1").Type().(*types.Named)
273        T2      = scope.Lookup("T2").Type().(*types.Named)
274        T1M     = T1.Method(0).Type()
275        T2M     = T2.Method(0).Type()
276        G       = scope.Lookup("G").Type()
277        GInt1   = instantiate(tGtypes.Typ[types.Int])
278        GInt2   = instantiate(tGtypes.Typ[types.Int])
279        GStr    = instantiate(tGtypes.Typ[types.String])
280        C       = scope.Lookup("C").Type()
281        CI      = C.Underlying().(*types.Interface)
282        I       = scope.Lookup("I").Type()
283        II      = I.Underlying().(*types.Interface)
284        U       = CI.EmbeddedType(0).(*typeparams.Union)
285        Fa1     = scope.Lookup("Fa1").Type().(*types.Signature)
286        Fa2     = scope.Lookup("Fa2").Type().(*types.Signature)
287        Fa1P    = typeparams.ForSignature(Fa1).At(0)
288        Fa2Q    = typeparams.ForSignature(Fa2).At(0)
289        Fb1     = scope.Lookup("Fb1").Type().(*types.Signature)
290        Fb1x    = Fb1.Params().At(0).Type()
291        Fb1y    = scope.Lookup("Fb1").(*types.Func).Scope().Lookup("y").Type()
292        Fb2     = scope.Lookup("Fb2").Type().(*types.Signature)
293        Fb2x    = Fb2.Params().At(0).Type()
294        G1      = scope.Lookup("G1").Type().(*types.Named)
295        G1M     = G1.Method(0).Type()
296        G1IntM1 = instantiate(tG1types.Typ[types.Int]).(*types.Named).Method(0).Type()
297        G1IntM2 = instantiate(tG1types.Typ[types.Int]).(*types.Named).Method(0).Type()
298        G1StrM  = instantiate(tG1types.Typ[types.String]).(*types.Named).Method(0).Type()
299        G2      = scope.Lookup("G2").Type()
300        // See below.
301        // G2M     = G2.Method(0).Type()
302        G2IntM  = instantiate(tG2types.Typ[types.Int]).(*types.Named).Method(0).Type()
303        ME1     = scope.Lookup("ME1").Type()
304        ME1Type = scope.Lookup("ME1Type").Type()
305        ME2     = scope.Lookup("ME2").Type()
306
307        Constraint  = scope.Lookup("Constraint").Type()
308        Foo         = scope.Lookup("Foo").Type()
309        Fn          = scope.Lookup("Fn").Type()
310        Bar         = scope.Lookup("Foo").Type()
311        Baz         = scope.Lookup("Foo").Type()
312        Quux        = scope.Lookup("Quux").Type()
313        Issue56048  = scope.Lookup("Issue56048").Type()
314        Issue56048b = scope.Lookup("Issue56048b").Type()
315    )
316
317    tmap := new(typeutil.Map)
318
319    steps := []struct {
320        typ      types.Type
321        name     string
322        newEntry bool
323    }{
324        {T1"T1"true},
325        {T2"T2"true},
326        {G"G"true},
327        {C"C"true},
328        {CI"CI"true},
329        {U"U"true},
330        {I"I"true},
331        {II"II"true}, // should not be identical to CI
332
333        // Methods can be identical, even with distinct receivers.
334        {T1M"T1M"true},
335        {T2M"T2M"false},
336
337        // Identical instances should map to the same entry.
338        {GInt1"GInt1"true},
339        {GInt2"GInt2"false},
340        // ..but instantiating with different arguments should yield a new entry.
341        {GStr"GStr"true},
342
343        // F1 and F2 should have identical signatures.
344        {Fa1"F1"true},
345        {Fa2"F2"false},
346
347        // The identity of P and Q should not have been affected by type parameter
348        // masking during signature hashing.
349        {Fa1P"F1P"true},
350        {Fa2Q"F2Q"true},
351
352        {Fb1y"Fb1y"true},
353        {Fb1x"Fb1x"false},
354        {Fb2x"Fb2x"true},
355        {Fb1"Fb1"true},
356
357        // Mapping elements of the function scope should not affect the identity of
358        // Fb2 or Fb1.
359        {Fb2"Fb1"false},
360
361        {G1"G1"true},
362        {G1M"G1M"true},
363        {G2"G2"true},
364
365        // See golang/go#49912: receiver type parameter names should be ignored
366        // when comparing method identity.
367        // {G2M, "G2M", false},
368        {G1IntM1"G1IntM1"true},
369        {G1IntM2"G1IntM2"false},
370        {G1StrM"G1StrM"true},
371        {G2IntM"G2IntM"false}, // identical to G1IntM1
372
373        {ME1"ME1"true},
374        {ME1Type"ME1Type"false},
375        {ME2"ME2"true},
376
377        // See golang/go#51314: avoid infinite recursion on cyclic type constraints.
378        {Constraint"Constraint"true},
379        {Foo"Foo"true},
380        {Fn"Fn"true},
381        {Bar"Bar"false},
382        {Baz"Baz"false},
383        {Quux"Quux"true},
384
385        {Issue56048"Issue56048"true},   // (not actually about generics)
386        {Issue56048b"Issue56048b"true}, // (not actually about generics)
387    }
388
389    for _step := range steps {
390        existing := tmap.At(step.typ)
391        if (existing == nil) != step.newEntry {
392            t.Errorf("At(%s) = %v, want new entry: %t"step.nameexistingstep.newEntry)
393        }
394        tmap.Set(step.typstep.name)
395    }
396}
397
398func instantiate(t *testing.Torigin types.Typetargs ...types.Typetypes.Type {
399    insterr := typeparams.Instantiate(nilorigintargstrue)
400    if err != nil {
401        t.Fatal(err)
402    }
403    return inst
404}
405
MembersX
TestMap.tmap
TestMapGenerics.fset
TestMap
TestMap.t
TestMap.l
TestMap.BlockStmt.want
TestMap.hamming
TestMapGenerics
checkEqualButNotIdentical
checkEqualButNotIdentical.x
TestAxioms.t
instantiate.origin
instantiate.err
instantiate.inst
checkEqualButNotIdentical.comment
TestMap.want2
TestMapGenerics.err
TestMapGenerics.conf
TestMapGenerics.steps
TestMapGenerics.RangeStmt_10699.step
TestMapGenerics.RangeStmt_10699.BlockStmt.existing
TestMap.s
TestMap.k
TestMapGenerics.pkg
instantiate.t
instantiate.targs
checkEqualButNotIdentical.y
TestAxioms
TestMapGenerics.src
checkEqualButNotIdentical.t
TestMap.v
TestMap.want1
TestMap.I
TestMapGenerics.t
TestMap.prev
TestMapGenerics.file
TestMapGenerics.scope
TestMapGenerics.tmap
instantiate
Members
X