1
2
3
4
5 package gcimporter_test
6
7 import (
8 "bytes"
9 "fmt"
10 "go/ast"
11 "go/parser"
12 "go/token"
13 "go/types"
14 "path/filepath"
15 "reflect"
16 "runtime"
17 "sort"
18 "strings"
19 "testing"
20
21 "golang.org/x/tools/internal/aliases"
22 "golang.org/x/tools/internal/gcimporter"
23 )
24
25 var isRace = false
26
27 func fileLine(fset *token.FileSet, obj types.Object) string {
28 posn := fset.Position(obj.Pos())
29 filename := filepath.Clean(strings.ReplaceAll(posn.Filename, "$GOROOT", runtime.GOROOT()))
30 return fmt.Sprintf("%s:%d", filename, posn.Line)
31 }
32
33 func equalType(x, y types.Type) error {
34 x = aliases.Unalias(x)
35 y = aliases.Unalias(y)
36 if reflect.TypeOf(x) != reflect.TypeOf(y) {
37 return fmt.Errorf("unequal kinds: %T vs %T", x, y)
38 }
39 switch x := x.(type) {
40 case *types.Interface:
41 y := y.(*types.Interface)
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 if x.NumMethods() != y.NumMethods() {
71 return fmt.Errorf("unequal methods: %d vs %d",
72 x.NumMethods(), y.NumMethods())
73 }
74 for i := 0; i < x.NumMethods(); i++ {
75 xm := x.Method(i)
76 ym := y.Method(i)
77 if xm.Name() != ym.Name() {
78 return fmt.Errorf("mismatched %dth method: %s vs %s", i, xm, ym)
79 }
80 if err := equalType(xm.Type(), ym.Type()); err != nil {
81 return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
82 }
83 }
84
85
86
87 case *types.Array:
88 y := y.(*types.Array)
89 if x.Len() != y.Len() {
90 return fmt.Errorf("unequal array lengths: %d vs %d", x.Len(), y.Len())
91 }
92 if err := equalType(x.Elem(), y.Elem()); err != nil {
93 return fmt.Errorf("array elements: %s", err)
94 }
95 case *types.Basic:
96 y := y.(*types.Basic)
97 if x.Kind() != y.Kind() {
98 return fmt.Errorf("unequal basic types: %s vs %s", x, y)
99 }
100 case *types.Chan:
101 y := y.(*types.Chan)
102 if x.Dir() != y.Dir() {
103 return fmt.Errorf("unequal channel directions: %d vs %d", x.Dir(), y.Dir())
104 }
105 if err := equalType(x.Elem(), y.Elem()); err != nil {
106 return fmt.Errorf("channel elements: %s", err)
107 }
108 case *types.Map:
109 y := y.(*types.Map)
110 if err := equalType(x.Key(), y.Key()); err != nil {
111 return fmt.Errorf("map keys: %s", err)
112 }
113 if err := equalType(x.Elem(), y.Elem()); err != nil {
114 return fmt.Errorf("map values: %s", err)
115 }
116 case *types.Named:
117 y := y.(*types.Named)
118 return cmpNamed(x, y)
119 case *types.Pointer:
120 y := y.(*types.Pointer)
121 if err := equalType(x.Elem(), y.Elem()); err != nil {
122 return fmt.Errorf("pointer elements: %s", err)
123 }
124 case *types.Signature:
125 y := y.(*types.Signature)
126 if err := equalType(x.Params(), y.Params()); err != nil {
127 return fmt.Errorf("parameters: %s", err)
128 }
129 if err := equalType(x.Results(), y.Results()); err != nil {
130 return fmt.Errorf("results: %s", err)
131 }
132 if x.Variadic() != y.Variadic() {
133 return fmt.Errorf("unequal variadicity: %t vs %t",
134 x.Variadic(), y.Variadic())
135 }
136 if (x.Recv() != nil) != (y.Recv() != nil) {
137 return fmt.Errorf("unequal receivers: %s vs %s", x.Recv(), y.Recv())
138 }
139 if x.Recv() != nil {
140
141
142
143
144
145
146
147 }
148 if err := equalTypeParams(x.TypeParams(), y.TypeParams()); err != nil {
149 return fmt.Errorf("type params: %s", err)
150 }
151 if err := equalTypeParams(x.RecvTypeParams(), y.RecvTypeParams()); err != nil {
152 return fmt.Errorf("recv type params: %s", err)
153 }
154 case *types.Slice:
155 y := y.(*types.Slice)
156 if err := equalType(x.Elem(), y.Elem()); err != nil {
157 return fmt.Errorf("slice elements: %s", err)
158 }
159 case *types.Struct:
160 y := y.(*types.Struct)
161 if x.NumFields() != y.NumFields() {
162 return fmt.Errorf("unequal struct fields: %d vs %d",
163 x.NumFields(), y.NumFields())
164 }
165 for i := 0; i < x.NumFields(); i++ {
166 xf := x.Field(i)
167 yf := y.Field(i)
168 if xf.Name() != yf.Name() {
169 return fmt.Errorf("mismatched fields: %s vs %s", xf, yf)
170 }
171 if err := equalType(xf.Type(), yf.Type()); err != nil {
172 return fmt.Errorf("struct field %s: %s", xf.Name(), err)
173 }
174 if x.Tag(i) != y.Tag(i) {
175 return fmt.Errorf("struct field %s has unequal tags: %q vs %q",
176 xf.Name(), x.Tag(i), y.Tag(i))
177 }
178 }
179 case *types.Tuple:
180 y := y.(*types.Tuple)
181 if x.Len() != y.Len() {
182 return fmt.Errorf("unequal tuple lengths: %d vs %d", x.Len(), y.Len())
183 }
184 for i := 0; i < x.Len(); i++ {
185 if err := equalType(x.At(i).Type(), y.At(i).Type()); err != nil {
186 return fmt.Errorf("tuple element %d: %s", i, err)
187 }
188 }
189 case *types.TypeParam:
190 y := y.(*types.TypeParam)
191 if x.String() != y.String() {
192 return fmt.Errorf("unequal named types: %s vs %s", x, y)
193 }
194
195
196
197
198
199 xc := makeExplicit(x.Constraint()).String()
200 yc := makeExplicit(y.Constraint()).String()
201 if xc != yc {
202 return fmt.Errorf("unequal constraints: %s vs %s", xc, yc)
203 }
204
205 default:
206 panic(fmt.Sprintf("unexpected %T type", x))
207 }
208 return nil
209 }
210
211
212
213 func cmpNamed(x, y *types.Named) error {
214 xOrig := x.Origin()
215 yOrig := y.Origin()
216 if xOrig.String() != yOrig.String() {
217 return fmt.Errorf("unequal named types: %s vs %s", x, y)
218 }
219 if err := equalTypeParams(x.TypeParams(), y.TypeParams()); err != nil {
220 return fmt.Errorf("type parameters: %s", err)
221 }
222 if err := equalTypeArgs(x.TypeArgs(), y.TypeArgs()); err != nil {
223 return fmt.Errorf("type arguments: %s", err)
224 }
225 if x.NumMethods() != y.NumMethods() {
226 return fmt.Errorf("unequal methods: %d vs %d",
227 x.NumMethods(), y.NumMethods())
228 }
229
230 var xms, yms []*types.Func
231 for i := 0; i < x.NumMethods(); i++ {
232 xms = append(xms, x.Method(i))
233 yms = append(yms, y.Method(i))
234 }
235 for _, ms := range [][]*types.Func{xms, yms} {
236 sort.Slice(ms, func(i, j int) bool {
237 return ms[i].Name() < ms[j].Name()
238 })
239 }
240 for i, xm := range xms {
241 ym := yms[i]
242 if xm.Name() != ym.Name() {
243 return fmt.Errorf("mismatched %dth method: %s vs %s", i, xm, ym)
244 }
245
246
247 if xm.String() != ym.String() {
248 return fmt.Errorf("unequal methods: %s vs %s", x, y)
249 }
250 }
251 return nil
252 }
253
254
255
256 func makeExplicit(typ types.Type) types.Type {
257 if iface, _ := typ.(*types.Interface); iface != nil && iface.IsImplicit() {
258 var methods []*types.Func
259 for i := 0; i < iface.NumExplicitMethods(); i++ {
260 methods = append(methods, iface.Method(i))
261 }
262 var embeddeds []types.Type
263 for i := 0; i < iface.NumEmbeddeds(); i++ {
264 embeddeds = append(embeddeds, iface.EmbeddedType(i))
265 }
266 return types.NewInterfaceType(methods, embeddeds)
267 }
268 return typ
269 }
270
271 func equalTypeArgs(x, y *types.TypeList) error {
272 if x.Len() != y.Len() {
273 return fmt.Errorf("unequal lengths: %d vs %d", x.Len(), y.Len())
274 }
275 for i := 0; i < x.Len(); i++ {
276 if err := equalType(x.At(i), y.At(i)); err != nil {
277 return fmt.Errorf("type %d: %s", i, err)
278 }
279 }
280 return nil
281 }
282
283 func equalTypeParams(x, y *types.TypeParamList) error {
284 if x.Len() != y.Len() {
285 return fmt.Errorf("unequal lengths: %d vs %d", x.Len(), y.Len())
286 }
287 for i := 0; i < x.Len(); i++ {
288 if err := equalType(x.At(i), y.At(i)); err != nil {
289 return fmt.Errorf("type parameter %d: %s", i, err)
290 }
291 }
292 return nil
293 }
294
295
296
297
298 func TestVeryLongFile(t *testing.T) {
299
300 longFile := "package foo" + strings.Repeat("\n", 123456) + "var X int"
301 fset1 := token.NewFileSet()
302 f, err := parser.ParseFile(fset1, "foo.go", longFile, 0)
303 if err != nil {
304 t.Fatal(err)
305 }
306 var conf types.Config
307 pkg, err := conf.Check("foo", fset1, []*ast.File{f}, nil)
308 if err != nil {
309 t.Fatal(err)
310 }
311
312
313 var out bytes.Buffer
314 if err := gcimporter.IExportData(&out, fset1, pkg); err != nil {
315 t.Fatal(err)
316 }
317 exportdata := out.Bytes()
318
319
320 imports := make(map[string]*types.Package)
321 fset2 := token.NewFileSet()
322 _, pkg2, err := gcimporter.IImportData(fset2, imports, exportdata, pkg.Path())
323 if err != nil {
324 t.Fatalf("BImportData(%s): %v", pkg.Path(), err)
325 }
326
327
328 posn1 := fset1.Position(pkg.Scope().Lookup("X").Pos())
329 posn2 := fset2.Position(pkg2.Scope().Lookup("X").Pos())
330 if want := "foo.go:1:1"; posn2.String() != want {
331 t.Errorf("X position = %s, want %s (orig was %s)",
332 posn2, want, posn1)
333 }
334 }
335
336 const src = `
337 package p
338
339 type (
340 T0 = int32
341 T1 = struct{}
342 T2 = struct{ T1 }
343 Invalid = foo // foo is undeclared
344 )
345 `
346
347 func checkPkg(t *testing.T, pkg *types.Package, label string) {
348 T1 := types.NewStruct(nil, nil)
349 T2 := types.NewStruct([]*types.Var{types.NewField(0, pkg, "T1", T1, true)}, nil)
350
351 for _, test := range []struct {
352 name string
353 typ types.Type
354 }{
355 {"T0", types.Typ[types.Int32]},
356 {"T1", T1},
357 {"T2", T2},
358 {"Invalid", types.Typ[types.Invalid]},
359 } {
360 obj := pkg.Scope().Lookup(test.name)
361 if obj == nil {
362 t.Errorf("%s: %s not found", label, test.name)
363 continue
364 }
365 tname, _ := obj.(*types.TypeName)
366 if tname == nil {
367 t.Errorf("%s: %v not a type name", label, obj)
368 continue
369 }
370 if !tname.IsAlias() {
371 t.Errorf("%s: %v: not marked as alias", label, tname)
372 continue
373 }
374 if got := tname.Type(); !types.Identical(got, test.typ) {
375 t.Errorf("%s: %v: got %v; want %v", label, tname, got, test.typ)
376 }
377 }
378 }
379
View as plain text