blob: 978c46e19325e67759f237cde5c649f8935d6881 [file] [log] [blame]
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +00001// Copyright 2016 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 gcimporter_test
6
7import (
Alan Donovanad74ff62023-05-11 18:07:57 -04008 "bytes"
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +00009 "fmt"
10 "go/ast"
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +000011 "go/parser"
12 "go/token"
13 "go/types"
Matthew Dempskyb79f76f2021-02-15 23:42:21 -080014 "path/filepath"
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +000015 "reflect"
16 "runtime"
Robert Findley81f084e2021-10-18 12:23:03 -040017 "sort"
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +000018 "strings"
19 "testing"
20
Alan Donovanaffa6032022-11-04 11:09:44 -040021 "golang.org/x/tools/internal/gcimporter"
Robert Findley91c880c2021-09-20 10:42:55 -040022 "golang.org/x/tools/internal/typeparams"
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +000023)
24
25var isRace = false
26
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +000027func fileLine(fset *token.FileSet, obj types.Object) string {
28 posn := fset.Position(obj.Pos())
Matthew Dempsky67e49ef2021-02-17 13:32:28 -080029 filename := filepath.Clean(strings.ReplaceAll(posn.Filename, "$GOROOT", runtime.GOROOT()))
30 return fmt.Sprintf("%s:%d", filename, posn.Line)
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +000031}
32
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +000033func equalType(x, y types.Type) error {
34 if reflect.TypeOf(x) != reflect.TypeOf(y) {
35 return fmt.Errorf("unequal kinds: %T vs %T", x, y)
36 }
37 switch x := x.(type) {
38 case *types.Interface:
39 y := y.(*types.Interface)
40 // TODO(gri): enable separate emission of Embedded interfaces
41 // and ExplicitMethods then use this logic.
42 // if x.NumEmbeddeds() != y.NumEmbeddeds() {
43 // return fmt.Errorf("unequal number of embedded interfaces: %d vs %d",
44 // x.NumEmbeddeds(), y.NumEmbeddeds())
45 // }
46 // for i := 0; i < x.NumEmbeddeds(); i++ {
47 // xi := x.Embedded(i)
48 // yi := y.Embedded(i)
49 // if xi.String() != yi.String() {
50 // return fmt.Errorf("mismatched %th embedded interface: %s vs %s",
51 // i, xi, yi)
52 // }
53 // }
54 // if x.NumExplicitMethods() != y.NumExplicitMethods() {
55 // return fmt.Errorf("unequal methods: %d vs %d",
56 // x.NumExplicitMethods(), y.NumExplicitMethods())
57 // }
58 // for i := 0; i < x.NumExplicitMethods(); i++ {
59 // xm := x.ExplicitMethod(i)
60 // ym := y.ExplicitMethod(i)
61 // if xm.Name() != ym.Name() {
62 // return fmt.Errorf("mismatched %th method: %s vs %s", i, xm, ym)
63 // }
64 // if err := equalType(xm.Type(), ym.Type()); err != nil {
65 // return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
66 // }
67 // }
68 if x.NumMethods() != y.NumMethods() {
69 return fmt.Errorf("unequal methods: %d vs %d",
70 x.NumMethods(), y.NumMethods())
71 }
72 for i := 0; i < x.NumMethods(); i++ {
73 xm := x.Method(i)
74 ym := y.Method(i)
75 if xm.Name() != ym.Name() {
76 return fmt.Errorf("mismatched %dth method: %s vs %s", i, xm, ym)
77 }
78 if err := equalType(xm.Type(), ym.Type()); err != nil {
79 return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
80 }
81 }
Robert Findleyce04ca32021-10-14 17:46:40 -040082 // Constraints are handled explicitly in the *TypeParam case below, so we
83 // don't yet need to consider embeddeds here.
84 // TODO(rfindley): consider the type set here.
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +000085 case *types.Array:
86 y := y.(*types.Array)
87 if x.Len() != y.Len() {
88 return fmt.Errorf("unequal array lengths: %d vs %d", x.Len(), y.Len())
89 }
90 if err := equalType(x.Elem(), y.Elem()); err != nil {
91 return fmt.Errorf("array elements: %s", err)
92 }
93 case *types.Basic:
94 y := y.(*types.Basic)
95 if x.Kind() != y.Kind() {
96 return fmt.Errorf("unequal basic types: %s vs %s", x, y)
97 }
98 case *types.Chan:
99 y := y.(*types.Chan)
100 if x.Dir() != y.Dir() {
101 return fmt.Errorf("unequal channel directions: %d vs %d", x.Dir(), y.Dir())
102 }
103 if err := equalType(x.Elem(), y.Elem()); err != nil {
104 return fmt.Errorf("channel elements: %s", err)
105 }
106 case *types.Map:
107 y := y.(*types.Map)
108 if err := equalType(x.Key(), y.Key()); err != nil {
109 return fmt.Errorf("map keys: %s", err)
110 }
111 if err := equalType(x.Elem(), y.Elem()); err != nil {
112 return fmt.Errorf("map values: %s", err)
113 }
114 case *types.Named:
115 y := y.(*types.Named)
Robert Findley91348082022-01-06 16:54:44 -0500116 return cmpNamed(x, y)
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +0000117 case *types.Pointer:
118 y := y.(*types.Pointer)
119 if err := equalType(x.Elem(), y.Elem()); err != nil {
120 return fmt.Errorf("pointer elements: %s", err)
121 }
122 case *types.Signature:
123 y := y.(*types.Signature)
124 if err := equalType(x.Params(), y.Params()); err != nil {
125 return fmt.Errorf("parameters: %s", err)
126 }
127 if err := equalType(x.Results(), y.Results()); err != nil {
128 return fmt.Errorf("results: %s", err)
129 }
130 if x.Variadic() != y.Variadic() {
131 return fmt.Errorf("unequal variadicity: %t vs %t",
132 x.Variadic(), y.Variadic())
133 }
134 if (x.Recv() != nil) != (y.Recv() != nil) {
135 return fmt.Errorf("unequal receivers: %s vs %s", x.Recv(), y.Recv())
136 }
137 if x.Recv() != nil {
138 // TODO(adonovan): fix: this assertion fires for interface methods.
139 // The type of the receiver of an interface method is a named type
140 // if the Package was loaded from export data, or an unnamed (interface)
141 // type if the Package was produced by type-checking ASTs.
142 // if err := equalType(x.Recv().Type(), y.Recv().Type()); err != nil {
143 // return fmt.Errorf("receiver: %s", err)
144 // }
145 }
Robert Findley91c880c2021-09-20 10:42:55 -0400146 if err := equalTypeParams(typeparams.ForSignature(x), typeparams.ForSignature(y)); err != nil {
147 return fmt.Errorf("type params: %s", err)
148 }
149 if err := equalTypeParams(typeparams.RecvTypeParams(x), typeparams.RecvTypeParams(y)); err != nil {
150 return fmt.Errorf("recv type params: %s", err)
151 }
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +0000152 case *types.Slice:
153 y := y.(*types.Slice)
154 if err := equalType(x.Elem(), y.Elem()); err != nil {
155 return fmt.Errorf("slice elements: %s", err)
156 }
157 case *types.Struct:
158 y := y.(*types.Struct)
159 if x.NumFields() != y.NumFields() {
160 return fmt.Errorf("unequal struct fields: %d vs %d",
161 x.NumFields(), y.NumFields())
162 }
163 for i := 0; i < x.NumFields(); i++ {
164 xf := x.Field(i)
165 yf := y.Field(i)
166 if xf.Name() != yf.Name() {
167 return fmt.Errorf("mismatched fields: %s vs %s", xf, yf)
168 }
169 if err := equalType(xf.Type(), yf.Type()); err != nil {
170 return fmt.Errorf("struct field %s: %s", xf.Name(), err)
171 }
172 if x.Tag(i) != y.Tag(i) {
173 return fmt.Errorf("struct field %s has unequal tags: %q vs %q",
174 xf.Name(), x.Tag(i), y.Tag(i))
175 }
176 }
177 case *types.Tuple:
178 y := y.(*types.Tuple)
179 if x.Len() != y.Len() {
180 return fmt.Errorf("unequal tuple lengths: %d vs %d", x.Len(), y.Len())
181 }
182 for i := 0; i < x.Len(); i++ {
183 if err := equalType(x.At(i).Type(), y.At(i).Type()); err != nil {
184 return fmt.Errorf("tuple element %d: %s", i, err)
185 }
186 }
Robert Findley91c880c2021-09-20 10:42:55 -0400187 case *typeparams.TypeParam:
188 y := y.(*typeparams.TypeParam)
Robert Findley25e1ac42022-01-20 11:28:07 -0500189 if x.String() != y.String() {
Robert Findley91c880c2021-09-20 10:42:55 -0400190 return fmt.Errorf("unequal named types: %s vs %s", x, y)
191 }
192 // For now, just compare constraints by type string to short-circuit
Robert Findleyce04ca32021-10-14 17:46:40 -0400193 // cycles. We have to make interfaces explicit as export data currently
194 // doesn't support marking interfaces as implicit.
195 // TODO(rfindley): remove makeExplicit once export data contains an
196 // implicit bit.
Robert Findley25e1ac42022-01-20 11:28:07 -0500197 xc := makeExplicit(x.Constraint()).String()
198 yc := makeExplicit(y.Constraint()).String()
Robert Findley91c880c2021-09-20 10:42:55 -0400199 if xc != yc {
200 return fmt.Errorf("unequal constraints: %s vs %s", xc, yc)
201 }
202
203 default:
204 panic(fmt.Sprintf("unexpected %T type", x))
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +0000205 }
206 return nil
207}
208
Robert Findley91348082022-01-06 16:54:44 -0500209// cmpNamed compares two named types x and y, returning an error for any
210// discrepancies. It does not compare their underlying types.
211func cmpNamed(x, y *types.Named) error {
212 xOrig := typeparams.NamedTypeOrigin(x)
213 yOrig := typeparams.NamedTypeOrigin(y)
Robert Findley25e1ac42022-01-20 11:28:07 -0500214 if xOrig.String() != yOrig.String() {
Robert Findley91348082022-01-06 16:54:44 -0500215 return fmt.Errorf("unequal named types: %s vs %s", x, y)
216 }
217 if err := equalTypeParams(typeparams.ForNamed(x), typeparams.ForNamed(y)); err != nil {
218 return fmt.Errorf("type parameters: %s", err)
219 }
220 if err := equalTypeArgs(typeparams.NamedTypeArgs(x), typeparams.NamedTypeArgs(y)); err != nil {
221 return fmt.Errorf("type arguments: %s", err)
222 }
223 if x.NumMethods() != y.NumMethods() {
224 return fmt.Errorf("unequal methods: %d vs %d",
225 x.NumMethods(), y.NumMethods())
226 }
227 // Unfortunately method sorting is not canonical, so sort before comparing.
228 var xms, yms []*types.Func
229 for i := 0; i < x.NumMethods(); i++ {
230 xms = append(xms, x.Method(i))
231 yms = append(yms, y.Method(i))
232 }
233 for _, ms := range [][]*types.Func{xms, yms} {
234 sort.Slice(ms, func(i, j int) bool {
235 return ms[i].Name() < ms[j].Name()
236 })
237 }
238 for i, xm := range xms {
239 ym := yms[i]
240 if xm.Name() != ym.Name() {
241 return fmt.Errorf("mismatched %dth method: %s vs %s", i, xm, ym)
242 }
243 // Calling equalType here leads to infinite recursion, so just compare
244 // strings.
Robert Findley25e1ac42022-01-20 11:28:07 -0500245 if xm.String() != ym.String() {
Robert Findley91348082022-01-06 16:54:44 -0500246 return fmt.Errorf("unequal methods: %s vs %s", x, y)
247 }
248 }
249 return nil
250}
251
Robert Findleyce04ca32021-10-14 17:46:40 -0400252// makeExplicit returns an explicit version of typ, if typ is an implicit
253// interface. Otherwise it returns typ unmodified.
254func makeExplicit(typ types.Type) types.Type {
255 if iface, _ := typ.(*types.Interface); iface != nil && typeparams.IsImplicit(iface) {
256 var methods []*types.Func
257 for i := 0; i < iface.NumExplicitMethods(); i++ {
258 methods = append(methods, iface.Method(i))
259 }
260 var embeddeds []types.Type
261 for i := 0; i < iface.NumEmbeddeds(); i++ {
262 embeddeds = append(embeddeds, iface.EmbeddedType(i))
263 }
264 return types.NewInterfaceType(methods, embeddeds)
265 }
266 return typ
267}
268
Robert Findley91c880c2021-09-20 10:42:55 -0400269func equalTypeArgs(x, y *typeparams.TypeList) error {
270 if x.Len() != y.Len() {
271 return fmt.Errorf("unequal lengths: %d vs %d", x.Len(), y.Len())
272 }
273 for i := 0; i < x.Len(); i++ {
274 if err := equalType(x.At(i), y.At(i)); err != nil {
275 return fmt.Errorf("type %d: %s", i, err)
276 }
277 }
278 return nil
279}
280
281func equalTypeParams(x, y *typeparams.TypeParamList) error {
282 if x.Len() != y.Len() {
283 return fmt.Errorf("unequal lengths: %d vs %d", x.Len(), y.Len())
284 }
285 for i := 0; i < x.Len(); i++ {
286 if err := equalType(x.At(i), y.At(i)); err != nil {
287 return fmt.Errorf("type parameter %d: %s", i, err)
288 }
289 }
290 return nil
291}
292
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +0000293// TestVeryLongFile tests the position of an import object declared in
294// a very long input file. Line numbers greater than maxlines are
295// reported as line 1, not garbage or token.NoPos.
296func TestVeryLongFile(t *testing.T) {
297 // parse and typecheck
298 longFile := "package foo" + strings.Repeat("\n", 123456) + "var X int"
299 fset1 := token.NewFileSet()
300 f, err := parser.ParseFile(fset1, "foo.go", longFile, 0)
301 if err != nil {
302 t.Fatal(err)
303 }
304 var conf types.Config
305 pkg, err := conf.Check("foo", fset1, []*ast.File{f}, nil)
306 if err != nil {
307 t.Fatal(err)
308 }
309
310 // export
Alan Donovanad74ff62023-05-11 18:07:57 -0400311 var out bytes.Buffer
312 if err := gcimporter.IExportData(&out, fset1, pkg); err != nil {
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +0000313 t.Fatal(err)
314 }
Alan Donovanad74ff62023-05-11 18:07:57 -0400315 exportdata := out.Bytes()
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +0000316
317 // import
318 imports := make(map[string]*types.Package)
319 fset2 := token.NewFileSet()
Alan Donovanad74ff62023-05-11 18:07:57 -0400320 _, pkg2, err := gcimporter.IImportData(fset2, imports, exportdata, pkg.Path())
Rebecca Stambler5c6ccfd2020-05-26 19:37:44 +0000321 if err != nil {
322 t.Fatalf("BImportData(%s): %v", pkg.Path(), err)
323 }
324
325 // compare
326 posn1 := fset1.Position(pkg.Scope().Lookup("X").Pos())
327 posn2 := fset2.Position(pkg2.Scope().Lookup("X").Pos())
328 if want := "foo.go:1:1"; posn2.String() != want {
329 t.Errorf("X position = %s, want %s (orig was %s)",
330 posn2, want, posn1)
331 }
332}
333
334const src = `
335package p
336
337type (
338 T0 = int32
339 T1 = struct{}
340 T2 = struct{ T1 }
341 Invalid = foo // foo is undeclared
342)
343`
344
345func checkPkg(t *testing.T, pkg *types.Package, label string) {
346 T1 := types.NewStruct(nil, nil)
347 T2 := types.NewStruct([]*types.Var{types.NewField(0, pkg, "T1", T1, true)}, nil)
348
349 for _, test := range []struct {
350 name string
351 typ types.Type
352 }{
353 {"T0", types.Typ[types.Int32]},
354 {"T1", T1},
355 {"T2", T2},
356 {"Invalid", types.Typ[types.Invalid]},
357 } {
358 obj := pkg.Scope().Lookup(test.name)
359 if obj == nil {
360 t.Errorf("%s: %s not found", label, test.name)
361 continue
362 }
363 tname, _ := obj.(*types.TypeName)
364 if tname == nil {
365 t.Errorf("%s: %v not a type name", label, obj)
366 continue
367 }
368 if !tname.IsAlias() {
369 t.Errorf("%s: %v: not marked as alias", label, tname)
370 continue
371 }
372 if got := tname.Type(); !types.Identical(got, test.typ) {
373 t.Errorf("%s: %v: got %v; want %v", label, tname, got, test.typ)
374 }
375 }
376}